R-TA commited on
Commit
041788a
·
verified ·
1 Parent(s): e15865f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +37 -86
app.py CHANGED
@@ -1,14 +1,10 @@
1
  import os
2
  import html
3
- import base64
4
  import subprocess
5
  import tempfile
6
- from typing import Optional, Dict, List, Tuple
7
 
8
  import gradio as gr
9
- from fastapi import FastAPI, HTTPException
10
- from fastapi.middleware.cors import CORSMiddleware
11
- from pydantic import BaseModel
12
 
13
 
14
  DESCRIPTION = """
@@ -93,20 +89,13 @@ def _parse_voices(output: str):
93
  return mapping
94
 
95
 
96
- def _get_voice_mapping() -> Dict[str, List[str]]:
97
  try:
98
  proc = subprocess.run(["mimic3", "--voices"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
99
  if proc.returncode != 0:
100
  err = proc.stderr.decode(errors="ignore")
101
  raise gr.Error(f"Failed to list voices.\n\n{err}")
102
- return _parse_voices(proc.stdout.decode(errors="ignore"))
103
- except FileNotFoundError:
104
- raise gr.Error("mimic3 CLI not found. Ensure 'mycroft-mimic3-tts' is installed.")
105
-
106
-
107
- def load_voices():
108
- try:
109
- mapping = _get_voice_mapping()
110
  if not mapping:
111
  raise gr.Error("No voices found. Try again after models are available.")
112
  languages = sorted(mapping.keys())
@@ -160,6 +149,14 @@ def on_language_change(lang: str, mapping: dict):
160
  return gr.update(choices=voices, value=(voices[0] if voices else None))
161
 
162
 
 
 
 
 
 
 
 
 
163
  with gr.Blocks(title="Mimic 3 TTS") as demo:
164
  gr.Markdown(f"# Mimic 3 TTS\n{DESCRIPTION}")
165
 
@@ -169,6 +166,11 @@ with gr.Blocks(title="Mimic 3 TTS") as demo:
169
  with gr.Row():
170
  language_dd = gr.Dropdown(label="Language", choices=[], interactive=True)
171
  voice_dd = gr.Dropdown(label="Voice", choices=[], interactive=True)
 
 
 
 
 
172
  voices_state = gr.State({})
173
 
174
  with gr.Accordion("Advanced (SSML)", open=False):
@@ -196,80 +198,29 @@ with gr.Blocks(title="Mimic 3 TTS") as demo:
196
  outputs=[voice_dd],
197
  )
198
 
199
- btn.click(
200
- fn=synthesize,
201
- inputs=[text, voice_dd, use_ssml, rate, pitch],
202
- outputs=[audio],
 
203
  )
204
 
205
- # --- FastAPI app and API endpoints ---
206
-
207
- app = FastAPI(title="Mimic 3 TTS API")
208
- app.add_middleware(
209
- CORSMiddleware,
210
- allow_origins=["*"],
211
- allow_credentials=True,
212
- allow_methods=["*"],
213
- allow_headers=["*"],
214
- )
215
-
216
- # Mount Gradio UI at root path
217
- from gradio.routes import mount_gradio_app # type: ignore
218
- mount_gradio_app(app, demo, path="/")
219
-
220
-
221
- class TtsRequest(BaseModel):
222
- text: str
223
- voice: Optional[str] = None
224
- use_ssml: bool = False
225
- rate: Optional[str] = None
226
- pitch: Optional[str] = None
227
-
228
-
229
- @app.get("/api/languages")
230
- def api_languages():
231
- mapping = _get_voice_mapping()
232
- languages = sorted(mapping.keys())
233
- return {"languages": languages}
234
-
235
-
236
- @app.get("/api/voices")
237
- def api_voices(language: Optional[str] = None):
238
- mapping = _get_voice_mapping()
239
- if language:
240
- return {"voices": mapping.get(language, [])}
241
- # Flattened list
242
- all_voices: List[Tuple[str, str]] = []
243
- for lang, voices in mapping.items():
244
- all_voices.extend([(lang, v) for v in voices])
245
- return {"voices_by_language": mapping, "all_voices": all_voices}
246
-
247
-
248
- @app.post("/api/tts")
249
- def api_tts(req: TtsRequest):
250
- if not req.text or not req.text.strip():
251
- raise HTTPException(status_code=400, detail="Text is required")
252
- try:
253
- out_path = synthesize(req.text, (req.voice or ""), req.use_ssml, (req.rate or ""), (req.pitch or ""))
254
- if not out_path or not os.path.exists(out_path):
255
- raise HTTPException(status_code=500, detail="TTS synthesis failed")
256
- with open(out_path, "rb") as f:
257
- wav_bytes = f.read()
258
- audio_b64 = base64.b64encode(wav_bytes).decode()
259
- return {
260
- "mime_type": "audio/wav",
261
- "audio_base64": audio_b64,
262
- "voice": req.voice,
263
- }
264
- finally:
265
- try:
266
- if out_path and os.path.exists(out_path):
267
- os.remove(out_path)
268
- except Exception:
269
- pass
270
 
 
 
 
271
 
272
- if __name__ == "__main__":
273
- import uvicorn
 
 
 
274
 
275
- uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
 
 
1
  import os
2
  import html
 
3
  import subprocess
4
  import tempfile
5
+ from typing import Optional
6
 
7
  import gradio as gr
 
 
 
8
 
9
 
10
  DESCRIPTION = """
 
