#[derive(Debug, Clone, Copy)]
pub enum MelScale {
HTK,
Slaney,
}
fn hz_to_mel(hz: f32, mel_scale: MelScale) -> f32 {
match mel_scale {
MelScale::HTK => 2595.0 * (1.0 + hz / 700.0).log10(),
MelScale::Slaney => {
if hz < 1000.0 {
3.0 * hz / 200.0
} else {
15.0 + 27.0 * (hz / 1000.0).log(6.4)
}
}
}
}
fn mel_to_hz(mel: f32, mel_scale: MelScale) -> f32 {
match mel_scale {
MelScale::HTK => 700.0 * (10.0f32.powf(mel / 2595.0) - 1.0),
MelScale::Slaney => {
if mel < 15.0 {
200.0 * mel / 3.0
} else {
6.4f32.powf((mel - 15.0) / 27.0) * 1000.0
}
}
}
}
fn create_mel_frequencies(f_min: f32, f_max: f32, n_mels: usize, mel_scale: MelScale) -> Vec<f32> {
let mel_min = hz_to_mel(f_min, mel_scale);
let mel_max = hz_to_mel(f_max, mel_scale);
let mel_freqs: Vec<f32> = (0..=n_mels - 1)
.map(|i| mel_min + (mel_max - mel_min) * i as f32 / (n_mels - 1) as f32)
.map(|f| mel_to_hz(f, mel_scale))
.collect();
mel_freqs
}
fn create_mel_filter_bank(
sr: u32,
n_fft: usize,
n_mels: usize,
f_min: Option<f32>, f_max: Option<f32>, mel_scale: MelScale,
) -> Vec<Vec<f32>> {
let f_min = f_min.unwrap_or(0.0);
let f_max = f_max.unwrap_or(sr as f32 / 2.0);
let n_freq_bins = 1 + n_fft / 2;
let mut weights: Vec<Vec<f32>> = vec![vec![0.0f32; n_freq_bins]; n_mels];
let fft_freqs: Vec<f32> = (0..=n_fft / 2_usize)
.map(|i| i as f32 * sr as f32 / n_fft as f32)
.collect();
let mel_freqs: Vec<f32> = create_mel_frequencies(f_min, f_max, n_mels + 2, mel_scale);
let mel_freqs_diffs: Vec<f32> = mel_freqs.windows(2).map(|w| w[1] - w[0]).collect();
let ramps: Vec<Vec<f32>> = mel_freqs
.iter()
.map(|&mel_freq| {
fft_freqs
.iter()
.map(|&fft_freq| mel_freq - fft_freq)
.collect()
})
.collect();
for i in 0..n_mels {
let lower: Vec<f32> = ramps[i].iter().map(|&r| -r / mel_freqs_diffs[i]).collect();
let upper: Vec<f32> = ramps[i + 2]
.iter()
.map(|&r| r / mel_freqs_diffs[i + 1])
.collect();
weights[i] = lower
.iter()
.zip(upper.iter())
.map(|(&l, &u)| 0.0f32.max(l.min(u)))
.collect();
}
let enorm: Vec<f32> = (0..n_mels)
.map(|i| 2.0 / (mel_freqs[i + 2] - mel_freqs[i]))
.collect();
for i in 0..n_mels {
for j in 0..n_freq_bins {
weights[i][j] *= enorm[i];
}
}
weights
}
pub fn convert_to_mel(
spectrogram: &[Vec<f32>],
sr: u32,
n_fft: usize,
n_mels: usize,
f_min: Option<f32>, f_max: Option<f32>, mel_scale: MelScale,
) -> Vec<Vec<f32>> {
let mel_filters = create_mel_filter_bank(sr, n_fft, n_mels, f_min, f_max, mel_scale);
let mut mel_spec = vec![vec![0.0; spectrogram[0].len()]; n_mels];
for (mel_idx, filter) in mel_filters.iter().enumerate() {
for time_idx in 0..spectrogram[0].len() {
mel_spec[mel_idx][time_idx] = spectrogram
.iter()
.zip(filter.iter())
.map(|(freq_bin, &filter_val)| freq_bin[time_idx] * filter_val)
.sum();
}
}
mel_spec
}