diff --git a/voice_service/app.py b/voice_service/app.py index 7e79baf..a0ee86f 100644 --- a/voice_service/app.py +++ b/voice_service/app.py @@ -183,10 +183,26 @@ def chatterbox_tts(text: str, language: str, ref_path: str) -> bytes: ref = ref_path or active_ref() if ref and os.path.exists(ref): kwargs["audio_prompt_path"] = ref # Zero-Shot Voice-Cloning aus Referenz - wav = model.generate(text, **kwargs) + # Chatterbox hat einen sporadischen Bug (alignment_stream_analyzer → NoneType), der einzelne + # Generierungen fehlschlagen lässt → im Voice-Loop „Stimme kam nicht". Ein Retry klappt meist. + wav = None + last_err: Exception | None = None + for attempt in range(3): + try: + wav = model.generate(text, **kwargs) + break + except Exception as exc: # noqa: BLE001 + last_err = exc + log.warning("Chatterbox-Generation fehlgeschlagen (Versuch %d/3): %s", attempt + 1, exc) + if wav is None: + raise HTTPException(502, f"Chatterbox-Generation fehlgeschlagen: {last_err}") # wav = torch.Tensor [1, N] @ model.sr → in WAV-Bytes serialisieren. import numpy as np arr = wav.squeeze(0).detach().cpu().numpy().astype(np.float32) + # Peak-Normalisierung gegen Übersteuern/Clipping (Klon-Output ist oft zu laut → kratzig). + peak = float(np.max(np.abs(arr))) if arr.size else 0.0 + if peak > 0: + arr = arr / peak * float(os.environ.get("VOICE_CHATTERBOX_PEAK", "0.9")) buf = io.BytesIO() sf.write(buf, arr, int(model.sr), format="WAV", subtype="PCM_16") return buf.getvalue()