Spaces:
Running on Zero
Running on Zero
Narration: all 28 English Kokoro voices, British pronunciation for the British ones
Browse files- README.md +1 -1
- app.py +10 -1
- tts_worker.py +15 -2
README.md
CHANGED
|
@@ -55,7 +55,7 @@ models:
|
|
| 55 |
|
| 56 |
**Transcribe tab (speech to text, speech recognition, automatic speech recognition):** Whisper large v3 turbo (openai) writes down speech from a file or the microphone, in its own language or translated to English.
|
| 57 |
|
| 58 |
-
**Narration tab (text to speech, speech generation, machine speech):** Kokoro (hexgrad) reads your text aloud in a choice of
|
| 59 |
|
| 60 |
**Describe tab:** three ways to describe what you have.
|
| 61 |
|
|
|
|
| 55 |
|
| 56 |
**Transcribe tab (speech to text, speech recognition, automatic speech recognition):** Whisper large v3 turbo (openai) writes down speech from a file or the microphone, in its own language or translated to English.
|
| 57 |
|
| 58 |
+
**Narration tab (text to speech, speech generation, machine speech):** Kokoro (hexgrad) reads your text aloud in a choice of 28 English voices, American and British.
|
| 59 |
|
| 60 |
**Describe tab:** three ways to describe what you have.
|
| 61 |
|
app.py
CHANGED
|
@@ -82,9 +82,18 @@ LISTEN_MODEL = "laion/clap-htsat-unfused" # Recognises genre, mood, instrum
|
|
| 82 |
SPEECH_MODEL = "openai/whisper-large-v3-turbo" # Speech to text
|
| 83 |
VOICE = "af_heart" # Kokoro voice
|
| 84 |
VOICE_RATE = 24000
|
|
|
|
| 85 |
VOICES = {"Heart (woman)": "af_heart", "Bella (woman)": "af_bella", "Nicole (woman, soft)": "af_nicole",
|
| 86 |
"Sarah (woman)": "af_sarah", "Adam (man)": "am_adam", "Michael (man)": "am_michael",
|
| 87 |
-
"Fenrir (man, deep)": "am_fenrir", "Puck (man, lively)": "am_puck"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
MAX_NARRATION_CHARS = 3000
|
| 89 |
WORDS_PER_SECOND = 2.5
|
| 90 |
|
|
|
|
| 82 |
SPEECH_MODEL = "openai/whisper-large-v3-turbo" # Speech to text
|
| 83 |
VOICE = "af_heart" # Kokoro voice
|
| 84 |
VOICE_RATE = 24000
|
| 85 |
+
# All 28 English voices of Kokoro. The first eight keep the names they had when there were only eight.
|
| 86 |
VOICES = {"Heart (woman)": "af_heart", "Bella (woman)": "af_bella", "Nicole (woman, soft)": "af_nicole",
|
| 87 |
"Sarah (woman)": "af_sarah", "Adam (man)": "am_adam", "Michael (man)": "am_michael",
|
| 88 |
+
"Fenrir (man, deep)": "am_fenrir", "Puck (man, lively)": "am_puck",
|
| 89 |
+
"Alloy (woman)": "af_alloy", "Aoede (woman)": "af_aoede", "Jessica (woman)": "af_jessica",
|
| 90 |
+
"Kore (woman)": "af_kore", "Nova (woman)": "af_nova", "River (woman)": "af_river", "Sky (woman)": "af_sky",
|
| 91 |
+
"Echo (man)": "am_echo", "Eric (man)": "am_eric", "Liam (man)": "am_liam", "Onyx (man)": "am_onyx",
|
| 92 |
+
"Santa (man)": "am_santa",
|
| 93 |
+
"Alice (British woman)": "bf_alice", "Emma (British woman)": "bf_emma",
|
| 94 |
+
"Isabella (British woman)": "bf_isabella", "Lily (British woman)": "bf_lily",
|
| 95 |
+
"Daniel (British man)": "bm_daniel", "Fable (British man)": "bm_fable",
|
| 96 |
+
"George (British man)": "bm_george", "Lewis (British man)": "bm_lewis"}
|
| 97 |
MAX_NARRATION_CHARS = 3000
|
| 98 |
WORDS_PER_SECOND = 2.5
|
| 99 |
|
tts_worker.py
CHANGED
|
@@ -14,13 +14,26 @@ os.dup2(2, 1)
|
|
| 14 |
import numpy as np
|
| 15 |
from kokoro import KPipeline
|
| 16 |
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
for line in sys.stdin:
|
| 20 |
try:
|
| 21 |
job = json.loads(line)
|
| 22 |
chunks = [np.asarray(audio, dtype=np.float32)
|
| 23 |
-
for _, _, audio in
|
| 24 |
np.save(job["out"], np.concatenate(chunks) if chunks else np.zeros(0, dtype=np.float32))
|
| 25 |
answer = {"ok": True}
|
| 26 |
except Exception as e:
|
|
|
|
| 14 |
import numpy as np
|
| 15 |
from kokoro import KPipeline
|
| 16 |
|
| 17 |
+
pipes = {"a": KPipeline(lang_code="a", device="cpu")} # Kokoro is tiny (82M) and fast enough on the CPU
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def pipe_for(voice):
|
| 21 |
+
"""American pronunciation for the af_/am_ voices, British for bf_/bm_. Both share one copy of the model."""
|
| 22 |
+
code = "b" if voice.startswith("b") else "a"
|
| 23 |
+
if code not in pipes:
|
| 24 |
+
try:
|
| 25 |
+
pipes[code] = KPipeline(lang_code=code, model=pipes["a"].model)
|
| 26 |
+
except Exception as e:
|
| 27 |
+
print(f"British pronunciation unavailable, using American: {type(e).__name__}: {e}", file=sys.stderr)
|
| 28 |
+
pipes[code] = pipes["a"]
|
| 29 |
+
return pipes[code]
|
| 30 |
+
|
| 31 |
|
| 32 |
for line in sys.stdin:
|
| 33 |
try:
|
| 34 |
job = json.loads(line)
|
| 35 |
chunks = [np.asarray(audio, dtype=np.float32)
|
| 36 |
+
for _, _, audio in pipe_for(job["voice"])(job["text"], voice=job["voice"], speed=float(job["speed"]))]
|
| 37 |
np.save(job["out"], np.concatenate(chunks) if chunks else np.zeros(0, dtype=np.float32))
|
| 38 |
answer = {"ok": True}
|
| 39 |
except Exception as e:
|