89
  return mapping
90
 
91
 
92
+ def load_voices():
93
  try:
94
  proc = subprocess.run(["mimic3", "--voices"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
95
  if proc.returncode != 0:
96
  err = proc.stderr.decode(errors="ignore")
97
  raise gr.Error(f"Failed to list voices.\n\n{err}")
98
+ mapping = _parse_voices(proc.stdout.decode(errors="ignore"))
 
 
 
 
 
 
 
99
  if not mapping:
100
  raise gr.Error("No voices found. Try again after models are available.")
101
  languages = sorted(mapping.keys())
 
149
  return gr.update(choices=voices, value=(voices[0] if voices else None))
150
 
151
 
152
+ def filter_voices(search: str, lang: str, mapping: dict):
153
+ voices = mapping.get(lang, []) if isinstance(mapping, dict) else []
154
+ if search:
155
+ s = search.strip().lower()
156
+ voices = [v for v in voices if s in v.lower()]
157
+ return gr.update(choices=voices, value=(voices[0] if voices else None))
158
+
159
+
160
  with gr.Blocks(title="Mimic 3 TTS") as demo:
161
  gr.Markdown(f"# Mimic 3 TTS\n{DESCRIPTION}")
162
 
 
166
  with gr.Row():
167
  language_dd = gr.Dropdown(label="Language", choices=[], interactive=True)
168
  voice_dd = gr.Dropdown(label="Voice", choices=[], interactive=True)
169
+ with gr.Row():
170
+ voice_search = gr.Textbox(label="Voice Search (filters by language)", placeholder="Type to filter voices, e.g., 'ko' or 'female' if present in key")
171
+ refresh_btn = gr.Button("Refresh Voices")
172
+ with gr.Row():
173
+ custom_voice = gr.Textbox(label="Custom Voice Key (optional)", placeholder="Overrides Voice dropdown if provided")
174
  voices_state = gr.State({})
175
 
176
  with gr.Accordion("Advanced (SSML)", open=False):
 
198
  outputs=[voice_dd],
199
  )
200
 
201
+ # Filter voices as user types
202
+ voice_search.change(
203
+ fn=filter_voices,
204
+ inputs=[voice_search, language_dd, voices_state],
205
+ outputs=[voice_dd],
206
  )
207
 
208
+ # Refresh voices list from CLI
209
+ refresh_btn.click(
210
+ fn=load_voices,
211
+ inputs=None,
212
+ outputs=[language_dd, voice_dd, voices_state],
213
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
+ def synthesize_with_custom(t: str, selected_voice: str, custom: str, use_ssml_val: bool, rate_val: str, pitch_val: str):
216
+ voice = custom.strip() if (custom and custom.strip()) else selected_voice
217
+ return synthesize(t, voice, use_ssml_val, rate_val, pitch_val)
218
 
219
+ btn.click(
220
+ fn=synthesize_with_custom,
221
+ inputs=[text, voice_dd, custom_voice, use_ssml, rate, pitch],
222
+ outputs=[audio],
223
+ )
224
 
225
+ if __name__ == "__main__":
226
+ demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))