| |
| |
| |
| |
| |
| |
|
|
| #include "fbank.hpp" |
|
|
| #include <algorithm> |
| #include <cstring> |
| #include <cstdio> |
|
|
| #ifndef M_PI |
| #define M_PI 3.14159265358979323846 |
| #endif |
|
|
| static float hz_to_mel(float freq) { |
| return 2595.0f * std::log10(1.0f + freq / 700.0f); |
| } |
|
|
| static float mel_to_hz(float mel) { |
| return 700.0f * (std::pow(10.0f, mel / 2595.0f) - 1.0f); |
| } |
|
|
| MelFilterBank::MelFilterBank() : m_n_freqs(0) {} |
|
|
| int MelFilterBank::Init(const Config& config) { |
| m_config = config; |
| m_n_freqs = config.n_fft / 2 + 1; |
|
|
| CreateWindow(); |
| CreateMelFilterbank(); |
|
|
| printf("FBANK init: n_mels=%d, n_fft=%d, hop=%d, sr=%d\n", |
| config.n_mels, config.n_fft, config.hop_length, config.sampling_rate); |
| return 0; |
| } |
|
|
| void MelFilterBank::CreateWindow() { |
| m_window.resize(m_config.n_fft); |
| for (int i = 0; i < m_config.n_fft; ++i) { |
| m_window[i] = 0.5f * (1.0f - std::cos(2.0f * M_PI * i / (m_config.n_fft - 1))); |
| } |
| } |
|
|
| void MelFilterBank::CreateMelFilterbank() { |
| int n_freqs = m_n_freqs; |
| int n_mels = m_config.n_mels; |
|
|
| |
| std::vector<float> all_freqs(n_freqs); |
| for (int i = 0; i < n_freqs; ++i) { |
| all_freqs[i] = i * m_config.sampling_rate / (2.0f * (n_freqs - 1)); |
| } |
|
|
| |
| float m_min = hz_to_mel(0.0f); |
| float m_max = hz_to_mel(m_config.sampling_rate / 2.0f); |
| std::vector<float> m_pts(n_mels + 2); |
| for (int i = 0; i < n_mels + 2; ++i) { |
| m_pts[i] = m_min + (m_max - m_min) * i / (n_mels + 1); |
| } |
|
|
| |
| std::vector<float> f_pts(n_mels + 2); |
| for (int i = 0; i < n_mels + 2; ++i) { |
| f_pts[i] = mel_to_hz(m_pts[i]); |
| } |
|
|
| |
| m_mel_basis.resize(n_freqs * n_mels, 0.0f); |
|
|
| for (int m = 0; m < n_mels; ++m) { |
| for (int k = 0; k < n_freqs; ++k) { |
| float freq = all_freqs[k]; |
| float left = f_pts[m]; |
| float center = f_pts[m + 1]; |
| float right = f_pts[m + 2]; |
|
|
| float val = 0.0f; |
| if (freq >= left && freq <= center) { |
| val = (freq - left) / (center - left); |
| } else if (freq >= center && freq <= right) { |
| val = (right - freq) / (right - center); |
| } |
| m_mel_basis[k * n_mels + m] = val; |
| } |
| } |
| } |
|
|
| int MelFilterBank::ComputeNumFrames(int num_samples, int hop_length) { |
| return (num_samples + hop_length / 2) / hop_length; |
| } |
|
|
| |
| static void fft(std::vector<float>& real, std::vector<float>& imag, bool inverse = false) { |
| int n = (int)real.size(); |
| |
| for (int i = 1, j = 0; i < n; ++i) { |
| int bit = n >> 1; |
| for (; j & bit; bit >>= 1) j ^= bit; |
| j ^= bit; |
| if (i < j) { |
| std::swap(real[i], real[j]); |
| std::swap(imag[i], imag[j]); |
| } |
| } |
| |
| for (int len = 2; len <= n; len <<= 1) { |
| float angle = 2.0f * M_PI / len * (inverse ? 1.0f : -1.0f); |
| float w_real = std::cos(angle); |
| float w_imag = std::sin(angle); |
| for (int i = 0; i < n; i += len) { |
| float cur_real = 1.0f, cur_imag = 0.0f; |
| for (int j = 0; j < len / 2; ++j) { |
| int a = i + j; |
| int b = i + j + len / 2; |
| float t_real = cur_real * real[b] - cur_imag * imag[b]; |
| float t_imag = cur_real * imag[b] + cur_imag * real[b]; |
| real[b] = real[a] - t_real; |
| imag[b] = imag[a] - t_imag; |
| real[a] += t_real; |
| imag[a] += t_imag; |
| float next_real = cur_real * w_real - cur_imag * w_imag; |
| cur_imag = cur_real * w_imag + cur_imag * w_real; |
| cur_real = next_real; |
| } |
| } |
| } |
| if (inverse) { |
| for (int i = 0; i < n; ++i) { |
| real[i] /= n; |
| imag[i] /= n; |
| } |
| } |
| } |
|
|
| void MelFilterBank::ComputeSTFT(const std::vector<float>& samples, |
| std::vector<float>& spec_real, |
| std::vector<float>& spec_imag, |
| int& num_frames) { |
| int n_fft = m_config.n_fft; |
| int hop = m_config.hop_length; |
| int num_samples = static_cast<int>(samples.size()); |
|
|
| num_frames = ComputeNumFrames(num_samples, hop); |
| int n_freqs = n_fft / 2 + 1; |
|
|
| spec_real.assign(num_frames * n_freqs, 0.0f); |
| spec_imag.assign(num_frames * n_freqs, 0.0f); |
|
|
| |
| int pad_amount = n_fft / 2; |
| std::vector<float> padded(pad_amount + num_samples + pad_amount, 0.0f); |
| std::copy(samples.begin(), samples.end(), padded.begin() + pad_amount); |
|
|
| |
| std::vector<float> fft_real(n_fft), fft_imag(n_fft); |
|
|
| for (int frame = 0; frame < num_frames; ++frame) { |
| int start = frame * hop; |
| |
| for (int n = 0; n < n_fft; ++n) { |
| fft_real[n] = padded[start + n] * m_window[n]; |
| fft_imag[n] = 0.0f; |
| } |
| |
| fft(fft_real, fft_imag, false); |
| |
| for (int k = 0; k < n_freqs; ++k) { |
| spec_real[frame * n_freqs + k] = fft_real[k]; |
| spec_imag[frame * n_freqs + k] = fft_imag[k]; |
| } |
| } |
| } |
|
|
| std::vector<float> MelFilterBank::Extract(const std::vector<float>& samples, int sample_rate) { |
| if (sample_rate != m_config.sampling_rate) { |
| printf("WARNING: sample_rate mismatch: expected %d, got %d\n", |
| m_config.sampling_rate, sample_rate); |
| } |
|
|
| int num_frames; |
| std::vector<float> spec_real, spec_imag; |
| ComputeSTFT(samples, spec_real, spec_imag, num_frames); |
|
|
| int n_freqs = m_n_freqs; |
| int n_mels = m_config.n_mels; |
|
|
| |
| std::vector<float> spec_mag(num_frames * n_freqs); |
| for (int i = 0; i < num_frames * n_freqs; ++i) { |
| spec_mag[i] = std::sqrt(spec_real[i] * spec_real[i] + spec_imag[i] * spec_imag[i]); |
| } |
|
|
| |
| std::vector<float> mel(num_frames * n_mels, 0.0f); |
| for (int f = 0; f < num_frames; ++f) { |
| for (int m = 0; m < n_mels; ++m) { |
| float sum = 0.0f; |
| for (int k = 0; k < n_freqs; ++k) { |
| sum += spec_mag[f * n_freqs + k] * m_mel_basis[k * n_mels + m]; |
| } |
| mel[f * n_mels + m] = std::log(std::max(sum, 1e-7f)); |
| } |
| } |
|
|
| |
| int expected_frames = ComputeNumFrames(static_cast<int>(samples.size()), m_config.hop_length); |
| if (num_frames > expected_frames) { |
| |
| std::vector<float> truncated(expected_frames * n_mels); |
| std::copy(mel.begin(), mel.begin() + expected_frames * n_mels, truncated.begin()); |
| return truncated; |
| } |
|
|
| return mel; |
| } |
|
|