TTS 音频质量评估

api/tests/common/tts.rs 把「合成 → 解码 → 打分」串成一条可复现的流程,用来比较模型、调 length_scale、判断某个改动是否损害音质。分析型用例需要真实模型,默认 #[ignore]。

共享辅助

函数说明
ws_root()monorepo 根路径(CARGO_MANIFEST_DIR 上溯 3 层)
test_audio_config()测试标准 AudioConfig(16000 Hz / mono / 20 ms 帧)
run_tts_test(tts_config, audio_config, wav)完整流程:建模型 → 流式推理 → Opus 解码 → 写 WAV
tts_stream(text)从字符串构造 TTS 输入 Stream
opus_pipeline(samples, sample_rate, encode_sr)走一遍编码再解码,用于估量 Opus 环节的损失
analyze_audio(...)返回 TtsAudioDiagnostics(见下)
estimate_std_duration(text)按文本权重估算标准时长
collect_rule_fsts(dir)收集目录下的 rule FST(文本正则)

run_length_scale_scan(...) 不是共享辅助,它定义在 api/tests/tts_analysis_test.rs 里。

标准时长

estimate_std_duration 用 OmniVoice 的 RuleDurationEstimator 权重:按 Unicode 区段给每个字符分配语音权重,再以固定 12.0 weight/sec(约 4 汉字/秒 或 150 WPM 英文)折算。它不随模型或 length_scale 变化,所以能当跨模型的一致标尺。

TEST_TTS_TEXT:2024年5月11号,拨打110或者18920240511,花了99块钱。我在学习machine learning和artificial intelligence。

标准时长约 14.1 秒(权重≈168.8 / 12.0)。

三维评分

TtsAudioDiagnostics 分三个维度独立报告,不合并总分——音质、速度、语速是三个正交问题,合成一个分数会掩盖主因。

维度字段依据等级
Audio(音质)shimmer_pct、dynamic_range_dbshimmer 70% + DR 30%A/B/C/D/F
Perf(性能)rtfRTF 阈值A/B/C/D/F
Timing(语速)duration_secs、std_duration_secs、std_diff_secs偏离标准时长的百分比A/B/C/D/F

等级换算:≥86 → A,66–85 → B,41–65 → C,21–40 → D,<21 → F。

Audio

shimmer 是帧间振幅变动率,阈值取自临床语音病理学:<3.81% → A(健康人声),3.81–5.0 → B,5.0–6.0 → C,6.0–10.0 → D,>10 超出算法可靠上限 → F。

动态范围:>20 dB → A(自然语音水平),15–20 dB → C(偏压缩),<15 dB → F(扁平发闷)。

audio_score = shimmer_score × 0.7 + dr_score × 0.3,两段都线性插值:shimmer 3.81→5.0 对应 100→75、5.0→6.0 对应 75→50、6.0→10.0 对应 50→25;DR 15→20 dB 对应 0→100。

Perf

RTF = 生成耗时 / 音频时长。<0.1 → 100(A),0.1–0.3 → 100→80(B),0.3–0.5 → 80→60(C),0.5–1.0 → 60→0(D),>=1.0 → 0(F)。

Timing

偏离 |actual - std| / std。<5% → 100(A),5–20% → 100→80(B),20–50% → 80→40(C),50–100% → 40→0(D),>=100% → 0(F)。

输出

Audio:scr=30(D) Perf:scr=84(B) Timing:scr=74(B) | sh=18.07%(F) dr=25.9dB(A) rtf=0.26 gen=2.8s dur=10.60s(std=14.1s-25%) glitch=3 clip=0 cf=14.2dB ev=0.0012 sc=1850Hz sf=0.08 sr=4200Hz zcr=0.12 dc=0.0003 snr=28.5dB lead=0ms trail=0ms Marginal...

三段的 scr/等级在前,原始指标在 | 之后。

字段说明
cf波峰因子(峰值/RMS),越高动态越大
ev帧间能量方差,低值通常意味着交叉淡入淡出把细节糊掉
sc频谱质心 Hz,语音约 1500–3000 Hz
sf频谱平坦度 0–1,越高越接近噪声
sr频谱滚降点 Hz(85% 能量阈值)
zcr过零率,语音约 0.05–0.15
dc直流偏移,应接近 0
snr估算信噪比 dB
lead / trail前导 / 尾部静音毫秒

运行

# 扫描 length_scale,找语速校准点
cargo test --package api --test tts_analysis_test -- test_tts_matcha_zh_en_scan_ls --ignored --nocapture

# 单个模型端到端,产出 WAV
cargo test --package api --test tts_test -- test_tts_matcha_zh_en --ignored --nocapture

tts_test 的 #[ignore] 理由是 Matcha 真实模型输出偶发削波,clipping 门禁随模型数据波动,不适合进 CI。

当前基线

matcha-icefall-zh-en 的 shimmer 约 17%,高于 12% 的算法可靠上限——这是模型本身的特征,不是管道引入的。

length_scale 默认 1.0,写在 api/src/component/tts/model/matcha/mod.rs 的构造参数里(opts 缺省时生效);校准到标准 14.1 秒用的就是这个值。