mirror of
https://github.com/Nighthawk42/Qwen3-TTS-streaming.git
synced 2026-08-30 08:52:27 +00:00
feat(audio): make overlap_samples fully configurable
Allow users to set any overlap_samples value including 0 to disable crossfade blending entirely. Renamed MIN_BLEND_SAMPLES to DEFAULT_BLEND_SAMPLES and removed the enforced minimum.
This commit is contained in:
@@ -51,10 +51,9 @@ Streaming TTS can produce clicks, pops, and artifacts at chunk boundaries. This
|
||||
Chunks are blended using a Hann window crossfade to eliminate boundary discontinuities:
|
||||
|
||||
```python
|
||||
MIN_BLEND_SAMPLES = 512 # ~21ms at 24kHz
|
||||
|
||||
# blend_samples is at least MIN_BLEND_SAMPLES, even if overlap_samples is smaller
|
||||
blend_samples = max(overlap_samples, MIN_BLEND_SAMPLES)
|
||||
# ~21ms at 24kHz, matches RMS check window
|
||||
# Lower values may cause clicks, set to 0 to disable
|
||||
DEFAULT_BLEND_SAMPLES = 512
|
||||
|
||||
# Hann crossfade
|
||||
fade_out = 0.5 * (1 + np.cos(np.pi * t))
|
||||
@@ -161,7 +160,7 @@ for chunk, sr in model.stream_generate_voice_clone(
|
||||
|-----------|---------|-------------|
|
||||
| `emit_every_frames` | 8 | Emit audio every N frames |
|
||||
| `decode_window_frames` | 80 | Decoder context window |
|
||||
| `overlap_samples` | 512 | Crossfade overlap between chunks |
|
||||
| `overlap_samples` | 512 | Crossfade overlap between chunks (0 to disable) |
|
||||
| `max_frames` | 10000 | Maximum codec frames to generate |
|
||||
| `first_chunk_emit_every` | 0 | Phase 1 emit interval (0 = disabled) |
|
||||
| `first_chunk_decode_window` | 48 | Phase 1 decode window |
|
||||
|
||||
@@ -103,9 +103,10 @@ def _crossfade(prev_tail: np.ndarray, new_head: np.ndarray) -> np.ndarray:
|
||||
return prev_tail[:n] * fade_out + new_head[:n] * fade_in
|
||||
|
||||
|
||||
# Minimum samples for boundary blending (prevents clicks even with overlap_samples=0)
|
||||
# Default blend samples for boundary blending
|
||||
# ~21ms at 24kHz, matches RMS check window for better coverage
|
||||
MIN_BLEND_SAMPLES = 512
|
||||
# Lower values may cause clicks, set to 0 to disable
|
||||
DEFAULT_BLEND_SAMPLES = 512
|
||||
|
||||
|
||||
def _add_ref_code_context(
|
||||
@@ -2814,7 +2815,7 @@ class Qwen3TTSForConditionalGeneration(Qwen3TTSPreTrainedModel, GenerationMixin)
|
||||
|
||||
# Crossfade with previous chunk tail for smooth transition
|
||||
# Always blend boundaries to prevent clicks from sliding window re-decode artifacts
|
||||
blend_samples = max(overlap_samples, MIN_BLEND_SAMPLES)
|
||||
blend_samples = overlap_samples
|
||||
if decoded_tail is not None:
|
||||
ov = min(blend_samples, len(decoded_tail), len(chunk))
|
||||
if ov > 0:
|
||||
@@ -2823,7 +2824,7 @@ class Qwen3TTSForConditionalGeneration(Qwen3TTSPreTrainedModel, GenerationMixin)
|
||||
|
||||
# Apply Hann fade-in to very first chunk to avoid pop at audio start
|
||||
# Always apply fade-in on first chunk to prevent pop
|
||||
blend_samples = max(overlap_samples, MIN_BLEND_SAMPLES)
|
||||
blend_samples = overlap_samples
|
||||
if decoded_tail is None:
|
||||
fade_len = min(blend_samples, len(chunk))
|
||||
if fade_len > 0:
|
||||
@@ -2867,7 +2868,7 @@ class Qwen3TTSForConditionalGeneration(Qwen3TTSPreTrainedModel, GenerationMixin)
|
||||
|
||||
# Crossfade with previous tail
|
||||
# Always blend flush boundary
|
||||
blend_samples = max(overlap_samples, MIN_BLEND_SAMPLES)
|
||||
blend_samples = overlap_samples
|
||||
if decoded_tail is not None and len(wav) > 0:
|
||||
ov = min(blend_samples, len(decoded_tail), len(wav))
|
||||
if ov > 0:
|
||||
|
||||
Reference in New Issue
Block a user