1. 在CB5654上实现语音交互
本文介绍如何在CB5654开发板上,使用相关组件开发语音交互程序,在编写前需要安装CDK
2. 使用CDK创建工程
访问 平头哥芯片开放平台,下载CDK
并安装
新建工作空间
新建空白工程 CDK菜单栏->Proiect->New project->
目录介绍
目录/文件 | 说明 |
---|---|
boards | 包含Flash分区信息、编译烧写脚本 |
include/yoc_config.h | 默认包含了YoC自带的可配置项 |
include/app_init.h | 应用程序相关配置及函数声明 |
src/init/cli_cmd.c | 初始化YoC支持的命令行 |
src/app_init.c | YoC系统的初始化,包含外设及基础服务 |
src/app_main.c | Solution的主文件 |
src/sys_event_handler.c | 系统异常处理 |
- 添加组件
右键工程下的
Components
->Components Runtime Management
,选择auicloud_sc5654
组件
3. 终端功能开发
终端需要增加网络功能
,音频服务
、麦克风服务
、aui_cloud
、云端数据解析
3.1 开启网络功能
在src/
下中增加app_net.c
,编写wifi相关代码
注册并启动网卡
在src/app_net.c
中增加网络初始化功能,根据硬件选择具体网卡并注册,启动netmgr功能
int yoc_network_init(void)
{
rtl8723ds_gpio_pin pin = {
.wl_en = PIN_WIFI_EN, //wifi芯片使能脚,管脚定义见 board_config.h
.power = PIN_WIFI_POWER_EN, //wifi芯片电源脚,管脚定义见 board_config.h
};
wifi_rtl8723ds_register(&pin); //注册rtl8723Wi-Fi驱动
netmgr_hdl_t netmgr_hdl = netmgr_dev_wifi_init(); //初始化wifi设备
netmgr_service_init(NULL); //初始化网络管理服务
if (netmgr_hdl)
netmgr_start(netmgr_hdl); //启动网络管理服务
net_event_subscribe(); //订阅网络事件
return 0;
}
网络事件订阅及处理
static void net_event_subscribe(void)
{
/* 系统事件订阅 */
event_subscribe(EVENT_NETMGR_GOT_IP, user_local_event_cb, NULL); //网络连接成功事件
event_subscribe(EVENT_NETMGR_NET_DISCON, user_local_event_cb, NULL); //网络断开事件
}
static void user_local_event_cb(uint32_t event_id, const void *param, void *context)
{
switch (event_id) {
case EVENT_NETMGR_GOT_IP: {
LOGD(TAG, "Net Got ip");
ntp_sync_time(NULL); //网络连接成功后需要进行对时,与云端连接时须使用
g_net_state = 1; //标记连接成功
} break;
case EVENT_NETMGR_NET_DISCON: {
LOGD(TAG, "Net down");
g_net_state = 0; //标记连接失败
} break;
default:
break;
}
}
wifi驱动使用
在src/app_main.c中:
void main()
{
...
yoc_network_init();;
...
}
测试网卡
通过cli的kv
命令设置wifi的ssid
和psk
,设置后重启终端,并连接成功
在src/app_init.c中的console 命令行增加网络相关命令
void cli_init(utask_t *task)
{
...
cli_reg_cmd_ping();
cli_reg_cmd_ifconfig_wifi();
cli_reg_cmd_ntp();
...
}
ifconfig
使用ifconfig命令查询网络连接状态ping
确认连接网络后,使用ping
命令测试网络是否连接外网,下图所示已连接
3.2 增加音频服务
在src/
下中增加app_media.c
,编写音频相关代码
初始化音频服务
- 在
src/app_net.c
中增加音频初始化代码
int app_media_init(void)
{
int ret = -1;
snd_card_register(CONFIG_VOL_MAX, CONFIG_LEFT_GAIN, CONFIG_RIGHT_GAIN); //注册声卡
utask_t *task_player = utask_new("app_player", 2 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI); //创建media utask
if (task_player) {
ret = aui_player_init(task_player, media_evt); //初始化media微服务
}
app_audio_pa_ctrl(1); //打开功放
return ret;
}
功放管理
- 在
src/app_net.c
中增加功放管理,开发板上定义PIN_PA_EN
,输出1
为打开功放,使喇叭有声音;0
为关闭功放,使喇叭静音
void app_audio_pa_ctrl(int enabled)
{
gpio_pin_handle_t pin_hdl = csi_gpio_pin_initialize(PIN_PA_EN, NULL); //初始化GPIO,管脚定义见 board_config.h
csi_gpio_pin_config_direction(pin_hdl, GPIO_DIRECTION_OUTPUT); //配置gpio为输出
csi_gpio_pin_write(pin_hdl, enabled); //写入gpio电平
csi_gpio_pin_uninitialize(pin_hdl); //去初始化GPIO
}
音频事件处理
- 在
src/app_net.c
中增加音频事件处理
//调用播放后的各类事件
static void media_evt(int type, aui_player_evtid_t evt_id)
{
LOGD(TAG, "media_evt type %d,evt_id %d", type, evt_id);
}
音频服务使用
- 在src/app_main.c使用
void main()
{
...
app_media_init();
...
}
添加音频cli命令
- 在
src/app_net.c
中增加media
命令
//media 命令实现,使用格式:media music url
static void cmd_ai_func(char *wbuf, int wbuf_len, int argc, char **argv)
{
if (strcmp(argv[1], "music") == 0) {
aui_player_play(MEDIA_MUSIC, argv[2], 0);
}
}
//注册media cli命令
void cli_reg_cmd_media(void)
{
static const struct cli_command cmd_info = {"media", "media info", cmd_ai_func};
aos_cli_register_command(&cmd_info);
}
- 在src/app_init.c中的console 命令行增加media相关命令,方便测试播放功能
void cli_init(utask_t *task)
{
...
cli_reg_cmd_media();
...
}
使用media
命令播放歌曲http://www.srcbin.net/ai/pingfanzhilu.mp3
3.3 增加麦克风服务
在src/
下中增加app_mic.c
,编写麦克风相关代码
初始化麦克风服务
在
src/app_mic.c
中增加初始化麦克风服务代码int app_mic_init(void) { int ret; yv_pcm_param_t param; memset(¶m, 0, sizeof(param)); yunvoice_mic_register(1); //注册mic voice_init(); //初始化麦克风数据采集 utask_t *task_mic = utask_new("mic", 2 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI); //创建mic utask ret = aui_mic_start(task_mic, mic_evt); //启动mic微服务 param.channels = 0x0200; // 参考音选择,左声道:0x100;右声道:0x200;PA后: param.ext_param1 = voice_get_adc_data(); //ap侧数据地址 param.ext_param2 = NULL; param.sentence_time_ms = 1500; //断句时间(ms) param.noack_time_ms = 8000; //无语音超时时间(ms) param.max_time_ms = 20000; //唤醒后总超时时间 aui_mic_set_param(¶m); return ret; }
麦克风数据采集初始化
- 在
src/app_mic.c
中增加初始化麦克风数据采集代码
void voice_init(void)
{
voice_sram_init(); //初始化数据存放所需sram
/*
1.参考音 增益, 前端反馈,理论 (16)0dB即可,但单端模补偿6dB
2.数值单位0.75dB 16 + 6/0.75 = 24
*/
voice_ref_init(24, 24);
/*
1.麦克风 增益, boost (3)20dB 模拟增益(8)0dB,伪差分补偿6dB,看信号还较小继续增加12dB
2.数值单位1.5dB 8 + 18/1.5 = 20
*/
int mic_gain_val = 8 + (CONFIG_MIC_GAIN * 2 / 3);
voice_mic_init(3, mic_gain_val, mic_gain_val);
}
麦克风事件处理
- 在
src/app_mic.c
中增加初始化麦克风时间处理代码
static void mic_evt(int source, mic_event_id_t evt_id, void *data, int size)
{
int ret;
switch (evt_id) {
case MIC_EVENT_PCM_DATA:
if (pcm_started == 0) //若未唤醒有数据,不做处理
break;
/* 麦克风数据,推到云端 */
ret = aui_cloud_push_pcm(&g_aui_handler, data, size);
if (ret < 0) {
/* 数据推送错误 */
pcm_started = 0;
LOGE(TAG, "cloud push pcm");
aui_mic_control(MIC_CTRL_STOP_PCM); //停止发送麦克风数据
aui_cloud_push_pcm_finish(&g_aui_handler); //结束数据上传到云端
}
break;
case MIC_EVENT_SESSION_START:
LOGD(TAG, "MIC_EVENT_SESSION_START,type= %d", *((int*)data));
/* 开始交互 */
if (pcm_started == 0) {
/* 网络检测,未连接网络不做处理 */
if (g_net_state == 0) {
LOGE(TAG, "mic_evt net connect failed");
return;
}
pcm_started = 1;
aui_mic_control(MIC_CTRL_START_PCM); //打开麦克风
}
break;
case MIC_EVENT_SESSION_STOP:
LOGD(TAG, "MIC_EVENT_SESSION_STOP");
/* 交互结束,需要关闭麦克风和aui_cloud */
if (pcm_started == 1) {
pcm_started = 0;
aui_mic_control(MIC_CTRL_STOP_PCM); //停止发送麦克风数据
aui_cloud_push_pcm_finish(&g_aui_handler); //结束数据上传到云端
}
break;
default:
;
}
}
3.4 增加aui_cloud功能
aui_cloud是专为rtos打造的语音解决方案,提供了语音合成、语义理解、本地语义端文本理解等接口,在src/
下中增加app_auicloud.c
,编写aui_cloud相关代码
初始化aui
int app_aui_nlp_init(void)
{
int ret;
g_aui_cfg.per = "xiaoyan"; //发音人选择
g_aui_cfg.vol = 50; //音量
g_aui_cfg.pit = 50; //音调
g_aui_cfg.spd = 50; //语速
g_aui_cfg.nlp_cb = aui_nlp_cb; //NLP回掉处理函数
g_aui_handler.config = &g_aui_cfg;
g_aui_handler.context = NULL;
ret = aui_init(&g_aui_handler); //初始化aui
if (ret != 0) {
LOGE(TAG, "ai engine error");
return -1;
}
app_aui_cmd_init(); //初始化NLP回掉处理函数
return ret;
}
NLP回掉处理
static void aui_nlp_cb(const char *json_text)
{
int ret;
LOGE(TAG, "json= %s", json_text);
/* 处理的主入口, 具体处理见初始化注册的处理函数 */
ret = aui_nlp_process_run(&g_aui_nlp_process, json_text);
if (ret < 0) {
LOGD(TAG, "json err");
}
}
添加NLP回掉处理函数
static int app_aui_cmd_init(void)
{
aui_nlp_process_add(&g_aui_nlp_process, aui_nlp_proc_cb_tts_mit); //添加 nlp:aui_nlp_proc_cb_tts_mit
return 0;
}
3.5 解析云端数据
在src/
下中增加app_cmd.c
,编写云端数据解析相关代码
云端数据格式如下,需要将display_text
中的文本进行tts播放即可。
json= {
"header": {
"namespace": "VirtualAssistant",
"name": "DialogResultGenerated",
"status": 20000000,
"message_id": "6fa46c290d404821a17c4e0e82d75b0f",
"task_id": "36ef01f49f709e87d0312e790980c744",
"status_text": "Gateway:SUCCESS:Success."
},
"payload": {
"display_text": "杭州今天小雨,当前温度25,最高温度24,最低温度21,空气湿度95,西北风,风力3级,空气质量优,PM2.5:37,幸福生活来之不易,好好享受下新鲜空气吧!",
"semantics": [
{
"score": 1,
"slots": {
"geo": [
{
"raw_value": "杭州",
"norm_value": "杭州"
}
]
},
"domain": "weather",
"source": "model",
"intent": "get_weather"
}
]
}
}
/**
* 解析到"payload.display_textt",对文本进行tts播放
*/
int aui_nlp_proc_cb_tts_mit(cJSON *js, const char *json_text)
{
LOGD(TAG, "Enter %s", __FUNCTION__);
cJSON *cmd = cJSON_GetObjectItemByPath(js, "payload.display_text"); //解析“payload.display_text”是否有数据
if (!cJSON_IsString(cmd)) {
return -1;
}
mit_tts_start(&g_aui_handler, cmd->valuestring); //将解析到的文本进行tts并播放
return 0;
}