in src/utils/audio.js [250:305]
export function mel_filter_bank(
num_frequency_bins,
num_mel_filters,
min_frequency,
max_frequency,
sampling_rate,
norm = null,
mel_scale = "htk",
triangularize_in_mel_space = false,
) {
if (norm !== null && norm !== "slaney") {
throw new Error('norm must be one of null or "slaney"');
}
if (num_frequency_bins < 2) {
throw new Error(`Require num_frequency_bins: ${num_frequency_bins} >= 2`);
}
if (min_frequency > max_frequency) {
throw new Error(`Require min_frequency: ${min_frequency} <= max_frequency: ${max_frequency}`);
}
const mel_min = hertz_to_mel(min_frequency, mel_scale);
const mel_max = hertz_to_mel(max_frequency, mel_scale);
const mel_freqs = linspace(mel_min, mel_max, num_mel_filters + 2);
let filter_freqs = mel_to_hertz(mel_freqs, mel_scale);
let fft_freqs; // frequencies of FFT bins in Hz
if (triangularize_in_mel_space) {
const fft_bin_width = sampling_rate / ((num_frequency_bins - 1) * 2);
fft_freqs = hertz_to_mel(Float64Array.from({ length: num_frequency_bins }, (_, i) => i * fft_bin_width), mel_scale);
filter_freqs = mel_freqs;
} else {
fft_freqs = linspace(0, Math.floor(sampling_rate / 2), num_frequency_bins);
}
const mel_filters = _create_triangular_filter_bank(fft_freqs, filter_freqs);
if (norm !== null && norm === "slaney") {
// Slaney-style mel is scaled to be approx constant energy per channel
for (let i = 0; i < num_mel_filters; ++i) {
const filter = mel_filters[i];
const enorm = 2.0 / (filter_freqs[i + 2] - filter_freqs[i]);
for (let j = 0; j < num_frequency_bins; ++j) {
// Apply this enorm to all frequency bins
filter[j] *= enorm;
}
}
}
// TODO warn if there is a zero row
return mel_filters;
}