Commit Graph

21 Commits

Author SHA1 Message Date
Kazeia Team 3dcf73aa38 Restore KV=100 + fix as-is embeds + multi-segment support
- KV_LEN restored to 100 (KV=64 caused quality loss from evicted role tokens)
- C++ uses pre-computed embeds as-is (no double codec_sum)
- Multi-segment format support in Kotlin (detects n_segments header)
- prepare_tts_segments.py: splits text + generates per-segment embeds
- Quality issue: Python-captured embeds differ from original working file
  (original was likely captured on-device, not from Python model.forward)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 22:26:20 +02:00
Kazeia Team 10a3904d7d Multi-segment TTS for long text: split → generate → concatenate
- prepare_tts_segments.py: splits text at sentence boundaries,
  generates Python pre-computed embeds per segment
- Kotlin: detects multi-segment file format, processes each segment
  independently (fresh KV cache), concatenates audio
- Long text tested: 3 segments, 335 tokens, 26.8s audio, RTF 1.67

File format: n_segments, then per segment: nPrefill, nTotal, embeds[]

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:34:05 +02:00
Kazeia Team 24157c0a68 Fix: use pre-computed embeds as-is (no double codec_sum)
Pre-computed embeds from Python already contain codec_sum+text.
Using them as-is works correctly. After exhausted, fallback to
our codec_sum + pad.

Long text: 191 tokens, 15.28s audio, RTF 1.27

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:10:23 +02:00
Kazeia Team f6df1738c5 Add prepare_tts_embeds.py for any text + codec_sum fix
- prepare_tts_embeds.py: generates pre-computed embeddings from any text
  via Python generate_voice_clone, capturing talker inputs
- C++ pipeline: always build codec_sum + trailing (not as-is)
- maxTokens: 4× trailing count (audio >> text tokens)
- Long text tested: 224 Python tokens → 125 NPU tokens (10s audio)
- Text-only embeds don't work (model needs Python pre-computed codec_sum)

Usage: python3 scripts/prepare_tts_embeds.py "Your text" output.bin
       adb push output.bin /data/local/tmp/.../full_pipeline_embeds.bin

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:05:42 +02:00
Kazeia Team 173606dae7 Stable: decoder 8T optimization + restore pre-computed embeds
- BigVGAN: 8 threads (2757→1872ms), pre_conv/pre_transformer: 4 threads
- Restored pre-computed embeds format (codec_sum+text from Python)
- Text-only trailing embeds don't work: model needs codec_sum for EOS

For long phrases, pre-computed embeds must be generated from Python.
RTF 1.26 on short phrase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:42:02 +02:00
Kazeia Team 42bbb96fd8 Optimize decoder: BigVGAN 8T, small models 4T → RTF 1.26
BigVGAN benefits from 8 intra-op threads (all perf cores).
Pre_conv and pre_transformer kept at 4T (small, less contention).

BigVGAN: 2757ms → 1872ms (-885ms), decode total: 2830ms → 2035ms
Pipeline: 6438ms → 5834ms → RTF 1.26

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:00:05 +02:00
Kazeia Team a688edc9ec Reduce talker KV_LEN 100→64: saves 148ms (RTF 1.31)
KV window of 64 sufficient for ~70 token generation (10 prefill + 58 gen).
36% less KV memcpy per talker step (28L × 2 × 64×8×128 vs 100×8×128).

Generation: 3795ms → 3647ms, total: 6438ms → 6093ms

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:47:30 +02:00
Kazeia Team 4dcc4bb8b3 Fix KV buffer + revert HTP decoder (BigVGAN too complex for HTP)
- Restored intermediate KV buffer for talker (direct output→input
  caused trembling from buffer overwrite during execute())
- BigVGAN HTP compilation takes >5min, not viable
- RTF 1.35 with clean audio quality

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:37:50 +02:00
Kazeia Team 985fd9cff9 Direct output→input KV copy: RTF 1.51 → 1.31
Skip intermediate KV buffer: copy output tensors directly into
next step's input pointers. Saves ~1.5GB/run of memcpy for talker
(28L × 2 × 100×8×128 floats × 58 steps) and CP similarly.

Generation: 4007ms → 3713ms, total: 7180ms → 6078ms

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:23:45 +02:00
Kazeia Team 14f7e5b05f Optimize CP+talker: eliminate prepare_input_tensors per step
Cache input tensor pointers after first prepare_input_tensors call,
then memcpy directly into them for all subsequent steps.

