Spaces:
Running
Running
6drf21e
commited on
Commit
•
b7bd253
1
Parent(s):
c71dabd
init
Browse files- .gitattributes +2 -0
- app.py +270 -0
- evaluation_results.csv +3 -0
- requirements.txt +3 -0
.gitattributes
CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
*.wav filter=lfs diff=lfs merge=lfs -text
|
37 |
+
*.csv filter=lfs diff=lfs merge=lfs -text
|
app.py
ADDED
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import base64
|
2 |
+
import os
|
3 |
+
import os.path
|
4 |
+
|
5 |
+
import gradio as gr
|
6 |
+
import pandas as pd
|
7 |
+
import requests
|
8 |
+
from dotenv import load_dotenv
|
9 |
+
from gradio_leaderboard import Leaderboard
|
10 |
+
from pandas import DataFrame
|
11 |
+
|
12 |
+
load_dotenv()
|
13 |
+
|
14 |
+
# 获取环境变量
|
15 |
+
storage_mode = os.getenv("STORAGE_MODE")
|
16 |
+
storage_path = os.getenv("STORAGE_PATH")
|
17 |
+
storage_url = os.getenv("STORAGE_URL")
|
18 |
+
|
19 |
+
# 临时文件目录
|
20 |
+
tmp_dir = os.path.join(os.getcwd(), "tmp")
|
21 |
+
os.makedirs(tmp_dir, exist_ok=True)
|
22 |
+
|
23 |
+
|
24 |
+
def file_to_base64(file_path):
|
25 |
+
with open(file_path, "rb") as file:
|
26 |
+
return base64.b64encode(file.read()).decode("utf-8")
|
27 |
+
|
28 |
+
|
29 |
+
def base64_to_file(base64_str, output_path):
|
30 |
+
with open(output_path, "wb") as file:
|
31 |
+
file.write(base64.b64decode(base64_str))
|
32 |
+
|
33 |
+
|
34 |
+
def convert_to_markdown(percentage_str):
|
35 |
+
"""
|
36 |
+
将百分比字符串转换为 markdown 格式
|
37 |
+
:param percentage_str:
|
38 |
+
:return:
|
39 |
+
"""
|
40 |
+
if not percentage_str:
|
41 |
+
return ""
|
42 |
+
if not isinstance(percentage_str, str):
|
43 |
+
return percentage_str
|
44 |
+
items = percentage_str.split(";")
|
45 |
+
markdown_str = " ".join([f"**{item.split(':')[0]}** {item.split(':')[1]}%" for item in items])
|
46 |
+
return markdown_str
|
47 |
+
|
48 |
+
|
49 |
+
# Load
|
50 |
+
df = pd.read_csv("evaluation_results.csv", encoding="utf-8")
|
51 |
+
|
52 |
+
df["rank_long"] = df["rank_long"].apply(lambda x: round(x, 2))
|
53 |
+
df["rank_multi"] = df["rank_multi"].apply(lambda x: round(x, 2))
|
54 |
+
df["rank_single"] = df["rank_single"].apply(lambda x: round(x, 2))
|
55 |
+
df["gender"] = df["gender"].apply(convert_to_markdown)
|
56 |
+
df["age"] = df["age"].apply(convert_to_markdown)
|
57 |
+
df["feature"] = df["feature"].apply(convert_to_markdown)
|
58 |
+
df["score"] = df["score"].apply(lambda x: round(x, 2))
|
59 |
+
|
60 |
+
|
61 |
+
def download_wav_file(seed_id, download_url, local_dir):
|
62 |
+
os.makedirs(local_dir, exist_ok=True)
|
63 |
+
local_file_path = os.path.join(local_dir, f"{seed_id}.wav")
|
64 |
+
file_url = f"{download_url}/{seed_id}_test.wav"
|
65 |
+
if not os.path.exists(local_file_path):
|
66 |
+
response = requests.get(file_url, stream=True)
|
67 |
+
if response.status_code == 200:
|
68 |
+
with open(local_file_path, "wb") as f:
|
69 |
+
f.write(response.content)
|
70 |
+
print(f"Downloaded {file_url} to {local_file_path}")
|
71 |
+
else:
|
72 |
+
print(f"Failed to download {file_url}: Status code {response.status_code}")
|
73 |
+
return local_file_path
|
74 |
+
|
75 |
+
|
76 |
+
def restore_wav_file(seed_id):
|
77 |
+
"""
|
78 |
+
根据给定的 seed_id 恢复 WAV 文件。如果 storage_mode 为 'local',
|
79 |
+
则从本地存储路径中获取文件。如果 storage_mode 为 'url',
|
80 |
+
则从远程 URL 下载文件到临时目录。
|
81 |
+
:param seed_id:
|
82 |
+
:return:
|
83 |
+
"""
|
84 |
+
if not seed_id:
|
85 |
+
return None
|
86 |
+
|
87 |
+
if storage_mode == "local":
|
88 |
+
local_file_path = os.path.join(storage_path, f"{seed_id}_test.wav")
|
89 |
+
if os.path.exists(local_file_path):
|
90 |
+
return local_file_path
|
91 |
+
else:
|
92 |
+
print(f"Local file {local_file_path} does not exist.")
|
93 |
+
return None
|
94 |
+
|
95 |
+
elif storage_mode == "url":
|
96 |
+
try:
|
97 |
+
return download_wav_file(seed_id, storage_url, tmp_dir)
|
98 |
+
except Exception as e:
|
99 |
+
print(f"Failed to download WAV file: {e}")
|
100 |
+
return None
|
101 |
+
|
102 |
+
else:
|
103 |
+
print(f"Invalid storage mode: {storage_mode}")
|
104 |
+
return None
|
105 |
+
|
106 |
+
|
107 |
+
def restore_pt_file(seed_id):
|
108 |
+
"""
|
109 |
+
根据给定的 seed_id 恢复 PT 文件。
|
110 |
+
:param seed_id:
|
111 |
+
:return:
|
112 |
+
"""
|
113 |
+
row = df[df["seed_id"] == seed_id]
|
114 |
+
if row.empty:
|
115 |
+
return None
|
116 |
+
row = row.iloc[0]
|
117 |
+
if not row.empty:
|
118 |
+
emb_data = row["emb_data"]
|
119 |
+
output_path = os.path.join(tmp_dir, f"{row['seed_id']}_restored_emb.pt")
|
120 |
+
base64_to_file(emb_data, output_path)
|
121 |
+
return output_path
|
122 |
+
else:
|
123 |
+
return None
|
124 |
+
|
125 |
+
|
126 |
+
def seed_change(evt: gr.SelectData, value=None):
|
127 |
+
"""
|
128 |
+
处理种子ID变化事件,根据选择的种子ID返回对应的.pt文件下载按钮和试听音频。
|
129 |
+
:param evt:
|
130 |
+
:param value:
|
131 |
+
"""
|
132 |
+
print(f"You selected {evt.value} at {evt.index} from {evt.target}")
|
133 |
+
|
134 |
+
if not isinstance(evt.index, list) or evt.index[1] != 0:
|
135 |
+
return [
|
136 |
+
None,
|
137 |
+
gr.DownloadButton(value=None, label="Download .pt File", visible=False),
|
138 |
+
gr.Audio(None, visible=False),
|
139 |
+
]
|
140 |
+
|
141 |
+
assert isinstance(value, DataFrame), "Expected value to be a DataFrame"
|
142 |
+
|
143 |
+
# seed_id
|
144 |
+
seed_id = evt.value
|
145 |
+
print(f"Selected seed_id: {seed_id}")
|
146 |
+
|
147 |
+
# 获取 pt 文件
|
148 |
+
down_file = restore_pt_file(seed_id)
|
149 |
+
|
150 |
+
# 获取试听文件
|
151 |
+
wav_file = restore_wav_file(seed_id)
|
152 |
+
if wav_file and not os.path.exists(wav_file):
|
153 |
+
print(f"WAV file {wav_file} does not exist.")
|
154 |
+
wav_file = None
|
155 |
+
|
156 |
+
return [
|
157 |
+
evt.index,
|
158 |
+
gr.DownloadButton(value=down_file, label=f"Download .pt File [{seed_id}]", visible=True),
|
159 |
+
gr.Audio(wav_file, visible=wav_file is not None),
|
160 |
+
]
|
161 |
+
|
162 |
+
|
163 |
+
with gr.Blocks() as demo:
|
164 |
+
gr.Markdown("# 🥇 ChatTTS Speaker Leaderboard ")
|
165 |
+
gr.Markdown("""
|
166 |
+
### 🎤 [ChatTTS](https://github.com/2noise/ChatTTS): 稳定音色查找与音色打标(实验性)欢迎下载试听音色!
|
167 |
+
|
168 |
+
本项目已开源:[ChatTTS_Speaker](https://github.com/6drf21e/ChatTTS_Speaker) 欢迎 PR 和 Star!
|
169 |
+
|
170 |
+
评估基于通义实验室:[eres2netv2_sv_zh-cn](https://modelscope.cn/models/iic/speech_eres2netv2_sv_zh-cn_16k-common/summary)
|
171 |
+
""")
|
172 |
+
|
173 |
+
with gr.Tab(label="🏆Leaderboard"):
|
174 |
+
with gr.Row():
|
175 |
+
with gr.Column(scale=1):
|
176 |
+
gr.Markdown("""
|
177 |
+
### 参数解释
|
178 |
+
|
179 |
+
- **rank_long**: 长句文本的音色稳定性评分。
|
180 |
+
- **rank_multi**: 多句文本的音色稳定性评分。
|
181 |
+
- **rank_single**: 单句文本的音色稳定性评分。
|
182 |
+
|
183 |
+
这三个参数用于衡量不同音色在生成不同类型文本时的一致性,数值越高表示音色越稳定。
|
184 |
+
|
185 |
+
- **score**: 音色性别、年龄、特征的可能性,越高越准确。
|
186 |
+
- **gender age feature**: 音色的性别、年龄、特征。(特征准确度不高 仅供参考)
|
187 |
+
|
188 |
+
### 如何下载音色
|
189 |
+
|
190 |
+
- 点选一个音色,点击最下方的 **Download .pt File** 按钮,即可下载对应的 .pt 文件。
|
191 |
+
|
192 |
+
### FAQ
|
193 |
+
|
194 |
+
- **Q**: 怎么使用 .pt 文件?
|
195 |
+
- **A**: 可以直接在一些项目:例如:[ChatTTS_colab](https://github.com/6drf21e/ChatTTS_colab) 中载入使用。
|
196 |
+
也可以使用类似代码载入:
|
197 |
+
```python
|
198 |
+
spk = torch.load(<PT-FILE-PATH>)
|
199 |
+
params_infer_code = {
|
200 |
+
'spk_emb': spk,
|
201 |
+
}
|
202 |
+
略
|
203 |
+
```
|
204 |
+
- **Q**: 为什么有的音色打分高但是很难听?
|
205 |
+
- **A**: 评分只是衡量音色的稳定性,不代表音色的好坏。可以根据自己的需求选择合适的音色。举个简单的例子:如果一个沙哑且结巴的音色一直很稳定,那么它的评分就会很高。
|
206 |
+
- **Q**: 我使用 seed_id 去生成音频,但是生成的音频不稳定?
|
207 |
+
- **A**: seed_id 只是一个参考ID 不同的环境下音色不一定一致。还是推荐使用 .pt 文件载入音色。
|
208 |
+
- **Q**: 音色标的男女准确吗?
|
209 |
+
- **A**: 当前第一批测试的音色有 2000 条,根据声纹相似性简单打标,准确度不高(特别是特征一项),仅供参考。如果大家有更好的标注方法,欢迎 PR。
|
210 |
+
|
211 |
+
""")
|
212 |
+
with gr.Column(scale=3, min_width=800):
|
213 |
+
leaderboard = Leaderboard(
|
214 |
+
value=df,
|
215 |
+
datatype=["markdown"] * 12,
|
216 |
+
select_columns=["seed_id", "rank_long", "rank_multi", "rank_single", "score", "gender", "age",
|
217 |
+
"feature"],
|
218 |
+
search_columns=["gender", "age"],
|
219 |
+
filter_columns=["rank_long", "rank_multi", "rank_single", ],
|
220 |
+
hide_columns=["emb_data"],
|
221 |
+
)
|
222 |
+
stats = gr.State(value=[1])
|
223 |
+
download_button = gr.DownloadButton("Download .pt File", visible=True)
|
224 |
+
test_audio = gr.Audio(visible=True)
|
225 |
+
gr.Markdown("选择 seed_id 才能下载 .pt 文件和试听音频。")
|
226 |
+
# download_button.click(download, inputs=[stats], outputs=[])
|
227 |
+
leaderboard.select(seed_change, inputs=[leaderboard], outputs=[stats, download_button, test_audio])
|
228 |
+
|
229 |
+
with gr.Tab(label="📊Details"):
|
230 |
+
gr.Markdown("""
|
231 |
+
# 音色稳定性初步评估
|
232 |
+
|
233 |
+
## 原理
|
234 |
+
|
235 |
+
利用 通义实验室开源的[eres2netv2_sv_zh-cn](https://modelscope.cn/models/iic/speech_eres2netv2_sv_zh-cn_16k-common/summary) **SERes2NetV2 说话人识别模型** ,对同一个音色进行测评,评估其在不同语音样本中的一致性。具体步骤如下:
|
236 |
+
|
237 |
+
1. **样本**:选择三个不同类型的测试样本:单句文本、多句文本和长句文本。
|
238 |
+
2. **音色一致性评分**:
|
239 |
+
- 对每对音频文件进行评分,计算它们是否来自同一说话人。
|
240 |
+
- 使用 eres2netv2 模型,对每对音频文件进行打分,获得相似度分数。
|
241 |
+
3. **稳定性评估**:
|
242 |
+
- 计算每组音频文件的平均相似度分数和标准差。
|
243 |
+
- 通过综合平均分和标准差,计算稳定性指数,用于衡量音色的一致性。
|
244 |
+
|
245 |
+
|
246 |
+
## 样本如下
|
247 |
+
|
248 |
+
### 单句文本
|
249 |
+
- 这是一段测试文本[uv_break] 用来测试多批次生产音频的稳定性。 X 6次
|
250 |
+
|
251 |
+
### 多句文本
|
252 |
+
- 今天早晨,市中心的主要道路因突发事故造成了严重堵塞[uv_break]。请驾驶员朋友们注意绕行,并听从现场交警的指挥。
|
253 |
+
- 亲爱的朋友们,无论你现在处于什么样的境地,都不要放弃希望[uv_break]。每一个伟大的成功,都是从不懈的努力和坚定的信念中诞生的。
|
254 |
+
- 很久很久以前,在一个宁静的小村庄里,住着一只名叫小花的可爱小猫咪[uv_break]。小花每天都喜欢在花园里玩耍,有一天,它遇到了一只迷路的小鸟。
|
255 |
+
- 您好,欢迎致电本公司客服中心。为了更好地服务您,请在听到提示音后选择所需服务[uv_break]。如果您需要咨询产品信息,[uv_break]请按一。
|
256 |
+
- 夜色如墨[uv_break],山间小道蜿蜒曲折。李逍遥轻踏树梢,身形如同幽灵一般,迅捷无声[uv_break]。他手中的宝剑在月光下闪烁着寒芒,心中却是一片平静。
|
257 |
+
- 亲爱的,你今天工作怎么样?[uv_break]有没有遇到什么开心的事。[uv_break]对了,晚上我们一起去那个新开的餐厅试试吧。
|
258 |
+
|
259 |
+
### 长句文本
|
260 |
+
- 今天早晨,市中心的主要道路因突发事故造成了严重堵塞[uv_break]。请驾驶员朋友们注意绕行,并听从现场交警的指挥[uv_break]。天气预报显示,未来几天将有大范围降雨[uv_break],请大家出门记得携带雨具,注意安全。另据报道,本次事故已造成数人受伤[uv_break],目前相关部门正在积极处理事故现场[uv_break],确保道路尽快恢复通畅。
|
261 |
+
- 亲爱的朋友们,无论你现在处于什么样的境地,都不要放弃希望[uv_break]。每一个伟大的成功,都是从不懈的努力和坚定的信念中诞生的[uv_break]。人生的道路上充满了挑战和困难[uv_break],但正是这些考验成就了我们的成长[uv_break]。记住,每一个今天的努力,都会成为明天成功的基石[uv_break],坚持下去,你将看到光明的未来。
|
262 |
+
- 很久很久以前,在一个宁静的小村庄里,住着一只名叫小花的可爱小猫咪[uv_break]。小花每天都喜欢在花园里玩耍,有一天,它遇到了一只迷路的小鸟[uv_break]。小花决定帮助小鸟找到回家的路[uv_break],于是它们一起穿过森林,翻过小山丘,经历了许多冒险[uv_break]。最终,在小花的帮助下,小鸟终于回到了自己的家[uv_break],它们成为了最好的朋友,从此过上了快乐的生活。
|
263 |
+
- 您好,欢迎致电本公司客服中心。为了更好地服务您,请在听到提示音后选择所需服务[uv_break]。如果您需要咨询产品信息,[uv_break]请按一[uv_break];如果您需要售后服务,请按二[uv_break];如果您需要与人工客服交流,请按零[uv_break]。感谢您的来电,我们将竭诚为您服务,祝您生活愉快[uv_break]。如有任何疑问,请随时联系我们。
|
264 |
+
- 夜色如墨[uv_break],山间小道蜿蜒曲折。李逍遥轻踏树梢,身形如同幽灵一般,迅捷无声[uv_break]。他手中的宝剑在月光下闪烁着寒芒,心中却是一片平静[uv_break]。突然,一声清脆的剑鸣打破了夜的静谧[uv_break],一个黑衣人出现在前方,冷笑道:‘李逍遥,你终于来了。’李逍遥目光如电,淡淡道:‘既然来了,就不打算走了[uv_break]。今天,我们就一决高下。",
|
265 |
+
- 亲爱的,你今天工作怎么样?[uv_break]有没有遇到什么开心的事。[uv_break]对了,晚上我们一起去那个新开的餐厅试试吧[uv_break]。我听说那里的牛排特别好吃,而且还有你最喜欢的巧克力蛋糕[uv_break]。啊,今天真的好累,但想到等会儿可以见到你,心情就好多了[uv_break]。你还记得上次我们去的那个公园吗?[uv_break]那里的樱花真的好美,我还拍了好多照片呢。
|
266 |
+
|
267 |
+
""")
|
268 |
+
|
269 |
+
if __name__ == "__main__":
|
270 |
+
demo.launch()
|
evaluation_results.csv
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:960259aa40c82dad65366011c15e127fe951c2eec65f8667cfcf41557fdd61ae
|
3 |
+
size 15619525
|
requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
gradio==4.20.0
|
2 |
+
gradio_leaderboard==0.0.9
|
3 |
+
python-dotenv
|