| |
| |
| |
| |
| |
|
|
| #include "src/vocoder.hpp" |
|
|
| #include <cmath> |
| #include <cstring> |
| #include <cstdio> |
| #include <algorithm> |
|
|
| #ifndef M_PI |
| #define M_PI 3.14159265358979323846 |
| #endif |
|
|
| Vocoder::Vocoder() : m_has_init(false), m_n_freqs(0) {} |
| Vocoder::~Vocoder() { |
| m_session.reset(); |
| if (m_fft_cfg) { rnn_fft_free(m_fft_cfg, 0); m_fft_cfg = nullptr; } |
| m_ifft_cfg = nullptr; |
| } |
|
|
| int Vocoder::Init(const Config& cfg) { |
| m_cfg = cfg; |
| m_n_freqs = cfg.n_fft / 2 + 1; |
|
|
| BuildWindow(); |
|
|
| |
| m_fft_cfg = rnn_fft_alloc(cfg.n_fft, NULL, NULL, 0); |
| if (!m_fft_cfg) { |
| printf("Failed to init kissfft for vocoder\n"); |
| return -1; |
| } |
| m_ifft_cfg = m_fft_cfg; |
|
|
| |
| m_session = std::make_unique<EngineWrapper>(); |
| if (m_session->Init(cfg.model_path.c_str(), 0, cfg.axclConfig) != 0) { |
| printf("Failed to load vocoder model: %s\n", cfg.model_path.c_str()); |
| return -1; |
| } |
|
|
| |
| if (!cfg.head_model_path.empty()) { |
| m_session_head = std::make_unique<EngineWrapper>(); |
| if (m_session_head->Init(cfg.head_model_path.c_str(), 0, cfg.axclConfig) != 0) { |
| printf("Failed to load vocoder head model: %s\n", cfg.head_model_path.c_str()); |
| return -1; |
| } |
| } |
|
|
| m_has_init = true; |
| printf("Vocoder initialized: n_fft=%d, hop=%d, n_mels=%d%s\n", |
| cfg.n_fft, cfg.hop_length, cfg.n_mels, |
| m_session_head ? " (split mode)" : ""); |
| return 0; |
| } |
|
|
| void Vocoder::BuildWindow() { |
| int n = m_cfg.n_fft; |
| m_window.resize(n); |
| m_window_sq.resize(n); |
| for (int i = 0; i < n; ++i) { |
| m_window[i] = 0.5f * (1.0f - std::cos(2.0f * M_PI * i / (n - 1))); |
| m_window_sq[i] = m_window[i] * m_window[i]; |
| } |
| } |
|
|
| int Vocoder::Decode(const std::vector<float>& mel, int T, float feat_scale, std::vector<float>& audio) { |
| if (!m_has_init || mel.empty()) return -1; |
|
|
| int feat_dim = m_cfg.n_mels; |
| int n_fft = m_cfg.n_fft; |
| int n_freqs = m_n_freqs; |
| int hop = m_cfg.hop_length; |
|
|
| |
| float inv_scale = (feat_scale != 0.0f) ? (1.0f / feat_scale) : 1.0f; |
| int T_model = 620; |
| std::vector<float> mel_padded(T_model * feat_dim, 0.0f); |
| for (int t = 0; t < T; ++t) { |
| for (int c = 0; c < feat_dim; ++c) { |
| mel_padded[c * T_model + t] = mel[t * feat_dim + c] * inv_scale; |
| } |
| } |
|
|
| |
| std::vector<float> real_spec, imag_spec; |
| int spec_size; |
|
|
| if (m_session_head) { |
| |
| int in_idx = m_session->GetInputIndex("mel"); |
| if (in_idx < 0) in_idx = 0; |
| m_session->SetInput(mel_padded.data(), in_idx); |
| if (m_session->RunSync() != 0) { printf("Vocoder backbone failed\n"); return -1; } |
|
|
| int feat_idx = m_session->GetOutputIndex("features"); |
| if (feat_idx < 0) feat_idx = 0; |
| std::vector<float> features(m_session->GetOutputSize(feat_idx) / sizeof(float)); |
| m_session->GetOutput(features.data(), feat_idx); |
|
|
| int h_in = m_session_head->GetInputIndex("features"); |
| if (h_in < 0) h_in = 0; |
| m_session_head->SetInput(features.data(), h_in); |
| if (m_session_head->RunSync() != 0) { printf("Vocoder head failed\n"); return -1; } |
|
|
| int real_idx = m_session_head->GetOutputIndex("real"); |
| int imag_idx = m_session_head->GetOutputIndex("imag"); |
| if (real_idx < 0) real_idx = 0; |
| if (imag_idx < 0) imag_idx = 1; |
| spec_size = m_session_head->GetOutputSize(real_idx); |
| real_spec.resize(spec_size / sizeof(float)); |
| imag_spec.resize(spec_size / sizeof(float)); |
| m_session_head->GetOutput(real_spec.data(), real_idx); |
| m_session_head->GetOutput(imag_spec.data(), imag_idx); |
| } else { |
| |
| int in_idx = m_session->GetInputIndex("mel"); |
| if (in_idx < 0) in_idx = 0; |
| m_session->SetInput(mel_padded.data(), in_idx); |
| if (m_session->RunSync() != 0) { printf("Vocoder NPU inference failed\n"); return -1; } |
|
|
| int real_idx = m_session->GetOutputIndex("real"); |
| int imag_idx = m_session->GetOutputIndex("imag"); |
| if (real_idx < 0) real_idx = 0; |
| if (imag_idx < 0) imag_idx = 1; |
| spec_size = m_session->GetOutputSize(real_idx); |
| real_spec.resize(spec_size / sizeof(float)); |
| imag_spec.resize(spec_size / sizeof(float)); |
| m_session->GetOutput(real_spec.data(), real_idx); |
| m_session->GetOutput(imag_spec.data(), imag_idx); |
| } |
|
|
| |
| int out_len = (T - 1) * hop + n_fft; |
| audio.assign(out_len, 0.0f); |
| std::vector<float> envelope(out_len, 0.0f); |
|
|
| std::vector<kiss_fft_cpx> cx_in(n_fft), cx_out(n_fft); |
|
|
| for (int t = 0; t < T; ++t) { |
| int spec_offset = t * n_freqs; |
|
|
| |
| cx_in[0].r = real_spec[spec_offset + 0]; |
| cx_in[0].i = 0.0f; |
| for (int k = 1; k < n_freqs - 1; ++k) { |
| cx_in[k].r = real_spec[spec_offset + k]; |
| cx_in[k].i = imag_spec[spec_offset + k]; |
| cx_in[n_fft - k].r = real_spec[spec_offset + k]; |
| cx_in[n_fft - k].i = -imag_spec[spec_offset + k]; |
| } |
| cx_in[n_freqs - 1].r = real_spec[spec_offset + (n_freqs - 1)]; |
| cx_in[n_freqs - 1].i = 0.0f; |
|
|
| rnn_ifft(m_ifft_cfg, cx_in.data(), cx_out.data(), 0); |
|
|
| |
| int pos = t * hop; |
| for (int n = 0; n < n_fft; ++n) { |
| float sample = (cx_out[n].r / n_fft) * m_window[n]; |
| int p = pos + n; |
| if (p < out_len) { |
| audio[p] += sample; |
| envelope[p] += m_window_sq[n]; |
| } |
| } |
| } |
|
|
| |
| for (int i = 0; i < out_len; ++i) { |
| if (envelope[i] > 1e-10f) { |
| audio[i] /= envelope[i]; |
| } |
| } |
|
|
| |
| int pad = n_fft / 2; |
| int trim_len = out_len - 2 * pad; |
| if (trim_len > 0) { |
| std::vector<float> trimmed(audio.begin() + pad, audio.begin() + pad + trim_len); |
| audio = std::move(trimmed); |
| } |
|
|
| return 0; |
| } |
|
|