Kazeia-engine/dist/jni/decoder_subprocess.cpp

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;
}