File size: 7,791 Bytes
28d160d | 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 | // HiFT 声码器宿主侧 DSP(numpy 版 hift_vocoder.py 的 C++ 移植)。
// 与 torch 原版逐位验证:源激励 max diff ~5e-10、STFT ~5.6e-9、ISTFT ~1.3e-7。
#pragma once
#include <cmath>
#include <complex>
#include <random>
#include <vector>
namespace hift {
constexpr int kSr = 24000;
constexpr int kTMel = 198; // 静态 mel 帧
constexpr float kPadVal = -11.0f; // mel 尾部补静音值
constexpr int kNfft = 16;
constexpr int kHop = 4;
constexpr int kHarmonics = 9; // 8 次谐波 + 基频
constexpr float kSineAmp = 0.1f;
constexpr float kNoiseStd = 0.003f;
constexpr float kVoicedTh = 10.0f;
constexpr int kUpsample = 480;
constexpr int kSrcLen = kTMel * kUpsample; // 95040
constexpr int kStftFrames = kSrcLen / kHop + 1; // 23761
constexpr float kAudioLimit = 0.99f;
using Cx = std::complex<float>;
inline std::vector<float> Hann16() {
std::vector<float> w(kNfft);
for (int i = 0; i < kNfft; ++i) {
w[i] = static_cast<float>(0.5 - 0.5 * std::cos(2.0 * M_PI * i / kNfft));
}
return w; // periodic hann == torch hann_window(16)
}
// 16 点 FFT(radix-2,直接实现,避免依赖 dsp.hpp 的 Bluestein 大 plan)
inline void Fft16(std::vector<Cx>& a, bool inverse) {
const int n = 16;
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(a[i], a[j]);
}
for (int len = 2; len <= n; len <<= 1) {
const double ang = (inverse ? 2.0 : -2.0) * M_PI / len;
const std::complex<double> wlen(std::cos(ang), std::sin(ang));
for (int i = 0; i < n; i += len) {
std::complex<double> w(1.0, 0.0);
for (int j = 0; j < len / 2; ++j) {
Cx u = a[i + j];
Cx v = a[i + j + len / 2] *
Cx(static_cast<float>(w.real()), static_cast<float>(w.imag()));
a[i + j] = u + v;
a[i + j + len / 2] = u - v;
w *= wlen;
}
}
}
if (inverse) {
for (auto& x : a) x /= static_cast<float>(n);
}
}
// f0[198] -> 源激励 s[95040](SineGen + SourceModuleHnNSF)。
// linear_w: 9 个谐波权重;linear_b: 偏置;phase/noise 由调用方生成(随机)。
inline std::vector<float> SourceFromF0(const std::vector<float>& f0,
const std::vector<float>& phase,
const std::vector<float>& noise,
const std::vector<float>& linear_w,
float linear_b) {
std::vector<float> f0_up(kSrcLen);
for (int i = 0; i < kTMel; ++i) {
const float v = f0[i];
for (int j = 0; j < kUpsample; ++j) f0_up[i * kUpsample + j] = v;
}
// F_mat[h][n] = f0_up * (h+1) / sr;theta = 2π * cumsum mod 1
std::vector<float> sine_wavs(kHarmonics * kSrcLen);
for (int h = 0; h < kHarmonics; ++h) {
// torch 原版用 float32 做 F_mat/cumsum(相位累加会随时间漂移,但这是模型自带行为);
// 这里必须用 float32 逐位对齐,否则源激励与 torch/numpy 参考偏差可达 0.1
float phase_acc = 0.0f;
const double twopi = 2.0 * M_PI;
for (int n = 0; n < kSrcLen; ++n) {
// 与 numpy 相同运算顺序:(f0_up * (h+1)) / SR,全程 float32
phase_acc += f0_up[n] * static_cast<float>(h + 1) / static_cast<float>(kSr);
double th = twopi * std::fmod(static_cast<double>(phase_acc), 1.0) + phase[h];
sine_wavs[h * kSrcLen + n] =
static_cast<float>(kSineAmp * std::sin(th));
}
}
std::vector<float> s(kSrcLen, 0.0f);
for (int n = 0; n < kSrcLen; ++n) {
const bool voiced = f0_up[n] > kVoicedTh;
const float noise_amp = voiced ? kNoiseStd : kSineAmp / 3.0f;
float merged = 0.0f;
for (int h = 0; h < kHarmonics; ++h) {
const float sw = sine_wavs[h * kSrcLen + n] * (voiced ? 1.0f : 0.0f) +
noise_amp * noise[h * kSrcLen + n];
merged += linear_w[h] * sw;
}
s[n] = std::tanh(merged + linear_b);
}
return s;
}
// 源激励 s[95040] -> s_stft[18 * 23761](9 real + 9 imag,16 点 STFT center=True)
inline void SourceStft(const std::vector<float>& s,
const std::vector<float>& hann,
std::vector<float>& s_stft) {
constexpr int pad = kNfft / 2; // 8
std::vector<float> xp(kSrcLen + 2 * pad);
// reflect pad
for (int i = 0; i < pad; ++i) {
xp[pad - 1 - i] = s[i + 1];
xp[kSrcLen + pad + i] = s[kSrcLen - 2 - i];
}
for (int i = 0; i < kSrcLen; ++i) xp[pad + i] = s[i];
s_stft.assign(2 * (kNfft / 2 + 1) * kStftFrames, 0.0f);
std::vector<Cx> frame(kNfft);
for (int t = 0; t < kStftFrames; ++t) {
for (int k = 0; k < kNfft; ++k) {
frame[k] = Cx(xp[t * kHop + k] * hann[k], 0.0f);
}
Fft16(frame, false);
for (int b = 0; b < kNfft / 2 + 1; ++b) {
s_stft[b * kStftFrames + t] = frame[b].real();
s_stft[(9 + b) * kStftFrames + t] = frame[b].imag();
}
}
}
// mag/phase(各 9 x 23761)-> wav[95040](16 点 ISTFT,overlap-add)
inline std::vector<float> Istft16(const std::vector<float>& mag,
const std::vector<float>& ph,
const std::vector<float>& hann) {
constexpr int pad = kNfft / 2;
const int n = (kStftFrames - 1) * kHop + kNfft; // 95056
std::vector<double> out(n, 0.0), wsum(n, 0.0);
std::vector<Cx> spec(9);
std::vector<Cx> frame(kNfft);
for (int t = 0; t < kStftFrames; ++t) {
for (int b = 0; b < 9; ++b) {
const double m = std::exp(static_cast<double>(mag[b * kStftFrames + t]));
const double p = std::sin(static_cast<double>(ph[b * kStftFrames + t]));
spec[b] = Cx(static_cast<float>(m * std::cos(p)),
static_cast<float>(m * std::sin(p)));
}
// irfft:补齐共轭镜像频点(bins 9..15 = conj(bin 16-b)),否则逆变换结果错误
for (int k = 0; k < 9; ++k) frame[k] = spec[k];
for (int k = 9; k < kNfft; ++k) frame[k] = std::conj(spec[kNfft - k]);
Fft16(frame, true); // irfft
const int st = t * kHop;
for (int k = 0; k < kNfft; ++k) {
const double v = frame[k].real() * hann[k];
out[st + k] += v;
wsum[st + k] += static_cast<double>(hann[k]) * hann[k];
}
}
std::vector<float> y(kSrcLen);
for (int i = 0; i < kSrcLen; ++i) {
double v = wsum[pad + i] > 1e-8 ? out[pad + i] / wsum[pad + i] : 0.0;
v = std::max(-static_cast<double>(kAudioLimit),
std::min(static_cast<double>(kAudioLimit), v));
y[i] = static_cast<float>(v);
}
return y;
}
// 便捷封装:mel(1,80,frames<=198) -> wav(frames*480)
inline std::vector<float> Synth(const std::vector<float>& mel_80xf,
int frames,
const std::vector<float>& linear_w,
float linear_b,
const std::vector<float>& f0,
const std::vector<float>& raw_mag,
const std::vector<float>& raw_ph) {
// 该函数仅用于演示接口;实际流程由 server 分步调用 f0/decode 模型。
(void)mel_80xf; (void)frames; (void)linear_w; (void)linear_b; (void)f0;
(void)raw_mag; (void)raw_ph;
return std::vector<float>();
}
} // namespace hift
|