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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

use num::{
    Float,
    FromPrimitive,
    ToPrimitive,
};
use super::{
    Letter,
    MAX_HZ,
    MIN_HZ,
    Octave,
    TOTAL_LETTERS
};
use utils::modulo;

/// Useful for conversions between Step and Hz.
const TWELFTH_ROOT_OF_TWO: f32 = 1.059463094359;
/// The pitch `A 4` represented in steps.
const TUNING_PITCH_A4: f32 = 69.0;
/// The pitch `A 4` represented in hz.
const PITCH_INDEX: f32 = 440.0;
/// Octave offset to match MIDI step standard (i.e. A4 == 69).
const MIDI_OCTAVE_OFFSET: Octave = 1;

pub type Hz = f32;
pub type Mel = f32;
pub type Perc = f64;
pub type Semitones = i32;
pub type Step = f32;
pub type Weight = f32;

/// Find and return the smallest distance
/// between two letters in semitones as an int.
#[inline]
pub fn difference_in_semitones(letter_a: Letter, letter_b: Letter) -> Semitones {
    let diff = (letter_a as Semitones - letter_b as Semitones).abs();
    if diff > 6 { diff - 12 } else { diff }
}

/// Calculate hz from (Letter, Octave).
#[inline]
pub fn hz_from_letter_octave(letter: Letter, octave: Octave) -> Hz {
    hz_from_step(step_from_letter_octave(letter, octave))
}

/// Calculate hz from mel.
#[inline]
pub fn hz_from_mel(mel: Mel) -> Hz {
    (10.0.powf(mel / 2595.0) - 1.0) * 700.0
}

/// Calculate frequency in hz from percentage.
#[inline]
pub fn hz_from_perc(perc: Perc) -> Hz {
    perc as Hz * (MAX_HZ - MIN_HZ) + MIN_HZ
}

/// Calculate hz from scaled percentage.
#[inline]
pub fn hz_from_scaled_perc(scaled: Perc, weight: Weight) -> Hz {
    hz_from_perc(perc_from_scaled_perc(scaled, weight))
}

/// Calculate hz from pitch as `step`.
#[inline]
pub fn hz_from_step(step: Step) -> Hz {
    PITCH_INDEX * TWELFTH_ROOT_OF_TWO.powf(step - TUNING_PITCH_A4)
}

/// Calculate (Letter, Octave) from hz.
#[inline]
pub fn letter_octave_from_hz(hz: Hz) -> (Letter, Octave) {
    letter_octave_from_step(step_from_hz(hz))
}

/// Calculate (Letter, Octave) from mel.
#[inline]
pub fn letter_octave_from_mel(mel: Mel) -> (Letter, Octave) {
    letter_octave_from_hz(hz_from_mel(mel))
}

/// Calculate (Letter, Octave) from a frequency percentage.
#[inline]
pub fn letter_octave_from_perc(perc: Perc) -> (Letter, Octave) {
    letter_octave_from_step(step_from_perc(perc))
}

/// Calculate (Letter, Octave) from a scaled frequency percentage.
#[inline]
pub fn letter_octave_from_scaled_perc(scaled: Perc, weight: Weight) -> (Letter, Octave) {
    letter_octave_from_step(step_from_scaled_perc(scaled, weight))
}

/// Calculate pitch as (Letter, Octave) from pitch as step.
#[inline]
pub fn letter_octave_from_step(step: Step) -> (Letter, Octave) {
    let rounded = step.round() as Octave;
    let letter_step = modulo(rounded, TOTAL_LETTERS as Octave);
    (FromPrimitive::from_i32(letter_step).unwrap(), (rounded - letter_step) / 12 - MIDI_OCTAVE_OFFSET)
}

/// Calculate mel from hz.
/// Formula used from http://en.wikipedia.org/wiki/Mel_scale
#[inline]
pub fn mel_from_hz(hz: Hz) -> Mel {
    (1.0 + hz / 700.0).log10() * 2595.0
}

