(一)参考源码
首先需要注明,我们参考的源码是海思SDK的audio的demo,并不是某一个github的项目。本次采用的demo来自于SDK:
https://dl.openipc.org/SDK/HiSilicon/Hi3518Ev20X_16Cv200/
的Hi3518E_SDK_V1.0.4.0,其中mpp文件夹下就是各类源码demo。
我们找到sample下的audio,这个就是官方audio例程。
里面打包有4个demo,其中 SAMPLE_AUDIO_AdecAo 这个demo比较合适,因为摄像头一般就是读文件然后播音,不需要读取网络音频流,比较方便。
(二)启动流程
海思SDK启动流程:
1-对任何应用都需要启动SYS和VB部分功能:
HI_MPI_VB_Init()
HI_MPI_SYS_Init()
2-对视频
获取/dev下的驱动文件的文件号:open()
启动摄像头部分功能:AE、AF、ISP,比如HI_MPI_ISP_MemInit,这些函数都在mpi_ae文件里,难道是after effect的意思?文档似乎没有说明。
设置VI属性:HI_MPI_VI_SetDevAttr、HI_MPI_VI_SetWDRAttr、HI_MPI_VI_EnableDev
设置VI通道:HI_MPI_VI_SetChnAttr、HI_MPI_VI_EnableChn
设置VPSS功能:HI_MPI_VPSS_CreateGrp、HI_MPI_VPSS_StartGrp
VPSS(Video Process Sub-System)支持对一幅输入图像进行统一预处理,如去噪、去 隔行等,然后再对各通道分别进行缩放、锐化等处理,最后输出多种不同分辨率的图 像。
设置VENC功能:HI_MPI_VENC_CreateChn
VENC 模块,即视频编码模块。
3-对音频
设置编码:(这段代码没看懂)
创建ADEC通道:HI_MPI_ADEC_CreateChn
启动AO功能:
设置AO属性:HI_MPI_AO_SetPubAttr
使能AO:HI_MPI_AO_Enable
使能AO 通道:HI_MPI_AO_EnableChn
还有些额外配置就不说了
AO通道与系统绑定:HI_MPI_SYS_Bind
打开音频文件:open
创建一个子线程不停地发送:HI_MPI_ADEC_SendStream
(三)源码结构
sample下的源码都有一个特点,都会对hi sdk的函数进行进一步的封装。
比如音频里面的 “AO通道与系统绑定:HI_MPI_SYS_Bind”
其将之封装进了一个叫 SAMPLE_COMM_AUDIO_AoBindAdec 的函数里面,这些函数定义在sample_comm_audio.c里面
(四)移植
1-我们要把 sample_comm_audio.c 这个源码添加进工程
2-sample_comm_audio.c的函数声明都在 sample_comm.h,这个头文件也要加进去
3-(选)系统初始化函数在sample_comm_sys.c 里面,但由于hi_minihttp这个开源工程已经实现了这部分功能,在start_sdk里面,就不需要额外移植
4-我们移植官方audio例程,其mian.c文件叫做sample_audio.c,里面同样有不少关键函数,也要拷贝过来:
static PAYLOAD_TYPE_E gs_enPayloadType = PT_LPCM;
static HI_BOOL gs_bAioReSample = HI_FALSE;
static AUDIO_SAMPLE_RATE_E enInSampleRate = AUDIO_SAMPLE_RATE_BUTT;
static AUDIO_SAMPLE_RATE_E enOutSampleRate = AUDIO_SAMPLE_RATE_BUTT;
HI_S32 SAMPLE_AUDIO_AdecAo(HI_VOID);
void SAMPLE_AUDIO_HandleSig(HI_S32 signo);
HI_VOID SAMPLE_COMM_SYS_Exit(void);
HI_S32 SAMPLE_COMM_SYS_Init(VB_CONF_S *pstVbConf);
static FILE *SAMPLE_AUDIO_OpenAdecFile(ADEC_CHN AdChn, PAYLOAD_TYPE_E enType);
//SAMPLE_PRT
#define SAMPLE_PRT(fmt...) \
do \
{ \
printf("[%s]-%d: ", __FUNCTION__, __LINE__); \
printf(fmt); \
} while (0)
//SAMPLE_DBG
#define SAMPLE_DBG(s32Ret) \
do \
{ \
printf("s32Ret=%#x,fuc:%s,line:%d\n", s32Ret, __FUNCTION__, __LINE__); \
} while (0)
我把声明罗列在这里,定义顺着拷过去就行。
5-由于官方例程的 SAMPLE_AUDIO_AdecAo 这个测试函数是初始化销毁一体的,需要拆分为start和stop:
start:
HI_S32 s32Ret;
AUDIO_DEV AoDev = SAMPLE_AUDIO_AO_DEV;
AO_CHN AoChn = 0;
ADEC_CHN AdChn = 0;
HI_S32 s32AoChnCnt;
FILE *pfd = NULL;
AIO_ATTR_S stAioAttr;
HI_S32 TINNU_AUDIO_AdecAo_Start(HI_VOID)
{
stAioAttr.enSamplerate = AUDIO_SAMPLE_RATE_16000;
stAioAttr.enBitwidth = AUDIO_BIT_WIDTH_16;
stAioAttr.enWorkmode = AIO_MODE_I2S_MASTER;
stAioAttr.enSoundmode = AUDIO_SOUND_MODE_MONO;
stAioAttr.u32EXFlag = 0;
stAioAttr.u32FrmNum = 30;
stAioAttr.u32PtNumPerFrm = SAMPLE_AUDIO_PTNUMPERFRM;
stAioAttr.u32ChnCnt = 1;
stAioAttr.u32ClkSel = 0;
gs_bAioReSample = HI_FALSE;
enInSampleRate = AUDIO_SAMPLE_RATE_BUTT;
enOutSampleRate = AUDIO_SAMPLE_RATE_BUTT;
s32Ret = SAMPLE_COMM_AUDIO_CfgAcodec(&stAioAttr);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_DBG(s32Ret);
goto ADECAO_ERR3;
}
s32Ret = SAMPLE_COMM_AUDIO_StartAdec(AdChn, gs_enPayloadType);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
goto ADECAO_ERR3;
}
s32AoChnCnt = stAioAttr.u32ChnCnt;
s32Ret = SAMPLE_COMM_AUDIO_StartAo(AoDev, s32AoChnCnt, &stAioAttr, enInSampleRate, gs_bAioReSample, NULL, 0);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
goto ADECAO_ERR2;
}
s32Ret = SAMPLE_COMM_AUDIO_AoBindAdec(AoDev, AoChn, AdChn);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
goto ADECAO_ERR1;
}
pfd = SAMPLE_AUDIO_OpenAdecFile(AdChn, gs_enPayloadType);
if (!pfd)
{
SAMPLE_DBG(HI_FAILURE);
goto ADECAO_ERR0;
}
s32Ret = SAMPLE_COMM_AUDIO_CreatTrdFileAdec(AdChn, pfd);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
goto ADECAO_ERR0;
}
return s32Ret;
s32Ret = SAMPLE_COMM_AUDIO_DestoryTrdFileAdec(AdChn);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
return HI_FAILURE;
}
ADECAO_ERR0:
s32Ret = SAMPLE_COMM_AUDIO_AoUnbindAdec(AoDev, AoChn, AdChn);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
}
ADECAO_ERR1:
s32Ret |= SAMPLE_COMM_AUDIO_StopAo(AoDev, s32AoChnCnt, gs_bAioReSample, HI_FALSE);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
}
ADECAO_ERR2:
s32Ret |= SAMPLE_COMM_AUDIO_StopAdec(AdChn);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
}
ADECAO_ERR3:
return s32Ret;
}
stop:
HI_S32 TINNU_AUDIO_AdecAo_Stop(HI_VOID)
{
s32Ret = SAMPLE_COMM_AUDIO_DestoryTrdFileAdec(AdChn);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
return HI_FAILURE;
}
s32Ret = SAMPLE_COMM_AUDIO_AoUnbindAdec(AoDev, AoChn, AdChn);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
}
s32Ret |= SAMPLE_COMM_AUDIO_StopAo(AoDev, s32AoChnCnt, gs_bAioReSample, HI_FALSE);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
}
s32Ret |= SAMPLE_COMM_AUDIO_StopAdec(AdChn);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_DBG(s32Ret);
}
}
5-在主循环附近调用,就能移植播放音乐:
TINNU_AUDIO_AdecAo_Start();
while (keepRunning)
{
nanosleep(&ts, NULL);
EventLoop(s32MainFd);
}
TINNU_AUDIO_AdecAo_Stop();
6-音频文件
音频文件的定义在 SAMPLE_AUDIO_OpenAdecFile 函数内:
snprintf(aszFileName, FILE_NAME_LEN, "audio_chn%d.%s", AdChn, SAMPLE_AUDIO_Pt2Str(enType));
AdChn为0,文件类型为pcm(demo里面默认是adpcm,我把
static PAYLOAD_TYPE_E gs_enPayloadType = PT_ADPCMA;
改为
static PAYLOAD_TYPE_E gs_enPayloadType = PT_ADPCMA;
)因此文件名为audio_chn0.pcm
这里有个标准的pcm文件供测试:http://ai.baidu.com/forum/topic/show/498327
不过这个pcm文件是16000采样率的,demo里面的stAioAttr.enSamplerate要改一下
stAioAttr.enSamplerate = AUDIO_SAMPLE_RATE_16000;
(五)使能8002A功放输出
经群里的 @一图 群友测试,需要额外使能8002a 功放的,首先把gpio7.3复用为通用gpio,再设置为输出,再输出高电平。只是输出高电平给贴片三极管基极,拉低集电极,给8002a第一脚shut一个低电平,从而开启功放推动喇叭。
himm 0x200F00EC 0x01
himm 0x201B0400 0x08
himm 0x201B03FC 0x08
finish!