kezhui commited on
Commit
d58f856
·
verified ·
1 Parent(s): 7a3d1bb

Add MP3 output option for TTS

Browse files
Files changed (1) hide show
  1. app.py +53 -8
app.py CHANGED
@@ -3,6 +3,7 @@ import io
3
  import json
4
  import os
5
  import shutil
 
6
  import sys
7
  import tempfile
8
  import threading
@@ -77,10 +78,45 @@ def decode_audio_b64(audio_base64: str, filename: str) -> Path:
77
  raise gr.Error("invalid base64 audio")
78
 
79
 
80
- def wav_bytes_to_b64(audio, sample_rate: int = 24000) -> str:
81
- buf = io.BytesIO()
82
- sf.write(buf, audio, sample_rate, format="WAV")
83
- return base64.b64encode(buf.getvalue()).decode("ascii")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
 
86
  def get_asr_pipeline():
@@ -177,7 +213,13 @@ def transcribe_b64(
177
  )
178
 
179
 
180
- def speech_b64(api_key: str, text: str, voice: str = "zf_xiaoxiao", speed: float = 1.0) -> str:
 
 
 
 
 
 
181
  check_key(api_key)
182
  speed = float(speed)
183
  if not text.strip():
@@ -197,12 +239,14 @@ def speech_b64(api_key: str, text: str, voice: str = "zf_xiaoxiao", speed: float
197
  import numpy as np
198
 
199
  audio = np.concatenate(chunks)
 
200
  return json.dumps(
201
  {
202
- "audio_base64": wav_bytes_to_b64(audio, 24000),
203
- "mime_type": "audio/wav",
204
  "sample_rate": 24000,
205
  "voice": voice,
 
206
  },
207
  ensure_ascii=False,
208
  )
@@ -238,10 +282,11 @@ with gr.Blocks(title="Voice API") as demo:
238
  text = gr.Textbox(label="text", lines=4)
239
  voice = gr.Textbox(label="voice", value="zf_xiaoxiao")
240
  speed = gr.Number(label="speed", value=1.0)
 
241
  tts_out = gr.Textbox(label="result", lines=8)
242
  gr.Button("Synthesize").click(
243
  speech_b64,
244
- inputs=[api_key, text, voice, speed],
245
  outputs=[tts_out],
246
  api_name="speech_b64",
247
  )
 
3
  import json
4
  import os
5
  import shutil
6
+ import subprocess
7
  import sys
8
  import tempfile
9
  import threading
 
78
  raise gr.Error("invalid base64 audio")
79
 
80
 
81
+ def audio_bytes_to_b64(audio, sample_rate: int = 24000, audio_format: str = "wav") -> tuple[str, str]:
82
+ audio_format = (audio_format or "wav").lower().strip()
83
+ if audio_format not in {"wav", "mp3"}:
84
+ raise gr.Error("audio_format must be wav or mp3")
85
+
86
+ wav_buf = io.BytesIO()
87
+ sf.write(wav_buf, audio, sample_rate, format="WAV")
88
+ wav_bytes = wav_buf.getvalue()
89
+ if audio_format == "wav":
90
+ return base64.b64encode(wav_bytes).decode("ascii"), "audio/wav"
91
+
92
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as wav_file:
93
+ wav_file.write(wav_bytes)
94
+ wav_path = Path(wav_file.name)
95
+ mp3_path = wav_path.with_suffix(".mp3")
96
+ try:
97
+ subprocess.run(
98
+ [
99
+ "ffmpeg",
100
+ "-y",
101
+ "-hide_banner",
102
+ "-loglevel",
103
+ "error",
104
+ "-i",
105
+ str(wav_path),
106
+ "-codec:a",
107
+ "libmp3lame",
108
+ "-b:a",
109
+ "128k",
110
+ str(mp3_path),
111
+ ],
112
+ check=True,
113
+ )
114
+ return base64.b64encode(mp3_path.read_bytes()).decode("ascii"), "audio/mpeg"
115
+ except Exception as exc:
116
+ raise gr.Error(f"mp3 encoding failed: {exc}") from exc
117
+ finally:
118
+ wav_path.unlink(missing_ok=True)
119
+ mp3_path.unlink(missing_ok=True)
120
 
121
 
122
  def get_asr_pipeline():
 
213
  )
214
 
215
 
216
+ def speech_b64(
217
+ api_key: str,
218
+ text: str,
219
+ voice: str = "zf_xiaoxiao",
220
+ speed: float = 1.0,
221
+ audio_format: str = "wav",
222
+ ) -> str:
223
  check_key(api_key)
224
  speed = float(speed)
225
  if not text.strip():
 
239
  import numpy as np
240
 
241
  audio = np.concatenate(chunks)
242
+ audio_base64, mime_type = audio_bytes_to_b64(audio, 24000, audio_format)
243
  return json.dumps(
244
  {
245
+ "audio_base64": audio_base64,
246
+ "mime_type": mime_type,
247
  "sample_rate": 24000,
248
  "voice": voice,
249
+ "audio_format": audio_format,
250
  },
251
  ensure_ascii=False,
252
  )
 
282
  text = gr.Textbox(label="text", lines=4)
283
  voice = gr.Textbox(label="voice", value="zf_xiaoxiao")
284
  speed = gr.Number(label="speed", value=1.0)
285
+ audio_format = gr.Textbox(label="audio_format", value="wav")
286
  tts_out = gr.Textbox(label="result", lines=8)
287
  gr.Button("Synthesize").click(
288
  speech_b64,
289
+ inputs=[api_key, text, voice, speed, audio_format],
290
  outputs=[tts_out],
291
  api_name="speech_b64",
292
  )