Jacky2305 commited on
Commit
9a9e06c
·
1 Parent(s): 92f0496

Switch to Gradio SDK

Browse files
Files changed (6) hide show
  1. Dockerfile +0 -19
  2. Dockerfile.test +0 -5
  3. README.md +14 -65
  4. app.py +125 -0
  5. requirements.txt +1 -3
  6. test_main.py +0 -5
Dockerfile DELETED
@@ -1,19 +0,0 @@
1
- # Use a more complete base image
2
- FROM python:3.11
3
-
4
- WORKDIR /app
5
-
6
- # System deps - use a single RUN to avoid cache issues
7
- RUN apt-get update && apt-get install -y espeak-ng libespeak-ng1 && rm -rf /var/lib/apt/lists/*
8
-
9
- # Python deps
10
- COPY requirements.txt .
11
- RUN pip install --no-cache-dir -r requirements.txt
12
-
13
- # App
14
- COPY main.py .
15
-
16
- ENV PORT=7860
17
- EXPOSE 7860
18
-
19
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dockerfile.test DELETED
@@ -1,5 +0,0 @@
1
- FROM python:3.11-slim
2
- WORKDIR /app
3
- COPY test_build.py .
4
- RUN pip install fastapi uvicorn
5
- CMD ["uvicorn", "test_build:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
 
README.md CHANGED
@@ -3,14 +3,14 @@ title: Kokoro 82M TTS
3
  emoji: 🗣️
4
  colorFrom: blue
5
  colorTo: purple
6
- sdk: docker
7
  app_port: 7860
8
  pinned: false
9
  ---
10
 
11
- # Kokoro 82M TTS API
12
 
13
- High-quality, fast text-to-speech on CPU using **Kokoro 82M** (Apache 2.0).
14
 
15
  ## Features
16
 
@@ -18,39 +18,7 @@ High-quality, fast text-to-speech on CPU using **Kokoro 82M** (Apache 2.0).
18
  - **Speed**: RTF ~0.64 on CPU Basic (1.6× faster than real-time)
19
  - **Voices**: 50+ voices (US/UK English, multiple speakers)
20
  - **License**: Apache 2.0 (commercial use OK)
21
- - **Simple REST API**: `POST /tts` → WAV audio
22
-
23
- ## API Endpoints
24
-
25
- ### POST /tts
26
-
27
- Synthesize speech from text.
28
-
29
- **Request:**
30
- ```json
31
- {
32
- "text": "Hello, this is Kokoro 82M!",
33
- "voice": "af_heart",
34
- "speed": 1.0,
35
- "sample_rate": 24000
36
- }
37
- ```
38
-
39
- **Parameters:**
40
- - `text` (required): Text to synthesize
41
- - `voice` (default: `af_heart`): Voice ID (see `/health` or `/` for full list)
42
- - `speed` (default: `1.0`): Speed multiplier (0.5–2.0)
43
- - `sample_rate` (default: `24000`): Output sample rate (Kokoro native is 24kHz)
44
-
45
- **Response:** WAV audio file (24kHz, 16-bit PCM)
46
-
47
- ### GET /health
48
-
49
- Health check + loaded voices.
50
-
51
- ### GET /
52
-
53
- API info + voice catalog organized by category.
54
 
55
  ## Voice Categories
56
 
@@ -62,47 +30,28 @@ API info + voice catalog organized by category.
62
  | `bm_` | UK Male | `bm_george`, `bm_lewis` |
63
  | Others | Multilingual/Other | `jf_alpha` (JA), `zf_xiaobei` (ZH), etc. |
64
 
65
- ## Usage Example (curl)
66
-
67
- ```bash
68
- curl -X POST "https://your-username-kokoro-tts.hf.space/tts" \
69
- -H "Content-Type: application/json" \
70
- -d '{"text": "你好,这是 Kokoro 82M 语音合成测试", "voice": "af_heart"}' \
71
- -o output.wav
72
- ```
73
-
74
- ## Python Client
75
-
76
- ```python
77
- import requests
78
 
79
- url = "https://your-username-kokoro-tts.hf.space/tts"
80
- resp = requests.post(url, json={"text": "Hello world!", "voice": "am_michael"})
81
- with open("output.wav", "wb") as f:
82
- f.write(resp.content)
83
  ```
 
84
 
85
- ## Deployment
86
-
87
- This Space uses Docker (`Dockerfile`) with:
88
- - Python 3.11 slim
89
- - `espeak-ng` for grapheme-to-phoneme
90
- - `kokoro-onnx` + `onnxruntime` for inference
91
- - Model files auto-downloaded on first run (~82 MB ONNX + ~30 MB voices bin)
92
 
93
- **Startup time**: ~15-20 seconds (first run downloads models)
94
- **Concurrent requests**: Limited by CPU; recommend sequential or small queue
95
 
96
  ## Model Info
97
 
98
  - **Architecture**: StyleTTS2-inspired, 82M parameters
99
- - **Training data**: High-quality English speech (Gigaspeech, etc.)
100
  - **Output**: 24kHz mono
101
- - **Source**: `hexgrad/Kokoro-82M` (PyTorch) → `onnx-community/Kokoro-82M-v1.0-ONNX`
102
  - **ONNX release**: `thewh1teagle/kokoro-onnx` model files
103
 
104
  ## License
105
 
106
  - Model weights: Apache 2.0
107
  - Code: MIT
108
- # force rebuild
 
3
  emoji: 🗣️
4
  colorFrom: blue
5
  colorTo: purple
6
+ sdk: gradio
7
  app_port: 7860
8
  pinned: false
9
  ---
10
 
11
+ # Kokoro 82M TTS
12
 
13
+ High-quality text-to-speech on CPU using **Kokoro 82M** (Apache 2.0).
14
 
15
  ## Features
16
 
 
18
  - **Speed**: RTF ~0.64 on CPU Basic (1.6× faster than real-time)
19
  - **Voices**: 50+ voices (US/UK English, multiple speakers)
20
  - **License**: Apache 2.0 (commercial use OK)
21
+ - **Interface**: Gradio web UI + REST API
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  ## Voice Categories
24
 
 
30
  | `bm_` | UK Male | `bm_george`, `bm_lewis` |
31
  | Others | Multilingual/Other | `jf_alpha` (JA), `zf_xiaobei` (ZH), etc. |
32
 
33
+ ## REST API (also available)
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
+ ### POST /tts
36
+ ```json
37
+ {"text": "Hello!", "voice": "af_heart", "speed": 1.0}
 
38
  ```
39
+ Returns: WAV audio (24kHz, 16-bit PCM)
40
 
41
+ ### GET /health
42
+ Health check + loaded voices.
 
 
 
 
 
43
 
44
+ ### GET /
45
+ API info + voice catalog.
46
 
47
  ## Model Info
48
 
49
  - **Architecture**: StyleTTS2-inspired, 82M parameters
 
50
  - **Output**: 24kHz mono
51
+ - **Source**: `hexgrad/Kokoro-82M` → `onnx-community/Kokoro-82M-v1.0-ONNX`
52
  - **ONNX release**: `thewh1teagle/kokoro-onnx` model files
53
 
54
  ## License
55
 
56
  - Model weights: Apache 2.0
57
  - Code: MIT
 
app.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from kokoro_onnx import Kokoro
3
+ import soundfile as sf
4
+ import numpy as np
5
+ from pathlib import Path
6
+ import requests
7
+ import tempfile
8
+ import os
9
+
10
+ # Model setup
11
+ MODEL_DIR = Path("/tmp/kokoro_models")
12
+ MODEL_DIR.mkdir(exist_ok=True)
13
+ MODEL_PATH = MODEL_DIR / "kokoro-v1.0.onnx"
14
+ VOICES_PATH = MODEL_DIR / "voices-v1.0.bin"
15
+ MODEL_URL = "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx"
16
+ VOICES_URL = "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/voices-v1.0.bin"
17
+
18
+ kokoro = None
19
+
20
+ def download_file(url, dest):
21
+ if dest.exists():
22
+ return
23
+ print(f"Downloading {dest.name}...")
24
+ with requests.get(url, stream=True, timeout=120, allow_redirects=True) as r:
25
+ r.raise_for_status()
26
+ with open(dest, "wb") as f:
27
+ for chunk in r.iter_content(chunk_size=8192):
28
+ f.write(chunk)
29
+ print(f"✓ Downloaded {dest.name}")
30
+
31
+ def get_kokoro():
32
+ global kokoro
33
+ if kokoro is not None:
34
+ return kokoro
35
+ download_file(MODEL_URL, MODEL_PATH)
36
+ download_file(VOICES_URL, VOICES_PATH)
37
+ kokoro = Kokoro(str(MODEL_PATH), str(VOICES_PATH))
38
+ print(f"✓ Kokoro loaded. Voices: {len(kokoro.get_voices())}")
39
+ return kokoro
40
+
41
+ # Warm up
42
+ try:
43
+ get_kokoro()
44
+ except Exception as e:
45
+ print(f"Warmup failed: {e}")
46
+
47
+ def tts(text, voice, speed):
48
+ if not text.strip():
49
+ return None, "请输入文本"
50
+
51
+ k = get_kokoro()
52
+ voices = k.get_voices()
53
+ if voice not in voices:
54
+ return None, f"语音 '{voice}' 不可用"
55
+
56
+ try:
57
+ audio, sr = k.create(text, voice=voice, speed=speed, lang="en-us")
58
+ # Save to temp file for Gradio
59
+ with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
60
+ sf.write(f.name, audio, sr, format="WAV", subtype="PCM_16")
61
+ return f.name, "生成成功"
62
+ except Exception as e:
63
+ return None, f"生成失败: {e}"
64
+
65
+ # Get voice list
66
+ try:
67
+ voices_list = get_kokoro().get_voices()
68
+ except:
69
+ voices_list = ["af_heart", "af_bella", "am_michael", "am_adam", "bf_emma", "bm_george"]
70
+
71
+ # Categorize voices
72
+ us_female = [v for v in voices_list if v.startswith("af_")]
73
+ us_male = [v for v in voices_list if v.startswith("am_")]
74
+ uk_female = [v for v in voices_list if v.startswith("bf_")]
75
+ uk_male = [v for v in voices_list if v.startswith("bm_")]
76
+ other = [v for v in voices_list if not v.startswith(("af_", "am_", "bf_", "bm_"))]
77
+
78
+ voice_choices = us_female + us_male + uk_female + uk_male + other
79
+
80
+ with gr.Blocks(title="Kokoro 82M TTS") as demo:
81
+ gr.Markdown("# 🗣️ Kokoro 82M TTS - High Quality CPU TTS")
82
+ gr.Markdown("**Model**: Kokoro 82M (Apache 2.0) | **Quality**: UTMOS MOS ~4.45 | **Speed**: RTF ~0.64 on CPU")
83
+
84
+ with gr.Row():
85
+ with gr.Column(scale=2):
86
+ text = gr.Textbox(
87
+ label="Text to synthesize",
88
+ placeholder="Enter text here...",
89
+ lines=3,
90
+ value="Hello, this is Kokoro 82M text to speech!"
91
+ )
92
+ voice = gr.Dropdown(
93
+ label="Voice",
94
+ choices=voice_choices,
95
+ value="af_heart" if "af_heart" in voice_choices else voice_choices[0],
96
+ info=f"Total {len(voice_choices)} voices available"
97
+ )
98
+ speed = gr.Slider(0.5, 2.0, value=1.0, step=0.1, label="Speed")
99
+ btn = gr.Button("Generate", variant="primary")
100
+ status = gr.Textbox(label="Status", interactive=False)
101
+
102
+ with gr.Column(scale=1):
103
+ audio_out = gr.Audio(label="Output", type="filepath")
104
+
105
+ btn.click(tts, inputs=[text, voice, speed], outputs=[audio_out, status])
106
+
107
+ with gr.Accordion("Voice Categories", open=False):
108
+ gr.Markdown(f"""
109
+ **US Female** ({len(us_female)}): {', '.join(us_female[:10])}{'...' if len(us_female) > 10 else ''}
110
+ **US Male** ({len(us_male)}): {', '.join(us_male[:10])}{'...' if len(us_male) > 10 else ''}
111
+ **UK Female** ({len(uk_female)}): {', '.join(uk_female[:10])}{'...' if len(uk_female) > 10 else ''}
112
+ **UK Male** ({len(uk_male)}): {', '.join(uk_male[:10])}{'...' if len(uk_male) > 10 else ''}
113
+ **Other** ({len(other)}): {', '.join(other[:10])}{'...' if len(other) > 10 else ''}
114
+ """)
115
+
116
+ gr.Markdown("""
117
+ ---
118
+ **API**: Also available via REST:
119
+ - `POST /tts` - `{"text": "...", "voice": "af_heart", "speed": 1.0}` → WAV
120
+ - `GET /health` - Health check
121
+ - `GET /` - Voice catalog
122
+ """)
123
+
124
+ if __name__ == "__main__":
125
+ demo.launch(server_name="0.0.0.0", server_port=7860)
requirements.txt CHANGED
@@ -1,7 +1,5 @@
1
- fastapi==0.115.0
2
- uvicorn[standard]==0.32.0
3
  kokoro-onnx==0.5.0
4
  onnxruntime==1.20.0
5
  soundfile==0.12.1
6
- pydantic==2.9.0
7
  requests==2.32.0
 
1
+ gradio==4.44.0
 
2
  kokoro-onnx==0.5.0
3
  onnxruntime==1.20.0
4
  soundfile==0.12.1
 
5
  requests==2.32.0
test_main.py DELETED
@@ -1,5 +0,0 @@
1
- from fastapi import FastAPI
2
- app = FastAPI()
3
- @app.get("/")
4
- def root():
5
- return {"message": "test"}