🎄一、概述
上篇文章 【海思SS528】MPP媒体处理软件V5.0 | 音频模块 - 学习笔记 学习了海思MPP媒体处理平台的一小部分音频知识,这篇文章继续学习与音频相关的例程,这样可以更好理解《MPP 媒体处理软件 V5.0 开发参考.pdf》中的音频模块知识。
本篇文章涉及到的SDK文件及路径说明:
- 《MPP 媒体处理软件 V5.0 开发参考.pdf》:在SDK的路径为
SS528ReleaseDoc\software\board\MPP
- 《22AP30 H.265编解码处理器用户指南.pdf》:在SDK的路径为
SS528ReleaseDoc\hardware\chip
- sample_audio.c :在SDK的路径为
SS528V100_SDK_V2.0.0.3/mpp/sample/audio/sample_audio.c
🎄二、main 函数解析
先看 main 函数,主要做了以下工作:
- 判断程序输入参数,错误的话,就打印程序功能及用法;
- 初始化系统和VB:sample_comm_sys_init
- 初始化 AAC 编码和解码:hi_mpi_aenc_aac_init、hi_mpi_adec_aac_init
- 根据输入参数,调用对应例子:main_inner
- 退出程序时的反初始化:hi_mpi_aenc_aac_deinit、hi_mpi_adec_aac_deinit、sample_comm_sys_exit
从main函数来看,主要的内容在
main_inner
函数。下面是main函数的代码:
/* function : main */
#ifdef __LITEOS__
hi_s32 app_main(int argc, char *argv[])
#else
hi_s32 main(int argc, char *argv[])
#endif
{
hi_s32 ret;
hi_vb_cfg vb_conf;
hi_u32 index;
if (argc != 2) { /* 2:argv num */
sample_audio_usage();
return HI_FAILURE;
}
if (!strncmp(argv[1], "-h", 2)) { /* 2:arg num */
sample_audio_usage();
return HI_FAILURE;
}
if ((strlen(argv[1]) != 1) ||
(argv[1][0] < '0' || argv[1][0] > '6')) { /* 6:arg num */
sample_audio_usage();
return HI_FAILURE;
}
index = atoi(argv[1]);
sample_sys_signal(&sample_audio_handle_sig);
#if defined(OT_VQE_USE_STATIC_MODULE_REGISTER)
ret = sample_audio_register_vqe_module();
if (ret != HI_SUCCESS) {
return HI_FAILURE;
}
#endif
ret = memset_s(&vb_conf, sizeof(hi_vb_cfg), 0, sizeof(hi_vb_cfg));
if (ret != EOK) {
printf("%s: vb_config init failed with %d!\n", __FUNCTION__, ret);
return HI_FAILURE;
}
ret = sample_comm_sys_init(&vb_conf);
if (ret != HI_SUCCESS) {
printf("%s: system init failed with %d!\n", __FUNCTION__, ret);
return HI_FAILURE;
}
hi_mpi_aenc_aac_init();
hi_mpi_adec_aac_init();
main_inner(index);
hi_mpi_aenc_aac_deinit();
hi_mpi_adec_aac_deinit();
sample_comm_sys_exit();
return ret;
}
🎄三、main_inner 函数解析
main_inner函数主要是根据程序输入的第二个参数来决定调用哪个例子,分别以下几种:
- sample_audio_ai_ao:从 AI 设备采集音频,然后从 AO 设备播放,最简单的一个例子;
- sample_audio_ai_aenc:从 AI 设备采集音频,然后进行编码写文件,最后再解码且从 AO 设备播放;
- sample_audio_adec_ao:从文件读取音频数据,解码,从 AO 设备播放;
- sample_audio_ai_vqe_process_ao:
- sample_audio_ai_hdmi_ao:
- sample_audio_ai_to_ao_sys_chn:
- sample_audio_ai_to_ext_resample:
这后面几个例子,还没使用,后面补充。2023-06-30 21:25:34
main_inner的代码如下,这也是没什么内容的一个函数。
static hi_void main_inner(hi_u32 index)
{
switch (index) {
case 0: { /* 0:ai->ao */
sample_audio_ai_ao();
break;
}
case 1: { /* 1:ai->aenc->adec->ao */
sample_audio_ai_aenc();
break;
}
case 2: { /* 2:file->adec->ao */
sample_audio_adec_ao();
break;
}
case 3: { /* 3:ai->ao vqe */
sample_audio_ai_vqe_process_ao();
break;
}
case 4: { /* 4:ai->ao hdmi */
sample_audio_ai_hdmi_ao();
break;
}
case 5: { /* 5:ai->ao synchn */
sample_audio_ai_to_ao_sys_chn();
break;
}
case 6: { /* 6:resample test */
sample_audio_ai_to_ext_resample();
break;
}
default: {
break;
}
}
}
🎄四、sample_audio_ai_ao 函数解析
这是 sample_audio.c 的第一个例子,可以带我们 熟悉AI设备和AO设备的启用流程。
这个例子已经涉及到很多AI和AO的API函数了,需要对开发文档《MPP 媒体处理软件 V5.0 开发参考.pdf》中音频相关的API函数有一定了解,可以再阅读过程碰到不认识的API再去开发文档查询。
✨4.1 Audio Codec相关配置
正式阅读代码前,先看一下有关Audio Codec的相关配置,因为在初始化参数时会使用到,如果AIO参数没有配置好的话,可能出现各种问题。
我使用的板子没有使用内置的Audio Codec,而是使用 ES7243S 去做采集音频的模数转换,采样率、采集精度、声道是驱动工程师写驱动时写死的,而 sample_audio.c 的例子中提供了sample_comm_audio_cfg_acodec
对Audio codec进行设置的,不设置的话,程序一跑就退出了。根据我的情况,我用不上这个函数,所以要注释掉,并且配置AIO参数时,也配置好对应的采样率、采集精度、声道和AI设备号。我的采样率、采集精度、声道和AI设备号如下:
- 采样率:48kHz
- 采样精度:16bit
- 声道:双声道
- AI设备号:1
- AO设备号:0
AI、AO设备号的确定需要看自己板子电路图的Audio codec芯片接在
I2S0_SD_RX
,还是I2S1_SD_RX
,
接在I2S0_SD_RX
则对应AI设备号为 0;
接在I2S1_SD_RX
则对应AI设备号为 1;
下图是我的板子原理图,对应AI设备号为 1;下图是接在AO的原理图,接在
I2S0_SD_TX
,对应的AO设备号为 0;
✨4.2 sample_audio_ai_ao 函数简析
这小节简单介绍 sample_audio_ai_ao 函数的基本内容:
- 初始化AIO参数:
sample_audio_ai_ao_init_param
,这个函数里的参数要配置对,下小节会介绍这函数。- 初始化vqe参数:
sample_audio_set_ai_vqe_param
,这个函数只是几个赋值,按照默认即可;- 启用AI设备:
sample_comm_audio_start_ai
,这个函数有启用AI的流程,需要学习;- 启用AO设备:
sample_comm_audio_start_ao
,这个函数有启用AO的流程,也需要学习;- 配置Audio codec:
sample_comm_audio_cfg_acodec
,这个不同板子不一样,我这里不需要,所以注释掉;- 绑定AI到AO:
sample_audio_ai_ao_inner
,使AI的音频帧发送到AO设备播放。- 其他:其他的是退出程序的一些工作,如:停止AO设备、停止AI设备等。
sample_audio_ai_ao 函数代码:
/* function : ai -> ao(with fade in/out and volume adjust) */
hi_s32 sample_audio_ai_ao(hi_void)
{
hi_s32 ret;
hi_u32 ai_chn_cnt;
hi_u32 ao_chn_cnt;
hi_audio_dev ai_dev;
hi_audio_dev ao_dev;
const hi_ai_chn ai_chn = 0;
const hi_ao_chn ao_chn = 0;
hi_aio_attr aio_attr = {0};
sample_comm_ai_vqe_param ai_vqe_param = {0};
sample_audio_ai_ao_init_param(&aio_attr, &ai_dev, &ao_dev);
/* enable AI channel */
ai_chn_cnt = aio_attr.chn_cnt;
sample_audio_set_ai_vqe_param(&ai_vqe_param, g_out_sample_rate, g_aio_resample, HI_NULL, 0);
ret = sample_comm_audio_start_ai(ai_dev, ai_chn_cnt, &aio_attr, &ai_vqe_param, -1);
if (ret != HI_SUCCESS) {
sample_dbg(ret);
goto ai_ao_err3;
}
/* enable AO channel */
ao_chn_cnt = aio_attr.chn_cnt;
ret = sample_comm_audio_start_ao(ao_dev, ao_chn_cnt, &aio_attr, g_in_sample_rate, g_aio_resample);
if (ret != HI_SUCCESS) {
sample_dbg(ret);
goto ai_ao_err2;
}
/* config internal audio codec */
ret = sample_comm_audio_cfg_acodec(&aio_attr);
if (ret != HI_SUCCESS) {
sample_dbg(ret);
goto ai_ao_err1;
}
sample_audio_ai_ao_inner(ai_dev, ai_chn, ao_dev, ao_chn);
ai_ao_err1:
ret = sample_comm_audio_stop_ao(ao_dev, ao_chn_cnt, g_aio_resample);
if (ret != HI_SUCCESS) {
sample_dbg(ret);
}
ai_ao_err2:
ret = sample_comm_audio_stop_ai(ai_dev, ai_chn_cnt, g_aio_resample, HI_FALSE);
if (ret != HI_SUCCESS) {
sample_dbg(ret);
}
ai_ao_err3:
return ret;
}
✨4.3 sample_audio_ai_ao 的 sample_audio_ai_ao_init_param 函数
sample_audio_ai_ao_init_param
函数主要是初始化AIO单元的参数,参数结构体的具体解析可以查看开发手册的hi_aio_attr
结构体。
这里只介绍几个与Audio codec相关的参数,我的参数在上面 4.1 节有介绍。采样率sample_rate
、采样精度bit_width
、声道chn_cnt
,这三个参数根据自己 Audio Codec 配置即可,我的对应参数是 48KHZ、16bit、双声道 ;然后clk_share
参数,我选择0。
下面是我修改过AIO参数的代码,你可以参考自己Audio codec的情况,参考修改:
/**
* @brief 初始化AIO模块参数 2023-06-30 09:55:01
*
* @param aio_attr :输出变量,模块参数
* @param ai_dev :输出变量,传出AI设备号
* @param ao_dev :输出变量,传出AO设备号
* @return hi_void
*/
static hi_void sample_audio_ai_ao_init_param(hi_aio_attr *aio_attr, hi_audio_dev *ai_dev, hi_audio_dev *ao_dev)
{
/**
* 注意:
* 1、采样率sample_rate、采样精度bit_width、声道chn_cnt,这三个参数根据自己 Audio Codec 配置即可,我的对应参数是 48KHZ、16bit、双声道
* 2、AI设备号:需要看硬件原理图,如果采集音频接口最终接到 I2S0_BCLK ,则是第0个AI设备,设备号0;如果接到 I2S1_BCLK 则设备号 1。
**/
aio_attr->sample_rate = HI_AUDIO_SAMPLE_RATE_48000;//HI_AUDIO_SAMPLE_RATE_16000;
aio_attr->bit_width = HI_AUDIO_BIT_WIDTH_16;
aio_attr->work_mode = HI_AIO_MODE_I2S_SLAVE;//HI_AIO_MODE_I2S_MASTER;
aio_attr->snd_mode = HI_AUDIO_SOUND_MODE_STEREO;
aio_attr->expand_flag = 0;
aio_attr->frame_num = 30; /* 30:frame num */
aio_attr->point_num_per_frame = AACLC_SAMPLES_PER_FRAME;
aio_attr->chn_cnt = 2; /* 2:chn cnt */
*ai_dev = 1;//SAMPLE_AUDIO_EXTERN_AI_DEV;
*ao_dev = SAMPLE_AUDIO_EXTERN_AO_DEV;
aio_attr->clk_share = 0;//1;
aio_attr->i2s_type = HI_AIO_I2STYPE_EXTERN;
g_aio_resample = HI_FALSE;
/* config ao resample attr if needed */
if (g_aio_resample == HI_TRUE) {
/* ai 48k -> 32k */
g_out_sample_rate = HI_AUDIO_SAMPLE_RATE_32000;
/* ao 32k -> 48k */
g_in_sample_rate = HI_AUDIO_SAMPLE_RATE_32000;
} else {
g_in_sample_rate = HI_AUDIO_SAMPLE_RATE_BUTT;
g_out_sample_rate = HI_AUDIO_SAMPLE_RATE_BUTT;
}
/* resample and anr should be user get mode */
g_user_get_mode = (g_aio_resample == HI_TRUE) ? HI_TRUE : HI_FALSE;
}
✨4.4 sample_audio_ai_ao 的 sample_comm_audio_start_ai 函数
sample_comm_audio_start_ai
函数介绍了AI设备启用流程,可以学习后,应用到自己代码。基本流程如下:
- 1、设置 AI 设备属性:
hi_mpi_ai_set_pub_attr
,参数在sample_audio_ai_ao_init_param
已设置好。- 2、使能 AI 设备:
hi_mpi_ai_enable
,该函数只需要传入AI设备号;- 3、启用 AI 通道:
hi_mpi_ai_enable_chn
,启用指定设备的AI通道;- 4、根据标志,判断是否启用 AI 重采样:hi_mpi_ai_enable_resample
- 5、使能AI的声音质量增强功能:sample_comm_audio_start_ai_vqe
sample_comm_audio_start_ai函数代码如下:
/* start ai */
hi_s32 sample_comm_audio_start_ai(hi_audio_dev ai_dev_id, hi_u32 ai_chn_cnt, hi_aio_attr *aio_attr,
const sample_comm_ai_vqe_param *ai_vqe_param, hi_audio_dev ao_dev_id)
{
hi_s32 i;
hi_s32 ret;
hi_u32 chn_cnt;
ret = hi_mpi_ai_set_pub_attr(ai_dev_id, aio_attr);
if (ret) {
printf("%s: hi_mpi_ai_set_pub_attr(%d) failed with %#x\n", __FUNCTION__, ai_dev_id, ret);
return ret;
}
ret = hi_mpi_ai_enable(ai_dev_id);
if (ret) {
printf("%s: hi_mpi_ai_enable(%d) failed with %#x\n", __FUNCTION__, ai_dev_id, ret);
return ret;
}
chn_cnt = ai_chn_cnt >> ((hi_u32)aio_attr->snd_mode);
for (i = 0; i < (hi_s32)chn_cnt; i++) {
ret = hi_mpi_ai_enable_chn(ai_dev_id, i);
if (ret) {
printf("%s: hi_mpi_ai_enable_chn(%d,%d) failed with %#x\n", __FUNCTION__, ai_dev_id, i, ret);
return ret;
}
if (ai_vqe_param->resample_en == HI_TRUE) {
ret = hi_mpi_ai_enable_resample(ai_dev_id, i, ai_vqe_param->out_sample_rate);
if (ret) {
printf("%s: hi_mpi_ai_enable_re_smp(%d,%d) failed with %#x\n", __FUNCTION__, ai_dev_id, i, ret);
return ret;
}
}
ret = sample_comm_audio_start_ai_vqe(ai_dev_id, i, ai_vqe_param, ao_dev_id);
if (ret != HI_SUCCESS) {
return ret;
}
}
return HI_SUCCESS;
}
✨4.5 sample_audio_ai_ao 的 sample_comm_audio_start_ao函数
sample_comm_audio_start_ao
函数介绍了AO设备启用流程,可以学习后,应用到自己代码。基本流程如下:
- 1、设置 AO 设备属性:
hi_mpi_ao_set_pub_attr
,参数在sample_audio_ai_ao_init_param
已设置好。- 2、使能 AO 设备:
hi_mpi_ao_enable
,该函数只需要传入AO设备号;- 3、启用 AO 通道:
hi_mpi_ao_enable_chn
,启用指定设备的AO通道;- 4、根据标志,判断是否启用 AO 重采样:hi_mpi_ao_enable_resample
sample_comm_audio_start_ao 函数代码:
/* start ao */
hi_s32 sample_comm_audio_start_ao(hi_audio_dev ao_dev_id, hi_u32 ao_chn_cnt, hi_aio_attr *aio_attr,
hi_audio_sample_rate in_sample_rate, hi_bool resample_en)
{
hi_s32 i;
hi_s32 ret;
hi_u32 chn_cnt;
if (ao_dev_id == SAMPLE_AUDIO_INNER_HDMI_AO_DEV) {
#ifdef OT_ACODEC_TYPE_HDMI
aio_attr->clk_share = 0;
sample_comm_audio_start_hdmi(aio_attr);
#endif
}
ret = hi_mpi_ao_set_pub_attr(ao_dev_id, aio_attr);
if (ret != HI_SUCCESS) {
printf("%s: hi_mpi_ao_set_pub_attr(%d) failed with %#x!\n", __FUNCTION__, ao_dev_id, ret);
return HI_FAILURE;
}
ret = hi_mpi_ao_enable(ao_dev_id);
if (ret != HI_SUCCESS) {
printf("%s: hi_mpi_ao_enable(%d) failed with %#x!\n", __FUNCTION__, ao_dev_id, ret);
return HI_FAILURE;
}
chn_cnt = ao_chn_cnt >> ((hi_u32)aio_attr->snd_mode);
for (i = 0; i < (hi_s32)chn_cnt; i++) {
ret = hi_mpi_ao_enable_chn(ao_dev_id, i);
if (ret != HI_SUCCESS) {
printf("%s: hi_mpi_ao_enable_chn(%d) failed with %#x!\n", __FUNCTION__, i, ret);
return HI_FAILURE;
}
if (resample_en == HI_TRUE) {
ret = hi_mpi_ao_disable_resample(ao_dev_id, i);
if (ret != HI_SUCCESS) {
printf("%s: hi_mpi_ao_disable_resample (%d,%d) failed with %#x!\n", __FUNCTION__, ao_dev_id, i, ret);
return HI_FAILURE;
}
ret = hi_mpi_ao_enable_resample(ao_dev_id, i, in_sample_rate);
if (ret != HI_SUCCESS) {
printf("%s: hi_mpi_ao_enable_resample(%d,%d) failed with %#x!\n", __FUNCTION__, ao_dev_id, i, ret);
return HI_FAILURE;
}
}
}
ret = hi_mpi_ao_enable_chn(ao_dev_id, HI_AO_SYS_CHN_ID);
if (ret != HI_SUCCESS) {
printf("%s: hi_mpi_ao_enable_chn(%d) failed with %#x!\n", __FUNCTION__, i, ret);
return HI_FAILURE;
}
return HI_SUCCESS;
}
✨4.6 sample_audio_ai_ao 的 sample_audio_ai_ao_inner 函数
sample_audio_ai_ao_inner
函数的内容是将AI绑定到AO,使音频帧从AI到AO,这个是本节主要想讲的;还有就是音量控制的,这个暂时用不到;下面就是使用getchar
函数等待用户来结束进程。代码如下:static hi_void sample_audio_ai_ao_inner(hi_audio_dev ai_dev, hi_ai_chn ai_chn, hi_audio_dev ao_dev, hi_ao_chn ao_chn) { hi_s32 ret; /* bind AI to AO channel */ ret = sample_audio_ao_bind_ai(ai_dev, ai_chn, ao_dev, ao_chn); if (ret != HI_SUCCESS) { return; } if (g_ao_volume_ctrl == HI_TRUE) { ret = sample_comm_audio_creat_trd_ao_vol_ctrl(ao_dev); if (ret != HI_SUCCESS) { sample_dbg(ret); goto ai_ao_err0; } } printf("\nplease press twice ENTER to exit this sample\n"); smaple_audio_getchar(); smaple_audio_getchar(); if (g_ao_volume_ctrl == HI_TRUE) { ret = sample_comm_audio_destory_trd_ao_vol_ctrl(ao_dev); if (ret != HI_SUCCESS) { sample_dbg(ret); } } ai_ao_err0: sample_audio_ao_unbind_ai(ai_dev, ai_chn, ao_dev, ao_chn); }
接下来,看看
sample_audio_ao_bind_ai
函数,它主要是绑定AI到AO,分两种情况:
- 1、用户模式:
sample_comm_audio_creat_trd_ai_ao
,创建一个线程sample_comm_audio_ai_proc
实现从AI取帧,然后发送到AO- 2、系统绑定:
sample_comm_audio_ao_bind_ai
,调用hi_mpi_sys_bind
绑定后,AI 的帧会自动发送到 AO。
sample_audio_ao_bind_ai 函数代码:
static hi_s32 sample_audio_ao_bind_ai(hi_audio_dev ai_dev, hi_ai_chn ai_chn, hi_audio_dev ao_dev, hi_ao_chn ao_chn) { hi_s32 ret; if (g_user_get_mode == HI_TRUE) { ret = sample_comm_audio_creat_trd_ai_ao(ai_dev, ai_chn, ao_dev, ao_chn); if (ret != HI_SUCCESS) { sample_dbg(ret); return HI_FAILURE; } } else { ret = sample_comm_audio_ao_bind_ai(ai_dev, ai_chn, ao_dev, ao_chn); if (ret != HI_SUCCESS) { sample_dbg(ret); return HI_FAILURE; } } printf("ai(%d,%d) bind to ao(%d,%d) ok\n", ai_dev, ai_chn, ao_dev, ao_chn); return HI_SUCCESS; }
文章来源:https://www.toymoban.com/news/detail-522893.html
🎄五、总结
本文主要介绍了 sample_audio.c
的 sample_audio_ai_ao 函数,我觉得关于这个函数的流程,以及重要子函数已经讲得足够清晰了。看完文章,结合自己的sample代码,可以对理解 sample_audio.c
音频例程有一定了解。
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁文章来源地址https://www.toymoban.com/news/detail-522893.html
到了这里,关于【海思SS528 | MPP】音频例程 sample_audio.c 源码阅读笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!