feat: add native MiraTTS community model - #395
Conversation
|
Added native MiraTTS segment-level streaming in The streaming session now:
Validation performed:
This is genuine progressive segment streaming. Each segment still runs the acoustic processor, DAC decoder, and FlashSR as a complete unit; token-level neural-codec streaming remains a possible future optimization. |
ec7f476 to
f5bdacb
Compare
|
Added commit Included
Original MiraTTS versus audio.cpp Q8
* The original cold request paid a one-time ONNX Runtime CUDA fallback cost; warm measurements are the representative steady-state comparison. The three warm short requests average 0.908 s upstream and 1.570 s in native Q8, making the original implementation about 1.73× faster in this measurement. For the 15.36-second long-form case, the original achieved RTF 0.1938 (5.16× real time), while native Q8 achieved RTF 0.3106 (3.22× real time). This is not precision-matched: the original uses BF16 while native uses GGUF Q8. The original also pre-encodes and retains the reference context, whereas native currently processes the reference input on each request. CUDA offline — long-lived session
The three identical requests remain bit-deterministic before and after the changed-prompt and long-form requests. CUDA streaming
Streaming emits multiple independently consumable audio events, and the merged repeat is bit-deterministic. Python/native decoder parity
This exact-token comparison uses identical upstream speech and context tokens, isolating the native processor/decoder path from autoregressive sampling. Resident memory — Q8 CUDA
These are held-resident samples rather than instrumented peaks across model loading. Backend coverage
Validation passed for all saved CUDA offline and streaming cases. |
f5bdacb to
fb2150b
Compare
|
I am working on performance optimization now and seems a shared Qwen runtime bug: the cached graph’s compact-logit index tensor is overwritten by the allocator after its first use, causing an illegal CUDA access on request two uder |
MiraTTS Q8 performance updateCommit
Compared with the previously measured warm original MiraTTS implementation, optimized audio.cpp is now approximately 9–15% faster on the repeated and long-form cases. The original cold measurement includes its one-time ONNX fallback/setup cost, so the warm comparisons are the useful ones. What changed
Validation
The CUDA fast ConvTranspose path changes floating-point operation ordering, so WAV hashes differ from the slower path; the strict frame-count and high waveform/log-mel similarity checks confirm that the resulting audio remains effectively equivalent. |
|
@mirek190 Could you isolate the Qwen3 runtime changes into a separate PR? It would make it easier to track commits that introduce behavioral changes. The only affected model by the change is Music3 and I'm not sure about the perf impact on low end GPUs. Another solution is probably not to disable reuse. It should mark the token-id tensor as input, then re-upload BTW, do you know how to stop people from flooding the repo with draft PRs? Pull request limits do not work. |
Sure
If they do not listen ... ban them ;) It is not worth to giving them attention. |
# Conflicts: # src/framework/modules/transformers/qwen_causal_decode_runtime.cpp
|
Updated this PR with MiraTTS CPU performance improvements and a Vulkan generation workaround, and merged upstream Commits: Changes
ValidationCPU, CUDA and Vulkan CLI targets rebuilt successfully against merged main.
CPU and CUDA Q8 WAV hashes match their pre-merge outputs exactly. Vulkan completes with the same duration; its WAV hash differs after the upstream backend update. These checks do not establish cross-backend waveform parity or subjective audio quality. Before the merge, Q8 and BF16 synthesis completed on all three backends and produced valid mono 48 kHz WAVs. Earlier validation of this optimization work also covered repeated CPU requests, a request after long-form synthesis, and segment streaming. The latest post-merge checks above are Q8 smoke tests. For the CPU optimization comparison, the same current implementation improved from approximately 21.65 s to 7.18 s before the merge (about 3x). The separate experimental CPU implementation measured 5.31 s; its specialized generation runtime has not been transplanted wholesale, so that performance gap remains. Build scope: the CLI targets passed; a build of all CPU targets encountered existing Windows |
|
MiraTTS now uses the Vulkan GPU generator, with no CPU-generator fallback. The generator runs on the selected Vulkan device alongside the acoustic stages. For the short test sentence, generation dropped from approximately 7.5 seconds with the previous CPU-generator fallback to 1.1 seconds on Vulkan GPU (roughly 6-7x faster). The latest Q8 run measured 1.154 seconds for 5.56 seconds of output audio on an RTX 3090. These are local before/after generation measurements, not a controlled benchmark of identical output waveforms, and exclude process startup/model loading. The fix uses F32 projection precision, grouped flash attention with materialized grouped K/V heads, and F32 output-projection inputs for the Vulkan generator. DirectSetRows KV updates and compact logits remain enabled. Validation covers Q8 and BF16 offline generation, repeated requests before/after a long request, and progressive streaming. Repeated audio hashes match within each precision. Both full short-sentence samples transcribe correctly with Qwen3-ASR. Other Vulkan devices/vendors and exact CUDA waveform parity are not claimed. Detailed timings are in tests/mira_tts/VULKAN_GPU_VALIDATION.md. I also merged the latest main (a8fccb4); the merge was clean. The failed macOS CPU check was caused by an unconditional omp.h include and omp_set_num_threads call in FlashSR, despite CI disabling OpenMP. These are now guarded by _OPENMP, allowing serial execution without requiring OpenMP. A local build with ENGINE_ENABLE_OPENMP=OFF and GGML_OPENMP=OFF successfully built the CLI, server, and GGUF converter. Loader/catalog consistency checks also pass. Fresh CI checks will verify the platform builds. |
Current cross-backend sentence benchmark\n\nTest sentence: Hello, this is a native Mira TTS test.\n\nAll six native WAVs were checked with Qwen3-ASR, which recognized the complete requested sentence.\n\n| Implementation | Precision | Generation | Audio length | Compared with original |\n|---|---:|---:|---:|---:|\n| Original MiraTTS, warm | BF16 | 0.851 s | 5.56 s | Baseline |\n| audio.cpp CUDA | Q8 | 0.905 s | 4.64 s | 1.06x slower |\n| audio.cpp CUDA | BF16 | 1.057 s | 5.28 s | 1.24x slower |\n| audio.cpp Vulkan | Q8 | 1.146 s | 5.68 s | 1.35x slower |\n| audio.cpp Vulkan | BF16 | 1.140 s | 5.26 s | 1.34x slower |\n| audio.cpp CPU | Q8 | 11.455 s | 5.40 s | 13.47x slower |\n| audio.cpp CPU | BF16 | 12.760 s | 5.70 s | 15.00x slower |\n\nThe revision-pinned original implementation's cold request took 2.218 seconds because it paid a one-time ONNX/CUDA fallback cost. Its representative warm request took 0.851 seconds.\n\nThis is not a perfectly precision- or workload-matched comparison: upstream uses BF16 and retains the encoded reference context, whereas audio.cpp currently processes the reference input on every request. Native CLI metrics exclude model loading. The generated durations also vary because each backend/precision can sample a different valid token sequence despite using the same text, reference, and seed.\n\nThe Vulkan results use the new GPU generator path with no CPU-generator fallback. On this exact run, Vulkan completed generation in about 1.14 seconds, compared with approximately 7.5 seconds through the former CPU fallback. |
|
Thanks @mirek190! I can’t test this right now, but a quick skim found one concern:
This changes shared QwenCausalDecodeRuntime behavior for any caller whose physical lm_head rows differ from decoder.logits_size, even when lm_head_row_offset is not explicitly used. Since this sparse lm-head window is only needed by MiraTTS, I’d prefer to keep this model-local, so QwenCausalDecodeRuntime does not need to change at all. E.g.,FireRedTTS3 keeps a local lm-head graph. BTW, we got the official VibeASR integration and audio.cpp is able to support BitNet models! |
Summary
Adds MiraTTS as an experimental native community TTS/voice-cloning model.
The port runs the full pipeline in C++:
It also adds a local checkpoint converter, model-spec v1 metadata, documentation, and an upstream-reference parity harness.
Implementation notes
min_p, covered by unit tests.ttsandclonoffline routes through the normal loader/session framework.Validation
Builds:
Focused tests:
Results:
mira_tts: tts (offline), clon (offline)Representative CUDA BF16 run:
Measured on RTX 3090:
Reference parity using identical original speech/context tokens:
Conversion
GGUF for testing
https://huggingface.co/mirek190/audio.cpp/blob/main/Text%20to%20audio%20(TTS)/mira-tts-q8.gguf
Current limitations