Pabloler21 Claude Fable 5 commited on
Commit
730c858
·
1 Parent(s): e6fcd38

fix: strip dashes from TTS input (Kokoro was saying 'dash')

Browse files
Files changed (2) hide show
  1. tests/test_voice.py +7 -0
  2. voice.py +12 -1
tests/test_voice.py CHANGED
@@ -23,3 +23,10 @@ def test_speak_is_guarded_against_errors(monkeypatch):
23
  # even if a pipeline exists, a synth error must degrade to None, never raise
24
  monkeypatch.setattr(voice, "_PIPELINE", object()) # a bogus pipeline
25
  assert voice.speak("anything") is None
 
 
 
 
 
 
 
 
23
  # even if a pipeline exists, a synth error must degrade to None, never raise
24
  monkeypatch.setattr(voice, "_PIPELINE", object()) # a bogus pipeline
25
  assert voice.speak("anything") is None
26
+
27
+
28
+ def test_clean_for_tts_strips_dashes():
29
+ assert "—" not in voice._clean_for_tts("i don't— i don't remember")
30
+ assert "-" not in voice._clean_for_tts("a well-kept secret")
31
+ # collapses to single spaces, trims
32
+ assert voice._clean_for_tts("a — b") == "a , b"
voice.py CHANGED
@@ -11,6 +11,7 @@ os.environ.setdefault("OPENBLAS_NUM_THREADS", "4")
11
  os.environ.setdefault("MKL_NUM_THREADS", "4")
12
 
13
  import base64
 
14
 
15
  import numpy as np
16
 
@@ -64,14 +65,24 @@ def _fog(x, intensity=_FILTER_INTENSITY, pitch=1.16):
64
  return (y / (np.max(np.abs(y)) + 1e-9) * 0.85).astype(np.float32)
65
 
66
 
 
 
 
 
 
 
 
67
  def speak(text: str) -> str | None:
68
  """Synthesize `text` as Hollow's whisper; return base64 WAV, or None if
69
  voice is unavailable or anything fails (never raises)."""
70
  if _PIPELINE is None or not text or not text.strip():
71
  return None
72
  try:
 
 
 
73
  audio = None
74
- for _, _, chunk in _PIPELINE(text, voice=_VOICE, speed=_SPEED):
75
  audio = chunk if audio is None else np.concatenate([audio, chunk])
76
  audio = np.asarray(audio, dtype=np.float32)
77
  audio = audio / (np.max(np.abs(audio)) + 1e-9) * 0.9
 
11
  os.environ.setdefault("MKL_NUM_THREADS", "4")
12
 
13
  import base64
14
+ import re
15
 
16
  import numpy as np
17
 
 
65
  return (y / (np.max(np.abs(y)) + 1e-9) * 0.85).astype(np.float32)
66
 
67
 
68
+ def _clean_for_tts(text: str) -> str:
69
+ """Kokoro verbalizes dashes ('—', '-') as words. Replace em/en dashes with a
70
+ comma pause and hyphens with a space, then collapse whitespace."""
71
+ text = text.replace("—", ", ").replace("–", ", ").replace("-", " ")
72
+ return re.sub(r"\s+", " ", text).strip()
73
+
74
+
75
  def speak(text: str) -> str | None:
76
  """Synthesize `text` as Hollow's whisper; return base64 WAV, or None if
77
  voice is unavailable or anything fails (never raises)."""
78
  if _PIPELINE is None or not text or not text.strip():
79
  return None
80
  try:
81
+ cleaned = _clean_for_tts(text)
82
+ if not cleaned:
83
+ return None
84
  audio = None
85
+ for _, _, chunk in _PIPELINE(cleaned, voice=_VOICE, speed=_SPEED):
86
  audio = chunk if audio is None else np.concatenate([audio, chunk])
87
  audio = np.asarray(audio, dtype=np.float32)
88
  audio = audio / (np.max(np.abs(audio)) + 1e-9) * 0.9