txh17 commited on
Commit
69781f1
·
verified ·
1 Parent(s): 5e62617

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -189
app.py CHANGED
@@ -1,112 +1,89 @@
1
  import gradio as gr
2
  import subprocess
3
  import os
4
- import pandas as pd # Still useful for example dataframes if needed for report tables later
5
 
6
- # --- TTS Model Functions (CPU Friendly) ---
7
 
8
  def synthesize_espeak(text: str, lang: str = "en-us") -> str | None:
9
  """
10
- Synthesizes speech using espeak-ng.
11
- Requires espeak-ng to be installed in the Space environment (via Dockerfile).
12
  """
13
  output_file = "espeak_output.wav"
14
 
15
- # Clean up previous output file if it exists
16
  if os.path.exists(output_file):
17
  os.remove(output_file)
18
 
19
  try:
20
- # Command to run espeak-ng. --stdout outputs to stdout, which we capture.
 
21
  command = ["espeak-ng", f"-v{lang}", "--stdout", text]
22
 
23
- # Execute the command. Added timeout to prevent infinite hangs.
24
  process = subprocess.run(command, capture_output=True, check=True, timeout=10)
25
 
26
- # Check if espeak-ng actually produced audio output
27
  if not process.stdout:
28
- gr.Warning("eSpeak-ng produced no audio output for the given text. Try different text.")
29
- print(f"eSpeak-ng produced no output for text: '{text}'")
30
- return None # Return None to clear the audio component
31
 
32
- # Write the captured stdout (audio data) to a WAV file
33
  with open(output_file, "wb") as f:
34
  f.write(process.stdout)
35
 
36
- print(f"eSpeak-ng synthesis successful: {output_file}")
37
- return output_file # Return the path to the generated audio file
38
 
39
  except FileNotFoundError:
40
- error_msg = "Error: espeak-ng not found. Please ensure it's installed in your Space's Dockerfile and the Space is rebuilt."
41
  print(error_msg)
42
- gr.Error(error_msg) # Show a persistent error message in Gradio
43
  return None
44
  except subprocess.CalledProcessError as e:
45
- error_msg = f"Error during espeak-ng synthesis. Command exited with code {e.returncode}. Stderr: {e.stderr.decode()}"
46
  print(error_msg)
47
  gr.Error(error_msg)
48
  return None
49
  except subprocess.TimeoutExpired:
50
- error_msg = "eSpeak-ng command timed out. The text might be too long or complex."
51
  print(error_msg)
52
  gr.Warning(error_msg)
53
  return None
54
  except Exception as e:
55
- error_msg = f"An unexpected error occurred during espeak-ng synthesis: {e}"
56
  print(error_msg)
57
  gr.Error(error_msg)
58
  return None
59
 
60
  def synthesize_api_tts(text: str) -> str | None:
61
  """
62
- Placeholder for an API-based Text-to-Speech service (e.g., Azure TTS, Google TTS).
63
- In a real application, you would make an HTTP request to the API here.
64
- For this demo, it returns a placeholder audio file.
65
  """
66
- print(f"Simulating API TTS for: '{text}'")
67
 
68
- # --- IMPORTANT: Replace this with your actual API call ---
69
- # Example using requests (requires 'requests' in requirements.txt):
70
- # import requests
71
- # try:
72
- # url = "YOUR_TTS_API_ENDPOINT" # Replace with your actual API endpoint
73
- # headers = {"Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json"} # Replace with your auth
74
- # data = {"text": text, "voice": "some_api_voice"} # Adjust payload as per API documentation
75
- # response = requests.post(url, json=data, timeout=15)
76
- # response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx)
77
- #
78
- # api_output_file = "api_output.mp3" # Or .wav, depending on API
79
- # with open(api_output_file, "wb") as f:
80
- # f.write(response.content)
81
- # return api_output_file
82
- # except requests.exceptions.RequestException as e:
83
- # error_msg = f"API TTS request failed: {e}"
84
- # print(error_msg)
85
- # gr.Error(error_msg)
86
- # return None
87
- # except Exception as e:
88
- # error_msg = f"An unexpected error occurred with API TTS: {e}"
89
- # print(error_msg)
90
- # gr.Error(error_msg)
91
- # return None
92
  # --------------------------------------------------------
