Spaces:
Running on Zero
Running on Zero
Streaming tab: collect full audio then play smoothly (ZeroGPU is sub-realtime)
Browse files
app.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
提供三種試用情境,並一律套用模型官方建議的最佳生成參數:
|
| 4 |
1. 指定語者:李宏毅 —— 用模型內附、已取得授權的語者向量控制音色
|
| 5 |
2. 聲音複製 —— 上傳一段參考音檔,模仿該語者的音色
|
| 6 |
-
3. 串流合成
|
| 7 |
|
| 8 |
最佳參數讀自模型發佈中繼資料 release_metadata.json 的 recommended_generation_defaults
|
| 9 |
(以 500 句難句 + ASR CER 調出,最佳 trial:cfg=2.8 / steps=9),確保 demo 永遠跟著模型走。
|
|
@@ -104,17 +104,12 @@ def _to_numpy(audio: torch.Tensor):
|
|
| 104 |
return audio.squeeze().float().cpu().numpy()
|
| 105 |
|
| 106 |
|
| 107 |
-
# 串流不支援 retry_badcase(見 model._generate 的警告),故濾掉 retry_* 參數。
|
| 108 |
STREAM_KWARGS = {k: v for k, v in GEN_KWARGS.items() if not k.startswith("retry_")}
|
| 109 |
|
| 110 |
|
| 111 |
-
def _pcm16(x: np.ndarray) -> np.ndarray:
|
| 112 |
-
"""把 float 波形轉成 16-bit PCM,供 Gradio 串流播放(格式最穩定)。"""
|
| 113 |
-
return (np.clip(x, -1.0, 1.0) * 32767.0).astype(np.int16)
|
| 114 |
-
|
| 115 |
-
|
| 116 |
# --------------------------------------------------------------------------- #
|
| 117 |
-
# 推論:
|
| 118 |
# --------------------------------------------------------------------------- #
|
| 119 |
@gpu
|
| 120 |
def tts_speaker(text):
|
|
@@ -144,16 +139,14 @@ def tts_clone(text, reference_audio):
|
|
| 144 |
return (SR, _to_numpy(audio))
|
| 145 |
|
| 146 |
|
| 147 |
-
_STREAM_SEG_SEC = 0.6 # 把模型的小 chunk 累積到約 0.6 秒再 yield,減少 Gradio
|
| 148 |
-
# 每段 HLS 的封裝開銷,避免播放卡頓(見對話中的量測)。
|
| 149 |
-
|
| 150 |
-
|
| 151 |
@gpu
|
| 152 |
def tts_stream(text, reference_audio):
|
| 153 |
-
"""串流
|
| 154 |
|
| 155 |
-
|
| 156 |
-
|
|
|
|
|
|
|
| 157 |
"""
|
| 158 |
text = (text or "").strip()
|
| 159 |
if not text:
|
|
@@ -167,19 +160,10 @@ def tts_stream(text, reference_audio):
|
|
| 167 |
target_text=text, speaker_centroid=SPK_CENTROID, **STREAM_KWARGS
|
| 168 |
)
|
| 169 |
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
buf.append(x)
|
| 175 |
-
nbuf += x.shape[-1]
|
| 176 |
-
if nbuf >= target:
|
| 177 |
-
seg = np.concatenate(buf)
|
| 178 |
-
buf, nbuf = [], 0
|
| 179 |
-
yield (SR, _pcm16(seg))
|
| 180 |
-
if buf:
|
| 181 |
-
seg = np.concatenate(buf)
|
| 182 |
-
yield (SR, _pcm16(seg))
|
| 183 |
|
| 184 |
|
| 185 |
# --------------------------------------------------------------------------- #
|
|
@@ -248,11 +232,12 @@ with gr.Blocks(title="BlueMagpie-TTS Demo", theme=gr.themes.Soft()) as demo:
|
|
| 248 |
c_out = gr.Audio(label="合成結果", type="numpy")
|
| 249 |
c_btn.click(tts_clone, [c_text, c_ref], c_out)
|
| 250 |
|
| 251 |
-
with gr.Tab("串流合成
|
| 252 |
gr.Markdown(
|
| 253 |
-
"**
|
| 254 |
-
"若上傳參考音檔則改為聲音複製(請只使用已取得授權的聲音)。\n\n"
|
| 255 |
-
">
|
|
|
|
| 256 |
)
|
| 257 |
with gr.Row():
|
| 258 |
with gr.Column():
|
|
@@ -266,7 +251,7 @@ with gr.Blocks(title="BlueMagpie-TTS Demo", theme=gr.themes.Soft()) as demo:
|
|
| 266 |
)
|
| 267 |
t_btn = gr.Button("串流合成", variant="primary")
|
| 268 |
with gr.Column():
|
| 269 |
-
t_out = gr.Audio(label="
|
| 270 |
gr.Examples(EXAMPLE_TEXTS, inputs=t_text, label="範例文字")
|
| 271 |
t_btn.click(tts_stream, [t_text, t_ref], t_out)
|
| 272 |
|
|
|
|
| 3 |
提供三種試用情境,並一律套用模型官方建議的最佳生成參數:
|
| 4 |
1. 指定語者:李宏毅 —— 用模型內附、已取得授權的語者向量控制音色
|
| 5 |
2. 聲音複製 —— 上傳一段參考音檔,模仿該語者的音色
|
| 6 |
+
3. 串流引擎合成 —— 用串流生成引擎逐段合成,完成後一次順暢播放(不支援自動重試)
|
| 7 |
|
| 8 |
最佳參數讀自模型發佈中繼資料 release_metadata.json 的 recommended_generation_defaults
|
| 9 |
(以 500 句難句 + ASR CER 調出,最佳 trial:cfg=2.8 / steps=9),確保 demo 永遠跟著模型走。
|
|
|
|
| 104 |
return audio.squeeze().float().cpu().numpy()
|
| 105 |
|
| 106 |
|
| 107 |
+
# 串流引擎不支援 retry_badcase(見 model._generate 的警告),故濾掉 retry_* 參數。
|
| 108 |
STREAM_KWARGS = {k: v for k, v in GEN_KWARGS.items() if not k.startswith("retry_")}
|
| 109 |
|
| 110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
# --------------------------------------------------------------------------- #
|
| 112 |
+
# 推論:三種模式都套用 GEN_KWARGS(官方最佳參數)
|
| 113 |
# --------------------------------------------------------------------------- #
|
| 114 |
@gpu
|
| 115 |
def tts_speaker(text):
|
|
|
|
| 139 |
return (SR, _to_numpy(audio))
|
| 140 |
|
| 141 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
@gpu
|
| 143 |
def tts_stream(text, reference_audio):
|
| 144 |
+
"""用模型的串流生成引擎(generate_streaming)逐段合成,蒐集完整後一次回傳。
|
| 145 |
|
| 146 |
+
ZeroGPU 上模型約 0.44x 實時,逐段即時串流播放必然卡頓,故改為「合成完成後
|
| 147 |
+
順暢播放」:背後仍走串流生成路徑(不支援自動重試),但等所有音訊區塊到齊再
|
| 148 |
+
合併成完整波形輸出,播放流暢且能正常結束。有參考音檔走聲音複製,否則用李宏毅
|
| 149 |
+
語者向量。
|
| 150 |
"""
|
| 151 |
text = (text or "").strip()
|
| 152 |
if not text:
|
|
|
|
| 160 |
target_text=text, speaker_centroid=SPK_CENTROID, **STREAM_KWARGS
|
| 161 |
)
|
| 162 |
|
| 163 |
+
parts = [chunk.squeeze().float().cpu().numpy() for chunk in gen]
|
| 164 |
+
if not parts:
|
| 165 |
+
raise gr.Error("合成失敗,請再試一次。")
|
| 166 |
+
return (SR, np.concatenate(parts))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
|
| 169 |
# --------------------------------------------------------------------------- #
|
|
|
|
| 232 |
c_out = gr.Audio(label="合成結果", type="numpy")
|
| 233 |
c_btn.click(tts_clone, [c_text, c_ref], c_out)
|
| 234 |
|
| 235 |
+
with gr.Tab("串流引擎合成"):
|
| 236 |
gr.Markdown(
|
| 237 |
+
"使用模型的**串流生成引擎**(`generate_streaming`)合成,完成後一次順暢播放。"
|
| 238 |
+
"預設用李宏毅語者向量;若上傳參考音檔則改為聲音複製(請只使用已取得授權的聲音)。\n\n"
|
| 239 |
+
"> ZeroGPU 上模型約 0.44x 實時,逐段即時播放會卡頓,故改為合成完成後再播放;"
|
| 240 |
+
"串流路徑不支援自動重試(`retry_badcase`)。"
|
| 241 |
)
|
| 242 |
with gr.Row():
|
| 243 |
with gr.Column():
|
|
|
|
| 251 |
)
|
| 252 |
t_btn = gr.Button("串流合成", variant="primary")
|
| 253 |
with gr.Column():
|
| 254 |
+
t_out = gr.Audio(label="合成結果", type="numpy", autoplay=True)
|
| 255 |
gr.Examples(EXAMPLE_TEXTS, inputs=t_text, label="範例文字")
|
| 256 |
t_btn.click(tts_stream, [t_text, t_ref], t_out)
|
| 257 |
|