From 25c994ecbcf0158fa765f7fad395f939486c7270 Mon Sep 17 00:00:00 2001 From: John Tennant Date: Wed, 19 Aug 2026 13:58:18 -0400 Subject: [PATCH] perf(huddle): enable bit-exact streaming TTS by default Signed-off-by: John Tennant --- desktop/src-tauri/src/huddle/tts.rs | 8 +-- desktop/src-tauri/src/huddle/tts_streaming.rs | 50 ++++++++++++++----- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/desktop/src-tauri/src/huddle/tts.rs b/desktop/src-tauri/src/huddle/tts.rs index aca2339a3c..f4071034bc 100644 --- a/desktop/src-tauri/src/huddle/tts.rs +++ b/desktop/src-tauri/src/huddle/tts.rs @@ -426,8 +426,8 @@ fn tts_worker( // `tts_active` lifecycle: set on the first append while idle, cleared // whenever the player has fully drained — either in the idle timeout // arm or on item receipt before synthesis begins. - // EXPERIMENTAL (latency bench): `Some(emit_frames)` = stream PCM deltas - // out of Pocket as they are generated (see tts_streaming.rs). + // `Some(emit_frames)` streams PCM deltas out of Pocket as they are + // generated (see tts_streaming.rs). `None` is the operational fallback. let tts_streaming = streaming_emit_frames(); // `first_append` = "no audio queued since the player last went idle". // Flipped by `build_sentence_append_buffer` on the first real append; the @@ -728,8 +728,8 @@ fn tts_worker( continue; } - // EXPERIMENTAL (latency bench): streaming synthesis path — see - // tts_streaming.rs for the mechanics and exactness constraints. + // Streaming synthesis path. See tts_streaming.rs for the mechanics + // and exactness constraints. if let Some(emit_frames) = tts_streaming { let outcome = synthesize_streaming( &engine, diff --git a/desktop/src-tauri/src/huddle/tts_streaming.rs b/desktop/src-tauri/src/huddle/tts_streaming.rs index 2df81d557d..6f4e641c4f 100644 --- a/desktop/src-tauri/src/huddle/tts_streaming.rs +++ b/desktop/src-tauri/src/huddle/tts_streaming.rs @@ -1,7 +1,8 @@ -//! EXPERIMENTAL (latency bench): streaming synthesis path for the TTS worker. +//! Streaming synthesis path for the TTS worker. //! -//! `BUZZ_TTS_STREAMING=1` streams PCM deltas out of Pocket as they are -//! generated instead of waiting for the full first-chunk synthesis. +//! PCM deltas stream out of Pocket as they are generated instead of waiting +//! for the full first-chunk synthesis. `BUZZ_TTS_STREAMING=0` restores the +//! batch path as an operational fallback. //! `BUZZ_TTS_EMIT_FRAMES` tunes the delta size in Flow LM frames (80 ms of //! audio each). Default 12 = the Mimi decoder's native chunk, which keeps //! streamed audio bit-identical to the batch path; smaller deltas are faster @@ -12,17 +13,20 @@ use super::*; use crate::huddle::pocket::{PocketTts, VoiceStyle}; -/// Read the streaming env overrides once per worker: `Some(emit_frames)` -/// when `BUZZ_TTS_STREAMING=1`, `None` for the production batch path. +/// Read the streaming env overrides once per worker. Streaming defaults to the +/// Mimi decoder's native 12-frame chunk; `BUZZ_TTS_STREAMING=0` opts out. pub(super) fn streaming_emit_frames() -> Option { - std::env::var("BUZZ_TTS_STREAMING") - .is_ok_and(|v| v == "1") - .then(|| { - std::env::var("BUZZ_TTS_EMIT_FRAMES") - .ok() - .and_then(|v| v.parse().ok()) - .unwrap_or(12) - }) + resolve_streaming_emit_frames( + std::env::var("BUZZ_TTS_STREAMING").ok().as_deref(), + std::env::var("BUZZ_TTS_EMIT_FRAMES").ok().as_deref(), + ) +} + +fn resolve_streaming_emit_frames( + enabled: Option<&str>, + emit_frames: Option<&str>, +) -> Option { + (enabled != Some("0")).then(|| emit_frames.and_then(|v| v.parse().ok()).unwrap_or(12)) } /// Playback context threaded through one streamed chunk. @@ -129,6 +133,26 @@ fn drive_streaming( mod tests { use super::*; + #[test] + fn streaming_defaults_to_the_bit_exact_decoder_chunk() { + assert_eq!(resolve_streaming_emit_frames(None, None), Some(12)); + assert_eq!(resolve_streaming_emit_frames(Some("1"), None), Some(12)); + } + + #[test] + fn streaming_can_be_disabled_for_operational_rollback() { + assert_eq!(resolve_streaming_emit_frames(Some("0"), None), None); + } + + #[test] + fn explicit_emit_frames_remain_available_for_latency_benches() { + assert_eq!(resolve_streaming_emit_frames(None, Some("6")), Some(6)); + assert_eq!( + resolve_streaming_emit_frames(None, Some("invalid")), + Some(12) + ); + } + #[test] fn inference_failure_fades_the_retained_final_block() { let cancel = AtomicBool::new(false);