当前位置: 首页 > 文档资料 > YoC 编程基础 >

实现在线音乐播放器

优质
小牛编辑
116浏览
2023-12-01

本文介绍如何在CB5654开发板上,使用相关组件开发语音交互程序,在编写前需要安装CDK

访问 平头哥芯片开放平台,下载CDK并安装

  • 新建工作空间
    img

  • 新建空白工程 CDK菜单栏->Proiect->New project->
    img img

  • 目录介绍
    img

目录/文件说明
boards包含Flash分区信息、编译烧写脚本
include/yoc_config.h默认包含了YoC自带的可配置项
include/app_init.h应用程序相关配置及函数声明
src/init/cli_cmd.c初始化YoC支持的命令行
src/app_init.cYoC系统的初始化,包含外设及基础服务
src/app_main.cSolution的主文件
src/sys_event_handler.c系统异常处理
  • 添加组件 右键工程下的Components->Components Runtime Management,选择auicloud_sc5654组件 img

终端需要增加网络功能音频服务麦克风服务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的ssidpsk,设置后重启终端,并连接成功

img img

在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命令测试网络是否连接外网,下图所示已连接 img

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

img

3.3 增加麦克风服务

src/下中增加app_mic.c,编写麦克风相关代码

初始化麦克风服务

  • src/app_mic.c中增加初始化麦克风服务代码

    int app_mic_init(void)
    {
        int ret;
        yv_pcm_param_t param;
        memset(&param, 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(&param);
    
        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;
}