fix: cuDNN error, FA2 dtype warning, choppy playback, stop behavior
1. Chatterbox cuDNN error: enable cudnn.benchmark and set
float32_matmul_precision("high") before loading on CUDA
2. Flash Attention 2 dtype warning: pass torch_dtype (not dtype) to
Qwen3TTSModel.from_pretrained so FA2 sees the specified bfloat16
3. Choppy TTS playback: batch-drain the playback queue before
synthesizing so multiple short text segments merge into one
synthesis+playback pass instead of individual choppy cycles.
Also check stop flag after synthesis before playing.
4. Mic stop not halting TTS: call tts_controller.stop_playback()
immediately in stop_recording() so in-flight TTS audio stops
when the user clicks stop
https://claude.ai/code/session_01965m1v3f1cK6gDV1JHioA8
+44 −6 · 3 files changed
-
src/tts_controller.py+24 −1 -
src/tts_provider.py+14 −5 -
src/whispering_ui/bridge.py+6 −0
@@ -324,7 +324,11 @@ class TTSController: self._playback_thread.start() def _playback_loop(self): - """Background thread that processes the playback queue.""" + """Background thread that processes the playback queue. + + Batches all queued text segments into a single synthesis+playback + pass to eliminate choppy gaps between segments. + """ import sounddevice as sd while not self._playback_stop.is_set(): @@ -338,6 +342,22 @@ class TTSController: break text, also_save, file_format = item + + # Drain any additional queued items and merge into one text block. + # This prevents choppy playback when many short segments arrive + # in rapid succession from the AI processor. + while True: + try: + extra = self._playback_queue.get_nowait() + except queue.Empty: + break + if extra is None: + break + extra_text, extra_save, extra_fmt = extra + text = text.rstrip() + " " + extra_text.lstrip() + also_save = also_save or extra_save + file_format = extra_fmt # use latest format + try: self._is_playing = True @@ -350,6 +370,9 @@ class TTSController: audio, sample_rate = result + if self._playback_stop.is_set(): + break + # Save to file if requested if also_save: import time @@ -124,6 +124,13 @@ class ChatterboxProvider(BaseTTSProvider): return try: + if self.device == "cuda": + # Prevent cuDNN errors by enabling benchmark mode and setting + # matmul precision for tensor cores + torch.backends.cudnn.enabled = True + torch.backends.cudnn.benchmark = True + torch.set_float32_matmul_precision("high") + if self.model_type == "multilingual": from chatterbox.mtl_tts import ChatterboxMultilingualTTS print(f"Loading multilingual ChatterboxTTS on {self.device}...") @@ -147,7 +154,8 @@ class ChatterboxProvider(BaseTTSProvider): if "cuDNN" in error_str or "CUDA" in error_str: raise RuntimeError( f"CUDA/cuDNN error loading ChatterboxTTS:\n{e}\n\n" - "Check that PyTorch CUDA version matches your GPU driver." + "Try: torch.backends.cudnn.benchmark = True\n" + "Or check that PyTorch CUDA version matches your GPU driver." ) from e raise RuntimeError(f"Failed to load ChatterboxTTS: {e}") from e @@ -254,9 +262,10 @@ class Qwen3TTSProvider(BaseTTSProvider): print(f"Loading Qwen3-TTS ({model_name}) on {self.device}...") # Determine dtype and attention implementation - dtype = torch.bfloat16 if self.device == "cuda" else torch.float32 + # Use torch_dtype (not dtype) so Flash Attention 2 sees the dtype + compute_dtype = torch.bfloat16 if self.device == "cuda" else torch.float32 kwargs = {"device_map": f"{self.device}:0" if self.device == "cuda" else self.device} - kwargs["dtype"] = dtype + kwargs["torch_dtype"] = compute_dtype # flash-attn is required try: @@ -298,9 +307,9 @@ class Qwen3TTSProvider(BaseTTSProvider): model_name = self._get_model_name(clone=True) print(f"Loading Qwen3-TTS clone model ({model_name})...") - dtype = torch.bfloat16 if self.device == "cuda" else torch.float32 + compute_dtype = torch.bfloat16 if self.device == "cuda" else torch.float32 kwargs = {"device_map": f"{self.device}:0" if self.device == "cuda" else self.device} - kwargs["dtype"] = dtype + kwargs["torch_dtype"] = compute_dtype kwargs["attn_implementation"] = "flash_attention_2" self._clone_model = Qwen3TTSModel.from_pretrained(model_name, **kwargs) @@ -276,6 +276,12 @@ class ProcessingBridge: self.ready[0] = False self.state.status_message = "Stopping..." + # Stop any in-flight TTS playback immediately + if self.tts_controller: + self.tts_controller.stop_playback() + self.state.tts_is_playing = False + self.state.tts_status_message = "" + # Stop polling if self.poll_timer: self.poll_timer.deactivate()