-
feat(tts): config-driven voices + two-voice dialogue/narration split (DEC-11)
released this
2026-08-11 08:02:30 -07:00 | 1 commits to main since this releaseVoice assignment moves from the hardcoded server map to ~/.config/ratatoskr/
voices.json (per-agent voice + optional narration_voice). An agent with a
narration_voice gets a two-voice split: quoted speech invoice, narration in
narration_voice, synthesized per-span and stitched under one WAV header.- new src/ratatoskr/voices.py: load_voice_config (degrade-not-crash),
segment_dialogue (quote-based, straight + curly), resolve_voice_spans - tts.py: tts_stream_stitched replaces tts_stream — serial per-span synth,
span 0 verbatim, spans 1..N header-stripped -> one gapless 48kHz stream;
a single-span list is a byte-identical passthrough (no single-voice regression) - server.py: _tts_endpoint resolves spans from app.state.voice_config; the
hardcoded _TTS_VOICE_MAP is retired; create_app gains a voice_config param - entrypoint.py: loads voices.json at startup
- contract DEC-11 + INV-TTS-5/6/7; initial config donut->donut,
sindra->miranda (dialogue) / emmie (narration)
Live-verified on :8765: Sindra mixed turn -> 2 dots calls (emmie+miranda)
stitched into one 48kHz WAV with a single RIFF header; Donut single-voice
unchanged. 545 tests green (incl. new test_voices.py).Downloads
- new src/ratatoskr/voices.py: load_voice_config (degrade-not-crash),