Commit Graph
22 Commits
Author SHA1 Message Date
enne2 7942e61b3a feat: anti-loop guards + KV cache configurabile
Docker / build (cpu, cpu) (push) Canceled after 0s
Docker / build (nvidia/cuda:12.9.2-devel-ubuntu22.04, nvidia/cuda:12.9.2-runtime-ubuntu22.04, cuda, cuda12) (push) Canceled after 0s
Docker / build (nvidia/cuda:13.3.1-devel-ubuntu22.04, nvidia/cuda:13.3.1-runtime-ubuntu22.04, cuda, cuda13) (push) Canceled after 0s
Docker / build (vulkan, vulkan) (push) Canceled after 0s
Prevenzione allucinazioni/loop infiniti (Qwen3-TTS autoregressivo):
- block_repeated_ngrams: maschera i token che ricreerebbero un n-gram gia visto (n=4)
- has_repeating_cycle: ferma la generazione su cicli periodici (periodo 1-16, 4 ripetizioni)
- stuck detector: token dominante nella finestra recente (4 occorrenze in 8 token)
- fallback EOS quando tutti i logits sono mascherati (evita NaN)
- KV cache talker configurabile (--kv-cache, default 8192): 4096 overflowava con
  reference lunghe + testi lunghi ("decode would overflow cache")
- parametri esposti via API (no_repeat_ngram_size, loop_max_period, loop_repeats,
  loop_window) e CLI (--no-repeat-ngram, --loop-period, --loop-repeats, --loop-window)
- Docker: TTS_KV_CACHE env (default 8192)

Validato: testo 2788 char che prima degenerava in loop ora sintetizza pulito
(141s via API, nessuna ripetizione).
2026-08-18 15:10:02 +02:00
Pascal 7b6ed4f6db codec: drop the fused streaming tail
The fused mode appended the codec stream tail to the predictor frame
graph, so one compute produced both a frame's codes and its 80 ms of
audio with no host round trip. The experiment applied to max_batch 1
with a streaming synthesis only, it cost throughput against the
buffered flush that stays the default, and it kept a second frame
graph, its ring inputs and an init flag alive for that single case. It
is not worth keeping.

Remove the tail helpers, the fused graph of CodePredGraphSet, the
codec_fused field of qt_init_params, the --codec-fused flag of both
tools and the harness switch that exercised it. The predictor frame
unroll and the in graph sampler are untouched.
2026-08-05 18:20:08 +02:00
Pascal 26dd8adbf0 predictor: unroll the frame into one cgraph and sample in standard ops
One static frame graph per batch width replaces the per step chain:
prefill and the 15 acoustic steps run in a single backend compute.
This is the target architecture for the llama.cpp Qwen3-TTS port and
serves as its working GGML reference while under test.

Sampling is a plain op chain batched over slots: temperature, argsort
top_k (descending order is guaranteed on every backend, unlike top_k),
softmax, cumsum, cdf crossing against a per step philox uniform.
Greedy draws with u = 0 and lands on the argmax. Faster than the
fused sampling op under CUDA graph capture, greedy codes stay exact
against the Python reference on CPU, CUDA and Vulkan.

Opt in single slot latency mode (--codec-fused on qwen-tts and
tts-server, codec_fused in qt_init_params): the codec stream tail
joins the frame graph at T=1, codes read through a device view, one
80 ms chunk per compute with no host round trip.

Predictor 3.34 -> 3.11 ms/frame on CUDA, end to end -4%.
2026-07-30 22:53:58 +02:00
Pascal d03ffb97f9 api: derived codec left context, chunk width hoisted to qt_init
The left context of the buffered chunked decode is no longer a caller
knob: it derives from the codec's own sliding window (2x144 frames),
placing the default decode at the residual floor of the split.
codec_chunk_sec moves from qt_tts_params to qt_init_params, resolved
once to frames at load. The mid-struct removal bumps the ABI to a
closed range [QT_ABI_MIN_VERSION, QT_ABI_VERSION] = [4, 4]; the probe
asserts both bounds reject through the range check.
2026-07-25 18:56:28 +02:00
Pascal 3a3069b8fd utf8: normalize inbound text bytes to UTF-8
Same gap as omnivoice.cpp: the UTF-8 boundary covers argv, console
output, and fopen but not the bytes arriving from stdin or text files.
Windows shells and editors hand those over as UTF-16 with BOM
(PowerShell redirection, Notepad, Out-File) or the ANSI codepage (cmd
pipes), and the raw bytes reach the tokenizer as garbage.

