// 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 // 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 #include #include #include #include #include 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 \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 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 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; }