| # 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 问题。 |
| |