93
 
94
- # Placeholder: Return a generic audio for demonstration
95
- # In a real scenario, you'd fetch an actual audio file from the API.
96
- return "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3" # Placeholder WAV/MP3
97
 
98
 
99
- # --- Gradio Interface ---
100
 
101
  with gr.Blocks(css="""
102
- /* Custom CSS from original template or add your own */
103
  .markdown-text {
104
  font-family: 'IBM Plex Sans', sans-serif;
105
  color: #333;
106
  line-height: 1.6;
107
  }
108
  h1, h2, h3, h4, h5, h6 {
109
- color: #0056b3; /* A nice blue */
110
  }
111
  table {
112
  width: 100%;
@@ -124,7 +101,7 @@ with gr.Blocks(css="""
124
  """) as demo:
125
  gr.HTML("<h1 style='text-align: center; color: #0056b3;'>🎙️ 文本转音频模型对比实验</h1>")
126
  gr.Markdown(
127
- "欢迎来到我们的文本转音频模型对比实验空间。在这里,我们将对比不同模型在文本转音频任务上的表现。",
128
  elem_classes="markdown-text"
129
  )
130
 
@@ -159,139 +136,21 @@ with gr.Blocks(css="""
159
  outputs=[api_tts_output]
160
  )
161
 
162
- # --- Report Tab (formerly About) ---
163
- with gr.TabItem("📝 Report", id=1):
164
- gr.Markdown(
165
- """
166
- # 文本转音频模型对比实验报告
167
-
168
- ## 1. 模型及类别选择
169
-
170
- 本次实验聚焦于**文本转音频 (Text-to-Speech, TTS)** 模型,特别是适用于**CPU 环境**运行的轻量级方案。我们选择了以下两种代表性模型进行横向对比:
171
-
172
- 1. **eSpeak-ng:**
173
- * **类型与背景:** eSpeak-ng 是一个开源的语音合成器,基于**音素拼接**技术。它通过将预先录制或合成的音素拼接起来生成语音。其核心优势在于极低的资源消耗和极快的合成速度,可以在没有 GPU 的情况下高效运行。它支持多种语言,但声音通常听起来比较机械和“机器人化”。
174
- * **用途对比::** 适用于对语音质量要求不高,但对资源限制严格、需要快速生成语音的场景,例如嵌入式设备、辅助技术或批量文本预览。
175
- * **异同点分析:** * **异:** 采用传统拼接技术,非深度学习模型。
176
- * **同:** 均能实现文本到语音的转换。
177
-
178
- 2. **API TTS (示例,可替换为 Azure TTS/Google TTS):**
179
- * **类型与背景:** 此处代表的是基于云服务的文本转音频 API。这类服务通常背后运行着最先进的**深度学习 TTS 模型**(如 Transformer-based、Diffusion-based 或 VITS 等),利用强大的云端 GPU 算力生成高度自然、富有表现力的语音。用户通过调用 API 获取结果,因此本地不需要任何计算资源。
180
- * **用途对比:** 适用于对语音质量有高要求、需要自然人声、支持多种音色和情感表达的场景,例如客服机器人、有声读物、视频配音等。
181
- * **异同点分析:** * **异:** 采用深度学习技术,需要云端算力;声音质量高,表现力强。
182
- * **同:** 均能实现文本到语音的转换。
183
-
184
- 简而言之,eSpeak-ng 代表了**极度轻量级、CPU 本地合成**的方案,而 API TTS 则代表了**高性能、云端服务**的方案,两者在资源消耗、合成质量和适用场景上形成鲜明对比。
185
-
186
- ## 2. 系统实现细节
187
-
188
- 我们的系统基于 Gradio 框架构建,旨在提供一个直观的用户界面,用于对比两种不同 TTS 模型的输出。
189
-
190
- ### Gradio 交互界面截图
191
- (请在���处插入您的 Gradio "Arena" 选项卡截图)
192
- ![Gradio Arena 界面截图](https://your-image-url-here/arena_screenshot.png)
193
-
194
- ### 输入与输出流程图
195
- ```mermaid
196
- graph TD
197
- A[用户输入文本] --> B{Gradio UI}
198
- B --> C[点击 "合成 (eSpeak-ng)"]
199
- B --> D[点击 "合成 (API TTS 示例)"]
200
- C --> E[调用 Python 函数: synthesize_espeak]
201
- D --> F[调用 Python 函数: synthesize_api_tts]
202
- E --> G[调用系统命令: espeak-ng]
203
- F --> H[发送 HTTP 请求到外部 TTS API (模拟)]
204
- G --> I[生成 WAV 音频文件]
205
- H --> J[接收音频数据并保存]
206
- I --> K[Gradio 显示 eSpeak-ng 输出音频]
207
- J --> L[Gradio 显示 API TTS 示例输出音频]
208
- ```
209
-
210
- ### 模型集成方式说明
211
-
212
- * **eSpeak-ng 集成:**
213
- * 我们通过 Python 的 `subprocess` 模块调用系统层面的 `espeak-ng` 命令。这意味着 `espeak-ng` 程序本身需要预先安装在 Hugging Face Space 的运行环境中。
214
- * 在 Hugging Face Space 中,这需要通过自定义 `Dockerfile` 来实现 `espeak-ng` 的安装。
215
- * `synthesize_espeak` 函数接收文本,将其作为参数传递给 `espeak-ng` 命令,并将 `espeak-ng` 输出的 WAV 数据捕获并保存为文件,然后 Gradio 将该文件作为音频输出。
216
-
217
- * **API TTS 示例集成:**
218
- * `synthesize_api_tts` 函数目前是一个占位符。在实际应用中,它会使用 `requests` 这样的 Python 库向 Microsoft Azure TTS 或 Google Cloud Text-to-Speech 等服务的 API 端点发送 POST 请求,并将文本作为请求体的一部分。
219
- * API 服务处理请求并返回音频数据,Python 函数将这些数据保存为音频文件(如 MP3 或 WAV),然后由 Gradio 展示。
220
- * 当前版本为了演示,直接返回了一个在线的通用音频文件 URL,以模拟 API 调用的结果,无需额外配置 API 密钥。
221
-
222
- 两位同学的代码提交职责:
223
- * **(请在此处填写两位同学在模型集成方面的具体分工,例如:)**
224
- * **学生 A 负责:** 集成 `espeak-ng` 模型,包括 `synthesize_espeak` 函数的实现和 `Dockerfile` 的编写。
225
- * **学生 B 负责:** 实现 `synthesize_api_tts` 占位符(后续可替换为真实的 API 调用逻辑),并负责 Gradio "Arena" 界面的构建。
226
-
227
- ## 3. GRACE 评估维度定义
228
-
229
- 我们选择了 GRACE 框架中的以下 **4 个维度**对模型进行比较分析:
230
-
231
- 1. **G: Generality (泛化性):**
232
- * **定义:** 模型在处理不同长度、复杂度和主题的文本输入时,是否能够稳定地生成可理解的语音。是否能够支持多种语言或口音。
233
- * **理由:** 评估模型在不同输入条件下的适应能力,对于 TTS 模型,这意味着其在各种文本场景下的通用表现。
234
-
235
- 2. **R: Relevance (相关性):**
236
- * **定义:** 模型生成的语音是否忠实地表达了输入文本的内容和潜在的语调信息。例如,文本中的疑问句是否能体现疑问语气,感叹句是否能体现情感。
237
- * **理由:** 语音输出的准确性和自然度是 TTS 的核心指标,直接影响用户体验。
238
-
239
- 3. **A: Artistry (创新表现力):**
240
- * **定义:** 模型生成的语音是否自然、流畅,是否具备人类语音的韵律、音调变化和情感色彩。是否能够避免生硬、机械或不自然的停顿。
241
- * **理由:** 这是衡量 TTS 模型“像不像人”的关键维度,对于追求高质量语音体验至关重要。
242
-
243
- 4. **E: Efficiency (效率性):**
244
- * **定义:** 模型在给定计算资源(CPU)下,从接收文本到生成可播放音频所需的时间。包括模型加载时间、推理时间和文件保存时间。
245
- * **理由:** 对于需要在资源受限环境或实时场景下使用的 TTS 模型,效率是决定其可用性的重要因素。
246
-
247
- ## 4. 结果与分析
248
-
249
- ### 多条统一输入样例输出结果表格
250
-
251
- | 输入文本 (中文) | 输入文本 (英文) | eSpeak-ng 输出特点 | API TTS 示例输出特点 |
252
- | :--- | :--- | :--- | :--- |
253
- | 你好,这是一个文本转音频的测试。 | Hello, this is a text-to-speech test. | 机械、音调平直,有明显的合成感。 | 流畅、自然,有起伏和韵律,接近人声。 |
254
- | 请问今天天��怎么样? | What's the weather like today? | 语调缺乏疑问语气,略显生硬。 | 能体现疑问语气,更自然。 |
255
- | 天气真好啊! | What a beautiful day! | 无法表达情感,音量和速度变化不大。 | 能体现积极情感,语调更富表现力。 |
256
- | 复杂一点的句子,比如人工智能的未来发展。 | The future development of artificial intelligence. | 容易在长句中出现不自然的停顿或节奏问题。 | 较好地处理复杂长句,保持流畅性。 |
257
- | (在此处添加更多您的测试样例) | (在此处添加更多您的测试样例) | (描述 eSpeak-ng 的输出) | (描述 API TTS 示例的输出) |
258
-
259
- ### 雷达图或柱状图展示维度评分
260
-
261
- (请在此处插入您的 GRACE 评估雷达图或柱状图。您可以根据上方表格的观察,为每个模型在 G, R, A, E 四个维度上打分(例如1-5分),然后使用 Python 库如 `matplotlib` 或 `plotly` 生成图表。)
262
-
263
- ![GRACE 评估雷达图](https://your-image-url-here/grace_radar_chart.png)
264
-
265
- ### 分析每个模型的优劣势
266
-
267
- * **eSpeak-ng 的优劣势:**
268
- * **优势:** 极高的**效率性 (E)**,启动快,合成速度快,资源消耗极低,完全支持 CPU 运行,部署简单。
269
- * **劣势:** **创新表现力 (A)** 和 **相关性 (R)** 较差,语音听起来机械、不自然,缺乏人类语音的韵律和情感。**泛化性 (G)** 在多语言支持上较好,但在复杂文本处理上表现一般。
270
-
271
- * **API TTS 示例的优劣势 (基于模拟的高质量 API):**
272
- * **优势:** 极高的**相关性 (R)** 和 **创新表现力 (A)**,语音自然、流畅,能够很好地传达文本的语调和情感,接近真实人声。**泛化性 (G)** 强,能处理复杂文本和多种语言(取决于具体 API)。
273
- * **劣势:** **效率性 (E)** 方面,虽然云端合成快,但存在网络延迟;并且依赖外部服务,可能产生费用,且需要网络连接。
274
-
275
- **总结:**
276
- eSpeak-ng 是一个功能性强但语音质量一般的“实用型”选择,适合资源受限场景。而 API TTS 则提供了卓越的语音质量和表现力,是追求用户体验的理想选择,但需要依赖网络和支付成本。两者在“CPU 运行”的背景下,清晰地展示了“本地低质高效率”与“云端高质量低本地消耗”之间的权衡。
277
-
278
- ## 5. 合作与期待
279
-
280
- ### 成员 A (请替换为您的姓名)
281
-
282
- * **负责内容:** (请填写您负责的具体内容,例如:) 主要负责 `espeak-ng` 模型的调研、集成与测试,包括 `synthesize_espeak` 函数的实现和 `Dockerfile` 的编写。同时参与了 GRACE 评估维度的定义和实验报告的初步草稿撰写。
283
- * **学到的内容:** 通过本次实验,我深入理解了文本转音频模型的基本原理,特别是传统拼接与现代深度学习方法的差异。学习了如何在 Hugging Face Space 中使用 `Dockerfile` 进行环境配置,以及 Gradio 界面的基本构建和事件绑定。对 `subprocess` 模块的使用也有了更实际的体会。
284
- * **遇到的困难:** 最初在 Hugging Face Space 中安装 `espeak-ng` 遇到了一些权限问题,通过查阅 `Dockerfile` 相关文档并多次尝试才成功配置。另外,调整 `subprocess` 调用 `espeak-ng` 的参数以确保音频文件正确生成也花费了一些时间。
285
-
286
- ### 成员 B (请替换为您的姓名)
287
-
288
- * **负责内容:** (请填写您负责的具体内容,例如:) 主要负责 Gradio "Arena" 交互界面的设计与实现,确保用户能通过统一输入驱动两个模型的输出。同时,负责 `synthesize_api_tts` 占位符的编写,并对整个实验报告的结构和 Markdown 格式进行了优化。参与了 GRACE 评估结果的分析和可视化部分。
289
- * **学到的内容:** 我对 Gradio 的组件和布局有了更深的理解,能够灵活构建复杂的 UI。通过对比实验,我更直观地认识到模型质量与资源消耗之间的权衡。在报告撰写过程中,提升了使用 Markdown 进行结构化表达和图文并茂展示的能力。
290
- * **遇到的困难:** 在设计“Arena”界面时,如何清晰地展示两个模型的并行输出并保持界面整洁是一个挑战。最初对 Gradio 的事件触发和输出更新机制理解不够深入,导致一些组件无法正确响应,经过调试和查阅文档后得以解决。
291
- """,
292
- elem_classes="markdown-text"
293
- )
294
-
295
- # --- Launch Gradio Demo ---
296
- # Using queue() is good practice for Spaces
297
  demo.queue().launch()
 
