Kazeia-engine/dist/jni/tts_pipeline.cpp

95 lines
4.1 KiB
C++

// CLI dev autour de tts_engine. La logique est maintenant dans tts_engine.{h,cpp}
// (réutilisée par le JNI). Ce binaire reste pour les bench A/B et la régression.
//
// Usage:
// tts_pipeline <talker_gguf> <dump_dir> <out.wav> [cpu|htp] [max_steps]
//
// Variables d'environnement :
// KZTTS_TEXT, KZTTS_VOCAB_GGUF : texte arbitraire au lieu de input_ids_full.bin (fixture)
// (le mode fixture est conservé pour bench/régression)
// KZTTS_CP_CACHE : 1 (défaut) = cp_predict_cached, 0 = cp_predict oracle
// KZTTS_SEED : seed sampling (défaut 42)
// KZTTS_THREADS : threads CPU (défaut 6)
// KZTTS_TEMP / TOPK / TOPP / REPP : sampling Talker
// KZTTS_CP_TEMP / CP_TOPK / CP_TOPP / CP_REPP : sampling CP
#include "tts_engine.h"
#include "kazeia_text_tokenizer.h"
#include "llama.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <string>
#include <vector>
static float env_f(const char* k, float dflt) { const char* v = getenv(k); return v ? (float)atof(v) : dflt; }
static int env_i(const char* k, int dflt) { const char* v = getenv(k); return v ? atoi(v) : dflt; }
int main(int argc, char** argv) {
if (argc < 4) {
printf("usage: %s <talker_gguf> <dump_dir> <out.wav> [cpu|htp] [max_steps]\n", argv[0]);
printf(" Texte arbitraire :\n");
printf(" KZTTS_VOCAB_GGUF=/path/qwen3.gguf KZTTS_TEXT=\"phrase libre\" %s ...\n", argv[0]);
printf(" Mode fixture (input_ids_full.bin) : aucune variable d'env requise.\n");
return 1;
}
const char* talker_gguf = argv[1];
std::string D = argv[2]; if (D.back() != '/') D += '/';
const char* out_wav = argv[3];
const bool use_htp = (argc >= 5 && !strcmp(argv[4], "htp"));
const int max_steps = (argc >= 6) ? atoi(argv[5]) : 256;
const char* kz_text = getenv("KZTTS_TEXT");
const char* kz_vocab_gguf = getenv("KZTTS_VOCAB_GGUF");
if (!kz_text || !kz_vocab_gguf) {
fprintf(stderr, "tts_pipeline: mode fixture (input_ids_full.bin) demandé mais non implémenté\n");
fprintf(stderr, " -> poser KZTTS_TEXT et KZTTS_VOCAB_GGUF pour utiliser le pipeline live.\n");
fprintf(stderr, " (Le mode fixture a été déporté en branche fixture pour test_engine séparé.)\n");
return 2;
}
TtsEngineLoadCfg lc;
lc.talker_gguf = talker_gguf;
lc.vocab_gguf = kz_vocab_gguf;
lc.dump_dir = D.c_str();
lc.use_htp = use_htp;
lc.n_threads = env_i("KZTTS_THREADS", 6);
lc.cp_use_cache = env_i("KZTTS_CP_CACHE", 1) != 0;
auto * eng = tts_engine_load(lc);
if (!eng) { fprintf(stderr, "tts_engine_load FAIL\n"); return 3; }
TtsSynthesizeCfg sc;
sc.text = kz_text;
sc.out_wav_path = out_wav;
sc.max_steps = max_steps;
sc.seed = env_i("KZTTS_SEED", 42);
sc.cp_temp = env_f("KZTTS_CP_TEMP", 0.9f);
sc.cp_top_k = env_i("KZTTS_CP_TOPK", 50);
sc.cp_top_p = env_f("KZTTS_CP_TOPP", 0.95f);
sc.cp_rep_penalty = env_f("KZTTS_CP_REPP", 1.10f);
sc.talker_temp = env_f("KZTTS_TEMP", 0.9f);
sc.talker_top_k = env_i("KZTTS_TOPK", 50);
sc.talker_top_p = env_f("KZTTS_TOPP", 0.95f);
sc.talker_rep_penalty = env_f("KZTTS_REPP", 1.10f);
auto R = tts_engine_synthesize(eng, sc);
if (R.err) { fprintf(stderr, "tts_engine_synthesize FAIL err=%d\n", R.err); tts_engine_free(eng); return 4; }
printf("=== TTS : N=%d frames (audio %.2fs) en %.3fs (RTF %.2f) ===\n",
R.frames, R.audio_s, R.total_s, R.total_s / R.audio_s);
printf(" prefill %.3fs | talker_loop %.3fs | cp_loop %.3fs | decoder %.3fs\n",
R.prefill_s, R.talker_loop_s, R.cp_loop_s, R.decoder_s);
printf(" per-frame talker=%.1fms cp=%.1fms\n",
R.talker_loop_s * 1000.0 / R.frames, R.cp_loop_s * 1000.0 / R.frames);
if (R.ttfb_s > 0) {
printf(" STREAMING : TTFB=%.3fs (premier chunk audio prêt) | %d chunks\n",
R.ttfb_s, R.n_chunks);
}
printf("WAV -> %s\n", out_wav);
tts_engine_free(eng);
return 0;
}