Commit Graph

9 Commits

Author SHA1 Message Date
Richard Loyer 8f6253204c chantier B STT #2 : S2 + S3 livrés (stt_engine ORT QNN + JNI + façade Kotlin + doc)
S2 port complet WhisperHybridEngine.kt -> C++ :
 - stt_engine.cpp impl complète (ORT 1.24.3 + QNN EP HTP V79, decoder KV-cache
   autoregressif 200 steps, override translate->transcribe, parsers JSON inline
   pour mel_filters et vocab.json, tokenizer BPE byte-level GPT-2/Whisper)
 - kazeia_stt_cli : binaire test (1.3 MB statique)
 - Bench bit-correct vs prod : transcription FR identique sur 3 runs déterministes
   ("Elle mat est un casque de chantier connecté et modulable.")
 - Perf SM8750 : RTF 0.41-0.43 (vs prod 0.51), RAM peak 378 MB (vs 545 MB)
 - Mel C++ 60 ms (FFT) vs prod 189 ms (DFT). Decoder per-token 100 ms vs 23 ms
   prod (overhead memcpy KV; optim IO bindings ORT non-bloquant)

S3 JNI + façade + doc :
 - kazeia_stt_jni.cpp : 7 fonctions JNI exportées (nativeLoad/Transcribe/Free
   + nativeVadNew/Push/Reset/Free). Sortie pipe-séparée parseable côté Kotlin.
 - libkazeia_stt.so : 168 KB SHARED, prête pour jniLibs/arm64-v8a/
 - SttEngine.kt : façade Kotlin minimale (~100 l) parallèle à TtsEngine.kt
   (SttJni / SttEngine / SttVad / SttResult)
 - Suppression côté app : WhisperHybridEngine.kt (527L), MelExtractor.kt,
   VadStage.kt, mel_extractor.cpp, libmel_extractor.so (documentée)

Doc livrable dev :
 - STT_INTEGRATION.md (10 KB) : guide complet migration + perf + erreurs +
   bench rapide + roadmap optim
 - KAZEIA_ENGINE_OVERVIEW.md : vue globale moteur unifié (LLM + TTS + STT),
   architecture des libs, API Kotlin unifiée, empreinte cascade complète
 - CLAUDE.md : section "STT unifié engine (31/05)" + verdict A invalidé

Runtime ORT 1.24.3 + QNN libs : extraites cache Gradle Maven (cf README_ORT.md
+ README_QNN.md dans dist/lib/). Pas de tarball ext. Modèles whisper-small-sm8750
prod inchangés (545 MB QAIRT contexte conservé).
2026-05-31 22:15:58 +02:00
Richard Loyer 4b43d9e3d7 chantier B STT #1 : S1 unification engine (mel partagé + skeleton stt + VAD C++)
S1.1 mel extractor unifié (kazeia_mel.{h,cpp}) :
 - Paramétrable : window (sym/periodic), compression (log/log10), post-norm
   (none/whisper), FFT auto radix-2 (zero-pad N_FFT pas puissance de 2)
 - Configs pré-faites : config_qwen3_tts_speaker() + config_whisper()
 - speaker_encoder.cpp migré dessus : régression damien cos=0.9997 et bit-exact
   (max abs diff = 0) vs avant. E2E clonage in-process intact (RTF inchangé).

