解码接口 ========================================= | 目前DAC一直工作在32K的采样率,所有解码通路经过重采样之后输出到DAC。 | 各种芯片与其支持音频格式如下所示: ======= ===================== ========= ======== ======== ======== ======== 格式 后缀名 AD14N AD15N AC104N AD17N AD18N ======= ===================== ========= ======== ======== ======== ======== f1a .f1a .f1b .f1c .f1x 支持 支持 支持 支持 支持 ump3 .ump3 支持 不支持 支持 不支持 支持 midi .mid .mfa .mda 支持 支持 支持 支持 支持 a .a .b .e 支持 支持 支持 支持 支持 MP3 .mp3 .mp2 .mp1 不支持 不支持 支持 不支持 支持 WAV .wav 不支持 不支持 支持 不支持 支持 ======= ===================== ========= ======== ======== ======== ======== 其中,midi文件后缀名详情: =========== ============= ================== 工具版本 乐谱后缀名 音色库后缀名 =========== ============= ================== 旧版无压缩 .mid .mid 旧版有压缩 .mid .mda 新版 .mfa .mda =========== ============= ================== 目前支持最多支持的解码通路如下: ====================== ================================================ ========================== CPU 两路解码 三路解码 ====================== ================================================ ========================== AD14N f1a / ump3 / midi + a解码 f1a1 + f1a2 + a解码 AC104N f1a / ump3 / midi / 标准mp3 / wav + a解码 f1a1 + f1a2 + a解码 AD15N f1a / midi + a解码 不支持 AD17N f1a / midi + a解码 不支持 AD18N f1a / ump3 / midi + a解码 f1a1 + f1a2 + a解码 ====================== ================================================ ========================== 解码管理的接口函数: :: void decoder_init(void); dec_obj *decoder_io(void *pfile, u32 dec_ctl, dp_buff * dbuff,u8 loop); void irq_decoder_ret(dec_obj *obj,u32 ret); 解码功能控制函数: :: void decoder_stop(dec_obj *obj, DEC_STOP_WAIT wait); void decoder_pause(dec_obj *obj); 各个格式的解码接口函数: :: u32 a_decode_api(void *p_file,void **p_dec,void *p_dp_buf) u32 f1a_decode_api_1(void *p_file,void **p_pdec,void *t_dp_buf) u32 f1a_decode_api_2(void *p_file,void **p_pdec,void *t_dp_buf) u32 midi_decode_api(void *p_file,void **ppdec,void *p_dp_buf) u32 ump3_decode_api(void *p_file, void **p_dec,void *p_dp_buf) u32 mp3_st_decode_api(void *p_file, void **p_dec, void *p_dp_buf) u32 mp3_st_decode_api(void *p_file, void **p_dec, void *p_dp_buf) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 解码使用一般流程 ################################ 在开机时需要调用函数 void decoder_init(void),此函数实现对解码管理器进行初始化,会初始化解码控制器里面的一些变量。 在解码之前要确保已经完成了需要解码的设备,以及设备中的文件进行了初始化。获取到了文件句柄,初始化DAC之后便可以开始正常的解码流程: 1、调用decoder_io实现对解码器的控制,此函数的传参可以控制: :: a.对文件在指定的几种解码格式中解析,自动选取正确的格式进行解码; b.控制解码输出的PCM数据结果是否经过音效处理,目前阶段主要是变速变调; c.输入一断点buff,有了此buff才能实现续播功能; d.控制续播的次数; 2、经过上面的一步已经完成了对解码的启动,此时可以调用decoder_pause、decoder_stop实现暂停解码与停止解码的功能; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 数据流获取 ################################ 所有解码格式初始化时,会注册数据输入、输出接口给解码器进行回调。用户可根据需求自行修改数据输入、输出接口。 涉及文件apps/app/bsp/common/decoder/mp_io.c,涉及的结构体如下: :: struct if_decoder_io { void *priv, int(*input)(void *priv, u32 addr, void *buf, int len); int(*output)(void *priv, void *data, int len); }; 结构体中注册的函数原型: :: int mp_input(void *priv, u32 addr, void *buf, int len) { dec_obj *obj = priv; vfs_seek(obj->p_file, addr, SEEK_SET); int rlen = vfs_read(obj->p_file, buf, len); return rlen; } /* 解码开始时会调用input接口读取音频文件数据,其中参数addr为文件内的偏移地址,len为读取长度,返回值为读到的字节数; */ int mp_output(void *priv, void *data, int len) { dec_obj *obj = priv; return sound_output(&obj->sound, data, len); } /* 数据解码完成后解码器会调用output接口将解码后的pcm数据写入dac的buff,其中参数data为数据输出的起始地址,len为输出长度,返回值为输出的字节数; */ .. image:: 1-2-dec-data_io.png :alt: "数据流输入输出结构" :align: center .. centered:: 数据流输入输出结构 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 解码管理接口函数 ################################ 函数dec_obj \*decoder_io(void \*pfile, u32 dec_ctl, dp_buff \* dbuff,u8 loop) *************************************************************************************************** 此函数实现对以及获取到的文件进行解码,其中参数: :: 1、void *pfile,已经获取到的文件句柄; 2、u32 dec_ctl,解码控制参数,通过对不BIT的置一操作实现对相应功能的控制: ① BIT_UMP3,置上此位为可以将UMP3解码加入到解码轮询中; ② BIT_F1A1/,置上此位可以将第一路f1a/b/c解码加入到解码轮询中; ③ BIT_F1A2,置上此位为可以将第二路f1a/b/c解码加入到解码轮询中; ④ BIT_MIDI,置上此位为可以将midi解码加入到解码轮询中; ⑤ BIT_A,置上此位可以将a/b/e解码加入到解码轮询中; ⑥ BIT_MP3_ST,置上此位可以将标准MP3解码加入到解码轮询中; ⑦ BIT_WAV,置上此位可以将标准WAV解码加入到解码轮询中; ⑧ BIT_SPEED,置上此位之后,当前这一路解码输出的PCM数据会经过变数变调之后再输出给DAC。 3、dp_buff * dbuff,断点缓存,用于续播功能: ① NULL,不能实现续播功能, ② 非NULL,会使用给定的buff实现续播功能 4、u8 loop,续播次数控制(0~255): ① 0,播放完成后不续播; ② 1~244,续播次数; ③ 255,一直持续续播; 5、返回值,解码句柄,利用解码句柄可以实现对当前解码通道进行控制(主要是暂停和播放)。 .. image:: dec_ctl_para.png :alt: "解码控制参数“ :align: center 函数void irq_decoder_ret(dec_obj \*obj,u32 ret) *************************************************************** 此函数是解码器在解码过程中的回调函数,不能再这里做长时间的操作,因为这样会卡慢解码的速度,其中参数: :: 1、dec_obj *obj,解码句柄 2、ret,解码运行的结果, 0为成功, 其他如图 ① MAD_ERROR_FILE_END,解码文件结束 ② MAD_ERROR_FILESYSTEM_ERR,未使用 ③ MAD_ERROR_DISK_ERR,未使用 ④ MAD_ERROR_SYNC_LIMIT,文件错误 ⑤ MAD_ERROR_FF_FR_FILE_END,快进到头 ⑥ MAD_ERROR_FF_FR_END,未使用 ⑦ MAD_ERROR_FF_FR_FILE_START,快退到头 ⑧ MAD_ERROR_LIMIT,未使用 ⑨ MAD_ERROR_NODATA,未使用 3、返回值,无。 .. image:: dec_callback_para.png :alt: "解码运行结果“ :align: center ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 解码功能控制函数 ################################ 函数void decoder_pause(dec_obj \*obj) *************************************************************************************************** 此函数实现在解码过程中对当前解码进行暂停播放与继续播放的控制,其中参数: :: 1、dec_obj *obj,解码句柄,启动解码时从函数decoder_io的返回值获取得到。 函数void decoder_stop(dec_obj \*obj, DEC_STOP_WAIT wait) *************************************************************************************************** 此函数实现停止当前解码的功能,其中参数: :: 1、dec_obj *obj,解码句柄,启动解码时从函数decoder_io的返回值获取得到。 2、DEC_STOP_WAIT wait,是否等待DAC消耗完解码输出的PCM数据后再停止解码 ① NO_WAIT,直接停止解码 ② NO_WAIT,等待DAC消耗完全部的解码输出数据后再停止解码 .. image:: dec_stop_wait.png :alt: "解码结束是否等待output数据输出完成“ :align: center ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 各个格式的解码函数 ################################ 各种格式的解码启动函数统一被decoder_io所调用,其一般名称为:XXX_decode_api。XXX为其格式名称。 :: u32 a_decode_api(void *p_file,void **p_dec,void *p_dp_buf) u32 f1a_decode_api_1(void *p_file,void **p_pdec,void *t_dp_buf) u32 f1a_decode_api_2(void *p_file,void **p_pdec,void *t_dp_buf) u32 midi_decode_api(void *p_file,void **ppdec,void *p_dp_buf) u32 mp3_decode_api(void *p_file, void **p_dec,void *p_dp_buf) 统一介绍decode_api *************************************************************************************************** 函数u32 XXX_decode_api(void \*p_file, void \**p_dec,void \*p_dp_buf): :: ① void *p_file,文件句柄 ② void **p_dec,存放解码句柄指针的指针 ③ void *p_dp_buf,断点buff,提供给续播使用,注意MIDI不支持 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. _F1X格式解码流程说明: F1X格式解码流程说明 ========================================= 简介 ################################ f1x音频,隶属于fla系列格式,能够做到自定义拼接多段音频达到重复播放以及无缝衔接的效果。 其中,使用“音频转换工具”可以调整参数改变音频间衔接的效果;代码中可以指定音频的播放循序。 SDK中默认按照f1X内的文件顺序选择文件,再根据g_loop_tab[]数组的值指定各段音频的循环次数。 *注:本文需要使用到“音频文件转换工具_1.2.12”及以上版本,需要AD14N / AD15N v1.0.7和AC104N v1.0.5及其之后的sdk支持。* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 结构体 / 函数说明 ################################ 结构体f1x_ctl *************************************************************************************************** 该结构体有以下参数: a. Index:当前解码的音频在f1x文件的序号,与工具制作f1x时输入的文件顺序有关; b. Cnt:当前音频的循环次数; c. loop_tab[F1X_MAX_UNIT]:用户自定义的音频循环次数,默认为u8 g_loop_tab[10]; d. buff[F1X_MAX_UNIT]:各个音频段的偏移地址; 函数void \*f1x_play_api(void \*pfile, u8 \*loop_tab, u32 size, u8 index, u32 addr) *************************************************************************************************** 该函数是f1x音频的初始化函数,通过该函数可以获得f1x文件各段音频的偏移地址;如有有断点,还会找到断点对应的音频序号;其中参数: a. Pfile:文件句柄; b. Loop_tab:用户传入的音频循环表的指针,sdk中传入的是g_loop_tab[]数组指针; c. Size:用户传入的音频循环表的大小; 注意:g_loop_tab默认是10个成员,与F1X_MAX_UNIT对应;若用户音频数量较多,需要同时修改g_loop_tab[]数组大小以及F1X_MAX_UNIT的值; d. Index:f1x解码通道,类似f1a可以同时解2路,由解码框架自行分配; e. Addr:有断点时,传入的断点信息; f. 返回值:成功 f1x管理句柄指针&f1x_io;失败 0 函数int f1x_parsing(void \*pfile, u32 \*buff, u32 sum) *************************************************************************************************** 该函数用于解析f1x音频文件,获取各段音频的偏移地址;用户可以根据各段音频的偏移地址在函数set_play_file()中自定义播放;其中参数: a. Pfile:文件句柄; b. Buff:用于保存各段音频偏移地址的buff; c. Sum:f1x文件中的音频总数; d. 返回值:成功 0,失败 错误请查看errno-base.h 函数u32 set_play_file(void \*priv, u32 \*startpos, u32 \*endpos) *************************************************************************************************** f1x经过函数f1x_play_api()初始化并播放完第一段音频后,解码器会调用本函数选择下一段要播放的音频;sdk中是按照f1x文件中默认的音频顺序进行播放的,用户可修改该函数自定义播放顺序;其中参数: a. Priv:该私有参数传入的是正在解码的f1x文件的管理结构体f1x_ctl; b. Startpos:下一段准备解码的音频起始偏移地址,由f1x_ctl的buff成员提供; c. Endpos:下一段准备解码的音频结束偏移地址; d. 返回值:播放下一段音频 0, 退出播放 -1 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 解码流程 ################################ 如下图,函数f1x_play_api()完成相应初始化操作,包括获取各段音频在f1x文件的偏移地址,以及确定各段音频的循环次数。 初始化结束开始播放第一段音频,第一段音频解码结束后会调用函数set_play_file()设置接下来要解码的音频偏移地址,以选择要播放的音频。 .. image:: f1x_loop.png :alt: "f1x解码简单流程图“ :align: center 函数set_play_file()是f1x功能的核心。 SDK中该函数默认流程是按照制作f1x文件时的文件顺序,根据g_loop_tab[]中定义的循环次数进行循环播放,不支持乱序播放。 如下图,用户可以修改函数set_play_file()中的逻辑判断部分,自定义播放顺序。 .. image:: f1x_code.png :alt: "SDK中函数set_play_file()解析“ :align: center