HY-2012 commited on
Commit
e405f21
·
verified ·
1 Parent(s): e2e7fcf

Upload the AX630C inference workflow.

Browse files
.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 (耗时/RTF) | C++ AX650 (耗时/RTF) | C++ AXCL (耗时/RTF) |
22
- |------|---------|------------------------|---------------------|---------------------|
23
- | 中文句子 | 6.41s | 1.992s / 0.311 | 1.057s / 0.165 | 0.918s / **0.143** |
24
- | 中文段落 | 44.97s | 13.457s / 0.301 | 7.357s / 0.164 | 6.443s / **0.143** |
25
- | 英文句子 | 6.41s | 2.045s / 0.319 | 1.067s / 0.166 | 0.919s / **0.143** |
26
- | 英文段落 | 59.16s | 19.715s / 0.305 | 10.529s / 0.178 | 9.207s / **0.156** |
27
 
28
  ### 普通模型 (num_step=10)
29
 
@@ -53,7 +53,7 @@ ZipVoice Distill 是 ZipVoice 的蒸馏版本,主要优势是在较小性能
53
  ## 目录结构
54
 
55
  ```text
56
- ZipVoice.AXERA_AXCL/
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 # AXERA 板端一键运行
71
- ├── run_axcl.sh # AXCL 算力卡一键运行
 
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`:选择模型目录。可选 `zipvoice_ax650``zipvoice_distill_ax650``zipvoice_distill_ax630C`。
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
- | 场景 | 音频时长 | Python AX650 (耗时/RTF) | C++ AX650 (耗时/RTF) | C++ AXCL (耗时/RTF) |
8
- |------|---------|------------------------|---------------------|---------------------|
9
- | 中文句子 | 6.41s | 1.992s / 0.311 | 1.057s / 0.165 | 0.918s / **0.143** |
10
- | 中文段落 | 44.97s | 13.457s / 0.301 | 7.357s / 0.164 | 6.443s / **0.143** |
11
- | 英文句子 | 6.41s | 2.045s / 0.319 | 1.067s / 0.166 | 0.919s / **0.143** |
12
- | 英文段落 | 59.16s | 19.715s / 0.305 | 10.529s / 0.178 | 9.207s / **0.156** |
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 ${CMAKE_SOURCE_DIR}/ax620e_bsp_sdk/msp/out/arm64_glibc)
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 vocos_full.axmodel
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 vocos_full.axmodel on NPU
81
- int input_idx = m_session->GetInputIndex("mel");
82
- if (input_idx < 0) input_idx = 0;
83
- m_session->SetInput(mel_padded.data(), input_idx);
84
-
85
- if (m_session->RunSync() != 0) {
86
- printf("Vocoder NPU inference failed\n");
87
- return -1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- const char* axclConfig = nullptr; // AXCL only: path to axcl.json. Ignored on AXERA.
 
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, 1.0f);
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
- vocoder = load_vocoder(repo_dir)
 
 
 
 
 
 
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
- audio = vocoder_decode_loaded(
187
- vocoder,
188
- pred_features,
189
- feat_scale=args.feat_scale,
190
- target_rms=args.target_rms,
191
- prompt_rms=prompt_rms,
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
- "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",
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:1f0bd9974d94f349c0001d624d3c2c7140295abbe9c12c0db3556d5759d845a8
3
- size 42595736
 
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:cfe296e53b7b8f3842aea1bbc60e48f946c0b5f0ae57c639faa4d31d75ff9c5e
3
- size 31881170
 
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:e95d65c96ba38c7a3cee6ecc558567a4d79e1803ba427749442690afc3b99da9
3
- size 35101628
 
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:ffbd78ded4a05cf472f78a66e34d9c4dac4c5525f6a7b1d2cc5dbb4d96e7538f
3
- size 8394155
 
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)