Upload the AX630C inference workflow.
Browse files- .gitattributes +5 -0
- README.md +22 -10
- cpp/README.md +12 -6
- cpp/cmake/msp_dependencies.cmake +1 -4
- cpp/download_bsp.sh +6 -2
- cpp/src/EngineWrapper_axera.cpp +10 -2
- cpp/src/vocoder.cpp +59 -22
- cpp/src/vocoder.hpp +4 -3
- cpp/src/zipvoice_engine.cpp +2 -0
- cpp/src/zipvoice_engine.hpp +1 -0
- cpp/vocoder/vocos_full_ax630c.axmodel +3 -0
- cpp/zipvoice.cpp +6 -1
- infer_zipvoice_axera.py +18 -8
- models/zipvoice_distill_ax630C/decoder4_split_manifest.json +102 -66
- models/zipvoice_distill_ax630C/decoder_part0.axmodel +2 -2
- models/zipvoice_distill_ax630C/decoder_part1.axmodel +2 -2
- models/zipvoice_distill_ax630C/decoder_part2.axmodel +2 -2
- models/zipvoice_distill_ax630C/decoder_part3_0.axmodel +3 -0
- models/zipvoice_distill_ax630C/decoder_part3_1.axmodel +3 -0
- models/zipvoice_distill_ax630C/decoder_part3_2.axmodel +3 -0
- models/zipvoice_distill_ax630C/decoder_part3_3.axmodel +3 -0
- models/zipvoice_distill_ax630C/encoder.axmodel +2 -2
- run_ax630c.sh +51 -0
- scripts/common_infer.py +71 -0
- scripts/zipvoice_decoder4_runtime.py +2 -0
- scripts/zipvoice_decoder4_runtime_encoder_onnx.py +87 -0
- scripts/zipvoice_decoder4_runtime_part0_onnx.py +94 -0
- scripts/zipvoice_decoder4_runtime_part3_onnx.py +93 -0
.gitattributes
CHANGED
|
@@ -39,6 +39,7 @@ cpp/install/ax650/zipvoice filter=lfs diff=lfs merge=lfs -text
|
|
| 39 |
cpp/install/ax650/zipvoice_axera filter=lfs diff=lfs merge=lfs -text
|
| 40 |
cpp/install/axcl/zipvoice filter=lfs diff=lfs merge=lfs -text
|
| 41 |
cpp/vocoder/vocos_full.axmodel filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 42 |
models/axmodels/decoder_part0.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 43 |
models/axmodels/decoder_part1.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 44 |
models/axmodels/decoder_part2.axmodel filter=lfs diff=lfs merge=lfs -text
|
|
@@ -53,6 +54,10 @@ models/zipvoice_distill_ax630C/decoder_part0.axmodel filter=lfs diff=lfs merge=l
|
|
| 53 |
models/zipvoice_distill_ax630C/decoder_part1.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 54 |
models/zipvoice_distill_ax630C/decoder_part2.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 55 |
models/zipvoice_distill_ax630C/decoder_part3.axmodel filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
models/zipvoice_distill_ax630C/encoder.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 57 |
models/zipvoice_distill_ax650/decoder_part0.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 58 |
models/zipvoice_distill_ax650/decoder_part1.axmodel filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 39 |
cpp/install/ax650/zipvoice_axera filter=lfs diff=lfs merge=lfs -text
|
| 40 |
cpp/install/axcl/zipvoice filter=lfs diff=lfs merge=lfs -text
|
| 41 |
cpp/vocoder/vocos_full.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
cpp/vocoder/vocos_full_ax630c.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 43 |
models/axmodels/decoder_part0.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 44 |
models/axmodels/decoder_part1.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 45 |
models/axmodels/decoder_part2.axmodel filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 54 |
models/zipvoice_distill_ax630C/decoder_part1.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 55 |
models/zipvoice_distill_ax630C/decoder_part2.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 56 |
models/zipvoice_distill_ax630C/decoder_part3.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
models/zipvoice_distill_ax630C/decoder_part3_0.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
models/zipvoice_distill_ax630C/decoder_part3_1.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
models/zipvoice_distill_ax630C/decoder_part3_2.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
models/zipvoice_distill_ax630C/decoder_part3_3.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 61 |
models/zipvoice_distill_ax630C/encoder.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 62 |
models/zipvoice_distill_ax650/decoder_part0.axmodel filter=lfs diff=lfs merge=lfs -text
|
| 63 |
models/zipvoice_distill_ax650/decoder_part1.axmodel filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -18,12 +18,12 @@ ZipVoice Distill 是 ZipVoice 的蒸馏版本,主要优势是在较小性能
|
|
| 18 |
|
| 19 |
### Distill 模型 (num_step=4)
|
| 20 |
|
| 21 |
-
| 场景 | 音频时长 | Python AX650
|
| 22 |
-
|------|---------|------------------------|---------------------|---------------------|
|
| 23 |
-
| 中文句子 | 6.41s | 1.992s
|
| 24 |
-
| 中文段落 | 44.97s | 13.457s
|
| 25 |
-
| 英文句子 | 6.41s | 2.045s
|
| 26 |
-
| 英文段落 | 59.16s | 19.715s
|
| 27 |
|
| 28 |
### 普通模型 (num_step=10)
|
| 29 |
|
|
@@ -53,7 +53,7 @@ ZipVoice Distill 是 ZipVoice 的蒸馏版本,主要优势是在较小性能
|
|
| 53 |
## 目录结构
|
| 54 |
|
| 55 |
```text
|
| 56 |
-
ZipVoice.
|
| 57 |
├── assets/
|
| 58 |
│ ├── moss_prompts/
|
| 59 |
│ └── paragraphs/
|
|
@@ -67,8 +67,9 @@ ZipVoice.AXERA_AXCL/
|
|
| 67 |
├── cpp/ # C++ 源码 (AXERA & AXCL)
|
| 68 |
├── scripts/
|
| 69 |
├── infer_zipvoice_axera.py # Python 推理入口
|
| 70 |
-
├── run_ax650.sh #
|
| 71 |
-
├──
|
|
|
|
| 72 |
├── requirements.txt
|
| 73 |
└── README.md
|
| 74 |
```
|
|
@@ -279,10 +280,21 @@ RTF: 0.3045
|
|
| 279 |
音频:[outputs/en_long_paragraph_distill_ax650.wav](outputs/en_long_paragraph_distill_ax650.wav)
|
| 280 |
提示音:[assets/moss_prompts/en_4_4p5s.wav](assets/moss_prompts/en_4_4p5s.wav)
|
| 281 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
|
| 283 |
### 参数说明
|
| 284 |
|
| 285 |
-
- `--model-name`:
|
| 286 |
- `--prompt-wav`:参考音频,用于控制音色,建议 3-5s。
|
| 287 |
- `--prompt-text`:参考音频对应文本,必须尽量和 `prompt-wav` 内容一致。
|
| 288 |
- `--num-step`:采样步数。默认从模型目录的 `runtime_config.json` 读取。
|
|
|
|
| 18 |
|
| 19 |
### Distill 模型 (num_step=4)
|
| 20 |
|
| 21 |
+
| 场景 | 音频时长 | Python AX650<br>耗时 RTF | C++ AX650<br>耗时 RTF | C++ AXCL<br>耗时 RTF | Python AX630C<br>耗时 RTF | C++ AX630C<br>耗时 RTF |
|
| 22 |
+
|------|---------|-------------------------|----------------------|---------------------|--------------------------|-----------------------|
|
| 23 |
+
| 中文句子 | 6.41s | 1.992s/0.311 | 1.057s/0.165 | 0.918s/**0.143** | 10.296s/1.606 | 4.650s/0.725 |
|
| 24 |
+
| 中文段落 | 44.97s | 13.457s/0.301 | 7.357s/0.164 | 6.443s/**0.143** | 71.574s/1.600 | 32.590s/0.725 |
|
| 25 |
+
| 英文句子 | 6.41s | 2.045s/0.319 | 1.067s/0.166 | 0.919s/**0.143** | 10.686s/1.667 | 4.654s/0.726 |
|
| 26 |
+
| 英文段落 | 59.16s | 19.715s/0.305 | 10.529s/0.178 | 9.207s/**0.156** | 106.183s/1.640 | 46.541s/0.787 |
|
| 27 |
|
| 28 |
### 普通模型 (num_step=10)
|
| 29 |
|
|
|
|
| 53 |
## 目录结构
|
| 54 |
|
| 55 |
```text
|
| 56 |
+
ZipVoice.AXERA/
|
| 57 |
├── assets/
|
| 58 |
│ ├── moss_prompts/
|
| 59 |
│ └── paragraphs/
|
|
|
|
| 67 |
├── cpp/ # C++ 源码 (AXERA & AXCL)
|
| 68 |
├── scripts/
|
| 69 |
├── infer_zipvoice_axera.py # Python 推理入口
|
| 70 |
+
├── run_ax650.sh # AX650 板端
|
| 71 |
+
├── run_ax630c.sh # AX630C 板端
|
| 72 |
+
├── run_axcl.sh # AXCL 算力卡
|
| 73 |
├── requirements.txt
|
| 74 |
└── README.md
|
| 75 |
```
|
|
|
|
| 280 |
音频:[outputs/en_long_paragraph_distill_ax650.wav](outputs/en_long_paragraph_distill_ax650.wav)
|
| 281 |
提示音:[assets/moss_prompts/en_4_4p5s.wav](assets/moss_prompts/en_4_4p5s.wav)
|
| 282 |
|
| 283 |
+
#### AX630C ZipVoice Distill
|
| 284 |
+
|
| 285 |
+
普通模型推理太慢,这边仅提供 Distill 模型,参数与 AX650 一致,仅 `--model-name` 改为 `zipvoice_distill_ax630C`:
|
| 286 |
+
|
| 287 |
+
```bash
|
| 288 |
+
python3 infer_zipvoice_axera.py \
|
| 289 |
+
--model-name zipvoice_distill_ax630C \
|
| 290 |
+
--text "今天午后天气很好,我打开窗户,听见远处有人聊天,水杯也轻轻晃了一下。" \
|
| 291 |
+
--prompt-text "不管怎么样我和汤姆还是要感谢贝尔卡金的援手" \
|
| 292 |
+
--prompt-wav assets/moss_prompts/zh_1_4p5s.wav --seed 42
|
| 293 |
+
```
|
| 294 |
|
| 295 |
### 参数说明
|
| 296 |
|
| 297 |
+
- `--model-name`:`zipvoice_ax650` / `zipvoice_distill_ax650` / `zipvoice_distill_ax630C`。
|
| 298 |
- `--prompt-wav`:参考音频,用于控制音色,建议 3-5s。
|
| 299 |
- `--prompt-text`:参考音频对应文本,必须尽量和 `prompt-wav` 内容一致。
|
| 300 |
- `--num-step`:采样步数。默认从模型目录的 `runtime_config.json` 读取。
|
cpp/README.md
CHANGED
|
@@ -4,12 +4,12 @@
|
|
| 4 |
|
| 5 |
### Distill 模型 (num_step=4)
|
| 6 |
|
| 7 |
-
| 场景 | 音频
|
| 8 |
-
|------|---------|------------------------|---------------------|---------------------|
|
| 9 |
-
| 中文句子 | 6.41s | 1.992s
|
| 10 |
-
| 中文段落 | 44.97s | 13.457s
|
| 11 |
-
| 英文句子 | 6.41s | 2.045s
|
| 12 |
-
| 英文段落 | 59.16s | 19.715s
|
| 13 |
|
| 14 |
### 普通模型 (num_step=10)
|
| 15 |
|
|
@@ -32,6 +32,7 @@ mkdir -p <TOOLCHAIN_DIR> && cd <TOOLCHAIN_DIR>
|
|
| 32 |
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/9.2-2019.12/binrel/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz
|
| 33 |
tar -xf gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz
|
| 34 |
git clone https://github.com/AXERA-TECH/ax650n_bsp_sdk.git --depth=1
|
|
|
|
| 35 |
git clone https://github.com/Abandon-ht/axcl_bsp_sdk.git --depth=1
|
| 36 |
```
|
| 37 |
|
|
@@ -46,6 +47,7 @@ pip install jieba numpy pypinyin piper_phonemize
|
|
| 46 |
|
| 47 |
```bash
|
| 48 |
bash build_ax650.sh # → install/ax650/zipvoice
|
|
|
|
| 49 |
bash build_axcl.sh # → install/axcl/zipvoice
|
| 50 |
```
|
| 51 |
|
|
@@ -67,6 +69,10 @@ bash run_ax650.sh standard en paragraph
|
|
| 67 |
# 算力卡
|
| 68 |
bash run_axcl.sh distill zh paragraph
|
| 69 |
bash run_axcl.sh standard en sentence
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
...
|
| 71 |
```
|
| 72 |
|
|
|
|
| 4 |
|
| 5 |
### Distill 模型 (num_step=4)
|
| 6 |
|
| 7 |
+
| 场景 | 音频 | Python AX650<br>耗时 RTF | C++ AX650<br>耗时 RTF | C++ AXCL<br>耗时 RTF | Python AX630C<br>耗时 RTF | C++ AX630C<br>耗时 RTF |
|
| 8 |
+
|------|------|-------------------------|----------------------|---------------------|--------------------------|-----------------------|
|
| 9 |
+
| 中文句子 | 6.41s | 1.992s/0.311 | 1.057s/0.165 | 0.918s/**0.143** | 10.296s/1.606 | 4.650s/0.725 |
|
| 10 |
+
| 中文段落 | 44.97s | 13.457s/0.301 | 7.357s/0.164 | 6.443s/**0.143** | 71.574s/1.600 | 32.590s/0.725 |
|
| 11 |
+
| 英文句子 | 6.41s | 2.045s/0.319 | 1.067s/0.166 | 0.919s/**0.143** | 10.686s/1.667 | 4.654s/0.726 |
|
| 12 |
+
| 英文段落 | 59.16s | 19.715s/0.305 | 10.529s/0.178 | 9.207s/**0.156** | 106.183s/1.640 | 46.541s/0.787 |
|
| 13 |
|
| 14 |
### 普通模型 (num_step=10)
|
| 15 |
|
|
|
|
| 32 |
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/9.2-2019.12/binrel/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz
|
| 33 |
tar -xf gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz
|
| 34 |
git clone https://github.com/AXERA-TECH/ax650n_bsp_sdk.git --depth=1
|
| 35 |
+
git clone https://github.com/AXERA-TECH/ax620e_bsp_sdk.git --depth=1
|
| 36 |
git clone https://github.com/Abandon-ht/axcl_bsp_sdk.git --depth=1
|
| 37 |
```
|
| 38 |
|
|
|
|
| 47 |
|
| 48 |
```bash
|
| 49 |
bash build_ax650.sh # → install/ax650/zipvoice
|
| 50 |
+
bash build_ax630c.sh # → install/ax630c/zipvoice
|
| 51 |
bash build_axcl.sh # → install/axcl/zipvoice
|
| 52 |
```
|
| 53 |
|
|
|
|
| 69 |
# 算力卡
|
| 70 |
bash run_axcl.sh distill zh paragraph
|
| 71 |
bash run_axcl.sh standard en sentence
|
| 72 |
+
|
| 73 |
+
# AX630C (仅 distill)
|
| 74 |
+
bash run_ax630c.sh zh sentence
|
| 75 |
+
bash run_ax630c.sh en paragraph
|
| 76 |
...
|
| 77 |
```
|
| 78 |
|
cpp/cmake/msp_dependencies.cmake
CHANGED
|
@@ -19,10 +19,7 @@ if (AX650)
|
|
| 19 |
elseif(AX630C)
|
| 20 |
add_definitions(-DAX630C)
|
| 21 |
if(NOT BSP_MSP_DIR)
|
| 22 |
-
set(BSP_MSP_DIR
|
| 23 |
-
endif()
|
| 24 |
-
if(NOT EXISTS ${BSP_MSP_DIR})
|
| 25 |
-
message(FATAL_ERROR "BSP_MSP_DIR ${BSP_MSP_DIR} not exist.")
|
| 26 |
endif()
|
| 27 |
set(MSP_INC_DIR ${BSP_MSP_DIR}/include)
|
| 28 |
set(MSP_LIB_DIR ${BSP_MSP_DIR}/lib)
|
|
|
|
| 19 |
elseif(AX630C)
|
| 20 |
add_definitions(-DAX630C)
|
| 21 |
if(NOT BSP_MSP_DIR)
|
| 22 |
+
set(BSP_MSP_DIR /data/shared/huyuan/toolchains/ax620e_bsp_sdk/msp/out/arm64_glibc)
|
|
|
|
|
|
|
|
|
|
| 23 |
endif()
|
| 24 |
set(MSP_INC_DIR ${BSP_MSP_DIR}/include)
|
| 25 |
set(MSP_LIB_DIR ${BSP_MSP_DIR}/lib)
|
cpp/download_bsp.sh
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
#!/bin/bash
|
| 2 |
# Download BSP SDKs to /data/shared/huyuan/toolchains/
|
| 3 |
-
# AXERA: ax650n_bsp_sdk | AXCL: axcl_bsp_sdk
|
| 4 |
|
| 5 |
TARGET=/data/shared/huyuan/toolchains
|
| 6 |
mkdir -p $TARGET && cd $TARGET
|
|
@@ -10,10 +9,15 @@ if [ ! -d ax650n_bsp_sdk ]; then
|
|
| 10 |
git clone https://github.com/AXERA-TECH/ax650n_bsp_sdk.git --depth=1
|
| 11 |
fi
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
if [ ! -d axcl_bsp_sdk ]; then
|
| 14 |
echo "Downloading axcl_bsp_sdk..."
|
| 15 |
git clone https://github.com/Abandon-ht/axcl_bsp_sdk.git --depth=1
|
| 16 |
fi
|
| 17 |
|
| 18 |
echo "Done. SDKs at $TARGET/"
|
| 19 |
-
ls -d ax650n_bsp_sdk axcl_bsp_sdk 2>/dev/null
|
|
|
|
| 1 |
#!/bin/bash
|
| 2 |
# Download BSP SDKs to /data/shared/huyuan/toolchains/
|
|
|
|
| 3 |
|
| 4 |
TARGET=/data/shared/huyuan/toolchains
|
| 5 |
mkdir -p $TARGET && cd $TARGET
|
|
|
|
| 9 |
git clone https://github.com/AXERA-TECH/ax650n_bsp_sdk.git --depth=1
|
| 10 |
fi
|
| 11 |
|
| 12 |
+
if [ ! -d ax620e_bsp_sdk ]; then
|
| 13 |
+
echo "Downloading ax620e_bsp_sdk..."
|
| 14 |
+
git clone https://github.com/AXERA-TECH/ax620e_bsp_sdk.git --depth=1
|
| 15 |
+
fi
|
| 16 |
+
|
| 17 |
if [ ! -d axcl_bsp_sdk ]; then
|
| 18 |
echo "Downloading axcl_bsp_sdk..."
|
| 19 |
git clone https://github.com/Abandon-ht/axcl_bsp_sdk.git --depth=1
|
| 20 |
fi
|
| 21 |
|
| 22 |
echo "Done. SDKs at $TARGET/"
|
| 23 |
+
ls -d ax650n_bsp_sdk ax620e_bsp_sdk axcl_bsp_sdk 2>/dev/null
|
cpp/src/EngineWrapper_axera.cpp
CHANGED
|
@@ -9,7 +9,9 @@
|
|
| 9 |
|
| 10 |
#include <cstdlib>
|
| 11 |
|
|
|
|
| 12 |
static const char *strAlgoModelType[AX_ENGINE_MODEL_TYPE_BUTT] = {"3.6T", "7.2T", "10.8T"};
|
|
|
|
| 13 |
|
| 14 |
// NPU type enum
|
| 15 |
typedef enum axNPU_TYPE_E {
|
|
@@ -21,6 +23,7 @@ typedef enum axNPU_TYPE_E {
|
|
| 21 |
AX_BL_VNPU_2 = (1 << 4)
|
| 22 |
} AX_NPU_TYPE_E;
|
| 23 |
|
|
|
|
| 24 |
static AX_S32 CheckModelVNpu(const std::string &strModel,
|
| 25 |
const AX_ENGINE_MODEL_TYPE_T &eModelType,
|
| 26 |
const AX_S32 &nNpuType, AX_U32 &nNpuSet) {
|
|
@@ -57,6 +60,7 @@ static AX_S32 CheckModelVNpu(const std::string &strModel,
|
|
| 57 |
}
|
| 58 |
return ret;
|
| 59 |
}
|
|
|
|
| 60 |
|
| 61 |
EngineWrapper::EngineWrapper()
|
| 62 |
: m_hasInit(false), m_handle(nullptr), m_io_info(nullptr),
|
|
@@ -99,7 +103,8 @@ int EngineWrapper::Init(const char* strModelPath, uint32_t nNpuType, const char*
|
|
| 99 |
}
|
| 100 |
};
|
| 101 |
|
| 102 |
-
// 1.1 Get Model Type
|
|
|
|
| 103 |
AX_ENGINE_MODEL_TYPE_T eModelType = AX_ENGINE_MODEL_TYPE0;
|
| 104 |
AX_S32 ret = AX_ENGINE_GetModelType(pModelBufferVirAddr, nModelBufferSize, &eModelType);
|
| 105 |
if (0 != ret || eModelType >= AX_ENGINE_MODEL_TYPE_BUTT) {
|
|
@@ -108,7 +113,6 @@ int EngineWrapper::Init(const char* strModelPath, uint32_t nNpuType, const char*
|
|
| 108 |
return -1;
|
| 109 |
}
|
| 110 |
|
| 111 |
-
// 1.2 Check VNPU
|
| 112 |
AX_U32 nNpuSet = 0;
|
| 113 |
ret = CheckModelVNpu(strModelPath, eModelType, nNpuType, nNpuSet);
|
| 114 |
if (0 != ret) {
|
|
@@ -116,6 +120,10 @@ int EngineWrapper::Init(const char* strModelPath, uint32_t nNpuType, const char*
|
|
| 116 |
freeModelBuffer();
|
| 117 |
return -1;
|
| 118 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
// 2. Create handle
|
| 121 |
AX_ENGINE_HANDLE handle = nullptr;
|
|
|
|
| 9 |
|
| 10 |
#include <cstdlib>
|
| 11 |
|
| 12 |
+
#if !defined(AX630C)
|
| 13 |
static const char *strAlgoModelType[AX_ENGINE_MODEL_TYPE_BUTT] = {"3.6T", "7.2T", "10.8T"};
|
| 14 |
+
#endif
|
| 15 |
|
| 16 |
// NPU type enum
|
| 17 |
typedef enum axNPU_TYPE_E {
|
|
|
|
| 23 |
AX_BL_VNPU_2 = (1 << 4)
|
| 24 |
} AX_NPU_TYPE_E;
|
| 25 |
|
| 26 |
+
#if !defined(AX630C)
|
| 27 |
static AX_S32 CheckModelVNpu(const std::string &strModel,
|
| 28 |
const AX_ENGINE_MODEL_TYPE_T &eModelType,
|
| 29 |
const AX_S32 &nNpuType, AX_U32 &nNpuSet) {
|
|
|
|
| 60 |
}
|
| 61 |
return ret;
|
| 62 |
}
|
| 63 |
+
#endif
|
| 64 |
|
| 65 |
EngineWrapper::EngineWrapper()
|
| 66 |
: m_hasInit(false), m_handle(nullptr), m_io_info(nullptr),
|
|
|
|
| 103 |
}
|
| 104 |
};
|
| 105 |
|
| 106 |
+
// 1.1 Get Model Type & Check VNPU (AX650 only)
|
| 107 |
+
#if !defined(AX630C)
|
| 108 |
AX_ENGINE_MODEL_TYPE_T eModelType = AX_ENGINE_MODEL_TYPE0;
|
| 109 |
AX_S32 ret = AX_ENGINE_GetModelType(pModelBufferVirAddr, nModelBufferSize, &eModelType);
|
| 110 |
if (0 != ret || eModelType >= AX_ENGINE_MODEL_TYPE_BUTT) {
|
|
|
|
| 113 |
return -1;
|
| 114 |
}
|
| 115 |
|
|
|
|
| 116 |
AX_U32 nNpuSet = 0;
|
| 117 |
ret = CheckModelVNpu(strModelPath, eModelType, nNpuType, nNpuSet);
|
| 118 |
if (0 != ret) {
|
|
|
|
| 120 |
freeModelBuffer();
|
| 121 |
return -1;
|
| 122 |
}
|
| 123 |
+
#endif
|
| 124 |
+
#if defined(AX630C)
|
| 125 |
+
AX_S32 ret = 0;
|
| 126 |
+
#endif
|
| 127 |
|
| 128 |
// 2. Create handle
|
| 129 |
AX_ENGINE_HANDLE handle = nullptr;
|
cpp/src/vocoder.cpp
CHANGED
|
@@ -36,16 +36,26 @@ int Vocoder::Init(const Config& cfg) {
|
|
| 36 |
}
|
| 37 |
m_ifft_cfg = m_fft_cfg;
|
| 38 |
|
| 39 |
-
// Load
|
| 40 |
m_session = std::make_unique<EngineWrapper>();
|
| 41 |
if (m_session->Init(cfg.model_path.c_str(), 0, cfg.axclConfig) != 0) {
|
| 42 |
printf("Failed to load vocoder model: %s\n", cfg.model_path.c_str());
|
| 43 |
return -1;
|
| 44 |
}
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
m_has_init = true;
|
| 47 |
-
printf("Vocoder initialized: n_fft=%d, hop=%d, n_mels=%d\n",
|
| 48 |
-
cfg.n_fft, cfg.hop_length, cfg.n_mels
|
|
|
|
| 49 |
return 0;
|
| 50 |
}
|
| 51 |
|
|
@@ -77,27 +87,54 @@ int Vocoder::Decode(const std::vector<float>& mel, int T, float feat_scale, std:
|
|
| 77 |
}
|
| 78 |
}
|
| 79 |
|
| 80 |
-
// 2. Run
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
}
|
| 89 |
|
| 90 |
-
int real_idx = m_session->GetOutputIndex("real");
|
| 91 |
-
int imag_idx = m_session->GetOutputIndex("imag");
|
| 92 |
-
if (real_idx < 0) real_idx = 0;
|
| 93 |
-
if (imag_idx < 0) imag_idx = 1;
|
| 94 |
-
|
| 95 |
-
int spec_size = m_session->GetOutputSize(real_idx);
|
| 96 |
-
std::vector<float> real_spec(spec_size / sizeof(float));
|
| 97 |
-
std::vector<float> imag_spec(spec_size / sizeof(float));
|
| 98 |
-
m_session->GetOutput(real_spec.data(), real_idx);
|
| 99 |
-
m_session->GetOutput(imag_spec.data(), imag_idx);
|
| 100 |
-
|
| 101 |
// 3. kissfft IRFFT + window + overlap-add
|
| 102 |
int out_len = (T - 1) * hop + n_fft;
|
| 103 |
audio.assign(out_len, 0.0f);
|
|
|
|
| 36 |
}
|
| 37 |
m_ifft_cfg = m_fft_cfg;
|
| 38 |
|
| 39 |
+
// Load backbone (or full) model
|
| 40 |
m_session = std::make_unique<EngineWrapper>();
|
| 41 |
if (m_session->Init(cfg.model_path.c_str(), 0, cfg.axclConfig) != 0) {
|
| 42 |
printf("Failed to load vocoder model: %s\n", cfg.model_path.c_str());
|
| 43 |
return -1;
|
| 44 |
}
|
| 45 |
|
| 46 |
+
// Load head_linear model (optional, for split mode)
|
| 47 |
+
if (!cfg.head_model_path.empty()) {
|
| 48 |
+
m_session_head = std::make_unique<EngineWrapper>();
|
| 49 |
+
if (m_session_head->Init(cfg.head_model_path.c_str(), 0, cfg.axclConfig) != 0) {
|
| 50 |
+
printf("Failed to load vocoder head model: %s\n", cfg.head_model_path.c_str());
|
| 51 |
+
return -1;
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
m_has_init = true;
|
| 56 |
+
printf("Vocoder initialized: n_fft=%d, hop=%d, n_mels=%d%s\n",
|
| 57 |
+
cfg.n_fft, cfg.hop_length, cfg.n_mels,
|
| 58 |
+
m_session_head ? " (split mode)" : "");
|
| 59 |
return 0;
|
| 60 |
}
|
| 61 |
|
|
|
|
| 87 |
}
|
| 88 |
}
|
| 89 |
|
| 90 |
+
// 2. Run NPU inference
|
| 91 |
+
std::vector<float> real_spec, imag_spec;
|
| 92 |
+
int spec_size;
|
| 93 |
+
|
| 94 |
+
if (m_session_head) {
|
| 95 |
+
// Split mode: backbone → features → head_linear → (real, imag)
|
| 96 |
+
int in_idx = m_session->GetInputIndex("mel");
|
| 97 |
+
if (in_idx < 0) in_idx = 0;
|
| 98 |
+
m_session->SetInput(mel_padded.data(), in_idx);
|
| 99 |
+
if (m_session->RunSync() != 0) { printf("Vocoder backbone failed\n"); return -1; }
|
| 100 |
+
|
| 101 |
+
int feat_idx = m_session->GetOutputIndex("features");
|
| 102 |
+
if (feat_idx < 0) feat_idx = 0;
|
| 103 |
+
std::vector<float> features(m_session->GetOutputSize(feat_idx) / sizeof(float));
|
| 104 |
+
m_session->GetOutput(features.data(), feat_idx);
|
| 105 |
+
|
| 106 |
+
int h_in = m_session_head->GetInputIndex("features");
|
| 107 |
+
if (h_in < 0) h_in = 0;
|
| 108 |
+
m_session_head->SetInput(features.data(), h_in);
|
| 109 |
+
if (m_session_head->RunSync() != 0) { printf("Vocoder head failed\n"); return -1; }
|
| 110 |
+
|
| 111 |
+
int real_idx = m_session_head->GetOutputIndex("real");
|
| 112 |
+
int imag_idx = m_session_head->GetOutputIndex("imag");
|
| 113 |
+
if (real_idx < 0) real_idx = 0;
|
| 114 |
+
if (imag_idx < 0) imag_idx = 1;
|
| 115 |
+
spec_size = m_session_head->GetOutputSize(real_idx);
|
| 116 |
+
real_spec.resize(spec_size / sizeof(float));
|
| 117 |
+
imag_spec.resize(spec_size / sizeof(float));
|
| 118 |
+
m_session_head->GetOutput(real_spec.data(), real_idx);
|
| 119 |
+
m_session_head->GetOutput(imag_spec.data(), imag_idx);
|
| 120 |
+
} else {
|
| 121 |
+
// Full model: mel → (real, imag)
|
| 122 |
+
int in_idx = m_session->GetInputIndex("mel");
|
| 123 |
+
if (in_idx < 0) in_idx = 0;
|
| 124 |
+
m_session->SetInput(mel_padded.data(), in_idx);
|
| 125 |
+
if (m_session->RunSync() != 0) { printf("Vocoder NPU inference failed\n"); return -1; }
|
| 126 |
+
|
| 127 |
+
int real_idx = m_session->GetOutputIndex("real");
|
| 128 |
+
int imag_idx = m_session->GetOutputIndex("imag");
|
| 129 |
+
if (real_idx < 0) real_idx = 0;
|
| 130 |
+
if (imag_idx < 0) imag_idx = 1;
|
| 131 |
+
spec_size = m_session->GetOutputSize(real_idx);
|
| 132 |
+
real_spec.resize(spec_size / sizeof(float));
|
| 133 |
+
imag_spec.resize(spec_size / sizeof(float));
|
| 134 |
+
m_session->GetOutput(real_spec.data(), real_idx);
|
| 135 |
+
m_session->GetOutput(imag_spec.data(), imag_idx);
|
| 136 |
}
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
// 3. kissfft IRFFT + window + overlap-add
|
| 139 |
int out_len = (T - 1) * hop + n_fft;
|
| 140 |
audio.assign(out_len, 0.0f);
|
cpp/src/vocoder.hpp
CHANGED
|
@@ -16,11 +16,11 @@
|
|
| 16 |
class Vocoder {
|
| 17 |
public:
|
| 18 |
struct Config {
|
| 19 |
-
std::string model_path;
|
| 20 |
-
|
|
|
|
| 21 |
int n_fft = 1024;
|
| 22 |
int hop_length = 256;
|
| 23 |
-
int win_length = 1024;
|
| 24 |
int n_mels = 100;
|
| 25 |
int sample_rate = 24000;
|
| 26 |
};
|
|
@@ -48,6 +48,7 @@ private:
|
|
| 48 |
Config m_cfg;
|
| 49 |
bool m_has_init;
|
| 50 |
std::unique_ptr<EngineWrapper> m_session;
|
|
|
|
| 51 |
|
| 52 |
// kissfft configs (shared forward/inverse twiddles)
|
| 53 |
kiss_fft_state* m_fft_cfg = nullptr;
|
|
|
|
| 16 |
class Vocoder {
|
| 17 |
public:
|
| 18 |
struct Config {
|
| 19 |
+
std::string model_path; // full model (or backbone in split mode)
|
| 20 |
+
std::string head_model_path; // head_linear model (optional, for split mode)
|
| 21 |
+
const char* axclConfig = nullptr;
|
| 22 |
int n_fft = 1024;
|
| 23 |
int hop_length = 256;
|
|
|
|
| 24 |
int n_mels = 100;
|
| 25 |
int sample_rate = 24000;
|
| 26 |
};
|
|
|
|
| 48 |
Config m_cfg;
|
| 49 |
bool m_has_init;
|
| 50 |
std::unique_ptr<EngineWrapper> m_session;
|
| 51 |
+
std::unique_ptr<EngineWrapper> m_session_head; // optional, for split mode
|
| 52 |
|
| 53 |
// kissfft configs (shared forward/inverse twiddles)
|
| 54 |
kiss_fft_state* m_fft_cfg = nullptr;
|
cpp/src/zipvoice_engine.cpp
CHANGED
|
@@ -159,6 +159,7 @@ int ZipVoiceEngine::LoadConfig(const std::string& model_dir) {
|
|
| 159 |
m_config.hop_length = json_get_int(json, "hop_length", 256);
|
| 160 |
m_config.num_step = json_get_int(json, "num_step", 10);
|
| 161 |
m_config.t_shift = json_get_float(json, "t_shift", 0.5f);
|
|
|
|
| 162 |
}
|
| 163 |
m_config.model_dir = model_dir;
|
| 164 |
return 0;
|
|
@@ -501,6 +502,7 @@ int ZipVoiceEngine::Sample(const std::vector<int32_t>& cat_tokens,
|
|
| 501 |
std::copy(prompt_features.begin(),
|
| 502 |
prompt_features.begin() + prompt_features_len * feat_dim,
|
| 503 |
speech_cond_padded.begin());
|
|
|
|
| 504 |
|
| 505 |
// padding_mask: [1, seq_len] as uint8_t (bool), NOT float32!
|
| 506 |
// Python: np.zeros((1, seq_len), dtype=np.bool_)
|
|
|
|
| 159 |
m_config.hop_length = json_get_int(json, "hop_length", 256);
|
| 160 |
m_config.num_step = json_get_int(json, "num_step", 10);
|
| 161 |
m_config.t_shift = json_get_float(json, "t_shift", 0.5f);
|
| 162 |
+
m_config.guidance_scale = json_get_float(json, "guidance_scale", 1.0f);
|
| 163 |
}
|
| 164 |
m_config.model_dir = model_dir;
|
| 165 |
return 0;
|
|
|
|
| 502 |
std::copy(prompt_features.begin(),
|
| 503 |
prompt_features.begin() + prompt_features_len * feat_dim,
|
| 504 |
speech_cond_padded.begin());
|
| 505 |
+
// DEBUG
|
| 506 |
|
| 507 |
// padding_mask: [1, seq_len] as uint8_t (bool), NOT float32!
|
| 508 |
// Python: np.zeros((1, seq_len), dtype=np.bool_)
|
cpp/src/zipvoice_engine.hpp
CHANGED
|
@@ -39,6 +39,7 @@ public:
|
|
| 39 |
int hop_length = 256;
|
| 40 |
int num_step = 10;
|
| 41 |
float t_shift = 0.5f;
|
|
|
|
| 42 |
};
|
| 43 |
|
| 44 |
struct ModelInfo {
|
|
|
|
| 39 |
int hop_length = 256;
|
| 40 |
int num_step = 10;
|
| 41 |
float t_shift = 0.5f;
|
| 42 |
+
float guidance_scale = 1.0f;
|
| 43 |
};
|
| 44 |
|
| 45 |
struct ModelInfo {
|
cpp/vocoder/vocos_full_ax630c.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4b704aa370e7875099615ebbb2772c24e7d97529df81f11b713ca04e4261903e
|
| 3 |
+
size 16208038
|
cpp/zipvoice.cpp
CHANGED
|
@@ -623,6 +623,8 @@ int main(int argc, char** argv) {
|
|
| 623 |
false, "");
|
| 624 |
cmd.add<std::string>("vocoder-model", 0, "Path to vocos_full.axmodel for C++ vocoder. When set, all-C++ pipeline, no Python.",
|
| 625 |
false, "");
|
|
|
|
|
|
|
| 626 |
cmd.add<std::string>("axcl-config", 0, "AXCL only: path to axcl.json config file",
|
| 627 |
false, "/usr/local/axcl/axcl.json");
|
| 628 |
cmd.add<int>("device-index", 0, "AXCL only: device index (0=first card)", false, 0);
|
|
@@ -636,7 +638,7 @@ int main(int argc, char** argv) {
|
|
| 636 |
cmd.add<int>("max-feat-len", 0, "Max feature sequence length", false, 1024);
|
| 637 |
cmd.add<int>("num-step", 0, "Number of flow-matching steps", false, 10);
|
| 638 |
cmd.add<float>("speed", 0, "Speech speed factor", false, 1.0f);
|
| 639 |
-
cmd.add<float>("guidance-scale", 0, "CFG guidance scale", false,
|
| 640 |
cmd.add<float>("t-shift", 0, "Time shift for flow scheduler", false, 0.5f);
|
| 641 |
cmd.add<float>("feat-scale", 0, "Feature scaling factor", false, 0.1f);
|
| 642 |
cmd.add<float>("target-rms", 0, "Target RMS for audio normalization", false, 0.1f);
|
|
@@ -658,6 +660,7 @@ int main(int argc, char** argv) {
|
|
| 658 |
auto output_feat = cmd.get<std::string>("output-feat");
|
| 659 |
auto repo_dir = cmd.get<std::string>("repo-dir");
|
| 660 |
auto vocoder_model = cmd.get<std::string>("vocoder-model");
|
|
|
|
| 661 |
auto axcl_config = cmd.get<std::string>("axcl-config");
|
| 662 |
int device_index = cmd.get<int>("device-index");
|
| 663 |
auto cat_tokens_file = cmd.get<std::string>("cat-tokens-file");
|
|
@@ -871,6 +874,7 @@ int main(int argc, char** argv) {
|
|
| 871 |
double t_vocoder_load = get_current_time_ms();
|
| 872 |
Vocoder::Config vcfg;
|
| 873 |
vcfg.model_path = vocoder_model;
|
|
|
|
| 874 |
vcfg.axclConfig = axcl_config.c_str();
|
| 875 |
if (vocoder.Init(vcfg) != 0) {
|
| 876 |
printf("ERROR: Failed to load vocoder model\n");
|
|
@@ -972,6 +976,7 @@ int main(int argc, char** argv) {
|
|
| 972 |
ZipVoiceEngine::Timing timing;
|
| 973 |
std::vector<float> output_features;
|
| 974 |
|
|
|
|
| 975 |
if (engine.Sample(cat_tokens,
|
| 976 |
prompt_tokens_len,
|
| 977 |
text_tokens_len,
|
|
|
|
| 623 |
false, "");
|
| 624 |
cmd.add<std::string>("vocoder-model", 0, "Path to vocos_full.axmodel for C++ vocoder. When set, all-C++ pipeline, no Python.",
|
| 625 |
false, "");
|
| 626 |
+
cmd.add<std::string>("vocoder-head-model", 0, "AX630C: path to head_linear axmodel (split mode, optional)",
|
| 627 |
+
false, "");
|
| 628 |
cmd.add<std::string>("axcl-config", 0, "AXCL only: path to axcl.json config file",
|
| 629 |
false, "/usr/local/axcl/axcl.json");
|
| 630 |
cmd.add<int>("device-index", 0, "AXCL only: device index (0=first card)", false, 0);
|
|
|
|
| 638 |
cmd.add<int>("max-feat-len", 0, "Max feature sequence length", false, 1024);
|
| 639 |
cmd.add<int>("num-step", 0, "Number of flow-matching steps", false, 10);
|
| 640 |
cmd.add<float>("speed", 0, "Speech speed factor", false, 1.0f);
|
| 641 |
+
cmd.add<float>("guidance-scale", 0, "CFG guidance scale (0=use config)", false, 0.0f);
|
| 642 |
cmd.add<float>("t-shift", 0, "Time shift for flow scheduler", false, 0.5f);
|
| 643 |
cmd.add<float>("feat-scale", 0, "Feature scaling factor", false, 0.1f);
|
| 644 |
cmd.add<float>("target-rms", 0, "Target RMS for audio normalization", false, 0.1f);
|
|
|
|
| 660 |
auto output_feat = cmd.get<std::string>("output-feat");
|
| 661 |
auto repo_dir = cmd.get<std::string>("repo-dir");
|
| 662 |
auto vocoder_model = cmd.get<std::string>("vocoder-model");
|
| 663 |
+
auto vocoder_head_model = cmd.get<std::string>("vocoder-head-model");
|
| 664 |
auto axcl_config = cmd.get<std::string>("axcl-config");
|
| 665 |
int device_index = cmd.get<int>("device-index");
|
| 666 |
auto cat_tokens_file = cmd.get<std::string>("cat-tokens-file");
|
|
|
|
| 874 |
double t_vocoder_load = get_current_time_ms();
|
| 875 |
Vocoder::Config vcfg;
|
| 876 |
vcfg.model_path = vocoder_model;
|
| 877 |
+
vcfg.head_model_path = vocoder_head_model;
|
| 878 |
vcfg.axclConfig = axcl_config.c_str();
|
| 879 |
if (vocoder.Init(vcfg) != 0) {
|
| 880 |
printf("ERROR: Failed to load vocoder model\n");
|
|
|
|
| 976 |
ZipVoiceEngine::Timing timing;
|
| 977 |
std::vector<float> output_features;
|
| 978 |
|
| 979 |
+
if (guidance_scale == 0.0f) guidance_scale = engine.GetConfig().guidance_scale;
|
| 980 |
if (engine.Sample(cat_tokens,
|
| 981 |
prompt_tokens_len,
|
| 982 |
text_tokens_len,
|
infer_zipvoice_axera.py
CHANGED
|
@@ -58,6 +58,7 @@ def parse_args() -> argparse.Namespace:
|
|
| 58 |
help="Allow raw features_len up to this ratio of max_feat_len before splitting",
|
| 59 |
)
|
| 60 |
p.add_argument("--silence-ms", type=int, default=140)
|
|
|
|
| 61 |
return p.parse_args()
|
| 62 |
|
| 63 |
|
|
@@ -158,7 +159,13 @@ def main() -> None:
|
|
| 158 |
t_shift=args.t_shift,
|
| 159 |
)
|
| 160 |
logging.debug("Loading vocoder...")
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
logging.info("模型加载完成")
|
| 163 |
|
| 164 |
audios: list[np.ndarray] = []
|
|
@@ -183,13 +190,16 @@ def main() -> None:
|
|
| 183 |
seed=args.seed + index - 1,
|
| 184 |
)
|
| 185 |
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
|
|
|
|
|
|
|
|
|
| 193 |
segment_sec = time.perf_counter() - t_segment_start
|
| 194 |
audio_sec = len(audio) / 24000
|
| 195 |
|
|
|
|
| 58 |
help="Allow raw features_len up to this ratio of max_feat_len before splitting",
|
| 59 |
)
|
| 60 |
p.add_argument("--silence-ms", type=int, default=140)
|
| 61 |
+
p.add_argument("--vocoder-model", default=None, help="Path to vocos axmodel. If set, use axmodel vocoder instead of PyTorch.")
|
| 62 |
return p.parse_args()
|
| 63 |
|
| 64 |
|
|
|
|
| 159 |
t_shift=args.t_shift,
|
| 160 |
)
|
| 161 |
logging.debug("Loading vocoder...")
|
| 162 |
+
if args.vocoder_model:
|
| 163 |
+
from scripts.common_infer import load_axmodel_vocoder, axmodel_vocoder_decode
|
| 164 |
+
vocoder = load_axmodel_vocoder(args.vocoder_model)
|
| 165 |
+
vocoder_is_axmodel = True
|
| 166 |
+
else:
|
| 167 |
+
vocoder = load_vocoder(repo_dir)
|
| 168 |
+
vocoder_is_axmodel = False
|
| 169 |
logging.info("模型加载完成")
|
| 170 |
|
| 171 |
audios: list[np.ndarray] = []
|
|
|
|
| 190 |
seed=args.seed + index - 1,
|
| 191 |
)
|
| 192 |
|
| 193 |
+
if vocoder_is_axmodel:
|
| 194 |
+
from scripts.common_infer import axmodel_vocoder_decode
|
| 195 |
+
audio = axmodel_vocoder_decode(
|
| 196 |
+
vocoder, pred_features,
|
| 197 |
+
feat_scale=args.feat_scale, target_rms=args.target_rms, prompt_rms=prompt_rms)
|
| 198 |
+
else:
|
| 199 |
+
audio = vocoder_decode_loaded(
|
| 200 |
+
vocoder, pred_features,
|
| 201 |
+
feat_scale=args.feat_scale, target_rms=args.target_rms,
|
| 202 |
+
prompt_rms=prompt_rms)
|
| 203 |
segment_sec = time.perf_counter() - t_segment_start
|
| 204 |
audio_sec = len(audio) / 24000
|
| 205 |
|
models/zipvoice_distill_ax630C/decoder4_split_manifest.json
CHANGED
|
@@ -1,68 +1,104 @@
|
|
| 1 |
{
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
]
|
| 13 |
-
},
|
| 14 |
-
"decoder_parts": [
|
| 15 |
-
{
|
| 16 |
-
"name": "fm_decoder_part0",
|
| 17 |
-
"file": "decoder_part0.axmodel",
|
| 18 |
-
"inputs": [
|
| 19 |
-
"t",
|
| 20 |
-
"x",
|
| 21 |
-
"text_condition",
|
| 22 |
-
"speech_condition",
|
| 23 |
-
"guidance_scale",
|
| 24 |
-
"padding_mask"
|
| 25 |
-
],
|
| 26 |
-
"outputs": [
|
| 27 |
-
"decoder_hidden_p0",
|
| 28 |
-
"time_hidden"
|
| 29 |
-
]
|
| 30 |
-
},
|
| 31 |
-
{
|
| 32 |
-
"name": "fm_decoder_part1",
|
| 33 |
-
"file": "decoder_part1.axmodel",
|
| 34 |
-
"inputs": [
|
| 35 |
-
"decoder_hidden_p0",
|
| 36 |
-
"time_hidden",
|
| 37 |
-
"padding_mask"
|
| 38 |
-
],
|
| 39 |
-
"outputs": [
|
| 40 |
-
"decoder_hidden_p1"
|
| 41 |
-
]
|
| 42 |
-
},
|
| 43 |
-
{
|
| 44 |
-
"name": "fm_decoder_part2",
|
| 45 |
-
"file": "decoder_part2.axmodel",
|
| 46 |
-
"inputs": [
|
| 47 |
-
"decoder_hidden_p1",
|
| 48 |
-
"time_hidden",
|
| 49 |
-
"padding_mask"
|
| 50 |
-
],
|
| 51 |
-
"outputs": [
|
| 52 |
-
"decoder_hidden_p2"
|
| 53 |
-
]
|
| 54 |
-
},
|
| 55 |
-
{
|
| 56 |
-
"name": "fm_decoder_part3",
|
| 57 |
-
"file": "decoder_part3.axmodel",
|
| 58 |
-
"inputs": [
|
| 59 |
-
"decoder_hidden_p2",
|
| 60 |
-
"time_hidden",
|
| 61 |
-
"padding_mask"
|
| 62 |
-
],
|
| 63 |
-
"outputs": [
|
| 64 |
-
"v"
|
| 65 |
-
]
|
| 66 |
-
}
|
| 67 |
]
|
| 68 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
{
|
| 2 |
+
"version": 1,
|
| 3 |
+
"model_type": "zipvoice_distill",
|
| 4 |
+
"encoder": {
|
| 5 |
+
"name": "encoder_core",
|
| 6 |
+
"file": "encoder.axmodel",
|
| 7 |
+
"inputs": [
|
| 8 |
+
"cat_tokens"
|
| 9 |
+
],
|
| 10 |
+
"outputs": [
|
| 11 |
+
"encoded"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
]
|
| 13 |
+
},
|
| 14 |
+
"decoder_parts": [
|
| 15 |
+
{
|
| 16 |
+
"name": "fm_decoder_part0",
|
| 17 |
+
"file": "decoder_part0.axmodel",
|
| 18 |
+
"inputs": [
|
| 19 |
+
"t",
|
| 20 |
+
"x",
|
| 21 |
+
"text_condition",
|
| 22 |
+
"speech_condition",
|
| 23 |
+
"guidance_scale",
|
| 24 |
+
"padding_mask"
|
| 25 |
+
],
|
| 26 |
+
"outputs": [
|
| 27 |
+
"decoder_hidden_p0",
|
| 28 |
+
"time_hidden"
|
| 29 |
+
]
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"name": "fm_decoder_part1",
|
| 33 |
+
"file": "decoder_part1.axmodel",
|
| 34 |
+
"inputs": [
|
| 35 |
+
"decoder_hidden_p0",
|
| 36 |
+
"time_hidden",
|
| 37 |
+
"padding_mask"
|
| 38 |
+
],
|
| 39 |
+
"outputs": [
|
| 40 |
+
"decoder_hidden_p1"
|
| 41 |
+
]
|
| 42 |
+
},
|
| 43 |
+
{
|
| 44 |
+
"name": "fm_decoder_part2",
|
| 45 |
+
"file": "decoder_part2.axmodel",
|
| 46 |
+
"inputs": [
|
| 47 |
+
"decoder_hidden_p1",
|
| 48 |
+
"time_hidden",
|
| 49 |
+
"padding_mask"
|
| 50 |
+
],
|
| 51 |
+
"outputs": [
|
| 52 |
+
"decoder_hidden_p2"
|
| 53 |
+
]
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"name": "fm_decoder_part3_0",
|
| 57 |
+
"file": "decoder_part3_0.axmodel",
|
| 58 |
+
"inputs": [
|
| 59 |
+
"decoder_hidden_p2",
|
| 60 |
+
"time_hidden",
|
| 61 |
+
"padding_mask"
|
| 62 |
+
],
|
| 63 |
+
"outputs": [
|
| 64 |
+
"/fm_decoder/4/0/bypass/Add_output_0"
|
| 65 |
+
]
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"name": "fm_decoder_part3_1",
|
| 69 |
+
"file": "decoder_part3_1.axmodel",
|
| 70 |
+
"inputs": [
|
| 71 |
+
"/fm_decoder/4/0/bypass/Add_output_0",
|
| 72 |
+
"time_hidden",
|
| 73 |
+
"padding_mask"
|
| 74 |
+
],
|
| 75 |
+
"outputs": [
|
| 76 |
+
"/fm_decoder/4/1/bypass/Add_output_0"
|
| 77 |
+
]
|
| 78 |
+
},
|
| 79 |
+
{
|
| 80 |
+
"name": "fm_decoder_part3_2",
|
| 81 |
+
"file": "decoder_part3_2.axmodel",
|
| 82 |
+
"inputs": [
|
| 83 |
+
"/fm_decoder/4/1/bypass/Add_output_0",
|
| 84 |
+
"time_hidden",
|
| 85 |
+
"padding_mask"
|
| 86 |
+
],
|
| 87 |
+
"outputs": [
|
| 88 |
+
"/fm_decoder/4/2/bypass/Add_output_0"
|
| 89 |
+
]
|
| 90 |
+
},
|
| 91 |
+
{
|
| 92 |
+
"name": "fm_decoder_part3_3",
|
| 93 |
+
"file": "decoder_part3_3.axmodel",
|
| 94 |
+
"inputs": [
|
| 95 |
+
"/fm_decoder/4/2/bypass/Add_output_0",
|
| 96 |
+
"time_hidden",
|
| 97 |
+
"padding_mask"
|
| 98 |
+
],
|
| 99 |
+
"outputs": [
|
| 100 |
+
"v"
|
| 101 |
+
]
|
| 102 |
+
}
|
| 103 |
+
]
|
| 104 |
+
}
|
models/zipvoice_distill_ax630C/decoder_part0.axmodel
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2113f394363a575904669017de685b4d8d73693736a285a4ce34782eba1a4531
|
| 3 |
+
size 38600656
|
models/zipvoice_distill_ax630C/decoder_part1.axmodel
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6afd89c302f971b10677f27093bf60e7cb8f81ec191cde5c43781954976142be
|
| 3 |
+
size 32510786
|
models/zipvoice_distill_ax630C/decoder_part2.axmodel
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c883008c38b1ee727b8a68989dd4861d368a1cb593f4548e44fc6200dee89e6d
|
| 3 |
+
size 35030020
|
models/zipvoice_distill_ax630C/decoder_part3_0.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0b4d6a68bbc3286ed14bbe1d7f783bcc8cd559fe709151a99079d6558c1ce7d9
|
| 3 |
+
size 10038370
|
models/zipvoice_distill_ax630C/decoder_part3_1.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:744d3121d74670fec1f29f9a7ce5d158cb215e9b822464ad123fe8daeb8247f1
|
| 3 |
+
size 10038193
|
models/zipvoice_distill_ax630C/decoder_part3_2.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:719c53fb3e4e0949b893c25814f705d33b12cd442fb84434d00dc3867c846ac1
|
| 3 |
+
size 10038201
|
models/zipvoice_distill_ax630C/decoder_part3_3.axmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7c44654f65f1398e611b02d9da2a23e931008ffa9ee6a53ebf027fd0da04bd17
|
| 3 |
+
size 10004459
|
models/zipvoice_distill_ax630C/encoder.axmodel
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7193cbc302f3063522194b8287e1adfc250b83ab67c38c064bba74bef3eacb94
|
| 3 |
+
size 7659503
|
run_ax630c.sh
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# ZipVoice AX630C 板端一键运行
|
| 3 |
+
# 用法: bash run_ax630c.sh [zh|en] [sentence|paragraph]
|
| 4 |
+
|
| 5 |
+
MODEL="${1:-zh}"
|
| 6 |
+
MODE="${2:-sentence}"
|
| 7 |
+
|
| 8 |
+
BIN="cpp/install/ax630c/zipvoice"
|
| 9 |
+
MODEL_DIR="./models/zipvoice_distill_ax630C"
|
| 10 |
+
TOKEN="./resources/zipvoice_hf/zipvoice/tokens.txt"
|
| 11 |
+
VOCODER="./cpp/vocoder/vocos_full_ax630c.axmodel"
|
| 12 |
+
|
| 13 |
+
cd "$(dirname "$0")"
|
| 14 |
+
|
| 15 |
+
if [ "$MODEL" = "en" ]; then
|
| 16 |
+
PWAV="./assets/moss_prompts/en_4_4p5s.wav"
|
| 17 |
+
PTEXT="This is almost twice the current industry production level per train."
|
| 18 |
+
DTEXT="This morning, a small train left the station, carrying sleepy passengers toward a bright coastal town."
|
| 19 |
+
TFILE="./assets/paragraphs/en_scavenger.txt"
|
| 20 |
+
REPO="--repo-dir ."
|
| 21 |
+
else
|
| 22 |
+
PWAV="./assets/moss_prompts/zh_1_4p5s.wav"
|
| 23 |
+
PTEXT="不管怎么样我和汤姆还是要感谢贝尔卡金的援手"
|
| 24 |
+
DTEXT="今天午后天气很好,我打开窗户,听见远处有人聊天,水杯也轻轻晃了一下。"
|
| 25 |
+
TFILE="./assets/paragraphs/zh_ginkgo.txt"
|
| 26 |
+
REPO=""
|
| 27 |
+
fi
|
| 28 |
+
|
| 29 |
+
OUT="output_ax630c_${MODEL}_${MODE}.wav"
|
| 30 |
+
|
| 31 |
+
echo "=========================================="
|
| 32 |
+
echo " ZipVoice AX630C"
|
| 33 |
+
echo "=========================================="
|
| 34 |
+
echo " MODEL: $MODEL_DIR"
|
| 35 |
+
echo " LANG: $MODEL MODE: $MODE"
|
| 36 |
+
echo " OUTPUT: $OUT"
|
| 37 |
+
echo "=========================================="
|
| 38 |
+
|
| 39 |
+
if [ "$MODE" = "paragraph" ]; then
|
| 40 |
+
exec $BIN \
|
| 41 |
+
--model-dir "$MODEL_DIR" --token-file "$TOKEN" \
|
| 42 |
+
--prompt-wav "$PWAV" --prompt-text "$PTEXT" \
|
| 43 |
+
--text-file "$TFILE" \
|
| 44 |
+
--vocoder-model "$VOCODER" --output-wav "$OUT" --seed 42 $REPO
|
| 45 |
+
else
|
| 46 |
+
exec $BIN \
|
| 47 |
+
--model-dir "$MODEL_DIR" --token-file "$TOKEN" \
|
| 48 |
+
--prompt-wav "$PWAV" --prompt-text "$PTEXT" \
|
| 49 |
+
--text "$DTEXT" \
|
| 50 |
+
--vocoder-model "$VOCODER" --output-wav "$OUT" --seed 42 $REPO
|
| 51 |
+
fi
|
scripts/common_infer.py
CHANGED
|
@@ -66,3 +66,74 @@ def vocoder_decode_loaded(
|
|
| 66 |
if prompt_rms < target_rms:
|
| 67 |
wav = wav * prompt_rms / target_rms
|
| 68 |
return wav.squeeze().cpu().numpy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
if prompt_rms < target_rms:
|
| 67 |
wav = wav * prompt_rms / target_rms
|
| 68 |
return wav.squeeze().cpu().numpy()
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def load_axmodel_vocoder(model_path: str):
|
| 72 |
+
"""Load vocoder axmodel for inference."""
|
| 73 |
+
import axengine as axe
|
| 74 |
+
|
| 75 |
+
session = axe.InferenceSession(model_path)
|
| 76 |
+
return session
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def axmodel_vocoder_decode(session, features: np.ndarray, feat_scale: float,
|
| 80 |
+
target_rms: float, prompt_rms: float) -> np.ndarray:
|
| 81 |
+
"""Decode mel features to audio using axmodel vocoder + IRFFT."""
|
| 82 |
+
import math
|
| 83 |
+
|
| 84 |
+
# features shape: (1, T, 100) or (T, 100), squeeze batch dim
|
| 85 |
+
features = np.squeeze(features)
|
| 86 |
+
if features.ndim == 2:
|
| 87 |
+
features = features.T # (T, 100) → (100, T)
|
| 88 |
+
T = features.shape[1]
|
| 89 |
+
n_mels = features.shape[0]
|
| 90 |
+
n_fft, hop = 1024, 256
|
| 91 |
+
T_model = 620
|
| 92 |
+
|
| 93 |
+
# Undo feat_scale and pad to [1, n_mels, T_model]
|
| 94 |
+
inv_scale = 1.0 / feat_scale if feat_scale != 0 else 1.0
|
| 95 |
+
mel_input = np.zeros((1, n_mels, T_model), dtype=np.float32)
|
| 96 |
+
t_actual = min(T, T_model)
|
| 97 |
+
for t in range(t_actual):
|
| 98 |
+
mel_input[0, :, t] = features[:, t] * inv_scale
|
| 99 |
+
|
| 100 |
+
# Run axmodel: mel → (real, imag)
|
| 101 |
+
real, imag = session.run(None, {"mel": mel_input})
|
| 102 |
+
real, imag = real.squeeze(0), imag.squeeze(0) # [T_model, n_freqs]
|
| 103 |
+
|
| 104 |
+
# IRFFT + overlap-add (matching C++ vocoder)
|
| 105 |
+
n_freqs = n_fft // 2 + 1
|
| 106 |
+
window = 0.5 * (1.0 - np.cos(2.0 * math.pi * np.arange(n_fft) / (n_fft - 1)))
|
| 107 |
+
window_sq = window ** 2
|
| 108 |
+
out_len = (T - 1) * hop + n_fft
|
| 109 |
+
audio = np.zeros(out_len, dtype=np.float32)
|
| 110 |
+
envelope = np.zeros(out_len, dtype=np.float32)
|
| 111 |
+
|
| 112 |
+
for t_idx in range(T):
|
| 113 |
+
spec = np.zeros(n_fft, dtype=np.complex64)
|
| 114 |
+
spec[0] = real[t_idx, 0]
|
| 115 |
+
for k in range(1, n_freqs - 1):
|
| 116 |
+
spec[k] = real[t_idx, k] + 1j * imag[t_idx, k]
|
| 117 |
+
spec[n_fft - k] = real[t_idx, k] - 1j * imag[t_idx, k]
|
| 118 |
+
spec[n_freqs - 1] = real[t_idx, n_freqs - 1]
|
| 119 |
+
ifft_out = np.fft.irfft(spec, n=n_fft).real
|
| 120 |
+
|
| 121 |
+
pos = t_idx * hop
|
| 122 |
+
for n in range(n_fft):
|
| 123 |
+
p = pos + n
|
| 124 |
+
if p < out_len:
|
| 125 |
+
audio[p] += ifft_out[n] * window[n]
|
| 126 |
+
envelope[p] += window_sq[n]
|
| 127 |
+
|
| 128 |
+
audio /= np.maximum(envelope, 1e-10)
|
| 129 |
+
pad = n_fft // 2
|
| 130 |
+
audio = audio[pad:out_len - pad].astype(np.float32)
|
| 131 |
+
|
| 132 |
+
# RMS normalize (numpy version)
|
| 133 |
+
rms = np.sqrt(np.mean(audio ** 2))
|
| 134 |
+
if rms < target_rms and rms > 1e-10:
|
| 135 |
+
audio = audio * (target_rms / rms)
|
| 136 |
+
if prompt_rms < target_rms:
|
| 137 |
+
audio = audio * (prompt_rms / target_rms)
|
| 138 |
+
return audio
|
| 139 |
+
|
scripts/zipvoice_decoder4_runtime.py
CHANGED
|
@@ -276,6 +276,7 @@ class Decoder4ZipVoiceBoardRuntime:
|
|
| 276 |
encoded = self.run_encoder(cat_tokens)
|
| 277 |
t_enc = time.perf_counter() - t_start
|
| 278 |
logging.debug(" encoder: %.3f s (output shape=%s)", t_enc, encoded.shape)
|
|
|
|
| 279 |
|
| 280 |
t_start = time.perf_counter()
|
| 281 |
text_condition, features_len = self.duration_expand(
|
|
@@ -287,6 +288,7 @@ class Decoder4ZipVoiceBoardRuntime:
|
|
| 287 |
)
|
| 288 |
t_dur = time.perf_counter() - t_start
|
| 289 |
logging.debug(" duration_expand: %.3f s (features_len=%d)", t_dur, features_len)
|
|
|
|
| 290 |
|
| 291 |
seq_len = self.decoder_seq_len or self.max_feat_len
|
| 292 |
if features_len > seq_len:
|
|
|
|
| 276 |
encoded = self.run_encoder(cat_tokens)
|
| 277 |
t_enc = time.perf_counter() - t_start
|
| 278 |
logging.debug(" encoder: %.3f s (output shape=%s)", t_enc, encoded.shape)
|
| 279 |
+
# DEBUG: dump intermediates
|
| 280 |
|
| 281 |
t_start = time.perf_counter()
|
| 282 |
text_condition, features_len = self.duration_expand(
|
|
|
|
| 288 |
)
|
| 289 |
t_dur = time.perf_counter() - t_start
|
| 290 |
logging.debug(" duration_expand: %.3f s (features_len=%d)", t_dur, features_len)
|
| 291 |
+
# DEBUG: dump
|
| 292 |
|
| 293 |
seq_len = self.decoder_seq_len or self.max_feat_len
|
| 294 |
if features_len > seq_len:
|
scripts/zipvoice_decoder4_runtime_encoder_onnx.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import logging
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Dict, List
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
import onnxruntime as ort
|
| 11 |
+
|
| 12 |
+
from scripts.zipvoice_decoder4_runtime import Decoder4ZipVoiceBoardRuntime
|
| 13 |
+
from scripts.zipvoice_runtime import AxeSession
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class _OrtSession:
|
| 17 |
+
"""ONNX Runtime wrapper with the AxeSession interface used by split runtime."""
|
| 18 |
+
|
| 19 |
+
_TYPE_TO_DTYPE = {
|
| 20 |
+
"tensor(float)": np.float32,
|
| 21 |
+
"tensor(float32)": np.float32,
|
| 22 |
+
"tensor(double)": np.float64,
|
| 23 |
+
"tensor(int32)": np.int32,
|
| 24 |
+
"tensor(int64)": np.int64,
|
| 25 |
+
"tensor(uint8)": np.uint8,
|
| 26 |
+
"tensor(bool)": np.bool_,
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
def __init__(self, model_path: str | Path):
|
| 30 |
+
self.path = Path(model_path)
|
| 31 |
+
if not self.path.exists():
|
| 32 |
+
raise FileNotFoundError(f"ONNX model not found: {self.path}")
|
| 33 |
+
self._session = ort.InferenceSession(
|
| 34 |
+
str(self.path),
|
| 35 |
+
providers=["CPUExecutionProvider"],
|
| 36 |
+
)
|
| 37 |
+
self._inputs = self._session.get_inputs()
|
| 38 |
+
self._outputs = self._session.get_outputs()
|
| 39 |
+
|
| 40 |
+
@property
|
| 41 |
+
def input_names(self) -> List[str]:
|
| 42 |
+
return [item.name for item in self._inputs]
|
| 43 |
+
|
| 44 |
+
@property
|
| 45 |
+
def output_names(self) -> List[str]:
|
| 46 |
+
return [item.name for item in self._outputs]
|
| 47 |
+
|
| 48 |
+
def _coerce_one(self, name: str, value: np.ndarray) -> np.ndarray:
|
| 49 |
+
info = next((item for item in self._inputs if item.name == name), None)
|
| 50 |
+
array = np.asarray(value)
|
| 51 |
+
if info is None:
|
| 52 |
+
return np.ascontiguousarray(array)
|
| 53 |
+
|
| 54 |
+
dtype = self._TYPE_TO_DTYPE.get(str(info.type).lower())
|
| 55 |
+
if dtype is not None:
|
| 56 |
+
array = array.astype(dtype, copy=False)
|
| 57 |
+
|
| 58 |
+
if list(info.shape) == [] and array.shape == (1,):
|
| 59 |
+
array = array.reshape(())
|
| 60 |
+
|
| 61 |
+
return np.ascontiguousarray(array)
|
| 62 |
+
|
| 63 |
+
def run(self, feed_dict: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]:
|
| 64 |
+
feed = {name: self._coerce_one(name, value) for name, value in feed_dict.items()}
|
| 65 |
+
outputs = self._session.run(None, feed)
|
| 66 |
+
return {name: value for name, value in zip(self.output_names, outputs)}
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class Decoder4ZipVoiceBoardRuntimeEncoderOnnx(Decoder4ZipVoiceBoardRuntime):
|
| 70 |
+
"""Runs encoder with ONNX Runtime, all decoder parts with axmodel."""
|
| 71 |
+
|
| 72 |
+
def _load_models(self) -> None:
|
| 73 |
+
self.sessions = {}
|
| 74 |
+
|
| 75 |
+
encoder_name = self.encoder_info["name"]
|
| 76 |
+
encoder_path = self.models_dir / "encoder_core.onnx"
|
| 77 |
+
logging.info("encoder 使用 ONNX Runtime: %s", encoder_path)
|
| 78 |
+
self.sessions[encoder_name] = _OrtSession(encoder_path)
|
| 79 |
+
|
| 80 |
+
for info in self.decoder_parts:
|
| 81 |
+
name = info["name"]
|
| 82 |
+
path = self.models_dir / info["file"]
|
| 83 |
+
logging.debug("Loading %s from %s", name, path)
|
| 84 |
+
self.sessions[name] = AxeSession(path)
|
| 85 |
+
|
| 86 |
+
self.decoder_label = "decoder4(encoder_onnx+part0-3_axmodel)"
|
| 87 |
+
logging.debug("Loaded encoder ONNX + %d decoder axmodels", len(self.decoder_parts))
|
scripts/zipvoice_decoder4_runtime_part0_onnx.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import logging
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Dict, List
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
import onnxruntime as ort
|
| 11 |
+
|
| 12 |
+
from scripts.zipvoice_decoder4_runtime import Decoder4ZipVoiceBoardRuntime
|
| 13 |
+
from scripts.zipvoice_runtime import AxeSession
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class _OrtSession:
|
| 17 |
+
"""ONNX Runtime wrapper with the small AxeSession interface used by the board runtime."""
|
| 18 |
+
|
| 19 |
+
_TYPE_TO_DTYPE = {
|
| 20 |
+
"tensor(float)": np.float32,
|
| 21 |
+
"tensor(float32)": np.float32,
|
| 22 |
+
"tensor(double)": np.float64,
|
| 23 |
+
"tensor(int32)": np.int32,
|
| 24 |
+
"tensor(int64)": np.int64,
|
| 25 |
+
"tensor(uint8)": np.uint8,
|
| 26 |
+
"tensor(bool)": np.bool_,
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
def __init__(self, model_path: str | Path):
|
| 30 |
+
self.path = Path(model_path)
|
| 31 |
+
if not self.path.exists():
|
| 32 |
+
raise FileNotFoundError(f"ONNX model not found: {self.path}")
|
| 33 |
+
self._session = ort.InferenceSession(
|
| 34 |
+
str(self.path),
|
| 35 |
+
providers=["CPUExecutionProvider"],
|
| 36 |
+
)
|
| 37 |
+
self._inputs = self._session.get_inputs()
|
| 38 |
+
self._outputs = self._session.get_outputs()
|
| 39 |
+
|
| 40 |
+
@property
|
| 41 |
+
def input_names(self) -> List[str]:
|
| 42 |
+
return [item.name for item in self._inputs]
|
| 43 |
+
|
| 44 |
+
@property
|
| 45 |
+
def output_names(self) -> List[str]:
|
| 46 |
+
return [item.name for item in self._outputs]
|
| 47 |
+
|
| 48 |
+
def _coerce_one(self, name: str, value: np.ndarray) -> np.ndarray:
|
| 49 |
+
info = next((item for item in self._inputs if item.name == name), None)
|
| 50 |
+
array = np.asarray(value)
|
| 51 |
+
if info is None:
|
| 52 |
+
return np.ascontiguousarray(array)
|
| 53 |
+
|
| 54 |
+
dtype = self._TYPE_TO_DTYPE.get(str(info.type).lower())
|
| 55 |
+
if dtype is not None:
|
| 56 |
+
array = array.astype(dtype, copy=False)
|
| 57 |
+
|
| 58 |
+
# The exported part0 ONNX keeps t/guidance_scale as scalar inputs ([]),
|
| 59 |
+
# while the axmodel path feeds them as shape [1]. Normalize only for ONNX.
|
| 60 |
+
if list(info.shape) == [] and array.shape == (1,):
|
| 61 |
+
array = array.reshape(())
|
| 62 |
+
|
| 63 |
+
return np.ascontiguousarray(array)
|
| 64 |
+
|
| 65 |
+
def run(self, feed_dict: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]:
|
| 66 |
+
feed = {name: self._coerce_one(name, value) for name, value in feed_dict.items()}
|
| 67 |
+
outputs = self._session.run(None, feed)
|
| 68 |
+
return {name: value for name, value in zip(self.output_names, outputs)}
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class Decoder4ZipVoiceBoardRuntimePart0Onnx(Decoder4ZipVoiceBoardRuntime):
|
| 72 |
+
"""Runs decoder part0 with ONNX Runtime, all other split models with axmodel."""
|
| 73 |
+
|
| 74 |
+
def _load_models(self) -> None:
|
| 75 |
+
self.sessions = {}
|
| 76 |
+
|
| 77 |
+
encoder_name = self.encoder_info["name"]
|
| 78 |
+
encoder_path = self.models_dir / self.encoder_info["file"]
|
| 79 |
+
logging.debug("Loading %s from %s", encoder_name, encoder_path)
|
| 80 |
+
self.sessions[encoder_name] = AxeSession(encoder_path)
|
| 81 |
+
|
| 82 |
+
for index, info in enumerate(self.decoder_parts):
|
| 83 |
+
name = info["name"]
|
| 84 |
+
if index == 0:
|
| 85 |
+
path = self.models_dir / "fm_decoder_part0.onnx"
|
| 86 |
+
logging.info("part0 使用 ONNX Runtime: %s", path)
|
| 87 |
+
self.sessions[name] = _OrtSession(path)
|
| 88 |
+
else:
|
| 89 |
+
path = self.models_dir / info["file"]
|
| 90 |
+
logging.debug("Loading %s from %s", name, path)
|
| 91 |
+
self.sessions[name] = AxeSession(path)
|
| 92 |
+
|
| 93 |
+
self.decoder_label = "decoder4(part0_onnx+part1-3_axmodel)"
|
| 94 |
+
logging.debug("Loaded encoder axmodel + part0 ONNX + %d decoder axmodels", len(self.decoder_parts) - 1)
|
scripts/zipvoice_decoder4_runtime_part3_onnx.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import logging
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Dict, List
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
import onnxruntime as ort
|
| 11 |
+
|
| 12 |
+
from scripts.zipvoice_decoder4_runtime import Decoder4ZipVoiceBoardRuntime
|
| 13 |
+
from scripts.zipvoice_runtime import AxeSession
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class _OrtSession:
|
| 17 |
+
"""ONNX Runtime wrapper with the AxeSession interface used by split runtime."""
|
| 18 |
+
|
| 19 |
+
_TYPE_TO_DTYPE = {
|
| 20 |
+
"tensor(float)": np.float32,
|
| 21 |
+
"tensor(float32)": np.float32,
|
| 22 |
+
"tensor(double)": np.float64,
|
| 23 |
+
"tensor(int32)": np.int32,
|
| 24 |
+
"tensor(int64)": np.int64,
|
| 25 |
+
"tensor(uint8)": np.uint8,
|
| 26 |
+
"tensor(bool)": np.bool_,
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
def __init__(self, model_path: str | Path):
|
| 30 |
+
self.path = Path(model_path)
|
| 31 |
+
if not self.path.exists():
|
| 32 |
+
raise FileNotFoundError(f"ONNX model not found: {self.path}")
|
| 33 |
+
self._session = ort.InferenceSession(
|
| 34 |
+
str(self.path),
|
| 35 |
+
providers=["CPUExecutionProvider"],
|
| 36 |
+
)
|
| 37 |
+
self._inputs = self._session.get_inputs()
|
| 38 |
+
self._outputs = self._session.get_outputs()
|
| 39 |
+
|
| 40 |
+
@property
|
| 41 |
+
def input_names(self) -> List[str]:
|
| 42 |
+
return [item.name for item in self._inputs]
|
| 43 |
+
|
| 44 |
+
@property
|
| 45 |
+
def output_names(self) -> List[str]:
|
| 46 |
+
return [item.name for item in self._outputs]
|
| 47 |
+
|
| 48 |
+
def _coerce_one(self, name: str, value: np.ndarray) -> np.ndarray:
|
| 49 |
+
info = next((item for item in self._inputs if item.name == name), None)
|
| 50 |
+
array = np.asarray(value)
|
| 51 |
+
if info is None:
|
| 52 |
+
return np.ascontiguousarray(array)
|
| 53 |
+
|
| 54 |
+
dtype = self._TYPE_TO_DTYPE.get(str(info.type).lower())
|
| 55 |
+
if dtype is not None:
|
| 56 |
+
array = array.astype(dtype, copy=False)
|
| 57 |
+
|
| 58 |
+
if list(info.shape) == [] and array.shape == (1,):
|
| 59 |
+
array = array.reshape(())
|
| 60 |
+
|
| 61 |
+
return np.ascontiguousarray(array)
|
| 62 |
+
|
| 63 |
+
def run(self, feed_dict: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]:
|
| 64 |
+
feed = {name: self._coerce_one(name, value) for name, value in feed_dict.items()}
|
| 65 |
+
outputs = self._session.run(None, feed)
|
| 66 |
+
return {name: value for name, value in zip(self.output_names, outputs)}
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class Decoder4ZipVoiceBoardRuntimePart3Onnx(Decoder4ZipVoiceBoardRuntime):
|
| 70 |
+
"""Runs decoder part3 with ONNX Runtime, encoder and part0-2 with axmodel."""
|
| 71 |
+
|
| 72 |
+
def _load_models(self) -> None:
|
| 73 |
+
self.sessions = {}
|
| 74 |
+
|
| 75 |
+
encoder_name = self.encoder_info["name"]
|
| 76 |
+
encoder_path = self.models_dir / self.encoder_info["file"]
|
| 77 |
+
logging.debug("Loading %s from %s", encoder_name, encoder_path)
|
| 78 |
+
self.sessions[encoder_name] = AxeSession(encoder_path)
|
| 79 |
+
|
| 80 |
+
last_index = len(self.decoder_parts) - 1
|
| 81 |
+
for index, info in enumerate(self.decoder_parts):
|
| 82 |
+
name = info["name"]
|
| 83 |
+
if index == last_index:
|
| 84 |
+
path = self.models_dir / "fm_decoder_part3.onnx"
|
| 85 |
+
logging.info("part3 使用 ONNX Runtime: %s", path)
|
| 86 |
+
self.sessions[name] = _OrtSession(path)
|
| 87 |
+
else:
|
| 88 |
+
path = self.models_dir / info["file"]
|
| 89 |
+
logging.debug("Loading %s from %s", name, path)
|
| 90 |
+
self.sessions[name] = AxeSession(path)
|
| 91 |
+
|
| 92 |
+
self.decoder_label = "decoder4(part0-2_axmodel+part3_onnx)"
|
| 93 |
+
logging.debug("Loaded encoder axmodel + %d decoder axmodels + part3 ONNX", last_index)
|