Skip to content

Commit ce884d3

Browse files
committed
fix: multi-speaker lines drifting into the first speaker's voice
Multi-speaker input was grouped into text batches of up to 5 turns, each generated in one pass. Within a pass the model tends to carry the voice it opened with into later turns, so other speakers drift toward it (in practice the narrator, speaker 0, who opens most batches). Measured on a 3-voice, 8-turn scene with speaker ID (Resemblyzer, calibrated on solo renders; Whisper word timestamps to segment turns), 24 seeds on an RTX 3090: 10/24 renders had wrong-voice lines (19 of 20 pulled into speaker 0's voice). Lower temperature made it worse. Start a new batch at every speaker change (consecutive lines by one speaker stay together): 0/24 renders with wrong-voice lines. Prefix reuse keeps the extra batches cheap; an 18-turn scene runs at the same RTF (0.70 alone, 0.82 with 4 concurrent) and transcribes intact. FISH_SPLIT_SPEAKERS=0 restores the old grouping.
1 parent e53bcb1 commit ce884d3

4 files changed

Lines changed: 36 additions & 3 deletions

File tree

‎.env.example‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ FISH_STREAM_CHUNK_TOKENS=32
4343
# Silence at each text-chunk boundary (ms). Chunks otherwise join with ~0.15 s
4444
# of pause versus ~0.4 s between sentences inside a chunk. 0 disables.
4545
FISH_CHUNK_PAUSE_MS=400
46+
# Multi-speaker: start a new generation at each speaker change (prevents voices
47+
# drifting into the first speaker's). 0 = old grouping.
48+
FISH_SPLIT_SPEAKERS=1
4649

4750
# --- Auth ---
4851
# Require this bearer token on requests (leave blank for an open server).

‎OPENAI_API_README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ accepted via `/v1/tts`.
196196
its last word, so chunks joined edge to edge sound rushed next to the ~0.4 s
197197
the model leaves between sentences within a chunk. The engine measures each
198198
chunk's trailing silence and pads up to this target. `0` disables.
199+
- `FISH_SPLIT_SPEAKERS` (1) — in multi-speaker input, start a new generation at
200+
every speaker change (consecutive lines by one speaker stay together). With
201+
several speakers in one generation, the model drifts other speakers toward the
202+
voice it opened with; a 3-voice scene had wrong-voice lines in 10/24 renders
203+
versus 0/24 with this on, at no speed cost. `0` restores the old grouping.
199204
- `FISH_RAS` (1) — Repetition Aware Sampling; `0` disables it (diagnostics).
200205
- `FISH_REF_CACHE` (on) — reuse encoded reference voices across requests. The
201206
voice add/delete endpoints invalidate it; set `off` if you edit files under

‎compose.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ services:
4242
FISH_ASR_MODEL: "${FISH_ASR_MODEL:-small}" # faster-whisper model (small=multilingual)
4343
FISH_ASR_DEVICE: "${FISH_ASR_DEVICE:-cpu}" # cpu | cuda:0 (cpu avoids VRAM contention)
4444
FISH_CHUNK_PAUSE_MS: "${FISH_CHUNK_PAUSE_MS:-400}" # pause at text-chunk boundaries
45+
FISH_SPLIT_SPEAKERS: "${FISH_SPLIT_SPEAKERS:-1}" # new generation per speaker change
4546
FISH_BATCH_SIZE: "${FISH_BATCH_SIZE:-1}" # >1 = continuous batching (needs FISH_QUANTIZE=none)
4647
FISH_CONCURRENCY: "${FISH_CONCURRENCY:-}" # empty = FISH_BATCH_SIZE
4748
FISH_QUEUE_TIMEOUT: "${FISH_QUEUE_TIMEOUT:-300}"

‎fish_speech/models/text2semantic/inference.py‎

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ def multinomial_sample_one_no_sync(probs_sort):
5050
# FISH_RAS=0 disables Repetition Aware Sampling (resampling a token that
5151
# repeats within the window at a higher temperature).
5252
RAS_ENABLED = os.environ.get("FISH_RAS", "1") != "0"
53+
# Start a new text batch at every speaker change so each generation opens with
54+
# its own speaker's voice. With several speakers in one batch the model drifts
55+
# other speakers toward the batch's opening voice (measured: 10/24 renders of a
56+
# 3-voice scene had wrong-voice lines vs 0/24 when split). FISH_SPLIT_SPEAKERS=0
57+
# restores the old grouping.
58+
SPLIT_ON_SPEAKER_CHANGE = os.environ.get("FISH_SPLIT_SPEAKERS", "1") != "0"
5359
RAS_HIGH_TEMP = 1.0
5460
RAS_HIGH_TOP_P = 0.9
5561

@@ -751,7 +757,10 @@ def split_text_by_speaker(text: str) -> list[str]:
751757

752758

753759
def group_turns_into_batches(
754-
turns: list[str], max_speakers: int = 3, max_bytes: int = 300
760+
turns: list[str],
761+
max_speakers: int = 3,
762+
max_bytes: int = 300,
763+
split_on_speaker_change: bool = False,
755764
) -> list[str]:
756765
"""
757766
Group turns into batches based on speaker count or byte limit.
@@ -768,10 +777,22 @@ def group_turns_into_batches(
768777
current_batch = []
769778
current_bytes = 0
770779

780+
speaker_re = re.compile(r"<\|speaker:(\d+)\|>")
781+
current_speaker = None
782+
771783
for turn in turns:
772784
turn_bytes = len(turn.encode("utf-8"))
785+
m = speaker_re.match(turn)
786+
speaker = m.group(1) if m else None
787+
# Each generation then starts from its own speaker tag; within one
788+
# generation the model tends to carry the opening voice into later
789+
# turns (other speakers drift toward speaker 0's voice).
790+
speaker_changed = (
791+
split_on_speaker_change and current_batch and speaker != current_speaker
792+
)
793+
current_speaker = speaker
773794

774-
would_exceed_speakers = len(current_batch) >= max_speakers
795+
would_exceed_speakers = len(current_batch) >= max_speakers or speaker_changed
775796
would_exceed_bytes = current_bytes + turn_bytes > max_bytes and current_batch
776797

777798
if would_exceed_speakers or would_exceed_bytes:
@@ -941,7 +962,10 @@ def generate_long_steps(
941962
turns = split_text_by_speaker(text)
942963
if turns:
943964
batches = group_turns_into_batches(
944-
turns, max_speakers=5, max_bytes=chunk_length
965+
turns,
966+
max_speakers=5,
967+
max_bytes=chunk_length,
968+
split_on_speaker_change=SPLIT_ON_SPEAKER_CHANGE,
945969
)
946970
else:
947971
batches = [text]

0 commit comments

Comments
 (0)