// Engine TTS Qwen3 in-process : load une fois (modèles + fixtures + tokenizer), // synthesize N fois (texte FR -> WAV). Conçu pour être appelé depuis : // - tts_pipeline.cpp (CLI dev) // - kazeia_tts_jni.cpp (bridge Kotlin) // - test_jni_tts.cpp (harness sans VM Java) // // État load-once (poids talker ~1.6 GB, CP ~250 MB, decoder ~325 MB, vocab ~50 MB, // fixtures text_embed ~1.2 GB) = ~3.5 GB cumulé sur tablette. Une seule instance // par process recommandée. #pragma once #include #include #include struct TtsEngine; // opaque struct TtsEngineLoadCfg { const char * talker_gguf = nullptr; // talker_f32.gguf const char * vocab_gguf = nullptr; // ex: Qwen3-4B-Q4_0.gguf (vocab_only) ; nullptr -> pas de tokenize const char * dump_dir = nullptr; // contient cp_f16.gguf, cp_heads.bin, cp_codec_embs.bin, // qwen3tts_decoder.gguf, text_embed.bin, tp_fc1/2_*, talker_tok_embd.bin, // damien_xvector.bin, manifest_text.txt bool use_htp = false; // true -> HTP0 prefill (option C), false -> CPU pur int n_threads = 6; bool cp_use_cache = true; // KV cache CP (cp_predict_cached) ; false -> cp_predict oracle // Clonage vocal embarqué (optionnel). Si renseignés, le speaker encoder est chargé en // RAM (~17 MB f16) et permet de calculer un x_vector depuis n'importe quel WAV via // tts_engine_encode_speaker_wav(). Sans ces deux fichiers, on garde le x_vector fixture // (damien_xvector.bin) chargé une fois pour toutes. const char * speaker_encoder_gguf = nullptr; // ex: speaker_encoder.gguf (17 MB f16) const char * mel_basis_path = nullptr; // mel_basis_qwen3tts.bin (128*513 f32) }; struct TtsSynthesizeCfg { const char * text = nullptr; const char * out_wav_path = nullptr; int max_steps = 256; uint32_t seed = 42; // Override du x_vector pour ce synth (clonage vocal). Si non-nullptr ET de longueur // hidden (1024 en pratique), remplace le x_vector chargé. nullptr -> on garde le // fixture chargé au load. Mémoire owned par l'appelant pour la durée du synth. const float * xvector_override = nullptr; int xvector_override_len = 0; // Sampling : défauts validés à l'écoute (panel A/B) sur tts_engine + chraac subproc. // top_p=0.95 + rep_penalty=1.10 : sweet spot moins robotique, moins de pauses 690 // doublées sur CB0 (cf TU.2 attracteurs identifiés). Ancien défaut top_p=1.0 // rep=1.05 disponible en surcharge via env KZTTS_REPP / KZTTS_TOPP. float cp_temp = 0.9f; int cp_top_k = 50; float cp_top_p = 0.95f; float cp_rep_penalty = 1.10f; float talker_temp = 0.9f; int talker_top_k = 50; float talker_top_p = 0.95f; float talker_rep_penalty = 1.10f; }; struct TtsSynthesizeResult { int err = 0; // 0=ok ; <0 erreur (load/tokenize/decode) int frames = 0; // N frames audio (12.5 Hz) double audio_s = 0; // = frames/12 double total_s = 0; // prefill + talker_loop + decoder (hors writing WAV) double prefill_s = 0; double talker_loop_s = 0; double cp_loop_s = 0; double decoder_s = 0; // Streaming TTFB (Time To First Byte audio) : si KZTTS_STREAM_CHUNK > 0, mesure le // délai entre le début de tts_engine_synthesize et le 1er chunk audio prêt à écrire. // 0 si streaming désactivé. Pour comparer à total_s : audio commence à sortir bien // avant que tout soit fini si streaming actif. double ttfb_s = 0; int n_chunks = 0; // nombre de chunks decoder exécutés (1 = pas de streaming) }; // Charge tout. Retourne nullptr en cas d'échec. // IMPORTANT : llama_backend_init() est appelé en interne (idempotent). TtsEngine * tts_engine_load(const TtsEngineLoadCfg & cfg); // Synthétise une phrase. Réinitialise la KV cache talker avant le prefill, donc // les appels sont indépendants (pas d'état conversationnel). TtsSynthesizeResult tts_engine_synthesize(TtsEngine * eng, const TtsSynthesizeCfg & cfg); // Libère tout. void tts_engine_free(TtsEngine * eng); // Clonage vocal embarqué : encode un fichier WAV (mono 16-bit 24kHz) en x_vector[1024] f32. // Requiert que tts_engine_load() ait reçu speaker_encoder_gguf + mel_basis_path. // Renvoie vecteur vide si encoder non chargé ou échec. std::vector tts_engine_encode_speaker_wav(TtsEngine * eng, const char * wav_path); // Idem mais depuis un buffer waveform en mémoire (mono 24kHz, f32 [-1..1]). std::vector tts_engine_encode_speaker_waveform(TtsEngine * eng, const float * wav, int n_samples);