Skip to content

feat: stream synthesis sentence-by-sentence for low-latency playback#31

Merged
StuBehan merged 2 commits into
mainfrom
feat/streaming-playback
Jul 8, 2026
Merged

feat: stream synthesis sentence-by-sentence for low-latency playback#31
StuBehan merged 2 commits into
mainfrom
feat/streaming-playback

Conversation

@StuBehan

@StuBehan StuBehan commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Makes speak() stream synthesis sentence-by-sentence so audio starts almost immediately instead of after the whole text is synthesized. Kokoro batches by phoneme count (~510), so a full paragraph synthesizes as one blob before any sound plays — that's the lead-in lag. Splitting into sentences ourselves means the first sentence (~0.2s to synthesize) plays right away while the rest synthesize in the background.

Changes

  • _split_sentences() — breaks text into sentence-ish chunks on ./!/? followed by whitespace, and on newlines. The whitespace requirement leaves decimals (0.95) and mid-token dots (file.ts) intact.
  • _stream_play() — a producer thread synthesizes ahead into a bounded queue (maxsize=8) while the main thread plays each chunk as it's ready, so later sentences are usually synthesized by the time the previous one finishes.
  • speak(blocking=True) streams inline; blocking=False runs _stream_play on a background daemon thread (_play_thread).
  • stop() now sets a _stop_event so an in-progress stream halts between sentences (in addition to sd.stop()).
  • Per-call voice/speed/lang overrides are threaded through to each sentence's synthesis.

Testing

Ran the full CI-equivalent suite locally, all green:

ruff check .            # All checks passed!
ruff format --check .   # 18 files already formatted
mypy                    # Success: no issues found in 16 source files
pytest -q               # 126 passed

New tests cover: one synth+play cycle per sentence, single-sentence playback, background-thread playback for blocking=False, engine defaults vs per-call overrides, stop() setting the event, and _split_sentences edge cases (decimals/mid-token dots preserved, newline splitting, blank input).

Related issues

@StuBehan StuBehan force-pushed the feat/streaming-playback branch from ebeb53b to 1187fd2 Compare July 7, 2026 23:29
@StuBehan StuBehan merged commit f7d4a70 into main Jul 8, 2026
10 checks passed
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.

1 participant