Eliminates ~14000 mallocs per pipeline run (986 CP + 58 talker calls).
Generation: 4640ms → 4007ms (-633ms), total RTF: 1.6 → 1.51

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:16:38 +02:00
Kazeia Team e647911329 Shared Module C++ pipeline: RTF 1.6 with perfect quality
Key breakthrough: C++ pipeline loop using the SAME Method* instances
that Java loaded (via Module::method("forward")). This gives:
- Same QNN compiled graph → identical numerical results → no trembling
- C++ loop → no Java Tensor/EValue allocation overhead
- prepare_input_tensors + memcpy + Method::execute (like cp_et_runner)

Pipeline: talker ~20ms/step + CP ~44ms/step + decoder 2.8s = 7.3s for 4.64s

Added to executorch JNI:
- Module.nativeSetCpModule() — registers CP module for pipeline
- Module.nativeRunTtsPipeline(...) — runs full talker+CP loop in C++
- Updated executorch.jar with new native method declarations

From RTF 4.9 (start of session) to RTF 1.6 with impeccable audio quality.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:05:58 +02:00
Kazeia Team 38c0e9874a Disable C++ pipeline (QNN non-deterministic), keep Java RTF 1.8
Root cause found: QNN HTP level=1 compilation is not bitwise
deterministic. Two loads of the same .pte produce slightly different
hidden states → audible trembling in decoded speech.

Java pipeline uses single QNN instance → no trembling, validated quality.
C++ pipeline code preserved for future use when QNN context caching
is fixed (would make both loads use same compiled graph).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:42:49 +02:00
Kazeia Team 439629c9bf Revert "Pre-allocate Tensor/EValue in Java pipeline: 16s → 8.9s (RTF 1.9)"
This reverts commit 0f027c5fde.
2026-04-09 11:03:52 +02:00
Kazeia Team 0f027c5fde Pre-allocate Tensor/EValue in Java pipeline: 16s → 8.9s (RTF 1.9)
Reuse float arrays and Tensor/EValue objects across talker steps
instead of creating new ones each iteration. Eliminates ~7s of
GC overhead from thousands of JNI object allocations.

Same validated audio quality as before, no C++ pipeline needed.
Talker 35ms/step, CP 58ms/step, total 8.9s for 4.64s audio.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:59:13 +02:00
Kazeia Team 8e536094df Fix C++ pipeline eos/pad + disable for quality (keep Java default)
- Fixed trailing embed handling (use pre-computed as-is)
- Added eos/pad embed params to nativeRun
- Improved C++ PRNG for sampling
- Disabled native pipeline: slight quality regression vs Java
  (two separate QNN instances give different numerical results)
- Java pipeline (RTF 1.8) kept as default for validated quality

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:53:19 +02:00
Kazeia Team 3b01302cfb Fix missing eos/pad embeddings in native C++ pipeline
The native pipeline was adding zeros after trailing text tokens
instead of tts_eos_embed then tts_pad_embed. This caused the model
to mispronounce final words (e.g. "développement" → "devopment").

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:35:05 +02:00
Kazeia Team 393ce79eb5 Native C++ pipeline: RTF 1.4 (was 3.6 in Java)
Full talker+CP autoregressive loop in C++ via JNI.
Talker 20ms/step, CP 44ms/step, total 6.6s for 4.64s audio.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:09:32 +02:00
Kazeia Team fb6045a635 Pre-load CP heads + GPU decoder test (reverted) + headArgmaxOffset
- Pre-load all 15 CP heads at first CP call (eliminates lazy-load lag)
- Tested BigVGAN on GPU Adreno: no gain (+300ms vs CPU), kept on CPU
- Added headArgmaxOffset for future batch optimization
- Cancel previous pipeline on new run_pipeline intent

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:57:01 +02:00
Kazeia Team 6e6c562d53 Add DSP warmup + fix pipeline thread contention
- Warmup forward() for talker+CP during init (avoids 7s DSP compilation
  on first pipeline run)
- Cancel previous pipeline job before starting new one
- Use Dispatchers.IO for pipeline intent

First run after warmup: talker 19ms/step, CP 59ms/step → RTF ~1.9

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:24:18 +02:00
Kazeia Team 8bfe6c7445 Add NEON SIMD heads argmax for CP — 2.3× speedup
CP head dot products (15 × 2048×1024) optimized with ARM NEON
vfmaq_f32 (4 accumulators, 16 floats/iteration).

CP/frame: 131ms → 58ms, total pipeline: 22.7s → 14.7s (RTF 3.2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 08:55:20 +02:00
Kazeia Team 389ffa7c61 Initial commit: Kazeia TTS pipeline on NPU via ExecuTorch
Full Qwen3-TTS-0.6B pipeline running on Snapdragon 8 Elite NPU:
  - Talker (28L) and Code Predictor (5L) as .pte on QNN HTP fp16
  - JNI integration, no root required
  - Validated audio quality: RTF 3.9

  Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 08:42:11 +02:00