1
  import gradio as gr
2
  import subprocess
3
  import os
4
+ import pandas as pd # 仍保留,以备报告内容需要用到数据帧时使用
5
 
6
+ # --- TTS 模型函数 (CPU 友好) ---
7
 
8
  def synthesize_espeak(text: str, lang: str = "en-us") -> str | None:
9
  """
10
+ 使用 espeak-ng 合成语音。
11
+ 需要在 Space 环境中安装 espeak-ng (通过 Dockerfile)
12
  """
13
  output_file = "espeak_output.wav"
14
 
15
+ # 清理之前的输出文件(如果存在)
16
  if os.path.exists(output_file):
17
  os.remove(output_file)
18
 
19
  try:
20
+ # 运行 espeak-ng 的命令。--stdout 输出到标准输出,我们捕获它。
21
+ # 添加了 timeout 以防止无限期挂起
22
  command = ["espeak-ng", f"-v{lang}", "--stdout", text]
23
 
 
24
  process = subprocess.run(command, capture_output=True, check=True, timeout=10)
25
 
26
+ # 检查 espeak-ng 是否实际产生了音频输出
27
  if not process.stdout:
28
+ gr.Warning("eSpeak-ng 没有为给定文本生成任何音频输出。请尝试不同的文本。")
29
+ print(f"eSpeak-ng 为文本 '{text}' 未产生输出。")
30
+ return None # 返回 None 以清空音频组件
31
 
