Skip to content

fix(tts/luxtts): remove spurious mid-phrase pauses and chunk long text (#937) - #942

Open
Alex-Wengg wants to merge 2 commits into
mainfrom
fix/issue-937-luxtts-pause
Open

Alex-Wengg wants to merge 2 commits into
mainfrom
fix/issue-937-luxtts-pause

Conversation

@Alex-Wengg

@Alex-Wengg Alex-Wengg commented Sep 20, 2026

Copy link
Copy Markdown
Member

Fixes #937.

The pause is model-inherent and stochastic, not a conversion or token-count bug: the upstream PyTorch reference renders the same texts with the same gap at speed 1.0, and sweeping speed 1.0–1.3 makes it come and go non-monotonically. Each (length, seed) is a fresh draw; longer text just has more chances to land one. Upstream hides it via a silent speed×1.3.

Host-side handling in LuxTtsManager.synthesize:

  • Text that fits one pass (≤ 102 tokens and the graph limits) is synthesized as before. The 102 cap is measured: the 106-token issue text paused on 6 of 8 raw single-pass draws, while as two ~53-token spans all six seeds were clean.
  • Longer text is split into balanced continuation-prompted spans, bounded by the prompt's frames-per-token ratio so each span stays ≤ 5 s and can prompt the next one untruncated, and by the 256-token bucket next to the prompt transcript. speed applies to the first span only; later spans inherit pace. Implausible prompt ratios (> 12 frames/token, or spans < 8 tokens) throw inputTooLong instead of rendering dozens of passes.
  • Each pass is scanned for gaps ≥ 80 ms between sustained-speech runs (−45 dB rel. peak). Gaps beyond the span's punctuation (incl. ellipses/quotes the G2P drops, via the text API) trigger a re-seed, up to maxRedraws (default 3, CLI --redraws, 0 pins the raw pass). Cleanest pass kept; never errors; the result reports redraws / residualPauses. Cancellation is checked before every pass.
  • Onset/tail trimming anchors on sustained speech, not the vocoder's opening click, bringing seams from ~190 ms to ~80 ms.

Verification (gpu variant, quick-brown-fox 5 s prompt): reporter's 101- and 138-char texts render with no gap ≥ 80 ms at −45 dB and verbatim Parakeet transcripts across seeds 42–47; a 305-char paragraph keeps only comma/period pauses plus one text-driven break the ladder cannot remove; speed 1.2 propagates without compounding; speed 0.8 no longer errors; E2E fixture parity exact with maxRedraws: 0. Each re-draw is one extra pass (~60–100 ms on GPU).

Reviewer notes: second commit addresses the code-review findings (details in its message). LuxTtsContinuationTests covers chunking, token/frame budgets, single-pass fit, pause detection incl. the onset click, trims, and the text pause allowance.

#937)

The reported pause is model-inherent, not a conversion or host bug: the
PyTorch reference renders the same 101/138-character texts with the same
gap at speed 1.0. It is also not a token-count threshold. Sweeping the
reference across speeds 1.0–1.3 (i.e. across generated-frame counts) the
pause appears and disappears non-monotonically (1.0 and 1.15 pause, 1.05,
1.1, 1.2, 1.3 clean), so each (length, seed) is effectively a fresh draw
and longer text simply has more chances to land one. A 72-token
continuation span reproduced a 290 ms pause, which rules out "≤ 102
target tokens is a stable regime" as the fix.

Host-side handling in LuxTtsManager.synthesize:

- Text that fits one pass (≤ 102 tokens, ≤ 555 generated frames, ≤ 1024
  total) is synthesized exactly as before; the 97-character control is
  byte-identical to the pre-fix output.
- Longer text is split into balanced spans at word/punctuation
  boundaries and continuation-prompted: each span's prompt is the
  previous span's untrimmed audio + tokens. Span size is bounded by the
  prompt's frames-per-token ratio so no span exceeds 468 generated
  frames (5 s), which keeps it untruncated as the next prompt. `speed`
  is applied to the first span only; later spans inherit the pace from
  their prompt (re-applying it compounded 0.8 → 0.64 → … and blew the
  1024-frame graph on span 2).
- Every pass is scanned for gaps ≥ 80 ms between runs of sustained
  speech (≥ 50 ms above −45 dB relative to peak). Gaps beyond the span's
  pause punctuation trigger a re-draw with the next seed, up to 3 times;
  the last two attempts also compress duration by 3 % / 6 % (surplus
  estimated frames are the other pause source), and the following span
  is prompted back at the original pace. The cleanest pass is kept, so
  text-driven breaks the model insists on are left alone rather than
  erroring.
- Seams: the vocoder opens every pass with a ~20 ms click followed by
  150–300 ms of silence. Anchoring onset/tail trimming and pause
  detection on sustained speech (not the first sample above threshold)
  brought the inter-span gap from ~190 ms down to ~80 ms; spans ending
  in punctuation keep their tail as the sentence break.

Verification (M-series, gpu variant, seed 42, quick-brown-fox 5 s prompt):
101-char and 138-char texts render with no gap ≥ 80 ms at −45 dB and
verbatim Parakeet transcripts; a 305-char two-sentence paragraph (4
spans) keeps only its comma/period pauses plus one plausible break at
"downstream | toward" that survives all re-draws; speed 1.2 propagates
without compounding (339/305/338/333 frames per span); speed 0.8 no
longer errors. Each re-draw is one extra pass (~60–100 ms on GPU).
@github-actions

Copy link
Copy Markdown

VAD Benchmark Results

Performance Comparison

Dataset Accuracy Precision Recall F1-Score RTFx Files
MUSAN 94.0% 89.3% 100.0% 94.3% 396.4x faster 50
VOiCES 94.0% 89.3% 100.0% 94.3% 724.3x faster 50

Dataset Details

  • MUSAN: Music, Speech, and Noise dataset - standard VAD evaluation
  • VOiCES: Voices Obscured in Complex Environmental Settings - tests robustness in real-world conditions

✅: Average F1-Score above 70%

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

Sortformer High-Latency Benchmark Results

ES2004a Performance (30.4s latency config)

Metric Value Target Status
DER 30.3% <35%
Miss Rate 28.2% - -
False Alarm 0.9% - -
Speaker Error 1.2% - -
RTFx 19.9x >1.0x
Speakers 4/4 - -

Sortformer High-Latency • ES2004a • Runtime: 2m 7s • 2026-09-20T16:43:27.319Z

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

Supertonic3 Smoke Test ✅

Check Result
Build
Model download (incl. VectorEstimatorVariants/ int4 buckets)
Model load
Synthesis pipeline (--ve-variant int4)
Output WAV ✅ (364.7 KB)

Runtime: 0m23s

Note: CI VMs lack a physical Neural Engine; the ANE-bucketed VectorEstimator falls back to CPU here. This validates download + variant resolution + synthesis, not ANE residency/perf.

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

Parakeet EOU Benchmark Results ✅

Status: Benchmark passed
Chunk Size: 320ms
Files Tested: 100/100

Performance Metrics

Metric Value Description
WER (Avg) 7.03% Average Word Error Rate
WER (Med) 4.17% Median Word Error Rate
RTFx 9.98x Real-time factor (higher = faster)
Total Audio 470.6s Total audio duration processed
Total Time 49.3s Total processing time

Streaming Metrics

Metric Value Description
Avg Chunk Time 0.049s Average chunk processing time
Max Chunk Time 0.099s Maximum chunk processing time
EOU Detections 0 Total End-of-Utterance detections

Test runtime: 0m57s • 09/20/2026, 12:35 PM EST

RTFx = Real-Time Factor (higher is better) • Processing includes: Model inference, audio preprocessing, state management, and file I/O

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

PocketTTS Smoke Test ✅

Check Result
Build
Model download
Model load
Synthesis pipeline
Output WAV ✅ (183.8 KB)

Runtime: 0m8s

Note: PocketTTS uses CoreML MLState (macOS 15) KV cache + Mimi streaming state. CI VM lacks physical GPU — audio quality and performance may differ from Apple Silicon.

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

Offline VBx Pipeline Results

Speaker Diarization Performance (VBx Batch Mode)

Optimal clustering with Hungarian algorithm for maximum accuracy

Metric Value Target Status Description
DER 10.4% <20% Diarization Error Rate (lower is better)
RTFx 9.71x >1.0x Real-Time Factor (higher is faster)

Offline VBx Pipeline Timing Breakdown

Time spent in each stage of batch diarization

Stage Time (s) % Description
Model Download 19.272 17.8 Fetching diarization models
Model Compile 8.259 7.6 CoreML compilation
Audio Load 0.118 0.1 Loading audio file
Segmentation 27.909 25.8 VAD + speech detection
Embedding 107.690 99.6 Speaker embedding extraction
Clustering (VBx) 0.167 0.2 Hungarian algorithm + VBx clustering
Total 108.119 100 Full VBx pipeline

Speaker Diarization Research Comparison

Offline VBx achieves competitive accuracy with batch processing

Method DER Mode Description
FluidAudio (Offline) 10.4% VBx Batch On-device CoreML with optimal clustering
FluidAudio (Streaming) 17.7% Chunk-based First-occurrence speaker mapping
Research baseline 18-30% Various Standard dataset performance

Pipeline Details:

  • Mode: Offline VBx with Hungarian algorithm for optimal speaker-to-cluster assignment
  • Segmentation: VAD-based voice activity detection
  • Embeddings: WeSpeaker-compatible speaker embeddings
  • Clustering: PowerSet with VBx refinement
  • Accuracy: Higher than streaming due to optimal post-hoc mapping

🎯 Offline VBx Test • AMI Corpus ES2004a • 1049.0s meeting audio • 135.8s processing • Test runtime: 2m 21s • 09/20/2026, 12:48 PM EST

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

ASR Benchmark Results ✅

Status: All benchmarks passed

Parakeet v3 (multilingual)

Dataset WER Avg WER Med RTFx Status
test-clean 0.57% 0.00% 5.95x
test-other 1.19% 0.00% 3.71x

Parakeet v2 (English-optimized)

Dataset WER Avg WER Med RTFx Status
test-clean 0.80% 0.00% 6.56x
test-other 1.00% 0.00% 3.91x

Streaming (v3)

Metric Value Description
WER 0.00% Word Error Rate in streaming mode
RTFx 0.69x Streaming real-time factor
Avg Chunk Time 1.311s Average time to process each chunk
Max Chunk Time 1.396s Maximum chunk processing time
First Token 1.567s Latency to first transcription token
Total Chunks 31 Number of chunks processed

Streaming (v2)

Metric Value Description
WER 0.00% Word Error Rate in streaming mode
RTFx 0.69x Streaming real-time factor
Avg Chunk Time 1.316s Average time to process each chunk
Max Chunk Time 1.474s Maximum chunk processing time
First Token 1.295s Latency to first transcription token
Total Chunks 31 Number of chunks processed

Streaming tests use 5 files with 0.5s chunks to simulate real-time audio streaming

25 files per dataset • Test runtime: 6m19s • 09/20/2026, 12:54 PM EST

RTFx = Real-Time Factor (higher is better) • Calculated as: Total audio duration ÷ Total processing time
Processing time includes: Model inference on Apple Neural Engine, audio preprocessing, state resets between files, token-to-text conversion, and file I/O
Example: RTFx of 2.0x means 10 seconds of audio processed in 5 seconds (2x faster than real-time)

Expected RTFx Performance on Physical M1 Hardware:

• M1 Mac: ~28x (clean), ~25x (other)
• CI shows ~0.5-3x due to virtualization limitations

Testing methodology follows HuggingFace Open ASR Leaderboard

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

Speaker Diarization Benchmark Results

Speaker Diarization Performance

Evaluating "who spoke when" detection accuracy

Metric Value Target Status Description
DER 15.1% <30% Diarization Error Rate (lower is better)
JER 24.9% <25% Jaccard Error Rate
RTFx 18.56x >1.0x Real-Time Factor (higher is faster)

Diarization Pipeline Timing Breakdown

Time spent in each stage of speaker diarization

Stage Time (s) % Description
Model Download 11.676 20.7 Fetching diarization models
Model Compile 5.004 8.9 CoreML compilation
Audio Load 0.091 0.2 Loading audio file
Segmentation 16.950 30.0 Detecting speech regions
Embedding 28.249 50.0 Extracting speaker voices
Clustering 11.300 20.0 Grouping same speakers
Total 56.530 100 Full pipeline

Speaker Diarization Research Comparison

Research baselines typically achieve 18-30% DER on standard datasets

Method DER Notes
FluidAudio 15.1% On-device CoreML
Research baseline 18-30% Standard dataset performance

Note: RTFx shown above is from GitHub Actions runner. On Apple Silicon with ANE:

  • M2 MacBook Air (2022): Runs at 150 RTFx real-time
  • Performance scales with Apple Neural Engine capabilities

🎯 Speaker Diarization Test • AMI Corpus ES2004a • 1049.0s meeting audio • 56.5s diarization time • Test runtime: 3m 5s • 09/20/2026, 12:44 PM EST

…peline

- Token bucket: fitsSinglePass and maxSpanTokens now leave room for the
  prompt transcript in the 256-token TextEncoder bucket (prompt + span +
  pad ≤ 256), so a long prompt transcript no longer throws inputTooLong
  on span 1.
- Pathological prompt ratios are rejected in the chunking path instead
  of turning into dozens of passes: frames-per-token > 12 (transcript
  covering only part of the clip) or spans under 8 tokens throw
  inputTooLong with guidance. Single-pass behaviour is unchanged.
- Re-draw budget is a parameter (`maxRedraws`, default 3; CLI
  `--redraws`), and the result reports `redraws` / `residualPauses`.
  LuxTtsE2ETests pins the raw pass with maxRedraws: 0 — the fixture text
  draws one breath beyond its two commas and would otherwise be re-drawn
  (verified: 838/432/220672 exact with 0, 825/419 without).
- Duration compression on re-draws removed. On the 101-char text at
  seed 42 the ×1.03 pass removed the pause but squeezed the final word
  into the last frames (tail 120 ms only 3 dB under body vs 6–9 dB for
  clean passes; ASR read an extra word), and it did not fix the two
  persistent cases. Re-draws are pure re-seeds; the pace bookkeeping is
  gone.
- Task.checkCancellation() before every pass.
- Pause allowance: hyphen token added; the text API credits ellipses and
  quotes/brackets the G2P renders silently (`textPauseAllowance`).
- Prompt clips longer than 5 s log a warning (audio is capped, the
  transcript is not, and the ratio now drives every span).
- One sustained-speech scan per kept pass (vDSP peak / mean-square);
  leading and trailing trims are a single slice into the crossfade.
- Resample failure on a continuation prompt is wrapped in
  LuxTtsError.inferenceFailed; single AudioConverter; maxPromptSamples
  constant; result-field docs describe the summed multi-span semantics;
  the "ratio the model produced" comment corrected (it is host-fixed).
- The 102-token single-pass cap is kept, now measured: the 106-token
  issue text paused on 6 of 8 raw single-pass draws and the ladder still
  failed one of six seeds, while as two ~53-token spans all six seeds
  were clean with two re-draws total.

Not changed: re-draws redo the text-encoder and prompt-mel stages
(5–12 % of a pass); caching them needs a synthesizer split. The
leading "word" ASR sometimes reports at 0.00–0.08 s (conf < 0.25) is the
vocoder's opening click and appears on pre-PR renders too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LuxTTS: a pause appears mid-phrase once the text passes ~100 characters; below that the same sentence is clean

1 participant