Metadata-Version: 2.4
Name: qwen3-tts-mnn
Version: 0.3.1
Summary: Qwen3-TTS (12Hz, voice clone) inference on pure CPU via MNN — no PyTorch required at runtime
Author: Qwen3-TTS MNN Community
License: Apache-2.0
Project-URL: Homepage, https://github.com/QwenLM/Qwen3-TTS
Project-URL: Model, https://huggingface.co/yunfengwang/Qwen3-TTS-12Hz-0.6B-Base-MNN
Project-URL: Source_Model, https://modelscope.cn/models/Qwen/Qwen3-TTS-12Hz-0.6B-Base
Keywords: tts,qwen3,mnn,voice-clone,speech-synthesis,cpu-inference
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.23
Requires-Dist: MNN>=3.0
Requires-Dist: regex>=2023.0.0
Requires-Dist: huggingface_hub>=0.20
Provides-Extra: extract
Requires-Dist: torch; extra == "extract"
Requires-Dist: qwen-tts; extra == "extract"
Requires-Dist: soundfile; extra == "extract"
Provides-Extra: export
Requires-Dist: torch; extra == "export"
Requires-Dist: qwen-tts; extra == "export"
Requires-Dist: onnx; extra == "export"
Requires-Dist: onnxruntime; extra == "export"
Requires-Dist: soundfile; extra == "export"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: soundfile; extra == "dev"
Dynamic: license-file

# qwen3-tts-mnn