32
+ # 将捕获到的标准输出(音频数据)写入 WAV 文件
33
  with open(output_file, "wb") as f:
34
  f.write(process.stdout)
35
 
36
+ print(f"eSpeak-ng 合成成功: {output_file}")
37
+ return output_file # 返回生成的音频文件路径
38
 
39
  except FileNotFoundError:
40
+ error_msg = "错误:未找到 espeak-ng。请确保它已安装在您 Space Dockerfile 中,并且 Space 已重建。"
41
  print(error_msg)
42
+ gr.Error(error_msg) # Gradio 中显示一个持久性错误消息
43
  return None
44
  except subprocess.CalledProcessError as e:
45
+ error_msg = f"eSpeak-ng 合成过程中出现错误。命令以代码 {e.returncode} 退出。错误输出:{e.stderr.decode()}"
46
  print(error_msg)
47
  gr.Error(error_msg)
48
  return None
49
  except subprocess.TimeoutExpired:
50
+ error_msg = "eSpeak-ng 命令超时。文本可能过长或过于复杂。"
51
  print(error_msg)
52
  gr.Warning(error_msg)
53
  return None
54
  except Exception as e:
55
+ error_msg = f"eSpeak-ng 合成过程中发生意外错误:{e}"
56
  print(error_msg)