S1.2 audit ORT C++ :
 - libonnxruntime.so + headers C++ trouvés dans cache Gradle (AAR Maven
   onnxruntime-android-qnn:1.24.3). API_VERSION=24, QNN EP intégré.
 - headers/ copiés dans dist/include/onnxruntime/ (versionné, ~1 MB)
 - libonnxruntime.so copiée dans dist/lib/ (gitignore, 20 MB, README pour
   reproduire l'extraction).

S1.3 skeleton stt_engine :
 - stt_engine.{h,cpp} : API parallèle à tts_engine.h
   (SttEngineLoadCfg / SttTranscribeCfg / SttTranscribeResult)
 - VAD RMS C++ complet (port de VadStage.kt prod : frame=1600, seuil=150,
   3 frames speech, 8 frames silence)
 - stt_engine_load + stt_engine_transcribe : stubs err=-99 jusqu'au port ORT
   QNN en S2 (encoder NPU + decoder loop KV-cache + tokenizer BPE)
 - Binaire kazeia_stt_test (1.2 MB statique) valide mel Whisper [80,3000] +
   VAD sur audio FR 5s, plage de valeurs cohérente.

Reste S2 : port encoder/decoder NPU via Ort::Session + QNN EP, port tokenizer
BPE byte-level, override transcribe. Quand S2 done : S3 = JNI + façade Kotlin
+ suppression libmel_extractor.so / VadStage.kt / WhisperHybridEngine.kt.
2026-05-31 19:19:28 +02:00
Richard Loyer e42e8f8464 SE.1+SE.2+SE.3 partiel : speaker encoder pipeline POC bout-en-bout
Chantier Option C : embarquer le speaker encoder Qwen3-TTS dans Kazeia-Engine
pour permettre le clonage vocal depuis n'importe quel WAV (5-10s) en
tout-tablette sans étape Python offline.

SE.1 Audit : speaker_encoder_weights.pt = ECAPA-TDNN classique
  - 76 tenseurs, 8.85M params (33.8 MB f32 -> 17 MB f16)
  - blocks.0 Conv1d(128->512 k=5) -> 3x SE-Res2Net (tdnn1+res2net+tdnn2+se)
    -> MFA concat(1536) -> ASP(stats pooling) -> FC(3072->1024)
  - Toutes ops supportées par ggml-cpu (Conv1d via im2col+matmul)

SE.2 Convert : dist/scripts/convert_speaker_encoder_to_gguf.py
  produit speaker_encoder.gguf (17 MB f16) + metadata mel config :
    n_mels=128, sr=24000, n_fft=1024, hop=256, win=1024, fmin=0, fmax=12000
  (bit-exact qwen_tts.core.models.modeling_qwen3_tts.mel_spectrogram)

SE.3 partiel : dist/jni/speaker_encoder.cpp + build_speaker_encoder.sh
  POC bout-en-bout sur tablette :
    WAV damien_5s (24kHz mono, 120k samples = 5s)
      -> mel 128 x 468 frames (FFT radix-2 + Hann + reflect pad + librosa basis)
      -> GGUF loaded (76 tensors)
      -> [TODO session suivante : forward ECAPA-TDNN complet, ~300-400 lignes]
      -> stub x_vector[1024] zeros écrit pour valider l'I/O

  Binaire : 53 KB statique, dépend juste de libggml dynamic.

  mel_basis librosa pré-calculé (24kHz/128 mel/0-12000Hz) dumpé en
  dist/models/mel_basis_qwen3tts.bin (262 KB f32 [128,513]) pour
  reproduire à l'identique torch.stft + librosa filter_bank.

À faire next session :
  - Valider mel C++ bit-exact vs torch.stft Python (sanity)
  - Implémenter forward ECAPA-TDNN : ggml_conv_1d, SE block (squeeze/excite),
    ASP (Attentive Statistics Pooling = stats globaux + tdnn attention),
    FC final.
  - Bit-match damien_xvector.bin référence Python.
  - JNI bridge + Kotlin cloneVoice(refWavPath): FloatArray

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 11:37:14 +02:00
Richard Loyer 2809b54943 CH chraac-llama : -18% RTF (2.43 -> 2.04) via swap ggml source
Découverte historique remise au jour : la mémoire 'TTS RTF<1 atteint via
chraac-llama' (projet d'origine /opt/Kazeia, 02/05) documente que le
décodeur TTS atteint RTF 0.96 avec ggml-cpu de chraac-llama (dev-refactoring
commit 897501a) vs 1.47 avec llama-upstream (= notre ql/ggml actuel).

Notre Kazeia-Engine builde contre ql/ggml = github.com/qualcomm/llama.cpp
fork = essentiellement llama-upstream + hexagon backend. NEON heads optim
sortie cette session a déjà compensé une partie mais pas tout.

Validation reproductibilité :
  Bench historique decoder seul (T=56) sur Pad3 chargée : RTF 0.935
  (legèrement mieux que 0.961 historique, batterie pleine + device froid).

Build chraac variant :
  - libs runtime : copies de /opt/Kazeia/chraac-llama/build-android-kazeia/bin
    poussées dans /data/local/tmp/kz-engine/bin-chraac
  - libqwen3tts-decoder.a : rebuild fresh contre chraac/ggml dans
    /opt/Kazeia/kazeia-tts-decoder-ggml/build-android-chraac-fresh
    (code actuel avec load_with_backends + snake_consts)
  - tts_pipeline_chraac : linké contre ces libs, headers
    /opt/Kazeia/chraac-llama/include + ggml/include

Mesure A/B Pad3 (KZTTS_THREADS=6 GGML_NUM_THREADS=8 seed=42) :

  Phrase 'Bonsoir, comment tu te sens ce soir ?' :
    Baseline ql/ggml  : N=41 RTF 2.43  talker=31.5  cp=70.2  decoder/frame=98.1 ms
    chraac-llama      : N=64 RTF 2.04  talker=31.2  cp=57.5  decoder/frame=76.9 ms
    Delta per-frame   :       -16%      -1%      -18%      -22%

  Phrase 'Bonjour Kazeia' :
    Baseline : N=33 RTF 2.47 / Chraac : N=64 RTF 2.02
    (Différence N = trajectoire diverge — précision f32 différente entre
    stacks fait que le sampler talker produit des codes différents et
    génère une phrase plus longue avant EOS)

