summaryrefslogtreecommitdiff
path: root/src/notes.rs
blob: 41eb45ef6ff8c4e5f5adcedc0c8a329fb60a2191 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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<f32> + ToPrimitive + std::fmt::Display + Copy + NumOps {}
//where <Self as std::ops::Mul>::Output: std::fmt::Display {}

//impl NoteValue for f32 {}
//impl NoteValue for f64 {}
impl<T> NoteValue for T
where T: Debug + PartialOrd + From<f32> + 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<f32> + 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<usize>, 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<ProcerNote<'nl, I>>, 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<f32>> 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<Note> {
    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}
        }
    }
}*/