语音合成组件
简介
ACOMP XTTS (Text-to-Speech) 是 ARCS SDK 的语音合成组件,提供基于文本输入的语音合成功能。该组件支持中英文语音合成,支持多种发音人角色切换,并提供语速、音量等参数配置接口,合成后的 PCM 音频通过 R2M 流通道输出。
主要特性
中英文语音合成:支持中文和英文文本的语音合成
多发音人支持:支持多种发音人角色切换(如凌小琪、Lucy 等)
语速控制:支持语音播报速度调节
音量控制:支持语音播报音量调节
事件回调机制:支持合成状态通知和音频流更新事件
PCM 音频输出:合成的 PCM 音频通过 R2M 流通道输出,应用层可直接获取播放
快捷 API:提供
acomp_xtts_do_prepare()/acomp_xtts_do_cleanup()快捷接口,简化资源管理角色热切换:支持通过
acomp_xtts_do_prepare_with_role()在不同发音人之间切换,相同角色复用已有实例跨核通信:基于 IPC 机制实现文本和音频数据的跨核传输
配置选项
基本配置
配置项 |
说明 |
默认值 |
|---|---|---|
|
启用 ACOMP 组件 |
n |
|
启用 ACOMP 组件的 XTTS 组件 |
n |
资源配置
XTTS 组件的资源分为 Flash 资源和 eMMC 资源两部分:
eMMC 资源:
配置项 |
说明 |
默认值 |
|---|---|---|
|
中文字典 eMMC 地址 |
0x5f00000 |
|
中文字典大小(字节) |
3166858 |
|
英文字典 eMMC 地址 |
0x6400000 |
|
英文字典大小(字节) |
2470275 |
|
CRF 模型 eMMC 地址 |
0x6900000 |
|
CRF 模型大小(字节) |
7791842 |
|
前端用户字典 eMMC 地址 |
0x360c000 |
|
前端用户字典大小(字节) |
3082688 |
|
后端模型 eMMC 地址 |
0x7d00000 |
|
后端模型大小(字节) |
4273824 |
|
后端 NHV 模型 eMMC 地址 |
0x8200000 |
|
后端 NHV 模型大小(字节) |
284992 |
Flash 资源 (通过 resmgr):
配置项 |
说明 |
默认值 |
|---|---|---|
|
角色资源在 Flash resmgr 中的 ID |
5 |
|
前端资源在 Flash resmgr 中的 ID |
6 |
流通道配置:
配置项 |
说明 |
默认值 |
|---|---|---|
|
流缓冲区大小 |
8192 |
|
流描述符数量 |
32 |
快速开始
方式一:使用快捷 API(推荐)
快捷 API 封装了完整的初始化、资源加载、流通道创建和启动流程:
#include "xtts/acomp_xtts.h"
void xtts_event_handler(uint32_t event, void *event_data,
uint32_t event_data_len, void *priv)
{
if (event & XTTS_CB_EVENT_STATUS) {
uint32_t status = *(uint32_t *)event_data;
printf("XTTS status: %u\n", status);
} else if (event & XTTS_CB_EVENT_STREAM_UPDATE) {
// 有新的 PCM 数据可读
printf("XTTS stream update\n");
}
}
void xtts_quick_start(void)
{
// 初始化 ACOMP 框架
acomp_init();
// 一键准备并启动 XTTS(使用默认角色凌小琪)
int ret = acomp_xtts_do_prepare(xtts_event_handler, NULL);
if (ret != 0) {
printf("XTTS prepare failed: %d\n", ret);
return;
}
// 合成文本
const char *text = "你好,世界";
acomp_xtts_synth_text(text, strlen(text));
// 读取合成的 PCM 音频
uint32_t len;
uint16_t desc_idx;
void *buffer = acomp_xtts_stream_rx_buffer_get(0, &len, &desc_idx);
if (buffer && len > 0) {
// 播放 PCM 音频数据
// play_audio(buffer, len);
acomp_xtts_stream_rx_buffer_release(0, desc_idx, len, buffer);
}
// 清理
acomp_xtts_do_cleanup();
}
方式一(扩展):使用指定角色
// 使用 Lucy 英文角色
int ret = acomp_xtts_do_prepare_with_role(2, xtts_event_handler, NULL);
// 合成英文
const char *text = "Hello, world";
acomp_xtts_synth_text(text, strlen(text));
方式二:使用标准 API
1. 启用组件
在项目的 prj.conf 文件中添加:
# 启用 ACOMP 组件
CONFIG_ACOMP=y
# 启用 ACOMP 组件的 XTTS 组件
CONFIG_ACOMP_XTTS=y
2. 初始化 XTTS 组件
#include "acomp.h"
#include "xtts/acomp_xtts.h"
int main(int argc, char **argv)
{
// 初始化 ACOMP 框架
acomp_init();
// 初始化 XTTS 组件
int ret = acomp_xtts_init();
if (ret != ACOMP_ERR_OK) {
printf("XTTS init failed: %d\n", ret);
return -1;
}
return 0;
}
3. 注册事件回调
#include "xtts/acomp_xtts.h"
void xtts_event_handler(uint32_t event, void *event_data,
uint32_t event_data_len, void *priv)
{
if (event & XTTS_CB_EVENT_STATUS) {
uint32_t status = *(uint32_t *)event_data;
printf("XTTS status: %u\n", status);
} else if (event & XTTS_CB_EVENT_STREAM_UPDATE) {
printf("XTTS stream update\n");
}
}
// 注册回调
int ret = acomp_xtts_add_callback(
XTTS_CB_EVENT_STATUS | XTTS_CB_EVENT_STREAM_UPDATE,
xtts_event_handler,
NULL
);
4. 准备资源并启动
void start_xtts_service(acomp_ipc_prepare_t *prepare)
{
int ret;
// 1. 就绪组件(传入资源配置)
ret = acomp_xtts_prepare(prepare);
if (ret != ACOMP_ERR_OK) {
printf("XTTS prepare failed: %d\n", ret);
return;
}
// 2. 创建 R2M 流通道(必须在 start 之前)
acomp_stream_chn_create_desc_t desc = {
.cname = "stream.xtts_pcm",
.direction = ACOMP_STREAM_DIRECTION_R2M, // Remote to Master
.index = 0,
.buffer_size = CONFIG_ACOMP_XTTS_STREAM_BUFFER_SIZE,
.num_descs = CONFIG_ACOMP_XTTS_STREAM_NUM_DESCS,
.kick_policy = 0,
};
acomp_xtts_stream_ch_enable(0, &desc);
// 3. 启动 XTTS
ret = acomp_xtts_start();
if (ret != ACOMP_ERR_OK) {
printf("XTTS start failed: %d\n", ret);
return;
}
printf("XTTS service started\n");
}
5. 合成文本并接收音频
void synth_and_play(const char *text)
{
// 1. 提交合成文本
int ret = acomp_xtts_synth_text(text, strlen(text));
if (ret != ACOMP_ERR_OK) {
printf("Synth text failed: %d\n", ret);
return;
}
// 2. 在 XTTS_CB_EVENT_STREAM_UPDATE 回调中读取 PCM 数据
uint32_t len;
uint16_t desc_idx;
void *buffer = acomp_xtts_stream_rx_buffer_get(0, &len, &desc_idx);
if (buffer && len > 0) {
// 播放 PCM 音频
// play_audio(buffer, len);
// 释放缓冲区
acomp_xtts_stream_rx_buffer_release(0, desc_idx, len, buffer);
}
}
API 参考
生命周期管理
acomp_xtts_init
int acomp_xtts_init(void);
功能:初始化 XTTS 组件
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_NO_MEM:内存不足ACOMP_ERR_INVALID_STATE:组件已初始化ACOMP_ERR_NOT_FOUND:设备未找到
acomp_xtts_prepare
int acomp_xtts_prepare(acomp_ipc_prepare_t *prepare);
功能:就绪 XTTS 组件,将资源配置发送给 AP 端
参数:
prepare:资源配置数据结构指针
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_ARG:参数错误
acomp_xtts_start
int acomp_xtts_start(void);
功能:启动 XTTS 组件
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态
acomp_xtts_stop
int acomp_xtts_stop(void);
功能:停止 XTTS 组件
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态
acomp_xtts_cleanup
int acomp_xtts_cleanup(void);
功能:清理 XTTS 组件资源,注销 IPC 回调并释放流对象
返回值:
ACOMP_ERR_OK:成功
快捷 API
acomp_xtts_do_prepare
int acomp_xtts_do_prepare(xtts_event_cb_t event_cb, void *cb_priv);
功能:一键准备并启动 XTTS 组件(使用默认中文角色凌小琪)
说明:封装了 init、回调注册、资源加载(Flash + eMMC)、流通道创建和 start 的完整流程。资源地址来自 Kconfig 配置。不会清理其他算法组件,调用方需自行协调。
参数:
event_cb:事件回调函数(首次调用时注册,可为 NULL)cb_priv:回调函数的私有数据指针
返回值:
0:成功负值:错误
acomp_xtts_do_prepare_with_role
int acomp_xtts_do_prepare_with_role(int role, xtts_event_cb_t event_cb, void *cb_priv);
功能:使用指定角色准备并启动 XTTS 组件
说明:如果请求的角色与当前已准备的角色相同,则复用现有实例;否则先停止、清理再重新准备。
参数:
role:角色标识event_cb:事件回调函数(首次调用时注册,可为 NULL)cb_priv:回调函数的私有数据指针
返回值:
0:成功负值:错误
acomp_xtts_do_cleanup
int acomp_xtts_do_cleanup(void);
功能:一键停止并清理 XTTS 组件
返回值:
0:成功
合成控制
acomp_xtts_synth_text
int acomp_xtts_synth_text(const char *text, uint32_t len);
功能:提交文本进行语音合成
参数:
text:待合成的文本字符串len:文本长度(字节)
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_ARG:text 为 NULL 或 len 为 0ACOMP_ERR_NO_MEM:内存不足ACOMP_ERR_INVALID_STATE:无效状态
参数配置
acomp_xtts_set_speed
int acomp_xtts_set_speed(int speed);
功能:设置语音合成速度
参数:
speed:语速值
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态
acomp_xtts_set_volume
int acomp_xtts_set_volume(int volume);
功能:设置语音合成音量
参数:
volume:音量值
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态
acomp_xtts_set_role
int acomp_xtts_set_role(int role);
功能:设置发音人角色
参数:
role:角色标识
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态
事件回调
acomp_xtts_add_callback
int acomp_xtts_add_callback(uint32_t events, xtts_event_cb_t cb, void *priv);
功能:添加事件回调函数
参数:
events:事件位掩码,可同时注册多个事件XTTS_CB_EVENT_STATUS:合成状态通知XTTS_CB_EVENT_STREAM_UPDATE:音频流数据更新(有新的 PCM 数据可读)
cb:回调函数指针priv:回调函数的私有数据指针
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态
acomp_xtts_remove_callback
int acomp_xtts_remove_callback(xtts_event_cb_t cb);
功能:移除回调函数
参数:
cb:待移除的回调函数
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态
音频流管理
acomp_xtts_stream_ch_enable
int acomp_xtts_stream_ch_enable(int chn, acomp_stream_chn_create_desc_t *desc);
功能:使能音频流通道,用于接收合成的 PCM 音频
说明:流通道必须在 acomp_xtts_start() 之前创建,否则 AP 核的 PCM 输出将被丢弃
参数:
chn:通道索引desc:通道描述符指针
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态ACOMP_ERR_INVALID_ARG:参数错误ACOMP_ERR_CREATE_STREAM_FAILED:创建流失败
acomp_xtts_stream_ch_disable
int acomp_xtts_stream_ch_disable(int chn);
功能:禁用音频流通道
参数:
chn:通道索引
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态ACOMP_ERR_INVALID_ARG:参数错误
acomp_xtts_stream_rx_buffer_get
void *acomp_xtts_stream_rx_buffer_get(int chn, uint32_t *len, uint16_t *desc_idx);
功能:获取 RX 流缓冲区中的 PCM 音频数据
参数:
chn:通道索引len:数据长度指针(输出)desc_idx:描述符索引指针(输出)
返回值:缓冲区指针,失败返回 NULL
acomp_xtts_stream_rx_buffer_release
int acomp_xtts_stream_rx_buffer_release(int chn, uint16_t desc_idx, uint32_t len, void *buffer);
功能:释放 RX 流缓冲区
参数:
chn:通道索引desc_idx:描述符索引len:数据长度buffer:缓冲区指针
返回值:
ACOMP_ERR_OK:成功ACOMP_ERR_INVALID_STATE:无效状态ACOMP_ERR_INVALID_ARG:参数错误
使用示例
完整示例
#include "acomp.h"
#include "xtts/acomp_xtts.h"
#include "lisa_log.h"
#define TAG "xtts_demo"
void xtts_event_handler(uint32_t event, void *event_data,
uint32_t event_data_len, void *priv)
{
if (event & XTTS_CB_EVENT_STATUS) {
uint32_t status = *(uint32_t *)event_data;
LISA_LOGI(TAG, "XTTS status: %u", status);
} else if (event & XTTS_CB_EVENT_STREAM_UPDATE) {
// 有新的 PCM 数据,在此处读取并播放
uint32_t len;
uint16_t desc_idx;
void *buffer = acomp_xtts_stream_rx_buffer_get(0, &len, &desc_idx);
if (buffer && len > 0) {
LISA_LOGI(TAG, "PCM data received, len: %u", len);
// play_audio(buffer, len);
acomp_xtts_stream_rx_buffer_release(0, desc_idx, len, buffer);
}
}
}
void xtts_demo(void)
{
int ret;
// 1. 初始化 ACOMP 框架
acomp_init();
// 2. 一键准备并启动 XTTS
ret = acomp_xtts_do_prepare(xtts_event_handler, NULL);
if (ret != 0) {
LISA_LOGE(TAG, "XTTS prepare failed: %d", ret);
return;
}
LISA_LOGI(TAG, "XTTS started");
// 3. 设置语音参数
acomp_xtts_set_speed(100);
acomp_xtts_set_volume(100);
// 4. 合成中文文本
const char *text = "你好,欢迎使用语音合成功能";
ret = acomp_xtts_synth_text(text, strlen(text));
if (ret != ACOMP_ERR_OK) {
LISA_LOGE(TAG, "Synth failed: %d", ret);
}
// 5. 等待合成完成
vTaskDelay(pdMS_TO_TICKS(5000));
// 6. 清理
acomp_xtts_do_cleanup();
LISA_LOGI(TAG, "XTTS cleaned up");
}
角色切换示例
void xtts_role_switch_demo(void)
{
acomp_init();
// 使用凌小琪中文角色
acomp_xtts_do_prepare_with_role(1, xtts_event_handler, NULL);
acomp_xtts_synth_text("你好", 6);
vTaskDelay(pdMS_TO_TICKS(3000));
// 切换到 Lucy 英文角色(会自动清理并重新准备)
acomp_xtts_do_prepare_with_role(2, xtts_event_handler, NULL);
acomp_xtts_synth_text("Hello", 5);
vTaskDelay(pdMS_TO_TICKS(3000));
// 再次使用同一角色(复用现有实例,无需重新准备)
acomp_xtts_do_prepare_with_role(2, xtts_event_handler, NULL);
acomp_xtts_synth_text("World", 5);
vTaskDelay(pdMS_TO_TICKS(3000));
acomp_xtts_do_cleanup();
}
注意事项
初始化顺序:必须先调用
acomp_init()初始化 ACOMP 框架流通道时序:R2M 流通道必须在
acomp_xtts_start()之前创建,否则 AP 核的 PCM 数据将被丢弃快捷 API 推荐:推荐使用
acomp_xtts_do_prepare()系列快捷 API,自动处理资源加载和流通道创建角色切换:
acomp_xtts_do_prepare_with_role()会自动判断是否需要重新准备,相同角色复用现有实例算法协调:快捷 API 不会清理其他算法组件(如 CV、Translation),调用方需自行管理算法生命周期
事件回调:回调函数在组件内部线程中执行,应避免长时间阻塞操作
PCM 缓冲区:使用 RX 流接口时,必须成对调用
rx_buffer_get/rx_buffer_release资源配置:确保 Flash 和 eMMC 中的模型资源已正确烧录,地址和大小与 Kconfig 配置匹配
跨核通信:音频流基于 IPC 机制,注意跨核数据传输的延迟和同步问题
常见问题
Q: XTTS 组件初始化失败怎么办?
A: 检查以下几点:
确保 ACOMP 框架已正确初始化
检查系统是否有足够的堆内存
确认 AP 核固件已正确烧录
Q: 合成后没有收到 PCM 音频数据?
A: 检查:
确认 R2M 流通道在 start 之前已创建
确认
XTTS_CB_EVENT_STREAM_UPDATE事件回调已注册检查合成文本是否为空
确认资源(字典、模型等)已正确加载
Q: 如何切换发音人?
A: 推荐使用 acomp_xtts_do_prepare_with_role() 接口,传入不同的 role 值。如果新角色与当前不同,组件会自动执行清理和重新准备。
Q: 合成速度很慢怎么办?
A: 建议:
减少每次合成的文本长度
确保 AP 核没有被其他算法任务占用
检查 eMMC 读取速度是否正常
依赖项
ACOMP 框架:算法组件框架ACOMP Stream IPC:音频流跨核通信resmgr:Flash 资源管理器lisa_log:日志系统lisa_mem:内存管理FreeRTOS:实时操作系统XTTS 算法库:语音合成算法引擎