57
  gr.Error(error_msg)
58
  return None
59
 
60
  def synthesize_api_tts(text: str) -> str | None:
61
  """
62
+ 基于 API 的文本转语音服务(例如 Azure TTS, Google TTS)的占位符。
63
+ 在真实应用中,您将在这里发出 HTTP 请求到 API
64
+ 对于此演示,它返回一个占位符音频文件。
65
  """
66
+ print(f"正在模拟 API TTS'{text}'")
67
 
68
+ # --- 重要:请用您真实的 API 调用替换此处 ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  # --------------------------------------------------------
70
 
71
+ # 占位符:返回一个通用音频用于演示
72
+ # 在实际场景中,您会从 API 获取一个真实的音频文件。
73
+ return "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3" # 占位符 WAV/MP3
74
 
75
 
76
+ # --- Gradio 界面 ---
77
 
78
  with gr.Blocks(css="""
79
+ /* 自定义 CSS from 原始模板或添加您自己的 */
80
  .markdown-text {
81
  font-family: 'IBM Plex Sans', sans-serif;
82
  color: #333;
83
  line-height: 1.6;
84
  }
85
  h1, h2, h3, h4, h5, h6 {
86
+ color: #0056b3; /* 漂亮的蓝色 */
87
  }
88
  table {
89
  width: 100%;
 
101
  """) as demo:
