File size: 5,448 Bytes
5eee449 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | # Inflect AX TTS Python SDK
`owensong/Inflect-Micro-v2`(VITS 系英文 TTS,文本 → 24 kHz 单声道波形)的 Encoder/Decoder 两段式 AXMODEL 推理 SDK,双目标 **AX620E(NPU2)/ AX637(NPU1)**。两套 AXMODEL 接口完全一致(同 shape 契约),SDK 通过模型路径切换目标,代码零改动。
## 推理流水线
```
text → [Host: eSpeak 音素化 + symbols 映射 + intersperse(add_blank)]
→ tokens[1,256](int,0 补齐)+ x_lengths[1]
→ encoder.axmodel → m_p/logs_p[1,192,256]、logw[1,1,256]
→ [Host: 取前 x_lengths 帧;w=exp(logw)*length_scale;ceil;generate_path;
matmul 扩展为 T' 帧;z_p=m_p'+randn(seed)*exp(logs_p')*variation]
→ 按 Tp=512 分块(64 帧重叠 crossfade)→ decoder.axmodel
→ 波形块拼接(尾部裁剪)→ edge fade 5ms + clip → 24kHz mono wav
```
- encoder 静态 shape `T=256`(tokens 以 0 右补齐,`x_lengths` 填真实长度;单 chunk 上限 255 个 intersperse 后 token,超长文本自动二次拆分)。
- decoder 静态 shape `Tp=512`(~5.46 s/块),Host 侧分块 + 64 帧重叠 crossfade,最后一块尾部裁剪(EXPORT_NOTES §5.3)。
- 噪声注入在 Host 侧:`np.random.default_rng(seed)`。**seed 在 SDK 内可复现,但与 PyTorch 参考(torch MT19937)不逐位一致。**
## 模型文件位置
| 目标 | encoder | decoder |
| --- | --- | --- |
| AX620E | `models/ax620e/encoder.axmodel` | `models/ax620e/decoder.axmodel` |
| AX637 | `models/ax637/encoder.axmodel` | `models/ax637/decoder.axmodel`(权重 S16 升级版) |
(路径相对于交付包根目录 `package/`;AX637 decoder 为权重 S16 升级版,wav cos 0.9995,详见 `../reports/accuracy_summary.md`。)
## 环境与安装
板端 / 主机(有 NPU 运行时):
```bash
pip install -r requirements.txt # numpy, phonemizer, espeakng-loader, num2words, pyaxengine
```
- eSpeak-ng:优先使用系统库(`/usr/lib/x86_64-linux-gnu/libespeak-ng.so.1` 或 aarch64 对应路径,Debian/Ubuntu:`apt install libespeak-ng1`);无系统库时 `espeakng-loader` pip 包自动提供。**仅真实文本合成需要;`--demo-tokens` 与 `synthesize_tokens()` 不依赖 eSpeak。**
- 本机(Host)无数值 NPU 时的验证替身:`pip install onnxruntime`,把 `../model_convert/export/encoder.onnx` / `../model_convert/export/decoder.onnx` 作为模型路径传入即可(backend 自动切换)。
## API
```python
from inflect_ax_tts import InflectTTS
tts = InflectTTS(
"../models/ax620e/encoder.axmodel",
"../models/ax620e/decoder.axmodel",
) # backend="auto": .axmodel -> pyaxengine, .onnx -> onnxruntime
# 文本合成(需要 eSpeak)
sr, wav = tts.synthesize("The quick brown fox.", speed=1.0, variation=0.667, seed=0)
tts.save("The quick brown fox.", "out.wav")
# 免前端:直接给音素 id(未 intersperse,SDK 内部加 blank)
sr, wav = tts.synthesize_tokens([81, 83, 16, 53, 65, 102, 53], seed=0)
```
- `speed` ∈ [0.5, 2.0](length_scale = 1/speed);`variation` ∈ [0.0, 1.0](噪声幅度,基线 0.667);`seed` 整数,同参可复现。
- 返回 `(24000, float32 ndarray)`,幅值 [-1, 1];`write_wav()` 写 16-bit PCM。
## 示例
```bash
cd package/python
# 板端真实文本(AX620E;AX637 换路径即可)
python example.py \
--encoder ../models/ax620e/encoder.axmodel \
--decoder ../models/ax620e/decoder.axmodel \
--text "The quick brown fox jumps over the lazy dog." \
--output out.wav
# Host 干跑:无 NPU、无 eSpeak,onnxruntime 替身 + 确定性 dummy tokens
python example.py --demo-tokens \
--encoder ../model_convert/export/encoder.onnx \
--decoder ../model_convert/export/decoder.onnx \
--output demo.wav
```
## Host 前后处理说明
- **前端**(仅 `synthesize()` 文本路径):`normalize_text`(数字/缩写/日期/货币展开,`num2words`)→ eSpeak `en-us` 音素化(`preserve_punctuation + with_stress`)→ 音素覆盖表(2 条)→ 逐字符 symbols 映射 → `intersperse(0)`。与 `origin/inference.py` 完全一致。
- **时长/对齐**(纯 numpy 整数逻辑):`w=exp(logw)*x_mask/speed` → `ceil` → `T'=max(sum,1)` → `generate_path`(cumsum 区间)→ `attn @ m_p/logs_p` 扩展。
- **噪声**:`z_p = m_p' + randn(seed) * exp(logs_p') * variation`。
- **decoder 分块**:`Tp=512`、64 帧重叠线性 crossfade;`T'<=512` 单块路径即 SIMULATE 验证路径。
- **后处理**:每块 edge fade 5 ms、句间停顿(按上一句末标点 0.08~0.28 s)、最终 clip [-1,1]。
## 测试(Host,onnxruntime 替身)
```bash
cd package/python
python tests/test_host_chain.py # 或 pytest tests/
```
覆盖:Host 链(`expand_priors`)与 PyTorch 参考逐位级一致性、encoder padding 语义、decoder 分块合成与 fp32 参考链路数值一致(ORT 层面)、端到端确定性、前端冒烟(espeak 可用时)。
## 已知差异 / 限制
- seed 语义与 PyTorch 参考不逐位一致(RNG 不同),仅保证 SDK 内可复现。
- encoder 输入 dtype:AXMODEL 运行时为 **int32**(input_processors S64 已折叠进模型),ONNX 为 int64;SDK 按 session 元数据自适应。
- decoder 多块(T'>512)crossfade 路径未做数值门禁验证(仿真仅覆盖单块路径)。
- 精度结论(仿真,详见 `../reports/accuracy_summary.md`):encoder 双目标 cos ≥ 0.9999;AX637 decoder(权重 S16 升级版)wav cos 0.9995 [PASS];**AX620E decoder(基线)wav cos ≈ 0.95,未达 0.99 门禁**(权重 S8 量化导致的高频细节损失,本工具链版本不可修复),属量化方案限制,非 SDK 问题。
|