use derive_more::Display; use std::fmt::{Debug, Write}; use num_traits::{NumOps, ToPrimitive}; #[derive(Debug, Clone, Display, Copy, PartialEq)] pub enum Notes { #[display(fmt=" c")] C, #[display(fmt="c#")] Cs, #[display(fmt=" d")] D, #[display(fmt="d#")] Ds, #[display(fmt=" e")] E, #[display(fmt=" f")] F, #[display(fmt="f#")] Fs, #[display(fmt=" g")] G, #[display(fmt="g#")] Gs, #[display(fmt=" a")] A, #[display(fmt="a#")] As, #[display(fmt=" b")] B, } pub type BaseNote = (Notes, f64); const MIDDLE_A_VAL: f64 = 440.; //const MIDDLE_A_LOWER: f64 = MIDDLE_A_VAL * (2f64).powf(-0.5f64 / 12f64); //const MIDDLE_A_UPPER: f64 = MIDDLE_A_VAL * (2f64).powf(0.5f64 / 12f64); pub fn calc_freq_from_offset(offset: f64) -> f64 { return MIDDLE_A_VAL * (2f64).powf(offset / 12f64); } pub const NOTE_MA_OFFSETS: [BaseNote; 12] = [ (Notes::C, -57.), (Notes::Cs, -56.), (Notes::D, -55.), (Notes::Ds, -54.), (Notes::E, -53.), (Notes::F, -52.), (Notes::Fs, -51.), (Notes::G, -50.), (Notes::Gs, -49.), (Notes::A, -48.), (Notes::As, -47.), (Notes::B, -46.), ]; /*const NOTE_ORDER: [BaseNote; 12] = [ (Notes::C, 16.352), (Notes::Cs, 17.324), (Notes::D, 18.354), (Notes::Ds, 19.445), (Notes::E, 20.602), (Notes::F, 21.827), (Notes::Fs, 23.125), (Notes::G, 24.5), (Notes::Gs, 25.957), (Notes::A, 27.5), (Notes::As, 29.135), (Notes::B, 30.868) ];*/ #[derive(Debug, Display)] #[display(fmt="{}{}", "note.0", octave)] pub struct Note { pub note: BaseNote, pub low_freq: f64, pub freq: f64, pub high_freq: f64, pub octave: i32, //value: f64, } pub trait NoteValue: Debug + PartialOrd + From + ToPrimitive + std::fmt::Display + Copy + NumOps {} //where ::Output: std::fmt::Display {} //impl NoteValue for f32 {} //impl NoteValue for f64 {} impl NoteValue for T where T: Debug + PartialOrd + From + ToPrimitive + std::fmt::Display + Copy + NumOps {} // elements here are (Note, fft/proc data range for calculating the value, summed value, max value // of each element in the bin) #[derive(Debug, Display)] //#[display(fmt="{}({:?}) {}", _0, _1 ,_2)] //#[display(bound="I: From + std::ops::Mul + std::fmt::Display")] #[display(fmt="{} {:.0} ", _0, "*_3*I::from(10000.0)")] pub struct ProcerNote<'nl, I: NoteValue>(pub &'nl Note, pub core::ops::Range, pub I, pub I); // .0 here is the notes data, .1 is the threshold for printing~ #[derive(Debug)] pub struct ProcerNotes<'nl, I: NoteValue>(pub Vec>, pub I); impl<'nl, I: NoteValue> std::fmt::Display for ProcerNotes<'nl, I> { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { //let threshold: I = I::from(0.00372314453125f32); let threshold = self.1; let mut tmp: String = String::with_capacity(1024); for pn in self.0.iter().filter(|pn| pn.3 >= threshold) { write!(tmp, "{pn}")?; }; fmt.pad(&tmp); Ok(()) } } /*impl<'nl, I: Debug + PartialOrd + From> Debug for ProcerNotes<'nl, I> { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { let threshold: I = I::from(122.0f32); fmt.debug_list().entries(self.0.iter().filter(|pn| pn.2 > threshold)).finish() } }*/ pub trait Octavable { fn get_octave(&self, octave: i32) -> Note; } impl Octavable for BaseNote { fn get_octave(&self, octave: i32) -> Note { let offset = self.1 + ((12i32 * octave) as f64); let low_offset = offset - 0.5; let high_offset = offset + 0.5; match octave { 0 => Note { note: *self, low_freq: calc_freq_from_offset(self.1 - 0.5), freq: calc_freq_from_offset(self.1), high_freq: calc_freq_from_offset(self.1 + 0.5), octave: 0//, value: 0. }, _ => Note { note: *self, low_freq: calc_freq_from_offset(low_offset), freq: calc_freq_from_offset(offset), high_freq: calc_freq_from_offset(high_offset), octave: octave//, value: 0. } } } } // start and end should be tuples of (Note, octave) pub fn note_range(start: (Notes, i32), end: (Notes, i32)) -> Vec { let mut ret = Vec::with_capacity((end.1+3 - start.1) as usize *12); let mut started: bool = false; let start_note = start.0; let (end_note, mut end_octave) = end; end_octave += 2; for octave in (start.1)..=end_octave { for bnote in NOTE_MA_OFFSETS { match (started, bnote.0, octave) { (false, cur_note, _) if cur_note == start_note => { started = true; ret.push(bnote.get_octave(octave)) }, (true, cur_note, _) if cur_note == end_note && octave == end_octave => { ret.push(bnote.get_octave(octave)); return ret; }, (true, _, _) => { ret.push(bnote.get_octave(octave)) }, (_, _, _) => {}, } } } return ret; } /*impl Octavable for Note { fn get_octave(&self, octave: i32) -> Self { match octave { 0 => Note {freq: self.note.1, octave: 0, value: 0., ..*self}, _ => Note {freq: self.note.1 * ((1 << octave) as f64), octave: octave, value: 0., ..*self} } } }*/