102
  gr.HTML("<h1 style='text-align: center; color: #0056b3;'>🎙️ 文本转音频模型对比实验</h1>")
103
  gr.Markdown(
104
+ "欢迎来到我们的文本转音频模型对比实验空间。在这里,您可以对比不同模型在文本转音频任务上的表现。",
105
  elem_classes="markdown-text"
106
  )
107
 
 
136
  outputs=[api_tts_output]
137
  )
138
 
139
+ # --- Report Tab (暂时注释掉) ---
140
+ # with gr.TabItem("📝 Report", id=1):
141
+ # gr.Markdown(
142
+ # """
143
+ # # 文本转音频模型对比实验报告
144
+ # # ... 大量报告内容 ...
145
+ # """,
146
+ # elem_classes="markdown-text"
147
+ # )
148
+ #
149
+ # 注意:为了让应用能够正常启动,我们暂时移除了 "Report" 选项卡。
150
+ # 如果应用能成功运行,说明问题出在报告内容上。
151
+ # 之后您可以尝试逐步添加报告内容,或简化其格式,以找出具体原因。
152
+ # 例如,可以先只放几段简单的文字,不包含表格或 mermaid 图。
153
+
154
+ # --- 启动 Gradio Demo ---
155
+ # 使用 queue() Spaces 的良好实践
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  demo.queue().launch()