91 lines
3.5 KiB
C++
91 lines
3.5 KiB
C++
// Sous-process décodeur TTS standalone, linké STATIQUE contre chraac-llama/ggml
|
|
// (RTF decoder 0.96 historique vs 1.47 qualcomm fork). Utilisé par tts_engine
|
|
// quand KZTTS_DECODER_SUBPROC=path/kazeia_decoder_chraac est posé.
|
|
//
|
|
// Le talker (sensible aux différences llama.cpp entre forks) reste dans le process
|
|
// principal sur ql/ggml (qualcomm). Le decoder ici tourne dans un process séparé
|
|
// linké static chraac → pas de mismatch ABI dans le binaire principal.
|
|
//
|
|
// Usage : kazeia_decoder_chraac <decoder.gguf> <codes.bin> <T> <out.wav>
|
|
// codes.bin : int32 [T, 16] time-major (= ce que tts_engine produit)
|
|
//
|
|
// Communication via FICHIERS (codes binaires en input, WAV en output). Simple,
|
|
// debuggable. Pour perf future : pipe stdin/stdout ou shared memory.
|
|
#include "decoder.h"
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cmath>
|
|
#include <fstream>
|
|
#include <vector>
|
|
|
|
using namespace kazeia::tts;
|
|
|
|
// Copié de tts_engine.cpp (libqwen3tts-decoder.a ne l'expose pas).
|
|
static bool write_wav_pcm16_mono(const char* path, const float* samples, size_t N, int sr = 24000) {
|
|
FILE* f = fopen(path, "wb");
|
|
if (!f) return false;
|
|
const uint32_t data_bytes = (uint32_t)(N * 2);
|
|
const uint32_t riff_size = 36 + data_bytes;
|
|
auto w16 = [&](uint16_t v){ fwrite(&v, 2, 1, f); };
|
|
auto w32 = [&](uint32_t v){ fwrite(&v, 4, 1, f); };
|
|
fwrite("RIFF", 1, 4, f); w32(riff_size); fwrite("WAVE", 1, 4, f);
|
|
fwrite("fmt ", 1, 4, f); w32(16); w16(1); w16(1);
|
|
w32((uint32_t)sr); w32((uint32_t)sr * 2); w16(2); w16(16);
|
|
fwrite("data", 1, 4, f); w32(data_bytes);
|
|
for (size_t i = 0; i < N; ++i) {
|
|
float v = samples[i]; if (v > 1.f) v = 1.f; else if (v < -1.f) v = -1.f;
|
|
int16_t pcm = (int16_t)std::lround(v * 32767.0f);
|
|
fwrite(&pcm, 2, 1, f);
|
|
}
|
|
fclose(f); return true;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc < 5) {
|
|
fprintf(stderr, "usage: %s <decoder.gguf> <codes.bin> <T> <out.wav>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
const char* model_path = argv[1];
|
|
const char* codes_path = argv[2];
|
|
const int T = atoi(argv[3]);
|
|
const char* out_wav = argv[4];
|
|
|
|
// Read codes time-major [T, 16]
|
|
std::ifstream f(codes_path, std::ios::binary | std::ios::ate);
|
|
if (!f) { fprintf(stderr, "open %s FAIL\n", codes_path); return 2; }
|
|
const size_t n_bytes = (size_t)f.tellg();
|
|
const size_t n_expected = (size_t)T * 16 * sizeof(int32_t);
|
|
if (n_bytes != n_expected) {
|
|
fprintf(stderr, "codes size mismatch: got %zu bytes, expected %zu (T=%d)\n",
|
|
n_bytes, n_expected, T);
|
|
return 3;
|
|
}
|
|
std::vector<int32_t> codes_time_major(T * 16);
|
|
f.seekg(0);
|
|
f.read((char*)codes_time_major.data(), n_expected);
|
|
f.close();
|
|
|
|
// Transpose [T, 16] -> [16, T] (codebook-major attendu par decoder.forward)
|
|
std::vector<int32_t> codes_dec(16 * T);
|
|
for (int t = 0; t < T; ++t)
|
|
for (int c = 0; c < 16; ++c)
|
|
codes_dec[c * T + t] = codes_time_major[t * 16 + c];
|
|
|
|
// Load decoder + forward
|
|
Decoder dec;
|
|
if (!dec.load(model_path)) {
|
|
fprintf(stderr, "decoder.load(%s) FAIL\n", model_path);
|
|
return 4;
|
|
}
|
|
auto wav = dec.forward(codes_dec, T);
|
|
|
|
if (!write_wav_pcm16_mono(out_wav, wav.data(), wav.size(), 24000)) {
|
|
fprintf(stderr, "write WAV %s FAIL\n", out_wav);
|
|
return 5;
|
|
}
|
|
fprintf(stderr, "kazeia_decoder_chraac OK: T=%d -> %zu samples -> %s\n",
|
|
T, wav.size(), out_wav);
|
|
return 0;
|
|
}
|