musical_note/
lib.rs

1//! Represents general musical note and allow to convert it,
2//! currently only to MIDI byte and back.
3//!
4//! Basically, this crate just makes very good work on alteration and resolving
5//! enharmonical representation of midi notes, based on given key and scale.
6//!
7//! At least, it works better, that built-in Reaper and MuseScore algorithms.
8//!
9//! It uses Nederlanden language as string representation, as it is the most
10//! consistent and easy to use (and hard to mistake) note representation.
11//!
12//! Basically:
13//! - B is always B (there is no H)
14//! - is — Sharp
15//! - isis — DoubleSharp
16//! - es (for E and A — Es and As respectively) — Flat
17//! - eses — DoubleFlat
18//!
19//! #Examples
20//!
21//! ```
22//! use musical_note::{midi_to_note, Accidental, Key, NoteName, Octave, ResolvedNote, Scale};
23//! let as3 = ResolvedNote::new(NoteName::A, Accidental::Flat, Octave::new(5), 68);
24//! assert_eq!(ResolvedNote::from_str("as3").unwrap(), as3);
25//! assert_eq!(ResolvedNote::from_str("Aes3").unwrap(), as3);
26//! let a_moll = Key::new(NoteName::A, Accidental::White, Scale::Minor);
27//!
28//! // automatically unwraps
29//! let a_dur = Key::from((&"a".to_string(), Scale::Major));
30//!
31//! let d_moll = Key::from_str("d", Scale::Minor).unwrap();
32//! let d_dur = Key::from_str("d", Scale::Major).unwrap();
33//!
34//! let fis_moll = Key::from_str("fis", Scale::Minor).unwrap();
35//! let fis_dur = Key::from_str("fis", Scale::Major).unwrap();
36//!
37//! let ais_dur = Key::from_str("ais", Scale::Major).unwrap();
38//! let des_moll = Key::from_str("des", Scale::Minor).unwrap();
39//! // first test against idiot mistake:
40//! assert_eq!(
41//!     midi_to_note(60, Key::from_str("c", Scale::Major).unwrap(), None),
42//!     ResolvedNote::new(NoteName::C, Accidental::White, Octave::new(60 / 12), 60)
43//! );
44//!
45//! // // test on Bb2
46//! let midi = 58u8;
47//! assert_eq!(
48//!     midi_to_note(midi, d_moll, None),
49//!     ResolvedNote::from_str("bes2").unwrap()
50//! );
51//! // // test against major alteration (VI♭)
52//! assert_eq!(
53//!     midi_to_note(midi, d_dur, None),
54//!     ResolvedNote::from_str("bes2").unwrap()
55//! );
56//!
57//! // // test against minor alteration (II♭)
58//! assert_eq!(
59//!     midi_to_note(midi, a_moll, None),
60//!     ResolvedNote::from_str("bes2").unwrap()
61//! );
62//! assert_eq!(
63//!     midi_to_note(midi, a_dur, None),
64//!     ResolvedNote::from_str("ais2").unwrap()
65//! );
66//! assert_eq!(
67//!     midi_to_note(midi, a_dur, Some(Accidental::Flat)),
68//!     ResolvedNote::from_str("bes2").unwrap()
69//! );
70//!
71//! assert_eq!(
72//!     midi_to_note(midi, fis_moll, None),
73//!     ResolvedNote::from_str("ais2").unwrap()
74//! );
75//! assert_eq!(
76//!     midi_to_note(midi, fis_dur, None),
77//!     ResolvedNote::from_str("ais2").unwrap()
78//! );
79//!
80//! // // test against tonality doublesharps and doubleflats
81//! assert_eq!(
82//!     midi_to_note(62, ais_dur, None),
83//!     ResolvedNote::from_str("cisis3").unwrap()
84//! );
85//! assert_eq!(
86//!     midi_to_note(57, des_moll, None),
87//!     ResolvedNote::from_str("beses2").unwrap()
88//! );
89//!
90//! // // some church scales
91//! let c_phrygian = Key::from_str("c", Scale::Phrygian).unwrap();
92//! assert_eq!(
93//!     midi_to_note(61, c_phrygian, None),
94//!     ResolvedNote::from_str("des3").unwrap()
95//! );
96//! assert_eq!(
97//!     midi_to_note(63, c_phrygian, None),
98//!     ResolvedNote::from_str("es3").unwrap()
99//! );
100//!
101//! let es3 = midi_to_note(63,
102//!                         Key::new(
103//!                             NoteName::C,
104//!                             Accidental::Sharp,
105//!                             Scale::Major,
106//!                         ),
107//!                         Some(Accidental::Flat));
108//! assert_eq!(
109//!     format!(
110//!         "{}{}{}",
111//!         es3.note.to_string(),
112//!         es3.accidental.to_string_by_note(es3.note),
113//!         es3.octave.as_midi()
114//!     ),
115//!     "es3".to_string()
116//! );
117//!
118//! assert_eq!(
119//!     midi_to_note(65, c_phrygian, None),
120//!     ResolvedNote::from_str("f3").unwrap()
121//! );
122//!
123//! let fis_dorian = Key::from_str("fis", Scale::Dorian).unwrap();
124//! assert_eq!(
125//!     midi_to_note(60, fis_dorian, None),
126//!     ResolvedNote::from_str("bis2").unwrap()
127//! );
128//!
129//! // // test against known bugs
130//! assert_eq!(
131//!     midi_to_note(64, fis_dur, None),
132//!     ResolvedNote::from_str("e3").unwrap()
133//! );
134//! assert_eq!(
135//!     midi_to_note(62, fis_dur, None),
136//!     ResolvedNote::from_str("d3").unwrap()
137//! );
138//! ```
139//!
140
141use lazy_static::lazy_static;
142use once_cell::sync::OnceCell;
143use regex::Regex;
144use serde::{Deserialize, Serialize};
145use std::collections::HashMap;
146
147// static mut NOTES_MAP: NotesMap = Lazy::new(NotesMap::empty());
148static _NOTES_MAP: OnceCell<NotesMap> = OnceCell::new();
149
150// type Octave = u8;
151
152/// Represents note, that can be written in score.
153///
154/// It has:
155/// - precise note
156/// - enharmonically correct accidental
157/// - octave
158#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Serialize, Deserialize)]
159pub struct ResolvedNote {
160    pub note: NoteName,
161    pub accidental: Accidental,
162    pub octave: Octave,
163    pub midi: u8,
164}
165
166impl ResolvedNote {
167    pub fn new(note: NoteName, accidental: Accidental, octave: Octave, midi: u8) -> Self {
168        Self {
169            note,
170            accidental,
171            octave,
172            midi,
173        }
174    }
175    /// "cisis3" resolved to C## 3 (62 raw MIDI note) with raw Octave{octave:5}.
176    pub fn from_str(name: &str) -> Option<ResolvedNote> {
177        lazy_static! {
178            static ref RESOLVED_NOTE_RE: Regex = Regex::new(r"(\w*)(-?\d)").unwrap();
179        }
180        for cap in RESOLVED_NOTE_RE.captures_iter(name) {
181            let tonic = note_from_str(&cap[1])?;
182            let (note, accidental) = tonic;
183            let mut octave = Octave::from(&cap[2]);
184            if note == NoteName::B
185                && (accidental == Accidental::Sharp || accidental == Accidental::DoubleSharp)
186            {
187                octave = Octave::new(octave.raw() + 1);
188            } else if note == NoteName::C
189                && (accidental == Accidental::Flat || accidental == Accidental::DoubleFlat)
190            {
191                octave = Octave::new(octave.raw() - 1);
192            }
193            return Some(Self {
194                note,
195                accidental,
196                octave,
197                midi: octave.apply_to_midi_note(NotesMap::get().get_by_note(note, accidental)),
198            });
199        }
200        None
201    }
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Serialize, Deserialize)]
205pub struct Octave {
206    octave: u8,
207}
208impl Octave {
209    /// octave number should be raw: like
210    /// ```
211    /// # use musical_note::Octave;
212    /// let middle_c = 60u8;
213    /// let octave = Octave::new(middle_c/12);
214    /// ```
215    pub fn new(octave: u8) -> Self {
216        Self { octave }
217    }
218    pub fn raw(&self) -> u8 {
219        self.octave
220    }
221    /// can be negative. Here middle-c considered as C3.
222    /// So, function return would be in range of -2..9
223    pub fn as_midi(&self) -> i8 {
224        self.octave as i8 - 2
225    }
226    pub fn from_midi(midi: u8) -> Self {
227        Self { octave: midi / 12 }
228    }
229    /// Get note index `(C=0, D=2, D#=3, B=11)` and octave.
230    pub fn split_midi(midi: u8) -> (u8, Self) {
231        (midi % 12, Self::from_midi(midi))
232    }
233    /// Return raw midi byte.
234    pub fn apply_to_midi_note(&self, note_idx: u8) -> u8 {
235        self.octave * 12 + (note_idx % 12)
236    }
237}
238impl From<&str> for Octave {
239    fn from(value: &str) -> Self {
240        let nr = value.parse::<i8>().unwrap();
241        return Self {
242            octave: (nr + 2) as u8,
243        };
244    }
245}
246
247/// Main function to convert midi to ResolvedNote.
248///
249/// If accidental is given — it tries to return note with this accidental.
250/// Otherwise — it uses Key (and, mainly, Scale) rules for alteration.
251pub fn midi_to_note(midi: u8, key: Key, accidental: Option<Accidental>) -> ResolvedNote {
252    Note::from_midi(midi, accidental).resolve(key)
253}
254
255/// Not yet resolved Note, constructed by MIDI.
256#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Serialize, Deserialize)]
257pub struct Note {
258    midi: u8,
259    accidental: Option<Accidental>,
260    octave: Octave,
261}
262impl Note {
263    pub fn from_midi(midi: u8, accidental: Option<Accidental>) -> Self {
264        Self {
265            midi,
266            accidental,
267            octave: Octave::from_midi(midi),
268        }
269    }
270    pub fn resolve(&self, key: Key) -> ResolvedNote {
271        let (midi_note, octave) = Octave::split_midi(self.midi);
272        // let midi_note = self.midi % 12;
273        let notes_map = NotesMap::get();
274        let res = self.resolve_by_accident(&notes_map, midi_note, octave);
275        if res.is_some() {
276            return res.unwrap();
277        }
278        let scale = key.resolve_scale(&notes_map);
279
280        scale.resolve_pitch(notes_map, midi_note, octave)
281    }
282    fn resolve_by_accident(
283        &self,
284        notes_map: &NotesMap,
285        midi_note: u8,
286        octave: Octave,
287    ) -> Option<ResolvedNote> {
288        if self.accidental.is_some() {
289            let acc = self.accidental.as_ref().unwrap();
290            let note = notes_map.get_by_midi(&midi_note).get(&acc);
291            if note.is_some() {
292                return Some(ResolvedNote::new(
293                    note.unwrap().clone(),
294                    acc.clone(),
295                    octave,
296                    octave.apply_to_midi_note(midi_note),
297                ));
298            }
299        }
300        None
301    }
302    pub fn midi(&self) -> u8 {
303        self.midi
304    }
305    pub fn set_midi(&mut self, midi: u8) {
306        self.midi = midi;
307    }
308    pub fn accidental(&self) -> Option<Accidental> {
309        self.accidental.clone()
310    }
311    pub fn set_accidental(&mut self, accidental: Option<Accidental>) {
312        self.accidental = accidental;
313    }
314}
315impl From<u8> for Note {
316    /// from midi, but without boilerplate.
317    fn from(midi: u8) -> Self {
318        Self::from_midi(midi, None)
319    }
320}
321impl From<ResolvedNote> for Note {
322    fn from(value: ResolvedNote) -> Self {
323        Self {
324            midi: value.midi,
325            accidental: Some(value.accidental),
326            octave: value.octave,
327        }
328    }
329}
330
331#[derive(Debug, PartialEq, PartialOrd, Hash, Eq, Clone, Copy, Serialize, Deserialize)]
332pub enum Accidental {
333    White,
334    Sharp,
335    DoubleSharp,
336    Flat,
337    DoubleFlat,
338}
339impl Accidental {
340    pub fn to_string_by_note(&self, note: NoteName) -> String {
341        match self {
342            Self::DoubleFlat | Self::Flat => {
343                if note.need_trunk() {
344                    let mut s = self.to_string();
345                    s.remove(0);
346                    s
347                } else {
348                    self.to_string()
349                }
350            }
351            _ => self.to_string(),
352        }
353    }
354    /// "es" is Flat, "is" is Sharp, "white" is White.
355    pub fn from_str(name: &str) -> Option<Self> {
356        match name.to_lowercase().as_str() {
357            "s" | "es" => Some(Self::Flat),
358            "eses" => Some(Self::DoubleFlat),
359            "is" => Some(Self::Sharp),
360            "isis" => Some(Self::DoubleSharp),
361            "white" => Some(Self::White),
362            _ => None,
363        }
364    }
365}
366impl Default for Accidental {
367    fn default() -> Self {
368        Self::White
369    }
370}
371impl ToString for Accidental {
372    fn to_string(&self) -> String {
373        match self {
374            Accidental::White => "white".to_string(),
375            Accidental::Sharp => "is".to_string(),
376            Accidental::DoubleSharp => "isis".to_string(),
377            Accidental::Flat => "es".to_string(),
378            Accidental::DoubleFlat => "eses".to_string(),
379        }
380    }
381}
382
383#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Serialize, Deserialize)]
384pub struct Key {
385    pub tonic: (NoteName, Accidental),
386    pub scale: Scale,
387}
388impl Key {
389    pub fn new(note: NoteName, accidental: Accidental, scale: Scale) -> Self {
390        Self {
391            tonic: (note, accidental),
392            scale,
393        }
394    }
395    /// `[root_midi, root_index]`
396    ///
397    /// `root_midi` is `u8` in rage 0..12, based on note name and accidental.
398    /// `root_index` is `u8` in rage 0..7, based only on note name.
399    pub fn get_root(&self) -> [u8; 2] {
400        let (tonic_note, tonic_acc) = self.tonic.clone();
401        let root_midi = NotesMap::get().get_by_note(tonic_note.clone(), tonic_acc);
402        let root_idx = tonic_note.index();
403        [root_midi, root_idx]
404    }
405    /// Returns concrete Scale representation (grades) for the key.
406    fn resolve_scale(&self, notes_map: &NotesMap) -> ResolvedScale {
407        self.scale.resolve_for_key(notes_map, self)
408    }
409    /// "as" or "eis" — without octave.
410    pub fn from_str(name: &str, scale: Scale) -> Option<Self> {
411        Some(Self {
412            tonic: note_from_str(name)?,
413            scale,
414        })
415    }
416}
417impl Default for Key {
418    fn default() -> Self {
419        Self::new(Default::default(), Default::default(), Default::default())
420    }
421}
422impl From<(&String, Scale)> for Key {
423    fn from(value: (&String, Scale)) -> Self {
424        let (name, scale) = value;
425        Self {
426            tonic: note_from_str(name).unwrap(),
427            scale,
428        }
429    }
430}
431
432fn note_from_str(name: &str) -> Option<(NoteName, Accidental)> {
433    let note = &name[0..1];
434    match NoteName::from_str(note) {
435        Some(note) => {
436            if name.len() <= 1 {
437                return Some((note, Accidental::White));
438            } else {
439                match Accidental::from_str(&name[1..]) {
440                    Some(acc) => Some((note, acc)),
441                    None => None,
442                }
443            }
444        }
445        None => None,
446    }
447}
448
449#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, PartialOrd, Serialize, Deserialize)]
450pub enum NoteName {
451    C,
452    D,
453    E,
454    F,
455    G,
456    A,
457    B,
458}
459impl NoteName {
460    fn need_trunk(&self) -> bool {
461        match self {
462            Self::A | Self::E => true,
463            _ => false,
464        }
465    }
466    fn index(self) -> u8 {
467        match self {
468            Self::C => 0,
469            Self::D => 1,
470            Self::E => 2,
471            Self::F => 3,
472            Self::G => 4,
473            Self::A => 5,
474            Self::B => 6,
475        }
476    }
477    pub fn from_str(name: &str) -> Option<Self> {
478        match name.to_uppercase().as_str() {
479            "C" => Some(Self::C),
480            "D" => Some(Self::D),
481            "E" => Some(Self::E),
482            "F" => Some(Self::F),
483            "G" => Some(Self::G),
484            "A" => Some(Self::A),
485            "B" => Some(Self::B),
486            _ => None,
487        }
488    }
489    fn by_index(mut index: u8) -> Self {
490        let names = [
491            Self::C,
492            Self::D,
493            Self::E,
494            Self::F,
495            Self::G,
496            Self::A,
497            Self::B,
498        ];
499        if index > 6 {
500            index -= 7;
501        }
502        names[index as usize].clone()
503    }
504}
505impl Default for NoteName {
506    fn default() -> Self {
507        Self::C
508    }
509}
510impl ToString for NoteName {
511    fn to_string(&self) -> String {
512        match self {
513            Self::C => String::from("c"),
514            Self::D => String::from("d"),
515            Self::E => String::from("e"),
516            Self::F => String::from("f"),
517            Self::G => String::from("g"),
518            Self::A => String::from("a"),
519            Self::B => String::from("b"),
520        }
521    }
522}
523
524/// Concrete Scale representation, based on the given root note.
525///
526/// Used to estimate alteration for notes, can be alterated by scale rules.
527#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
528pub struct ResolvedScale {
529    pub key: Key,
530    /// 0..12 midi bytes, representing every grade of the scale.
531    /// E.g. for C-Dur it would be: `[0, 2, 4, 5, 7, 9, 11]`,
532    /// and for A-moll: `[9, 11, 0, 2, 4, 5, 7]`
533    pub degree_midi: [u8; 7],
534    /// the same, but, holding Tuples of note names and accidentals.
535    pub degree_notes: [(NoteName, Accidental); 7],
536    /// Collects accidentals, used for degrees to estimate, which
537    /// alteration of non-degree notes is preferable.
538    /// TODO: think on using this as weight.
539    pub used_accidentals: Vec<Accidental>,
540}
541impl ResolvedScale {
542    pub fn resolve_pitch(
543        &self,
544        notes_map: &NotesMap,
545        midi_note: u8,
546        octave: Octave,
547    ) -> ResolvedNote {
548        let note: (NoteName, Accidental);
549        let key_root_midi = self.key.get_root()[0];
550        match self.degree_midi.binary_search(&midi_note) {
551            Ok(note_index) => {
552                note = self.degree_notes[note_index];
553                // println!("found note from scale {:?} at index {:?}", note, note_index);
554            }
555            Err(_err) => {
556                if midi_note == key_root_midi + 1 && self.key.scale == Scale::Minor {
557                    // println!("that is minor II♭");
558                    note = notes_map
559                        .resolve_note_for_midi(self.degree_notes[1 as usize], midi_note)
560                        .unwrap();
561                } else if midi_note == (key_root_midi + 8) % 12 && self.key.scale == Scale::Major {
562                    // println!("that is major VI♭");
563                    let candidate_note = self.degree_notes[5 as usize];
564                    note = notes_map
565                        .resolve_note_for_midi(candidate_note, midi_note)
566                        .unwrap();
567                } else {
568                    // println!("just search for anything for midi: {:?}", midi_note);
569                    note = notes_map
570                        .resolve_enharmonic(self.used_accidentals.last().copied(), midi_note);
571                }
572            }
573        }
574        ResolvedNote::new(note.0, note.1, octave, octave.apply_to_midi_note(midi_note))
575    }
576}
577
578#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Serialize, Deserialize)]
579pub enum Scale {
580    Major,
581    Minor,
582    Dorian,
583    Phrygian,
584    Lydian,
585    Mixolidyan,
586    Locrian,
587}
588impl Scale {
589    /// Get interval structure of the scale.
590    pub fn structure(&self) -> [u8; 6] {
591        match self {
592            Self::Major => [2, 2, 1, 2, 2, 2],
593            Self::Minor => [2, 1, 2, 2, 1, 2],
594            Self::Dorian => [2, 1, 2, 2, 2, 1],
595            Self::Phrygian => [1, 2, 2, 2, 1, 2],
596            Self::Lydian => [2, 2, 2, 1, 2, 2],
597            Self::Mixolidyan => [2, 2, 1, 2, 2, 1],
598            Self::Locrian => [1, 2, 2, 1, 2, 2],
599        }
600    }
601    /// Make concrete representation, based on given Key.
602    fn resolve_for_key(&self, notes_map: &NotesMap, key: &Key) -> ResolvedScale {
603        let root = key.get_root();
604        let (root_midi, _root_idx) = (root[0], root[1]);
605        let mut degree_midi = [root_midi; 7];
606        let mut degree_notes = [key.tonic; 7];
607        let mut used_accidentals = Vec::with_capacity(10);
608        for (idx, interval) in self.structure().iter().enumerate() {
609            let mut next_midi = degree_midi[idx];
610            next_midi += interval;
611            if next_midi > 11 {
612                next_midi %= 12;
613            }
614            let index = degree_notes[idx].0.index() + 1;
615            let candidate_note = NoteName::by_index(index);
616            // println!(
617            //     "candidate_note: {:?}, next_midi: {:?}",
618            //     candidate_note, next_midi
619            // );
620            let next_note = notes_map
621                .resolve_note_for_midi((candidate_note, Accidental::White), next_midi)
622                .unwrap();
623            degree_notes[idx + 1] = next_note;
624            degree_midi[idx + 1] = next_midi;
625            if next_note.1 != Accidental::White {
626                used_accidentals.push(next_note.1);
627            }
628            // println!(
629            //     "next_note: {:?}, degree_notes: {:?}, degree_midi: {:?}, used_accidentals: {:?}",
630            //     next_note, degree_notes, degree_midi, used_accidentals
631            // )
632        }
633        ResolvedScale {
634            key: *key,
635            degree_midi,
636            degree_notes,
637            used_accidentals,
638        }
639    }
640}
641impl Default for Scale {
642    fn default() -> Self {
643        Self::Major
644    }
645}
646impl ToString for Scale {
647    fn to_string(&self) -> String {
648        match self {
649            Self::Major => "major".to_string(),
650            Self::Minor => "minor".to_string(),
651            Self::Dorian => "dorian".to_string(),
652            Self::Phrygian => "phrygian".to_string(),
653            Self::Lydian => "lydian".to_string(),
654            Self::Mixolidyan => "mixolidyan".to_string(),
655            Self::Locrian => "locrian".to_string(),
656        }
657    }
658}
659
660/// Effectively maps notes to midi and backwards.
661///
662/// Example
663/// ```
664/// use musical_note::{NotesMap, NoteName, Accidental};
665/// let map = NotesMap::get();
666/// assert_eq!(map.get_by_note(NoteName::F, Accidental::DoubleFlat), 3);
667/// ```
668#[derive(Debug)]
669pub struct NotesMap {
670    note_index: HashMap<(NoteName, Accidental), u8>,
671    midi_index: HashMap<u8, HashMap<Accidental, NoteName>>,
672}
673impl NotesMap {
674    pub fn get() -> &'static NotesMap {
675        if _NOTES_MAP.get().is_none() {
676            _NOTES_MAP.set(Self::new()).unwrap();
677        }
678        _NOTES_MAP.get().expect("logger is not initialized")
679    }
680    // fn empty() -> Self {}
681    fn new() -> Self {
682        let mut obj = Self {
683            note_index: HashMap::with_capacity(35),
684            midi_index: HashMap::with_capacity(35),
685        };
686        obj.add(NoteName::C, Accidental::White, 0);
687        obj.add(NoteName::B, Accidental::Sharp, 0);
688        obj.add(NoteName::D, Accidental::DoubleFlat, 0);
689        //
690        obj.add(NoteName::C, Accidental::Sharp, 1);
691        obj.add(NoteName::B, Accidental::DoubleSharp, 1);
692        obj.add(NoteName::D, Accidental::Flat, 1);
693        //
694        obj.add(NoteName::D, Accidental::White, 2);
695        obj.add(NoteName::C, Accidental::DoubleSharp, 2);
696        obj.add(NoteName::E, Accidental::DoubleFlat, 2);
697        //
698        obj.add(NoteName::D, Accidental::Sharp, 3);
699        obj.add(NoteName::E, Accidental::Flat, 3);
700        obj.add(NoteName::F, Accidental::DoubleFlat, 3);
701        //
702        obj.add(NoteName::E, Accidental::White, 4);
703        obj.add(NoteName::D, Accidental::DoubleSharp, 4);
704        obj.add(NoteName::F, Accidental::Flat, 4);
705        //
706        obj.add(NoteName::F, Accidental::White, 5);
707        obj.add(NoteName::E, Accidental::Sharp, 5);
708        obj.add(NoteName::G, Accidental::DoubleFlat, 5);
709        //
710        obj.add(NoteName::F, Accidental::Sharp, 6);
711        obj.add(NoteName::E, Accidental::DoubleSharp, 6);
712        obj.add(NoteName::G, Accidental::Flat, 6);
713        //
714        obj.add(NoteName::G, Accidental::White, 7);
715        obj.add(NoteName::F, Accidental::DoubleSharp, 7);
716        obj.add(NoteName::A, Accidental::DoubleFlat, 7);
717        //
718        obj.add(NoteName::G, Accidental::Sharp, 8);
719        obj.add(NoteName::A, Accidental::Flat, 8);
720        //
721        obj.add(NoteName::A, Accidental::White, 9);
722        obj.add(NoteName::G, Accidental::DoubleSharp, 9);
723        obj.add(NoteName::B, Accidental::DoubleFlat, 9);
724        //
725        obj.add(NoteName::A, Accidental::Sharp, 10);
726        obj.add(NoteName::B, Accidental::Flat, 10);
727        obj.add(NoteName::C, Accidental::DoubleFlat, 10);
728        //
729        obj.add(NoteName::B, Accidental::White, 11);
730        obj.add(NoteName::A, Accidental::DoubleSharp, 11);
731        obj.add(NoteName::C, Accidental::Flat, 11);
732        //
733        obj
734    }
735    fn add(&mut self, note: NoteName, acc: Accidental, midi: u8) {
736        self.note_index.insert((note.clone(), acc.clone()), midi);
737        match self.midi_index.get_mut(&midi) {
738            Some(map) => {
739                map.insert(acc, note);
740            }
741            None => {
742                let mut map = HashMap::with_capacity(4);
743                map.insert(acc, note);
744                self.midi_index.insert(midi, map);
745            }
746        }
747    }
748    pub fn get_by_midi(&self, midi: &u8) -> &HashMap<Accidental, NoteName> {
749        let midi = midi % 12;
750        self.midi_index.get(&midi).unwrap()
751    }
752    pub fn get_by_note(&self, note: NoteName, acc: Accidental) -> u8 {
753        *self.note_index.get(&(note, acc)).unwrap()
754    }
755    /// Accidental of "root" is not counted.
756    ///
757    /// For the given note and MIDI-byte finds only possible enharmonic variant,
758    /// or returns None.
759    ///
760    /// For example: with the given `NoteName::C` and midi `2`, only possible
761    /// enharmonic variant is `(NoteName::C, Accidental::DoubleSharp)`, which
762    /// will be returned.
763    ///
764    /// If None is returned — something goes very wrong and it is better to panic.
765    pub fn resolve_note_for_midi(
766        &self,
767        note: (NoteName, Accidental),
768        midi: u8,
769    ) -> Option<(NoteName, Accidental)> {
770        let notes = self.get_by_midi(&midi);
771        for (acc, candidate) in notes.iter() {
772            if candidate == &note.0 {
773                return Some((*candidate, *acc));
774            }
775        }
776        None
777    }
778    pub fn resolve_enharmonic(
779        &self,
780        accidental: Option<Accidental>,
781        midi: u8,
782    ) -> (NoteName, Accidental) {
783        let notes = self.get_by_midi(&(midi % 12));
784        // println!(
785        //     "resolve_enharmonic:(accidental: {:?}, midi: {:?}, notes: {:?})",
786        //     accidental, midi, notes
787        // );
788        let acc_final: Accidental;
789        match accidental {
790            Some(acc) => {
791                if notes.contains_key(&acc) {
792                    acc_final = acc;
793                } else {
794                    acc_final = Accidental::White;
795                }
796            }
797            None => {
798                acc_final = Accidental::White;
799            }
800        }
801        (notes[&acc_final], acc_final)
802    }
803}