/// Calculate mel from (Letter, Octave).
#[inline]
pub fn mel_from_letter_octave(letter: Letter, octave: Octave) -> Mel {
    mel_from_hz(hz_from_letter_octave(letter, octave))
}

/// Calculate mel from percentage.
#[inline]
pub fn mel_from_perc(perc: Perc) -> Mel {
    mel_from_hz(hz_from_perc(perc))
}

/// Calculate mel from scaled percentage.
#[inline]
pub fn mel_from_scaled_perc(scaled: Perc, weight: Weight) -> Mel {
    mel_from_hz(hz_from_scaled_perc(scaled, weight))
}

/// Calculate mel from step.
#[inline]
pub fn mel_from_step(step: Step) -> Mel {
    mel_from_hz(hz_from_step(step))
}

/// Calculate percentage from hz.
#[inline]
pub fn perc_from_hz(hz: Hz) -> Perc {
    (hz - MIN_HZ) as Perc / (MAX_HZ - MIN_HZ) as Perc
}

/// Calculate percentage from letter octave.
#[inline]
pub fn perc_from_letter_octave(letter: Letter, octave: Octave) -> Perc {
    perc_from_step(step_from_letter_octave(letter, octave))
}

/// Calculate percentage from mel.
#[inline]
pub fn perc_from_mel(mel: Mel) -> Perc {
    perc_from_hz(hz_from_mel(mel))
}

/// Calculate percentage from scaled percentage.
#[inline]
pub fn perc_from_scaled_perc(scaled: Perc, weight: Weight) -> Perc {
    scaled.powf(weight as Perc)
}

/// Calculate frequency percentage from pitch as `step`.
#[inline]
pub fn perc_from_step(step: Step) -> Perc {
    perc_from_hz(hz_from_step(step))
}

/// Calculate scaled percentage from hz.
#[inline]
pub fn scaled_perc_from_hz(hz: Hz, weight: Weight) -> Perc {
    scaled_perc_from_perc(perc_from_hz(hz), weight)
}

/// Calculate scaled percentage from letter octave.
#[inline]
pub fn scaled_perc_from_letter_octave(letter: Letter, octave: Octave, weight: Weight) -> Perc {
    scaled_perc_from_step(step_from_letter_octave(letter, octave), weight)
}

/// Calculate scaled percentage from mel.
#[inline]
pub fn scaled_perc_from_mel(mel: Mel, weight: Weight) -> Perc {
    scaled_perc_from_hz(hz_from_mel(mel), weight)
}

/// Calculate scaled percentage from percentage.
#[inline]
pub fn scaled_perc_from_perc(perc: Perc, weight: Weight) -> Perc {
    perc.powf(1.0 / weight as Perc)
}

/// Calculate scaled frequency percentage from pitch as `step`.
#[inline]
pub fn scaled_perc_from_step(step: Step, weight: Weight) -> Perc {
    scaled_perc_from_hz(hz_from_step(step), weight)
}

/// Calculate the pitch `step` from frequency in hz.
#[inline]
pub fn step_from_hz(hz: Hz) -> Step {
    (hz / PITCH_INDEX).log2() / TWELFTH_ROOT_OF_TWO.log2() + TUNING_PITCH_A4
}

/// Calculate the pitch `step` from (Letter, Octave).
#[inline]
pub fn step_from_letter_octave(letter: Letter, octave: Octave) -> Step {
    (MIDI_OCTAVE_OFFSET + octave) as Step * 12.0 + letter.to_f32().unwrap()
}

/// Calculate the pitch `step` from mel.
#[inline]
pub fn step_from_mel(mel: Mel) -> Step {
    step_from_hz(hz_from_mel(mel))
}

/// Calculate the pitch `step` from frequency precentage.
#[inline]
pub fn step_from_perc(perc: Perc) -> Step {
    step_from_hz(hz_from_perc(perc))
}

/// Calculate the pitch `step` from a scaled frequency precentage.
#[inline]
pub fn step_from_scaled_perc(scaled: Perc, weight: Weight) -> Step {
    step_from_hz(hz_from_scaled_perc(scaled, weight))
}