1#![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
32macro_rules! define_note {
36 ( $name:ident, $named_pitch:expr, $octave_num:ident, $octave:expr) => {
37 paste! {
38 pub const [<$name$octave_num>]: Note = Note {
40 named_pitch: $named_pitch,
41 octave: $octave,
42 };
43 }
44 };
45}
46
47macro_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
108pub trait IntoChord {
112 fn into_chord(self) -> Chord;
114}
115
116pub trait NoteRecreator {
118 fn with_named_pitch(self, named_pitch: NamedPitch) -> Self;
120 fn with_octave(self, octave: Octave) -> Self;
122}
123
124pub trait HasPrimaryHarmonicSeries {
126 fn primary_harmonic_series(self) -> Vec<Note>;
128}
129
130pub trait HasNoteId {
132 fn id(self) -> u128;
134
135 fn id_index(self) -> u8;
137
138 fn from_id(id: u128) -> Res<Self>
140 where
141 Self: Sized;
142
143 fn id_mask(notes: &[Self]) -> u128
145 where
146 Self: Sized;
147
148 fn from_id_mask(id_mask: u128) -> Res<Vec<Self>>
150 where
151 Self: Sized;
152}
153
154pub trait ToUniversal {
158 fn to_universal(self) -> Self;
160}
161
162#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
169#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
170pub struct Note {
171 octave: Octave,
173 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
183impl Note {
186 pub fn new(pitch: NamedPitch, octave: Octave) -> Self {
188 Self { named_pitch: pitch, octave }
189 }
190
191 pub fn try_from_midi(midi: u8) -> Res<Self> {
193 let pitch_index = midi % 12;
194 let octave_component = midi / 12;
195
196 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 #[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 #[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 #[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 Ok(result.pitches.iter().map(|&pitch| Note::new(NamedPitch::from(pitch), Octave::Four)).collect())
246 }
247
248 #[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 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 let wrapping_octave = if new_pitch.pitch() < self.pitch() { Octave::One } else { Octave::Zero };
447
448 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 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 let wrapping_octave = if new_pitch.pitch() > self.pitch() { Octave::One } else { Octave::Zero };
488
489 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 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
538define_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
552pub const FTripleFlat: Note = FTripleFlatFour;
556pub const CTripleFlat: Note = CTripleFlatFour;
558pub const GTripleFlat: Note = GTripleFlatFour;
560pub const DTripleFlat: Note = DTripleFlatFour;
562pub const ATripleFlat: Note = ATripleFlatFour;
564pub const ETripleFlat: Note = ETripleFlatFour;
566pub const BTripleFlat: Note = BTripleFlatFour;
568
569pub const FDoubleFlat: Note = FDoubleFlatFour;
571pub const CDoubleFlat: Note = CDoubleFlatFour;
573pub const GDoubleFlat: Note = GDoubleFlatFour;
575pub const DDoubleFlat: Note = DDoubleFlatFour;
577pub const ADoubleFlat: Note = ADoubleFlatFour;
579pub const EDoubleFlat: Note = EDoubleFlatFour;
581pub const BDoubleFlat: Note = BDoubleFlatFour;
583
584pub const FFlat: Note = FFlatFour;
586pub const CFlat: Note = CFlatFour;
588pub const GFlat: Note = GFlatFour;
590pub const DFlat: Note = DFlatFour;
592pub const AFlat: Note = AFlatFour;
594pub const EFlat: Note = EFlatFour;
596pub const BFlat: Note = BFlatFour;
598
599pub const F: Note = FFour;
601pub const C: Note = CFour;
603pub const G: Note = GFour;
605pub const D: Note = DFour;
607pub const A: Note = AFour;
609pub const E: Note = EFour;
611pub const B: Note = BFour;
613
614pub const FSharp: Note = FSharpFour;
616pub const CSharp: Note = CSharpFour;
618pub const GSharp: Note = GSharpFour;
620pub const DSharp: Note = DSharpFour;
622pub const ASharp: Note = ASharpFour;
624pub const ESharp: Note = ESharpFour;
626pub const BSharp: Note = BSharpFour;
628
629pub const FDoubleSharp: Note = FDoubleSharpFour;
631pub const CDoubleSharp: Note = CDoubleSharpFour;
633pub const GDoubleSharp: Note = GDoubleSharpFour;
635pub const DDoubleSharp: Note = DDoubleSharpFour;
637pub const ADoubleSharp: Note = ADoubleSharpFour;
639pub const EDoubleSharp: Note = EDoubleSharpFour;
641pub const BDoubleSharp: Note = BDoubleSharpFour;
643
644pub const FTripleSharp: Note = FTripleSharpFour;
646pub const CTripleSharp: Note = CTripleSharpFour;
648pub const GTripleSharp: Note = GTripleSharpFour;
650pub const DTripleSharp: Note = DTripleSharpFour;
652pub const ATripleSharp: Note = ATripleSharpFour;
654pub const ETripleSharp: Note = ETripleSharpFour;
656pub const BTripleSharp: Note = BTripleSharpFour;
658
659pub 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
677pub 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#[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 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 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 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 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 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}