utf8_normalize() closes the gap: UTF-8 BOM stripped on every platform,
UTF-16 BOM losslessly recoded to UTF-8, bytes failing UTF-8 validation
decoded from the ANSI codepage. Valid UTF-8 passes through untouched.
Wired into read_stdin_text (binary mode stdin so CRLF translation
cannot eat UTF-16 0x0D bytes) and read_text_file, which also moves
from raw fopen to utf8_fopen so a non-ASCII --ref-text path opens.
2026-07-04 01:14:00 +02:00
Pascal 0bf4a18b22 codec: add pre-encoded voice reference (--ref-spk / --ref-rvq)
qwen-codec --talker extracts the speaker embedding (.spk, raw f32)
and the ICL codes (.rvq) in one pass, encode truncated to the hop
boundary conforming to the --ref-wav path. qwen-tts loads them via
--ref-spk / --ref-rvq and skips the speaker encoder and codec encode
on every synthesis: TTFA 205 ms -> 89 ms. Extends qt_tts_params with
ABI v2 latent fields, adds qt_num_codebooks(), ships freeman.spk +
freeman.rvq and switches clone scripts to the latent path. Output is
bit-identical to the raw path at fixed seed.
2026-06-11 21:45:13 +02:00
PascalandJeffrey van Binsbergen df66c67706 tts: add --stream-by-line, one utterance and one WAV header per line
With -o '-', stdin is read line by line and every line synthesises
immediately as its own utterance, model and speaker staying resident
across lines. Each utterance after the first opens with a fresh RIFF
header, armed at end of line and consumed lazily at the next audio,
so a client can split the stream into standalone WAV clips on the
RIFF magic. Port of the feature contributed to omnivoice.cpp in
ServeurpersoCom/omnivoice.cpp#11.

Co-authored-by: Jeffrey van Binsbergen <comgenie@comgenie.com>
2026-06-06 23:13:46 +02:00
Pascal ca0c779f49 tts: default language to auto, NULL lang selects auto, reject NULL text 2026-06-06 13:31:51 +02:00
Pascal f3cfa5cf47 server: add OpenAI compatible TTS server (chunked PCM streaming, WAV one-shot) 2026-06-06 12:47:10 +02:00
Pascal 4bf578ec7b qwen-tts: drop --text, align help on omnivoice style 2026-05-14 23:48:27 +02:00
Pascal dda50c2225 codec: add chunked decode with rolling left context 2026-05-14 22:53:28 +02:00
Pascal b1339f7cae abi: drop QT_CODEC_SAMPLE_RATE macro, hardcode 24000 like omnivoice 2026-05-14 21:55:23 +02:00
Pascal 38bf6d762a abi, pipeline, cli: conform qwentts on omnivoice convention 2026-05-14 21:42:52 +02:00
Pascal 259e7059f6 align symbol naming on omnivoice convention 2026-05-14 18:09:07 +02:00
Pascal e8f1c2a053 nits 2026-05-14 16:47:45 +02:00
Pascal b1e864cb8d cli, facade: rename --ref-audio to --ref-wav, align with omnivoice convention 2026-05-14 15:46:46 +02:00
Pascal 6ad6a7db7a facade, abi: thin CLI on top of qwen_* public ABI, lock contract with C99 probe 2026-05-14 15:31:47 +02:00
Pascal eb48f33f09 utf8: portable Windows UTF-8 boundary for argv, fopen and CreateFile 2026-05-14 14:10:26 +02:00
Pascal d5f1e20c08 pipeline: zero-init qenc_host_ready, value-init PipelineTTS
The lazy-init flag started on stack garbage. When non-zero the encode
path skipped the codebook load and silently returned empty RVQ codes.
2026-05-11 18:27:06 +02:00
Pascal 7f532f1ed0 cli, pipeline: decode --ref-audio once at the CLI, pipeline takes a mono buffer
Clone mode used to read the same WAV twice (speaker encoder then codec
encoder), which failed silently on Windows where pipeline_codec_encode
returned empty on the second pass. PipelineTTSSynthesizeParams now takes
ref_audio_24k + ref_n_samples instead of a path. The CLI calls
audio_read_mono once, holds the buffer in a unique_ptr until synthesis
returns, and feeds the same pointer to both encoders.
2026-05-11 18:14:59 +02:00
Pascal d700ffea43 cli: --ref-text takes a file path instead of inline string content 2026-05-11 17:57:40 +02:00
Pascal add3f940a0 Initial release 2026-05-10 15:57:15 +02:00