Keep streaming TTS open; name the swappable-provider pattern

Utterances cross as chunks with an explicit terminator, so whole-utterance is
a stream of one chunk and a streaming TTS needs no protocol change. Records
what must not be built — a single-body download-then-play player — since that
forecloses streaming silently.

Also notes the transport is fully exercisable with espeak-ng or a canned WAV,
so audio transport need not wait on GPT-SoVITS.

The provider-behind-an-interface shape has now been picked three times
independently, so it is named as the project idiom rather than re-derived.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kj58Jugkic783b8ZrtTVZ
This commit is contained in:
2026-09-04 14:02:41 +09:00
parent 1d24d90c36
commit 2fe9665664
2 changed files with 47 additions and 10 deletions
+27 -9
View File
@@ -138,17 +138,35 @@ The tablet is the media source, so it holds the aux cable whatever is decided
about speech. Giving the Orin its own aux line would not free the display unit —
it would add a second permanent cable to the car and leave the first one in place.
### TTS crosses as utterances, not as a stream
### TTS crosses as chunked utterances
An utterance is short, finite, and fully synthesised before it is needed. So it
ships as a **discrete object over the reliable channel**, not as a real-time audio
stream: no jitter buffer, no underrun handling, no clock synchronisation, and a
dropped packet is a retry rather than an audible gap.
An utterance travels as a **sequence of chunks with an explicit terminator**, over
the reliable channel. Not as one object, and not as a clock-synchronised audio
stream.
The cost is that speech begins only once synthesis *and* transfer complete. For a
car assistant that is fine — nothing here is lip-synced. If streaming TTS is ever
wanted, so that speech starts before synthesis finishes, this is the decision to
revisit.
This is deliberately the shape that keeps both modes open, because whole-utterance
is simply a stream of one chunk:
| TTS capability | What it sends |
|---|---|
| Synthesises fully, then returns | one chunk, then terminator |
| Streams as it synthesises | chunks as produced, then terminator |
Same protocol, same receiver. The receiver starts playback on the first chunk,
appends as more arrive, and finishes on the terminator — which is what Android's
`AudioTrack` streaming mode does natively.
**What must not be built:** an utterance as a single response body with a
"download the file, then play the file" player. That works today and forecloses
streaming permanently. Whether the TTS we land on streams is still open, so the
transport must not assume either way.
### The transport is testable with no real TTS
Audio is the lowest-priority part of the system, but its transport does not have
to wait for GPT-SoVITS. A canned WAV, or `espeak-ng` — instant, tiny, and awful —
exercises chunking, ordering, the terminator, ducking and the no-sink path
completely. Voice quality is irrelevant to every one of those.
### Ducking is the tablet's job