Gains :
  - Decoder -22% confirmé (cohérent avec ratio chraac 0.96 / llama-upstream 1.47)
  - CP -18 à -21%  (ggml_graph_compute repack/sgemm ARM optimisé chraac)
  - Talker -1% (déjà au plafond NEON via llama.cpp)
  - RTF total -16 à -18%

Bug découvert (à traiter plus tard) :
  Phrase historique 56 codes 'Bonjour, je m'appelle Kazeia, je suis encore en phase de développement' (= ~60 frames) crash ggml_sin chraac (GGML_ABORT 'unsupported types' probable). Phrases moyennes (<= 50 frames)
  passent OK. Sans doute incompat de types entre snake_consts (ctx_const dans
  Decoder) et le path ggml_sin de chraac (qui n'a peut-être pas la même
  variante f16/f32 que les ops récentes).

Path actuel ql/ggml RESTE le défaut. Le chraac est build séparé tts_pipeline_chraac
opt-in pour A/B. Libs chraac dans dist/lib-chraac/ (gitignored).

À faire next :
  - Investiger crash ggml_sin chraac sur T>= ~50 frames
  - Décider : swap définitif Kazeia-Engine sur chraac (avec lib-chraac
    comme runtime principal) OU rester sur ql/ggml et patch chraac repack
    optims dedans

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 12:38:47 +02:00
Richard Loyer 27d893c73f Vulkan Adreno 830 testé : neutre au mieux, régresse au pire (commit aussi le code opt-in)
Re-analyse globale plateforme : le GPU n'avait JAMAIS été testé. Build
ggml-vulkan pour Android arm64 (NDK r27d + SPIRV-Headers + vulkan.hpp
téléchargés depuis github, libomp.so poussé sur tablette).

Adreno 830 détecté : uma=1 fp16=1 matrix cores=NONE (compute shaders seuls).

3 patterns testés :
  (1) Decoder weights Vulkan buft + CPU compute : NEUTRE
      RTF 2.45 -> 2.40-2.47 (bruit), WAV bit-exact
      Cause : UMA = même DRAM, pas de gain BW
  (2) Decoder + sched (GPU compute attendu) : tout CPU
      sched route UMA buffer -> CPU (logique is_host)
      Cause : sched ne déclenche pas l'offload pour buffers UMA
  (3) Talker offload 29/29 layers via llama.cpp : REGRESSION +128%
      Prefill 87->775 ms, talker 32->45 ms, decoder 3.3->6.6s
      Cause : 0.6B + batch=1 + bus contention GPU/CPU sur DRAM partagée

Verdict cumulé HTP + Vulkan : sur SM8750 ce TTS spécifique, ni HTP ni
GPU n'apportent gain net. NEON CPU optimisé reste la voie unique
réaliste sur ce hardware.

Conso énergie : Vulkan talker = pipeline ×2.3 plus long, conso ~×2.
NEON CPU = meilleure efficience.
Verdict : RESTE SUR NEON CPU pour la prod.

Code committé opt-in (KZTTS_VULKAN_LIB, KZTTS_DECODER_VULKAN,
KZTTS_TALKER_VULKAN), désactivé par défaut. Path principal RTF 2.45 intact.

Détails complets dans dist/PERF_VULKAN_TESTS.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 22:28:16 +02:00
Richard Loyer 777c6979c1 eval: kit qualité A/B aveugle Qwen3.5-lmq4 vs dense (decode CPU, template, reasoning off)
Bascule perf->qualité. Runner reproductible: genere un cahier de notation aveugle
(out/results_blind.md) + cle (out/results_key.csv) sur 16 prompts FR (SUPPORT/SAFETY/
BOUNDARY/INSTRUCTION/GENERAL). Notation humaine, regle de decision dans PROTOCOLE.
Resout l'eval shell (avant: template non applique -> ramble anglais): -cnv + -sysf +
--reasoning off -> FR propre. out/ ignore (artefacts generes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 22:24:23 +02:00
Richard Loyer b2fc564f04 Engine sur fork qualcomm: decode dense 15.5>pte, 3.5 ~11, KVq8+t4+fa+lm_head-Q4; prefill 93/285; RAG cross 24-28k
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 15:53:14 +02:00
Richard Loyer 7dbf38db97 Doc complete: README+MODELS+PERF+PITFALLS, build nettoye 2026-05-24 21:59:56 +02:00
Richard Loyer cc5b033762 Engine cadrage: CLAUDE.md analyse R&D, verdict NPU-prefill/CPU-decode
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:34:14 +02:00