Richard Loyer
82d206137f
chantier B TTS #8 : speaker encoder embedded + clonage in-process
...
SE.3+SE.4+SE.5 : ECAPA-TDNN ggml C++ bit-exact + intégration TtsEngine.
speaker_encoder.{h,cpp} :
- API publique : speaker_encoder_load / encode_wav / encode_waveform / free
- mel C++ (FFT radix-2, Hann, reflect pad, librosa basis) bit-exact Python
- ECAPA-TDNN forward : conv0 + 3 SE-Res2Net + MFA + ASP + FC -> x_vector[1024]
- validation : cos=0.9997 vs Python ref (damien_5s.wav)
- SPK_STANDALONE -> binaire CLI kazeia_speaker_encode
tts_engine :
- TtsEngineLoadCfg.speaker_encoder_gguf + mel_basis_path (load-time opt-in)
- TtsSynthesizeCfg.xvector_override (per-call, RAII restore)
- tts_engine_encode_speaker_wav / _waveform exposés
tts_pipeline : KZTTS_SPK_GGUF + KZTTS_MEL_BASIS + KZTTS_REF_WAV -> clonage
in-process (un seul binaire, plus de subprocess swap manuel).
Pièges trouvés en route (documentés CLAUDE.md project_tts_*) :
- ggml_conv_1d exige weights F16 -> cast au call site
- input layout : data[c*T+t] (ggml ne[0]=T fastest) pas memcpy [T,C]
- refs Python en bytes [C,T] = ggml [T,C] côté cpp
- RIFF reader chunk-parsing (ffmpeg = LIST/JUNK avant data)
Test E2E : 4 voix (damien/amir/elodie/zelda) clonées à partir de WAV ref 5s,
toutes audibles et distinctes.
Reste : JNI nativeSynthesizeWithReference (refactor mécanique, prochaine session).
2026-05-31 15:54:29 +02:00
Richard Loyer
19ac123afa
TU sampler tuning : top_p 1.0->0.95 + rep_penalty 1.05->1.10 nouveaux défauts
...
Validation à l'écoute (panel A/B 4 combos sur 'Bonsoir, comment tu te sens
ce soir ?'). Verdict utilisateur : combinaison TOPP_0.95_REPP_1.10 sonne le
moins robotique.
Analyse complémentaire TU.2 (dump codes via KZTTS_DUMP_CODES=path) sur 4
phrases panel a confirmé :
- code 690 CB0 = attracteur silence (5.9% du panel, doublé systématiquement
en début de phrase, jusqu'à 4 répétitions consécutives mesurées)
- rep_penalty 1.05 trop doux pour casser ces 690 690
- rep_penalty 1.10 plus nucleus 0.95 -> top_p coupe la queue improbable
+ rep défavorise les codes récents -> moins de pause robotique
Nouveaux défauts (tts_engine.h TtsSynthesizeCfg + tts_pipeline env defaults
+ TtsEngine.kt TtsSampling) :
cp_top_p = 0.95f (était 1.0)
cp_rep_penalty = 1.10f (était 1.05)
talker_top_p = 0.95f (était 1.0)
talker_rep_penalty = 1.10f (était 1.05)
cp_temp, top_k, talker_temp/top_k inchangés (0.9, 50).
Mesure panel 4 phrases avec nouveaux défauts + decoder chraac subproc :
'Bonjour Kazeia' : N=29 RTF 2.19
'Bonsoir, comment tu te sens ce soir ?' : N=48 RTF 2.21
'Je rumine la nuit.' : N=34 RTF 2.21
'Comment puis-je t'aider aujourd'hui ?' : N=29 RTF 2.27
RTF stable ~2.2. Compression légère sur phrases courtes (29 vs 33 baseline),
expansion sur phrases interrogatives ('Bonsoir' N=48 vs 41). Cohérent
avec top_p resserré qui choisit des codes plus pertinents.
KZTTS_DUMP_CODES=path ajouté dans tts_engine pour analyse future
(int32 binaire [N,16] time-major).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 11:18:25 +02:00
Richard Loyer
d6acf378d5
POC streaming TTFB ÷4-6 (KZTTS_STREAM_CHUNK=K)
...
Concept validé : decoder appelé incrementalement sur codes accumulés tous
les K frames. Premier appel donne TTFB rapide.
TtsSynthesizeResult ajoute ttfb_s + n_chunks. tts_engine_synthesize
détecte KZTTS_STREAM_CHUNK > 0 et appelle decoder.forward(codes_so_far)
tous les K codes, accumule la portion nouvelle dans wav_stream.
Mesure Pad3 phrase 'Bonjour Kazeia' (33 frames) :
baseline : TTFB 6.80s, RTF 2.47
KZTTS_STREAM_CHUNK=8 : TTFB 1.90s (-72%), 5 chunks, total RTF 8.67 (×3.5 cost)
KZTTS_STREAM_CHUNK=4 : TTFB 1.13s (-83%), 9 chunks, total RTF 13.82 (×5.6 cost)
Limitations connues du POC :
1. Total time explose : recompute decoder sur N_so_far à chaque chunk =
somme des coûts O(N_chunks × N_avg) au lieu de O(N_total).
2. WAV pas bit-exact vs baseline : decoder.forward(K) ne match pas le
prefix de decoder.forward(N). Probable conv_transpose_1d qui regarde
au-delà du strict causal kernel/stride. À comparer à l'oreille.
POC opt-in (KZTTS_STREAM_CHUNK=0 par défaut = path baseline intact).
Pour rendre utilisable en prod (sessions suivantes) :
- Refactor decoder avec state KV cache (pre_transformer en KV cache style
comme cp_forward_cached_step + BigVGAN avec context buffer ~5 frames input)
- Threading 2-thread : generator (talker+CP) en parallèle de decoder
- Cible : TTFB ~500ms, total time ≈ baseline + overhead minimal
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 22:56:14 +02:00
Richard Loyer
930f3c8c1f
chantier B TTS #11 : libkazeia_tts.so + Kotlin wrapper, JNI validé bout-en-bout
...
Refactor: tts_engine.{h,cpp} = API publique de l'engine TTS (load une fois,
synthesize N fois, free). État load-once dans struct TtsEngine (talker
model+ctx, CPState, Decoder, KzTextTokenizer, fixtures + embeds spéciaux
+ role_proj pré-calculé). KV cache talker reset via llama_memory_clear
au début de chaque synthesize -> appels indépendants.
tts_pipeline.cpp devient un thin CLI dessus (refonte sans changement de
sortie : WAV md5 identique au pré-refactor sur 'Bonjour je m'appelle Kazeia').
JNI bridge: kazeia_tts_jni.cpp expose 3 fonctions :
Java_com_kazeia_tts_TtsJni_nativeLoad / Synthesize / Free
Signatures alignées avec TtsEngine.kt (companion loadLibrary 'kazeia_tts').
Build: build_kazeia_tts.sh -> b-jni/libkazeia_tts.so (~900 KB).
Test_engine_2calls (sans JNI) : 3 synth sur même instance, KV reset OK,
2 appels identiques -> WAV md5 identique, appel 3 différent -> codes
différents.
Test_jni_tts (harness JNI sans VM Java) : dlopen libkazeia_tts.so, JNIEnv
mock minimal (GetStringUTFChars/Release + NewIntArray/SetIntArrayRegion),
appels load + 2 synth + free. WAV md5 identiques aux runs directs. exit=0.
Empreinte tablette mesurée: ~3.5 GB par instance (talker f32 1.6 GB + CP
f16 mixte 250 MB + decoder 325 MB + vocab Qwen3 vocab_only 50 MB + fixtures
text_embed/tp_* 1.2 GB). Une instance par process.
RTF stable autour de 3.0 (CPU 6t), inchangé vs avant refonte.
Reste sur la liste initiale : tuning sampling (itératif à l'oreille).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 21:54:21 +02:00