voidful commited on
Commit
01153e3
·
verified ·
1 Parent(s): dd9fd54

Streaming tab: sentence-by-sentence streaming for long text (synth & play per sentence)

Browse files
Files changed (1) hide show
  1. app.py +68 -31
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,8 +104,36 @@ 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
  # --------------------------------------------------------------------------- #
@@ -141,29 +169,31 @@ def tts_clone(text, reference_audio):
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:
153
  raise gr.Error("請先輸入要合成的文字。")
154
- if reference_audio:
155
- gen = model.generate_streaming(
156
- target_text=text, reference_wav_path=reference_audio, **STREAM_KWARGS
157
- )
158
- else:
159
- gen = model.generate_streaming(
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
  # --------------------------------------------------------------------------- #
@@ -175,6 +205,13 @@ EXAMPLE_TEXTS = [
175
  "台灣藍鵲是一種叫聲響亮、辨識度很高的鳥。",
176
  ]
177
 
 
 
 
 
 
 
 
178
  _PARAMS_NOTE = (
179
  f"已套用模型官方建議最佳參數:"
180
  f"`cfg_value={GEN_KWARGS.get('cfg_value')}`、"
@@ -232,27 +269,27 @@ with gr.Blocks(title="BlueMagpie-TTS Demo", theme=gr.themes.Soft()) as demo:
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():
244
  t_text = gr.Textbox(
245
- label="要合成的文字", lines=3,
246
- placeholder="輸入中文或中英混合的文字…",
247
  )
248
  t_ref = gr.Audio(
249
  label="參考音檔(選填;留空則用李宏毅音色)", type="filepath",
250
  sources=["upload", "microphone"],
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
 
258
  gr.Markdown(
 
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
+ def _pcm16(x: np.ndarray) -> np.ndarray:
108
+ """float 波形 -> 16-bit PCM,供 Gradio 串流輸出(格式最穩定)。"""
109
+ return (np.clip(x, -1.0, 1.0) * 32767.0).astype(np.int16)
110
+
111
+
112
+ # 長文逐句串流:以句末標點/換行切句,最多示範前 N 句(避免超過 ZeroGPU 單次時限)。
113
+ _SENT_ENDERS = "。!?;!?…\n"
114
+ _MAX_STREAM_SENTENCES = 12
115
+
116
+
117
+ def _split_sentences(text):
118
+ out, cur = [], []
119
+ for ch in text:
120
+ cur.append(ch)
121
+ if ch in _SENT_ENDERS:
122
+ s = "".join(cur).strip()
123
+ if s:
124
+ out.append(s)
125
+ cur = []
126
+ s = "".join(cur).strip()
127
+ if s:
128
+ out.append(s)
129
+ # 把落單的標點(如連續「!?」被拆出的單一字元)併回前一句
130
+ merged = []
131
+ for s in out:
132
+ if merged and len(s) <= 1:
133
+ merged[-1] += s
134
+ else:
135
+ merged.append(s)
136
+ return merged
137
 
138
 
139
  # --------------------------------------------------------------------------- #
 
169
 
170
  @gpu
171
  def tts_stream(text, reference_audio):
172
+ """長文逐句串流:把長文切成句子,逐句合成、合成一句就播一句,做出串流效果。
173
 
174
+ 每句都用一般 generate(含 retry_badcase)產生完整音檔再 yield,Gradio 依序串流
175
+ 播放;句與句之間可能有短暫間隔(ZeroGPU 約 0.44x 實時所致)。預設用李宏毅語者
176
+ 向量,上傳參考音檔則改為聲音複製。
 
177
  """
178
  text = (text or "").strip()
179
  if not text:
180
  raise gr.Error("請先輸入要合成的文字。")
181
+ sentences = _split_sentences(text)
182
+ if not sentences:
183
+ raise gr.Error("找不到可合成的句子。")
184
+ if len(sentences) > _MAX_STREAM_SENTENCES:
185
+ gr.Warning(f"長文較長,僅示範前 {_MAX_STREAM_SENTENCES} 句。")
186
+ sentences = sentences[:_MAX_STREAM_SENTENCES]
187
+ for sent in sentences:
188
+ if reference_audio:
189
+ audio = model.generate(
190
+ target_text=sent, reference_wav_path=reference_audio, **GEN_KWARGS
191
+ )
192
+ else:
193
+ audio = model.generate(
194
+ target_text=sent, speaker_centroid=SPK_CENTROID, **GEN_KWARGS
195
+ )
196
+ yield (SR, _pcm16(_to_numpy(audio)))
197
 
198
 
199
  # --------------------------------------------------------------------------- #
 
205
  "台灣藍鵲是一種叫聲響亮、辨識度很高的鳥。",
206
  ]
207
 
208
+ STREAM_EXAMPLES = [
209
+ "台灣藍鵲是台灣特有的鳥類,羽色以亮藍為主,尾羽修長。牠們常成群在樹林間活動,"
210
+ "叫聲響亮而容易辨認。今天天氣真好,我們一起出去走走,順便看看這些美麗的鳥吧!",
211
+ "歡迎使用這個示範。這是一段比較長的文字,會被切成好幾句。"
212
+ "系統會合成一句、播放一句,做出串流的效果。希望你會喜歡這個聲音!",
213
+ ]
214
+
215
  _PARAMS_NOTE = (
216
  f"已套用模型官方建議最佳參數:"
217
  f"`cfg_value={GEN_KWARGS.get('cfg_value')}`、"
 
269
  c_out = gr.Audio(label="合成結果", type="numpy")
270
  c_btn.click(tts_clone, [c_text, c_ref], c_out)
271
 
272
+ with gr.Tab("長文逐句串流"):
273
  gr.Markdown(
274
+ "**長文逐句串流**:把長文切成句子,合成一句就播一句,做出串流(邊聽邊等)效果。"
275
+ "第一句很快就能聽到,後面邊播邊合成。\n\n"
276
+ "預設用李宏毅語者向量;若上傳參考音檔則改為聲音複製(請只使用已取得授權的聲音)。\n"
277
+ "> ZeroGPU 約 0.44x 實時,句與句之間可能有短暫間隔;每句都套用最佳參數(含自動重試)。"
278
  )
279
  with gr.Row():
280
  with gr.Column():
281
  t_text = gr.Textbox(
282
+ label="要合成的長文", lines=5,
283
+ placeholder="貼上一段多句的文字,會逐句合成、逐句播放…",
284
  )
285
  t_ref = gr.Audio(
286
  label="參考音檔(選填;留空則用李宏毅音色)", type="filepath",
287
  sources=["upload", "microphone"],
288
  )
289
+ t_btn = gr.Button("逐句串流合成", variant="primary")
290
  with gr.Column():
291
+ t_out = gr.Audio(label="逐句串流播放", streaming=True, autoplay=True)
292
+ gr.Examples(STREAM_EXAMPLES, inputs=t_text, label="長文範例")
293
  t_btn.click(tts_stream, [t_text, t_ref], t_out)
294
 
295
  gr.Markdown(