Spaces:
Sleeping
Sleeping
Commit ·
730c858
1
Parent(s): e6fcd38
fix: strip dashes from TTS input (Kokoro was saying 'dash')
Browse files- tests/test_voice.py +7 -0
- 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(
|
| 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
|