[![PyPI](https://img.shields.io/pypi/v/qwen3-tts-mnn)](https://pypi.org/project/qwen3-tts-mnn/)
[![Model on HF](https://img.shields.io/badge/%F0%9F%A4%97%20Model-Qwen3--TTS--MNN-blue)](https://huggingface.co/yunfengwang/Qwen3-TTS-12Hz-0.6B-Base-MNN)

[Qwen3-TTS-12Hz-0.6B-Base](https://modelscope.cn/models/Qwen/Qwen3-TTS-12Hz-0.6B-Base) 的**纯 CPU** 推理包 — 基于 [MNN](https://github.com/alibaba/MNN),支持零样本声音克隆。

- 运行时只需 `numpy + MNN`(pymnn),**无需 PyTorch / CUDA**
- **macOS arm64 附带 C++ 原生加速扩展**(MNN 静态库 + Accelerate BLAS + CP session 池),RTF < 1 可实时;其他平台自动回退纯 pymnn 路径
- 内置纯 Python Qwen2 BPE 分词器(与 HuggingFace 输出逐 token 一致)
- 与原生 PyTorch 推理 codec 匹配率 ≥99%(fp16 权重,实测 g0 99.0% / 全 16 组 99.9%)
- 输出 24kHz 单声道波形

## 安装

```bash
pip install qwen3-tts-mnn
```

### 一行体验 (零配置, 自动下载模型 + 内置声线)

```bash
uv run --with qwen3-tts-mnn qwen3-tts-mnn --text '你好呀,我是小柒!' -o out.wav
```

首次运行自动从 HuggingFace 下载模型 (~1.9GB, 之后走本地缓存) 与内置 demo 声线。

## 模型资产

PyPI 包不含模型权重(~1.9GB)。转换好的 MNN 资产已发布在 HuggingFace:

```bash
pip install huggingface_hub
python -c "from huggingface_hub import snapshot_download; \
    snapshot_download('yunfengwang/Qwen3-TTS-12Hz-0.6B-Base-MNN', local_dir='model_dir')"
```

或自行准备 MNN 资产目录:

```
model_dir/
  talker_step.mnn      # Talker LM 单步 (带 KV cache)
  cp_step.mnn          # CodePredictor 单步
  vocoder.mnn          # 12Hz codec -> 24kHz 波形
  weights/*.bin        # 嵌入表/头 fp16 + meta.json
  tokenizer/{vocab.json, merges.txt}
```

声线目录(克隆资产,纯 numpy,运行时无 torch 依赖):

```
voice_dir/
  ref_code.i32.bin     # [T,16] int32, 参考音频的 12Hz codec
  spk_emb.f32.bin      # [1024] float32, 说话人嵌入
  meta.json            # {ref_text, ref_ids, ref_frames}
```

## 快速开始

### Python API

```python
from qwen3_tts_mnn import Qwen3TtsMnn

tts = Qwen3TtsMnn('model_dir', num_threads=6)   # 默认 backend='auto': 原生扩展优先
tts.load_voice('voice_dir')                     # 原生后端首次加载声线时自动预热 (~1.5s)

pcm = tts.synthesize('你好呀,我是小柒!')        # float32 [-1,1], 24kHz
tts.synthesize_to_file('你好呀', 'out.wav')      # 直接写 wav (标准库)
```

`backend`: `'auto'`(默认,原生优先)/ `'native'`(强制)/ `'pymnn'`(纯 Python)。
可调参数:`temperature / top_k / top_p / repetition_penalty / seed / max_steps / min_new_tokens`,
以及 `synthesize_codes()`(取 codec 帧)与 `codes_to_wav()`(离线解码)、`on_frame` 流式回调。
原生后端暂不支持 `on_frame` 与 `top_p/min_new_tokens` 自定义(top_p 固定 1.0、min_new 固定 2)。

### 命令行

```bash
qwen3-tts-mnn --model-dir model_dir --voice voice_dir \
    --text '你好呀,我是小柒!' -o out.wav --threads 8 --seed 42
```

## 克隆新声音

用官方 PyTorch 模型**离线一次性**提取声线资产(之后推理不再需要 torch):

```bash
pip install qwen3-tts-mnn[extract]
qwen3-tts-mnn-extract --hf-model /path/to/Qwen3-TTS-12Hz-0.6B-Base \
    --ref-audio ref.wav --ref-text '参考音频的逐字文本' -o my_voice/
```

参考音频建议 3~15s 清晰人声,`--ref-text` 必须与音频内容逐字一致。

```python
from qwen3_tts_mnn.voice import extract_voice
profile = extract_voice(hf_model_path, 'ref.wav', '逐字文本', out_dir='my_voice/')
```

## 架构

```
文本 ──> Qwen2 BPE ──> text_proj ──┐
                                   ├─> Talker LM (28层, KV cache) ──> g0 logits ──> CP (15步) ──> codes [T,16]
ref_code+spk_emb ──> ICL 装配 ─────┘                                                   │
                                                              vocoder.mnn <──────────┘
                                                                   │
                                                              24kHz wav
```

- Talker/CodePredictor 以 `*_step.mnn`(prefill/decode 共用单步 + KV cache 输入输出)运行
- 嵌入表/头等小权重为 fp16 bin,由 numpy 查表/矩阵乘完成
- 采样对齐原生实现:temperature + top_k + top_p + repetition_penalty(unique 历史)

## 性能参考

Apple M5 Pro(6 线程):

| 后端 | RTF | 说明 |
|---|---|---|
| C++ 原生 (macOS arm64, 默认) | **0.75~0.95** | 可实时; vocoder fp16 计算, 与 fp32 波形相关 0.999996 |
| 纯 pymnn | 1.4~1.7 | 跨平台回退 |

原生后端优化:MNN 静态链接 + CP session 池(15 种固定 shape 免 resize)+ Accelerate
BLAS 采样矩阵乘 + vocoder fp16 计算 + 首次加载声线自动预热。

## 原生扩展自行编译 (非 macOS arm64)

PyPI wheel 仅内置 macOS arm64 原生扩展。Linux / 其他平台可用 MNN 静态库自行编译
(源码在 [native/](native/)):

```bash
pip install pybind11
MNN_ROOT=/path/to/MNN bash native/build_native.sh
```

编译产出 `src/qwen3_tts_mnn/mnn_tts_native.so` 后,`backend='auto'` 即自动启用。

## License

Apache-2.0(代码)。模型权重遵循 Qwen 模型许可证。
