Skip to main content

klib/core/
note.rs

1//! A module for working with notes.
2//!
3//! A note is a named pitch with an octave.
4
5#![allow(dead_code)]
6#![allow(non_upper_case_globals)]
7
8use std::{
9    cmp::Ordering,
10    fmt::{self, Display, Formatter},
11    ops::{Add, AddAssign, Sub},
12};
13
14use crate::core::{
15    base::{HasName, HasStaticName, Parsable, Res},
16    chord::Chord,
17    interval::{HasEnharmonicDistance, Interval, PRIMARY_HARMONIC_SERIES},
18    named_pitch::{HasNamedPitch, NamedPitch},
19    octave::{HasOctave, Octave, ALL_OCTAVES},
20    parser::{note_str_to_note, octave_str_to_octave, ChordParser, Rule},
21    pitch::{HasBaseFrequency, HasFrequency, HasPitch, Pitch, ALL_PITCHES},
22};
23use paste::paste;
24use pest::Parser;
25use std::sync::LazyLock;
26
27use super::interval::ALL_INTERVALS;
28
29#[cfg(feature = "serde")]
30use serde::{Deserialize, Serialize};
31
32// Macros.
33
34/// Defines a note from a [`NamedPitch`].
35macro_rules! define_note {
36    ( $name:ident, $named_pitch:expr, $octave_num:ident, $octave:expr) => {
37        paste! {
38            /// The note [<$name$octave_num>].
39            pub const [<$name$octave_num>]: Note = Note {
40                named_pitch: $named_pitch,
41                octave: $octave,
42            };
43        }
44    };
45}
46
47/// Defines an octave of notes.
48macro_rules! define_octave {
49    ($octave_num:ident, $octave:expr) => {
50        define_note!(FTripleFlat, NamedPitch::FTripleFlat, $octave_num, $octave);
51        define_note!(CTripleFlat, NamedPitch::CTripleFlat, $octave_num, $octave);
52        define_note!(GTripleFlat, NamedPitch::GTripleFlat, $octave_num, $octave);
53        define_note!(DTripleFlat, NamedPitch::DTripleFlat, $octave_num, $octave);
54        define_note!(ATripleFlat, NamedPitch::ATripleFlat, $octave_num, $octave);
55        define_note!(ETripleFlat, NamedPitch::ETripleFlat, $octave_num, $octave);
56        define_note!(BTripleFlat, NamedPitch::BTripleFlat, $octave_num, $octave);
57
58        define_note!(FDoubleFlat, NamedPitch::FDoubleFlat, $octave_num, $octave);
59        define_note!(CDoubleFlat, NamedPitch::CDoubleFlat, $octave_num, $octave);
60        define_note!(GDoubleFlat, NamedPitch::GDoubleFlat, $octave_num, $octave);
61        define_note!(DDoubleFlat, NamedPitch::DDoubleFlat, $octave_num, $octave);
62        define_note!(ADoubleFlat, NamedPitch::ADoubleFlat, $octave_num, $octave);
63        define_note!(EDoubleFlat, NamedPitch::EDoubleFlat, $octave_num, $octave);
64        define_note!(BDoubleFlat, NamedPitch::BDoubleFlat, $octave_num, $octave);
65
66        define_note!(FFlat, NamedPitch::FFlat, $octave_num, $octave);
67        define_note!(CFlat, NamedPitch::CFlat, $octave_num, $octave);
68        define_note!(GFlat, NamedPitch::GFlat, $octave_num, $octave);
69        define_note!(DFlat, NamedPitch::DFlat, $octave_num, $octave);
70        define_note!(AFlat, NamedPitch::AFlat, $octave_num, $octave);
71        define_note!(EFlat, NamedPitch::EFlat, $octave_num, $octave);
72        define_note!(BFlat, NamedPitch::BFlat, $octave_num, $octave);
73
74        define_note!(F, NamedPitch::F, $octave_num, $octave);
75        define_note!(C, NamedPitch::C, $octave_num, $octave);
76        define_note!(G, NamedPitch::G, $octave_num, $octave);
77        define_note!(D, NamedPitch::D, $octave_num, $octave);
78        define_note!(A, NamedPitch::A, $octave_num, $octave);
79        define_note!(E, NamedPitch::E, $octave_num, $octave);
80        define_note!(B, NamedPitch::B, $octave_num, $octave);
81
82        define_note!(FSharp, NamedPitch::FSharp, $octave_num, $octave);
83        define_note!(CSharp, NamedPitch::CSharp, $octave_num, $octave);
84        define_note!(GSharp, NamedPitch::GSharp, $octave_num, $octave);
85        define_note!(DSharp, NamedPitch::DSharp, $octave_num, $octave);
86        define_note!(ASharp, NamedPitch::ASharp, $octave_num, $octave);
87        define_note!(ESharp, NamedPitch::ESharp, $octave_num, $octave);
88        define_note!(BSharp, NamedPitch::BSharp, $octave_num, $octave);
89
90        define_note!(FDoubleSharp, NamedPitch::FDoubleSharp, $octave_num, $octave);
91        define_note!(CDoubleSharp, NamedPitch::CDoubleSharp, $octave_num, $octave);
92        define_note!(GDoubleSharp, NamedPitch::GDoubleSharp, $octave_num, $octave);
93        define_note!(DDoubleSharp, NamedPitch::DDoubleSharp, $octave_num, $octave);
94        define_note!(ADoubleSharp, NamedPitch::ADoubleSharp, $octave_num, $octave);
95        define_note!(EDoubleSharp, NamedPitch::EDoubleSharp, $octave_num, $octave);
96        define_note!(BDoubleSharp, NamedPitch::BDoubleSharp, $octave_num, $octave);
97
98        define_note!(FTripleSharp, NamedPitch::FTripleSharp, $octave_num, $octave);
99        define_note!(CTripleSharp, NamedPitch::CTripleSharp, $octave_num, $octave);
100        define_note!(GTripleSharp, NamedPitch::GTripleSharp, $octave_num, $octave);
101        define_note!(DTripleSharp, NamedPitch::DTripleSharp, $octave_num, $octave);
102        define_note!(ATripleSharp, NamedPitch::ATripleSharp, $octave_num, $octave);
103        define_note!(ETripleSharp, NamedPitch::ETripleSharp, $octave_num, $octave);
104        define_note!(BTripleSharp, NamedPitch::BTripleSharp, $octave_num, $octave);
105    };
106}
107
108// Traits.
109
110/// A trait for types that can be converted into a [`Chord`].
111pub trait IntoChord {
112    /// Converts this type into a [`Chord`] (usually a [`Note`]).
113    fn into_chord(self) -> Chord;
114}
115
116/// A trait which allows for a [`Note`] to be recreated with different properties.
117pub trait NoteRecreator {
118    /// Recreates this [`Note`] with the given [`NamedPitch`].
119    fn with_named_pitch(self, named_pitch: NamedPitch) -> Self;
120    /// Recreates this [`Note`] with the given [`Octave`].
121    fn with_octave(self, octave: Octave) -> Self;
122}
123
124/// A trait which allows for obtaining the primary harmonic series of the note.
125pub trait HasPrimaryHarmonicSeries {
126    /// Returns the primary harmonic series of the note.
127    fn primary_harmonic_series(self) -> Vec<Note>;
128}
129
130/// A trait which allows for encoding the note as a [`u128`] ID.
131pub trait HasNoteId {
132    /// Returns the ID of the note.
133    fn id(self) -> u128;
134
135    /// Returns the position of the 1 for the ID of the note.
136    fn id_index(self) -> u8;
137
138    /// Returns the note from the given ID.
139    fn from_id(id: u128) -> Res<Self>
140    where
141        Self: Sized;
142
143    /// Returns the ID mask for the given notes.
144    fn id_mask(notes: &[Self]) -> u128
145    where
146        Self: Sized;
147
148    /// Returns the notes from the given ID mask.
149    fn from_id_mask(id_mask: u128) -> Res<Vec<Self>>
150    where
151        Self: Sized;
152}
153
154/// A trait which allows for converting a note to the same octave, but using universal [`Pitch`]es.
155///
156/// Essentially, this would convert an F#4 into a Gb4, since [`Pitch`]es prefer the flats.
157pub trait ToUniversal {
158    /// Converts this note to a universal pitch.
159    fn to_universal(self) -> Self;
160}
161
162// Struct.
163
164/// A note type.
165///
166/// This is a named pitch with an octave.  This type allows for correctly attributing octave changes
167/// across an interval from one [`Note`] to another.
168#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
169#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
170pub struct Note {
171    /// The octave of the note.
172    octave: Octave,
173    /// The named pitch of the note.
174    named_pitch: NamedPitch,
175}
176
177impl Display for Note {
178    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
179        write!(f, "{}", self.name())
180    }
181}
182
183// Impls.
184
185impl Note {
186    /// Creates a new [`Note`] from the given [`NamedPitch`] and [`Octave`].
187    pub fn new(pitch: NamedPitch, octave: Octave) -> Self {
188        Self { named_pitch: pitch, octave }
189    }
190
191    /// Attempts to create a [`Note`] from a MIDI note number.
192    pub fn try_from_midi(midi: u8) -> Res<Self> {
193        let pitch_index = midi % 12;
194        let octave_component = midi / 12;
195
196        // MIDI note numbers are defined such that note 60 is C4, which corresponds to octave index 4.
197        // Therefore, subtract 1 to map to the [`Octave`] enum where 0 == C0.
198        let octave_value = (octave_component as i16) - 1;
199
200        if octave_value < 0 {
201            return Err(anyhow::Error::msg(format!("MIDI note {midi} is below the supported octave range.")));
202        }
203
204        let octave = Octave::try_from(octave_value as u8).map_err(anyhow::Error::msg)?;
205        let pitch = Pitch::try_from(pitch_index).map_err(anyhow::Error::msg)?;
206
207        Ok(Self::new(NamedPitch::from(pitch), octave))
208    }
209}
210
211impl Note {
212    /// Attempts to use the default microphone to listen to audio for the specified time
213    /// to identify the notes in the recorded audio.
214    ///
215    /// Currently, this does not work with WASM.
216    #[coverage(off)]
217    #[cfg(feature = "analyze_mic")]
218    pub async fn try_from_mic(length_in_seconds: u8) -> Res<Vec<Note>> {
219        use crate::analyze::mic::get_notes_from_microphone;
220
221        get_notes_from_microphone(length_in_seconds).await
222    }
223
224    /// Attempts to use the provided to identify the notes in the audio data.
225    #[cfg(feature = "analyze_base")]
226    pub fn try_from_audio(data: &[f32], length_in_seconds: u8) -> Res<Vec<Note>> {
227        use crate::analyze::base::get_notes_from_audio_data;
228
229        get_notes_from_audio_data(data, length_in_seconds)
230    }
231
232    /// Attempts to use the default microphone to listen to audio for the specified time
233    /// to identify the notes in the recorded audio using ML.
234    ///
235    /// Currently, this does not work with WASM.
236    #[coverage(off)]
237    #[cfg(all(feature = "ml_infer", feature = "analyze_mic"))]
238    pub async fn try_from_mic_ml(length_in_seconds: u8) -> Res<Vec<Self>> {
239        use crate::{analyze::mic::get_audio_data_from_microphone, ml::infer::infer};
240
241        let audio_data = get_audio_data_from_microphone(length_in_seconds).await?;
242        let result = infer(&audio_data, length_in_seconds)?;
243
244        // Convert pitches to notes at octave 4.
245        Ok(result.pitches.iter().map(|&pitch| Note::new(NamedPitch::from(pitch), Octave::Four)).collect())
246    }
247
248    /// Attempts to use the provided audio data to identify the notes using ML.
249    #[cfg(all(feature = "ml_infer", feature = "analyze_base"))]
250    pub fn try_from_audio_ml(data: &[f32], length_in_seconds: u8) -> Res<Vec<Self>> {
251        use crate::ml::infer::infer;
252
253        let result = infer(data, length_in_seconds)?;
254
255        // Convert pitches to notes at octave 4.
256        Ok(result.pitches.iter().map(|&pitch| Note::new(NamedPitch::from(pitch), Octave::Four)).collect())
257    }
258}
259
260impl HasPitch for Note {
261    fn pitch(&self) -> Pitch {
262        self.named_pitch.pitch()
263    }
264}
265
266impl HasNamedPitch for Note {
267    fn named_pitch(&self) -> NamedPitch {
268        self.named_pitch
269    }
270}
271
272impl HasOctave for Note {
273    fn octave(&self) -> Octave {
274        self.octave
275    }
276}
277
278impl HasStaticName for Note {
279    fn static_name(&self) -> &'static str {
280        self.named_pitch.static_name()
281    }
282}
283
284impl HasName for Note {
285    fn name(&self) -> String {
286        format!("{}{}", self.named_pitch.static_name(), self.octave.static_name())
287    }
288}
289
290impl HasFrequency for Note {
291    fn frequency(&self) -> f32 {
292        let mut octave = self.octave();
293        let base_frequency = self.pitch().base_frequency();
294
295        match self.named_pitch {
296            NamedPitch::ATripleSharp | NamedPitch::BTripleSharp | NamedPitch::BDoubleSharp | NamedPitch::BSharp => {
297                octave += 1;
298            }
299            NamedPitch::DTripleFlat | NamedPitch::CTripleFlat | NamedPitch::CDoubleFlat | NamedPitch::CFlat => {
300                octave -= 1;
301            }
302            _ => {}
303        }
304
305        base_frequency * 2.0_f32.powf(octave as u8 as f32)
306    }
307}
308
309impl IntoChord for Note {
310    fn into_chord(self) -> Chord {
311        Chord::new(self)
312    }
313}
314
315impl Parsable for Note {
316    fn parse(input: &str) -> Res<Self>
317    where
318        Self: Sized,
319    {
320        let root = ChordParser::parse(Rule::note_with_octave, input)?.next().unwrap();
321
322        assert_eq!(Rule::note_with_octave, root.as_rule());
323
324        let mut components = root.into_inner();
325
326        let note = components.next().unwrap();
327
328        assert_eq!(Rule::note, note.as_rule());
329
330        let mut result = note_str_to_note(note.as_str())?;
331
332        if let Some(octave) = components.next() {
333            assert_eq!(Rule::digit, octave.as_rule());
334
335            let octave = octave_str_to_octave(octave.as_str())?;
336
337            result = result.with_octave(octave);
338        }
339
340        Ok(result)
341    }
342}
343
344impl NoteRecreator for Note {
345    fn with_named_pitch(self, named_pitch: NamedPitch) -> Self {
346        Self::new(named_pitch, self.octave)
347    }
348
349    fn with_octave(self, octave: Octave) -> Self {
350        Self::new(self.named_pitch, octave)
351    }
352}
353
354impl HasPrimaryHarmonicSeries for Note {
355    fn primary_harmonic_series(self) -> Vec<Self> {
356        PRIMARY_HARMONIC_SERIES.iter().map(|interval| self + *interval).collect()
357    }
358}
359
360impl HasNoteId for Note {
361    fn id(self) -> u128 {
362        1 << self.id_index()
363    }
364
365    fn id_index(self) -> u8 {
366        let mut shift = 0u8;
367
368        shift += 12 * self.octave as u8;
369        shift += self.named_pitch.pitch() as u8;
370
371        shift
372    }
373
374    fn from_id(id: u128) -> Res<Self> {
375        let mut shift = 0u8;
376
377        while id >> shift != 1 {
378            shift += 1;
379        }
380
381        let octave_num = shift / 12;
382        let pitch_num = shift % 12;
383
384        let octave = Octave::try_from(octave_num).map_err(anyhow::Error::msg)?;
385        let pitch = Pitch::try_from(pitch_num).map_err(anyhow::Error::msg)?;
386
387        Ok(Self::new(NamedPitch::from(pitch), octave))
388    }
389
390    fn id_mask(notes: &[Self]) -> u128
391    where
392        Self: Sized,
393    {
394        notes.iter().fold(0, |acc, note| acc | note.id())
395    }
396
397    fn from_id_mask(id_mask: u128) -> Res<Vec<Self>>
398    where
399        Self: Sized,
400    {
401        let mut notes = Vec::new();
402        let mut shift = 0u8;
403
404        while id_mask >> shift != 0 {
405            if id_mask & (1 << shift) != 0 {
406                notes.push(Self::from_id(1 << shift)?);
407            }
408
409            shift += 1;
410        }
411
412        Ok(notes)
413    }
414}
415
416impl ToUniversal for Note {
417    fn to_universal(self) -> Note {
418        self.with_named_pitch(NamedPitch::from(self.pitch()))
419    }
420}
421
422impl Sub for Note {
423    type Output = Interval;
424
425    fn sub(self, rhs: Self) -> Self::Output {
426        let (low, high) = if self < rhs { (self, rhs) } else { (rhs, self) };
427
428        for interval in ALL_INTERVALS.iter() {
429            if low + *interval == high {
430                return *interval;
431            }
432        }
433
434        panic!("{high} - {low} is not a valid interval");
435    }
436}
437
438impl Add<Interval> for Note {
439    type Output = Self;
440
441    #[rustfmt::skip]
442    fn add(self, rhs: Interval) -> Self::Output {
443        let new_pitch = self.named_pitch() + rhs.enharmonic_distance();
444
445        // Compute whether or not we "crossed" an octave.
446        let wrapping_octave = if new_pitch.pitch() < self.pitch() { Octave::One } else { Octave::Zero };
447
448        // There is a "special wrap" for `Cb`, and `Dbbb`, since they don't technically loop; and, for B#, etc., on the other side.
449        // Basically, if we were already "on" the weird one (this is a perfect unision, or perfect octave, etc.), then we don't
450        // do anything special.  Otherwise, if we landed on on of these edge cases, then we need to adjust the octave.
451        let mut special_octave = 0;
452
453        if self.named_pitch != new_pitch {
454            if new_pitch == NamedPitch::CFlat
455                || new_pitch == NamedPitch::CDoubleFlat
456                || new_pitch == NamedPitch::CTripleFlat
457                || new_pitch == NamedPitch::DTripleFlat
458            {
459                special_octave = 1;
460            } else if new_pitch == NamedPitch::BSharp
461                || new_pitch == NamedPitch::BDoubleSharp
462                || new_pitch == NamedPitch::BTripleSharp
463                || new_pitch == NamedPitch::ATripleSharp
464            {
465                special_octave = -1
466            }
467        }
468
469        // Get whether or not the interval itself contains an octave.
470        let interval_octave = rhs.octave();
471
472        Note {
473            octave: self.octave + wrapping_octave + special_octave + interval_octave,
474            named_pitch: new_pitch,
475        }
476    }
477}
478
479impl Sub<Interval> for Note {
480    type Output = Self;
481
482    #[rustfmt::skip]
483    fn sub(self, rhs: Interval) -> Self::Output {
484        let new_pitch = self.named_pitch() - rhs.enharmonic_distance();
485
486        // Compute whether or not we "crossed" an octave.
487        let wrapping_octave = if new_pitch.pitch() > self.pitch() { Octave::One } else { Octave::Zero };
488
489        // There is a "special wrap" for `Cb`, and `Dbbb`, since they don't technically loop; and, for B#, etc., on the other side.
490        // Basically, if we were already "on" the weird one (this is a perfect unision, or perfect octave, etc.), then we don't
491        // do anything special.  Otherwise, if we landed on on of these edge cases, then we need to adjust the octave.
492        let mut special_octave = 0;
493
494        if self.named_pitch != new_pitch {
495            if new_pitch == NamedPitch::CFlat
496                || new_pitch == NamedPitch::CDoubleFlat
497                || new_pitch == NamedPitch::CTripleFlat
498                || new_pitch == NamedPitch::DTripleFlat
499            {
500                special_octave = -1;
501            } else if new_pitch == NamedPitch::BSharp
502                || new_pitch == NamedPitch::BDoubleSharp
503                || new_pitch == NamedPitch::BTripleSharp
504                || new_pitch == NamedPitch::ATripleSharp
505            {
506                special_octave = 1
507            }
508        }
509
510        // Get whether or not the interval itself contains an octave.
511        let interval_octave = rhs.octave();
512
513        Note {
514            octave: self.octave - wrapping_octave - special_octave - interval_octave,
515            named_pitch: new_pitch,
516        }
517    }
518}
519
520impl AddAssign<Interval> for Note {
521    fn add_assign(&mut self, rhs: Interval) {
522        *self = *self + rhs;
523    }
524}
525
526impl PartialOrd for Note {
527    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
528        Some(self.cmp(other))
529    }
530}
531
532impl Ord for Note {
533    fn cmp(&self, other: &Self) -> Ordering {
534        self.frequency().partial_cmp(&other.frequency()).unwrap_or(Ordering::Equal)
535    }
536}
537
538// Define octaves.
539
540define_octave!(Zero, Octave::Zero);
541define_octave!(One, Octave::One);
542define_octave!(Two, Octave::Two);
543define_octave!(Three, Octave::Three);
544define_octave!(Four, Octave::Four);
545define_octave!(Five, Octave::Five);
546define_octave!(Six, Octave::Six);
547define_octave!(Seven, Octave::Seven);
548define_octave!(Eight, Octave::Eight);
549define_octave!(Nine, Octave::Nine);
550define_octave!(Ten, Octave::Ten);
551
552// Define notes.
553
554/// The default F triple flat (in the fourth octave).
555pub const FTripleFlat: Note = FTripleFlatFour;
556/// The default C triple flat (in the fourth octave).
557pub const CTripleFlat: Note = CTripleFlatFour;
558/// The default G triple flat (in the fourth octave).
559pub const GTripleFlat: Note = GTripleFlatFour;
560/// The default D triple flat (in the fourth octave).
561pub const DTripleFlat: Note = DTripleFlatFour;
562/// The default A triple flat (in the fourth octave).
563pub const ATripleFlat: Note = ATripleFlatFour;
564/// The default E triple flat (in the fourth octave).
565pub const ETripleFlat: Note = ETripleFlatFour;
566/// The default B triple flat (in the fourth octave).
567pub const BTripleFlat: Note = BTripleFlatFour;
568
569/// The default F double flat (in the fourth octave).
570pub const FDoubleFlat: Note = FDoubleFlatFour;
571/// The default C double flat (in the fourth octave).
572pub const CDoubleFlat: Note = CDoubleFlatFour;
573/// The default G double flat (in the fourth octave).
574pub const GDoubleFlat: Note = GDoubleFlatFour;
575/// The default D double flat (in the fourth octave).
576pub const DDoubleFlat: Note = DDoubleFlatFour;
577/// The default A double flat (in the fourth octave).
578pub const ADoubleFlat: Note = ADoubleFlatFour;
579/// The default E double flat (in the fourth octave).
580pub const EDoubleFlat: Note = EDoubleFlatFour;
581/// The default B double flat (in the fourth octave).
582pub const BDoubleFlat: Note = BDoubleFlatFour;
583
584/// The default F flat (in the fourth octave).
585pub const FFlat: Note = FFlatFour;
586/// The default C flat (in the fourth octave).
587pub const CFlat: Note = CFlatFour;
588/// The default G flat (in the fourth octave).
589pub const GFlat: Note = GFlatFour;
590/// The default D flat (in the fourth octave).
591pub const DFlat: Note = DFlatFour;
592/// The default A flat (in the fourth octave).
593pub const AFlat: Note = AFlatFour;
594/// The default E flat (in the fourth octave).
595pub const EFlat: Note = EFlatFour;
596/// The default B flat (in the fourth octave).
597pub const BFlat: Note = BFlatFour;
598
599/// The default F (in the fourth octave).
600pub const F: Note = FFour;
601/// The default C (in the fourth octave).
602pub const C: Note = CFour;
603/// The default G (in the fourth octave).
604pub const G: Note = GFour;
605/// The default D (in the fourth octave).
606pub const D: Note = DFour;
607/// The default A (in the fourth octave).
608pub const A: Note = AFour;
609/// The default E (in the fourth octave).
610pub const E: Note = EFour;
611/// The default B (in the fourth octave).
612pub const B: Note = BFour;
613
614/// The default F sharp (in the fourth octave).
615pub const FSharp: Note = FSharpFour;
616/// The default C sharp (in the fourth octave).
617pub const CSharp: Note = CSharpFour;
618/// The default G sharp (in the fourth octave).
619pub const GSharp: Note = GSharpFour;
620/// The default D sharp (in the fourth octave).
621pub const DSharp: Note = DSharpFour;
622/// The default A sharp (in the fourth octave).
623pub const ASharp: Note = ASharpFour;
624/// The default E sharp (in the fourth octave).
625pub const ESharp: Note = ESharpFour;
626/// The default B sharp (in the fourth octave).
627pub const BSharp: Note = BSharpFour;
628
629/// The default F double sharp (in the fourth octave).
630pub const FDoubleSharp: Note = FDoubleSharpFour;
631/// The default C double sharp (in the fourth octave).
632pub const CDoubleSharp: Note = CDoubleSharpFour;
633/// The default G double sharp (in the fourth octave).
634pub const GDoubleSharp: Note = GDoubleSharpFour;
635/// The default D double sharp (in the fourth octave).
636pub const DDoubleSharp: Note = DDoubleSharpFour;
637/// The default A double sharp (in the fourth octave).
638pub const ADoubleSharp: Note = ADoubleSharpFour;
639/// The default E double sharp (in the fourth octave).
640pub const EDoubleSharp: Note = EDoubleSharpFour;
641/// The default B double sharp (in the fourth octave).
642pub const BDoubleSharp: Note = BDoubleSharpFour;
643
644/// The default F triple sharp (in the fourth octave).
645pub const FTripleSharp: Note = FTripleSharpFour;
646/// The default C triple sharp (in the fourth octave).
647pub const CTripleSharp: Note = CTripleSharpFour;
648/// The default G triple sharp (in the fourth octave).
649pub const GTripleSharp: Note = GTripleSharpFour;
650/// The default D triple sharp (in the fourth octave).
651pub const DTripleSharp: Note = DTripleSharpFour;
652/// The default A triple sharp (in the fourth octave).
653pub const ATripleSharp: Note = ATripleSharpFour;
654/// The default E triple sharp (in the fourth octave).
655pub const ETripleSharp: Note = ETripleSharpFour;
656/// The default B triple sharp (in the fourth octave).
657pub const BTripleSharp: Note = BTripleSharpFour;
658
659// Statics.
660
661/// All the notes in all octaves.
662pub static ALL_PITCH_NOTES: LazyLock<[Note; 192]> = LazyLock::new(|| {
663    let mut all_notes = Vec::with_capacity(132);
664
665    for octave in ALL_OCTAVES.iter() {
666        for pitch in ALL_PITCHES.iter() {
667            all_notes.push(Note {
668                octave: *octave,
669                named_pitch: pitch.into(),
670            });
671        }
672    }
673
674    all_notes.try_into().unwrap()
675});
676
677/// All the notes in all octaves with their frequency.
678pub static ALL_PITCH_NOTES_WITH_FREQUENCY: LazyLock<[(Note, f32); 192]> = LazyLock::new(|| {
679    let mut all_notes = Vec::with_capacity(132);
680
681    for note in ALL_PITCH_NOTES.iter() {
682        all_notes.push((*note, note.frequency()));
683    }
684
685    all_notes.try_into().unwrap()
686});
687
688// Tests.
689
690#[cfg(test)]
691mod tests {
692    use super::*;
693    use pretty_assertions::assert_eq;
694
695    #[test]
696    fn test_text() {
697        assert_eq!(CFlat.static_name(), "C♭");
698        assert_eq!(C.to_string(), "C4");
699    }
700
701    #[test]
702    fn test_intervals() {
703        // Additions.
704
705        assert_eq!(C + Interval::PerfectUnison, C);
706        assert_eq!(C + Interval::DiminishedSecond, DDoubleFlat);
707
708        assert_eq!(C + Interval::AugmentedUnison, CSharp);
709        assert_eq!(C + Interval::MinorSecond, DFlat);
710
711        assert_eq!(C + Interval::MajorSecond, D);
712        assert_eq!(C + Interval::DiminishedThird, EDoubleFlat);
713
714        assert_eq!(C + Interval::AugmentedSecond, DSharp);
715        assert_eq!(C + Interval::MinorThird, EFlat);
716
717        assert_eq!(C + Interval::MajorThird, E);
718        assert_eq!(C + Interval::DiminishedFourth, FFlat);
719
720        assert_eq!(C + Interval::AugmentedThird, ESharp);
721        assert_eq!(C + Interval::PerfectFourth, F);
722
723        assert_eq!(C + Interval::AugmentedFourth, FSharp);
724        assert_eq!(C + Interval::DiminishedFifth, GFlat);
725
726        assert_eq!(C + Interval::PerfectFifth, G);
727        assert_eq!(C + Interval::DiminishedSixth, ADoubleFlat);
728
729        assert_eq!(C + Interval::AugmentedFifth, GSharp);
730        assert_eq!(C + Interval::MinorSixth, AFlat);
731
732        assert_eq!(C + Interval::MajorSixth, A);
733        assert_eq!(C + Interval::DiminishedSeventh, BDoubleFlat);
734
735        assert_eq!(C + Interval::AugmentedSixth, ASharp);
736        assert_eq!(C + Interval::MinorSeventh, BFlat);
737
738        assert_eq!(C + Interval::MajorSeventh, B);
739        assert_eq!(C + Interval::DiminishedOctave, CFlatFive);
740
741        assert_eq!(C + Interval::AugmentedSeventh, BSharp);
742        assert_eq!(C + Interval::PerfectOctave, CFive);
743
744        assert_eq!(C + Interval::PerfectOctave + Interval::PerfectFifth, GFive);
745
746        assert_eq!(C + Interval::MinorNinth, DFlatFive);
747        assert_eq!(C + Interval::MajorNinth, DFive);
748        assert_eq!(C + Interval::AugmentedNinth, DSharpFive);
749
750        assert_eq!(C + Interval::DiminishedEleventh, FFlatFive);
751        assert_eq!(C + Interval::PerfectEleventh, FFive);
752        assert_eq!(C + Interval::AugmentedEleventh, FSharpFive);
753
754        assert_eq!(C + Interval::MinorThirteenth, AFlatFive);
755        assert_eq!(C + Interval::MajorThirteenth, AFive);
756        assert_eq!(C + Interval::AugmentedThirteenth, ASharpFive);
757
758        // Subtractions.
759
760        assert_eq!(C - Interval::PerfectUnison, C);
761        assert_eq!(DFlat - Interval::MinorSecond, C);
762        assert_eq!(G - Interval::PerfectOctave, GThree);
763        assert_eq!(G - Interval::PerfectFifth, C);
764        assert_eq!(DFlat - Interval::DiminishedSecond, CSharp);
765        assert_eq!(ATripleSharpSix - Interval::TwoPerfectOctaves, ATripleSharp);
766        assert_eq!(GFlat - Interval::PerfectFifth, CFlat);
767        assert_eq!(GDoubleFlat - Interval::PerfectFifth, CDoubleFlat);
768
769        // Special cases to check.
770
771        assert_eq!(C + Interval::DiminishedOctave, CFlatFive);
772        assert_eq!(BFlat + Interval::MinorNinth, CFlatSix);
773        assert_eq!(BFlatThree + Interval::MinorNinth, CFlatFive);
774        assert_eq!(A + Interval::AugmentedNinth, BSharpFive);
775        assert_eq!(CSharp + Interval::AugmentedSeventh, BDoubleSharp);
776
777        assert_eq!(DTripleFlat + Interval::PerfectOctave, DTripleFlatFive);
778        assert_eq!(DTripleFlat + Interval::PerfectUnison, DTripleFlat);
779
780        assert_eq!(BSharp + Interval::PerfectOctave, BSharpFive);
781        assert_eq!(BSharp + Interval::PerfectUnison, BSharp);
782
783        assert_eq!(ATripleSharp + Interval::TwoPerfectOctaves, ATripleSharpSix);
784    }
785
786    #[test]
787    fn test_distances() {
788        assert_eq!(C - C, Interval::PerfectUnison);
789        assert_eq!(C - D, Interval::MajorSecond);
790        assert_eq!(D - C, Interval::MajorSecond);
791        assert_eq!(C - E, Interval::MajorThird);
792    }
793
794    #[test]
795    fn test_parse() {
796        assert_eq!(Note::parse("C").unwrap(), C);
797        assert_eq!(Note::parse("C#").unwrap(), CSharp);
798        assert_eq!(Note::parse("Bb3").unwrap(), BFlatThree);
799        assert_eq!(Note::parse("D#7").unwrap(), DSharpSeven);
800    }
801
802    #[test]
803    #[should_panic]
804    fn test_parse_panic() {
805        assert_eq!(Note::parse("C11").unwrap(), C);
806    }
807
808    #[test]
809    fn test_pitch() {
810        assert_eq!(Note::new(NamedPitch::C, Octave::Four).frequency(), (CThree + Interval::PerfectOctave).frequency());
811        assert_eq!(CFlatFour.frequency(), BThree.frequency());
812        assert_eq!(BSharp.frequency(), CFive.frequency());
813        assert_eq!(DTripleFlatFive.frequency(), B.frequency());
814        assert_eq!(BDoubleSharpFive.with_named_pitch(NamedPitch::A).frequency(), AFive.frequency());
815    }
816
817    #[test]
818    fn test_harmonics() {
819        assert_eq!(
820            C.primary_harmonic_series(),
821            vec![CFive, GFive, CSix, ESix, GSix, BFlatSix, DSeven, ESeven, FSharpSeven, GSeven, AFlatSeven, BFlatSeven, BSeven]
822        );
823    }
824
825    #[test]
826    fn test_id() {
827        // Individual notes.
828
829        assert_eq!(CZero.id(), 1 << 0);
830        assert_eq!(CSharpZero.id(), 1 << 1);
831        assert_eq!(BZero.id(), 1 << 11);
832        assert_eq!(Note::parse("C1").unwrap().id(), 1 << 12);
833        assert_eq!(Note::parse("C#1").unwrap().id(), 1 << 13);
834        assert_eq!(Note::parse("Db1").unwrap().id(), 1 << 13);
835        assert_eq!(Note::parse("C4").unwrap().id(), 1 << 48);
836
837        assert_eq!(Note::from_id(1 << 0).unwrap(), CZero);
838        assert_eq!(Note::from_id(1 << 1).unwrap(), DFlatZero);
839        assert_eq!(Note::from_id(1 << 11).unwrap(), BZero);
840        assert_eq!(Note::from_id(1 << 12).unwrap(), Note::parse("C1").unwrap());
841        assert_eq!(Note::from_id(1 << 13).unwrap(), Note::parse("Db1").unwrap());
842        assert_eq!(Note::from_id(1 << 48).unwrap(), Note::parse("C4").unwrap());
843
844        // Chords.
845
846        assert_eq!(Note::id_mask(&[CZero, CSharpZero]), 1 << 0 | 1 << 1);
847        assert_eq!(Note::id_mask(&[CZero, CSharpZero, DFlatZero]), 1 << 0 | 1 << 1);
848        assert_eq!(Note::id_mask(&[CZero, CSharpZero, BZero]), 1 << 0 | 1 << 1 | 1 << 11);
849
850        assert_eq!(Note::from_id_mask(1 << 0 | 1 << 1).unwrap(), vec![CZero, DFlatZero]);
851        assert_eq!(Note::from_id_mask(1 << 0 | 1 << 1 | 1 << 11).unwrap(), vec![CZero, DFlatZero, BZero]);
852        assert_eq!(Note::from_id_mask(1 << 13 | 1 << 48).unwrap(), vec![DFlatOne, CFour]);
853    }
854
855    #[test]
856    fn test_universal() {
857        assert_eq!(FSharpFive.to_universal(), Note::parse("Gb5").unwrap());
858    }
859}