97 lines
4.7 KiB
C++
97 lines
4.7 KiB
C++
// 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 <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<float> 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<float> tts_engine_encode_speaker_waveform(TtsEngine * eng, const float * wav, int n_samples);
|