录音例程涉及了录音和播放两大块内容,这篇笔记就先来说说播放,暂且先击破解码这部分功能。

我的锤子便签中有上个月记下的一句话,“斯蒂芬·平克说,写作之难,在于把网状思考,用树状结构,体现在线性展开的语句里。”这篇代码解析也有类似的困难,代码的网状结构,如何用文章这种线性载体来体现。我尽量挑出了主干,来讲解自己的理解。另外在文章最后添加了一个模块拓扑图来帮助消化。

我还是建议大家还是多琢磨下源码,代码的事还是让代码来说话,笔记是一个辅助的概括梳理。

本文作者twowinter,转载请注明:http://blog.csdn.net/iotisan/

查看代码主逻辑,主要是App_StartPlay和App_ProcessPlay这两个函数。下面就分别进行分析。

第一部分 App_StartPlay

BOOL App_StartPlay(void)
{
// Initiate NuLiteEx audio decode lib with callback functions stored in g_asAppCallBack[0]
NuLiteExApp_DecodeInitiate(&g_sApp.sNuLiteExAppDecode, (UINT8 *)&g_sApp.uTempBuf, ); // Start NuLiteEx decode lib to decode NuLiteEx file stored from address and played from audio channel 0.
// And decode the first frame of PCMs.
if ( NuLiteExApp_DecodeStartPlayByAddr(&g_sApp.sNuLiteExAppDecode, AUDIOROM_STORAGE_START_ADDR, ) == FALSE )
return FALSE; // Light playback led(PB9) for display status.
OUT4(); // Start Ultraio Timer & HW pwm for UltraIO curve output
ULTRAIO_START(); // Start to playback audio.
Playback_StartPlay();
}

可以看到App_StartPlay主要牵扯了NuLiteExApp和Playback两部分子函数。

重中之重 NuLiteExApp_DecodeStartPlayByAddr

由于对音频编解码这块比较陌生,我还是给对应代码做了中文注解方便消化。

BOOL NuLiteExApp_DecodeStartPlayByAddr(S_NULITEEX_APP_DECODE *psNuLiteExAppDecode, UINT32 u32NuLiteExStorageStartAddr, UINT8 u8PlaybackChannel)
{
UINT16 u16SampleRate;
// NuLiteEx解码库初始化对应的工作缓冲区,应用层传入temp缓存来方便解码库内部工作。另外根据传入的SPI地址从SPI取文件,获取采样率。
// NuLiteEx decoder initiates work buffer and returns sample rate.
if ( (u16SampleRate = NuLiteEx_DecodeInitiate( (UINT8*)psNuLiteExAppDecode->au32DecodeWorkBuf,
psNuLiteExAppDecode->pau8TempBuf,
u32NuLiteExStorageStartAddr,
g_asAppCallBack[psNuLiteExAppDecode->u8CallbackIndex].pfnReadDataCallback )) == )
return FALSE; // 给Playback模块对接对应的工作缓冲区,方便其下一步播放。
// Initiate and set output buffer variable(include frame size, buffer size etc.)
Playback_SetOutputBuf( &psNuLiteExAppDecode->sOutBufCtrl,
NULITEEXAPP_OUT_BUF_SIZE,
psNuLiteExAppDecode->i16OutBuf,
NULITEEXAPP_OUT_SAMPLES_PER_FRAME,
u16SampleRate ); // 工作缓冲区,置有效位。
// Trigger active flag of output buffer for NuLiteEx decoding
BUF_CTRL_SET_ACTIVE(&psNuLiteExAppDecode->sOutBufCtrl); // 工作缓冲区中的读写指针赋值。
// Pre-decode one frame
psNuLiteExAppDecode->sOutBufCtrl.u16BufWriteIdx = NULITEEXAPP_OUT_SAMPLES_PER_FRAME;
if ( NuLiteExApp_DecodeProcess(psNuLiteExAppDecode) == FALSE )
{
BUF_CTRL_SET_INACTIVE(&psNuLiteExAppDecode->sOutBufCtrl);
return FALSE;
}
psNuLiteExAppDecode->sOutBufCtrl.u16BufReadIdx = NULITEEXAPP_OUT_SAMPLES_PER_FRAME; // 记录当前播放的channel,用来停止播放。
// Record play channel index for stopping to play.
psNuLiteExAppDecode->u8PlaybackChannel = u8PlaybackChannel;
// 准备播放,把这里的循环缓冲区同playback共用。
// Add audio codec into channel and preper to play codec.
Playback_Add(psNuLiteExAppDecode->u8PlaybackChannel, &psNuLiteExAppDecode->sOutBufCtrl); return TRUE;
}

也很重要的Playback_StartPlay

void Playback_StartPlay(void)
{
INT16 *pi16PcmBuf; if( s_u8PlayCtrl == PLAYBACK_NOACTION ) // 这个s_u8PlayCtrl是playback模块内部处理的。
{
#if ( PLAYBACK_CHANNEL_COUNT > 1)
pi16PcmBuf = g_ai16DACSamples;
#else
pi16PcmBuf = &g_psDacBufCtrl->pi16Buf[g_psDacBufCtrl->u16BufReadIdx];// PCM数据缓冲区复制。
#endif #if ((APU_FILTER_ENABLE == 1)&&(APU_UPSAMPLE == 2))
NuDACFilterEx_Up2Initial(g_au8Up2WorkBuf);
#elif ((APU_FILTER_ENABLE == 1)&&(APU_UPSAMPLE == 4))
NuDACFilterEx_Up4Initial(g_au8Up4WorkBuf);
#endif
g_u8AppCtrl|=APPCTRL_PLAY;
s_u8PlayCtrl |= PLAYBACK_START;
#if (APU_ENABLE)
{
UINT8 u8Count; for( u8Count = ; u8Count < ; u8Count ++)
g_ai16DACSamples[u8Count] = ; //Clear virtual buffer
}
#endif Playback_ResetChannelVolume(); SPK_Start(); // 这里头开始调用DPWM来播放DPWM->DATA,DPWM_START_PLAY(DPWM); #if (APU_PDMA_ENABLE)
PdmaCtrl_Start(APU_PDMA_CH, (uint32_t *)pi16PcmBuf, (uint32_t *)&DPWM->DATA, );// 将PCM缓冲数据传到DPWM->DATA中。
#endif }
}

第二部分 App_ProcessPlay

App_ProcessPlay只调用了如下这个函数

BOOL NuLiteExApp_DecodeProcess(S_NULITEEX_APP_DECODE *psNuLiteExAppDecode)
{
INT16 *pi16OutBuf; // 环形缓冲区非激活状态,这个只有在应用层置位(按键停止、或者启动失败等情况)
if (BUF_CTRL_IS_INACTIVE(&psNuLiteExAppDecode->sOutBufCtrl))
return FALSE; // 环形缓冲区还有未读数据
if ( Playback_NeedUpdateOutputBuf(&psNuLiteExAppDecode->sOutBufCtrl) )
{
// 由核心库来判断这个文件是否解析完了
// Check end of file
if(NuLiteEx_DecodeIsEnd((UINT8*)psNuLiteExAppDecode->au32DecodeWorkBuf))
{
// Trigger inactive flag of output buffer to stop NuLiteEx decoding
BUF_CTRL_SET_INACTIVE(&psNuLiteExAppDecode->sOutBufCtrl);
// Use to represnt no active(or end) of decoding
psNuLiteExAppDecode->sOutBufCtrl.u16SampleRate = ;
return FALSE;
} // Record output data buffer pointer(for duplicate & process)
pi16OutBuf = (PINT16)&psNuLiteExAppDecode->sOutBufCtrl.pi16Buf[psNuLiteExAppDecode->sOutBufCtrl.u16BufWriteIdx]; // 核心库继续发挥其巨大作用,开足马力读取文件中PCM数据转到缓冲区。
NuLiteEx_DecodeProcess( (UINT8*)psNuLiteExAppDecode->au32DecodeWorkBuf,
psNuLiteExAppDecode->pau8TempBuf,
pi16OutBuf,
g_asAppCallBack[psNuLiteExAppDecode->u8CallbackIndex].pfnReadDataCallback,
g_asAppCallBack[psNuLiteExAppDecode->u8CallbackIndex].pfnUserEventCallback); // PlayBack依旧共享这个缓冲区,准备对数据进行进一步处理
// Update write index of output buffer and avoid buffer overflow
Playback_UpdateOutputBuf(&psNuLiteExAppDecode->sOutBufCtrl); // Duplicate data into buffer for using duplication callback function.
if ( psNuLiteExAppDecode->u8CtrlFlag&(NULITEEXAPP_CTRL_DUPLICATE_TO_BUF|NULITEEXAPP_CTRL_DUPLICATE_TO_FUNC) )
{
if ( psNuLiteExAppDecode->u8CtrlFlag & NULITEEXAPP_CTRL_DUPLICATE_TO_BUF )
BufCtrl_WriteWithCount(psNuLiteExAppDecode->psDuplicateOutBufCtrl, NULITEEXAPP_OUT_SAMPLES_PER_FRAME, pi16OutBuf );
else
psNuLiteExAppDecode->pfnDuplicateFunc(NULITEEXAPP_OUT_SAMPLES_PER_FRAME, pi16OutBuf);
}
}
return TRUE;
}

总结

源码拓扑结构

ISD9160学习笔记03_ISD9160音频解码代码分析的更多相关文章

  1. ISD9160学习笔记04_ISD9160音频编码代码分析

    前言 录音例程涉及了录音和播放两大块内容,上篇笔记说了播放,这篇就来说说录音这块,也就是音频编码这部分功能. 上篇笔记中的这段话太装逼了,我决定再复制下,嘿嘿. “我的锤子便签中有上个月记下的一句话, ...

  2. memcached学习笔记——存储命令源码分析上篇

    原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...

  3. input子系统学习笔记六 按键驱动实例分析下【转】

    转自:http://blog.chinaunix.net/uid-20776117-id-3212095.html 本文接着input子系统学习笔记五 按键驱动实例分析上接续分析这个按键驱动实例! i ...

  4. tensorflow笔记:多层LSTM代码分析

    tensorflow笔记:多层LSTM代码分析 标签(空格分隔): tensorflow笔记 tensorflow笔记系列: (一) tensorflow笔记:流程,概念和简单代码注释 (二) ten ...

  5. memcached学习笔记——存储命令源码分析下篇

    上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...

  6. ArcGIS案例学习笔记2_1_学校选址适宜性分析

    ArcGIS案例学习笔记2_1_学校选址适宜性分析 计划时间:第二天上午 目的:学校选址,适宜性分析 内容:栅格数据分析 教程:pdf page=323 数据:chapter8/ex1/教育,生活,土 ...

  7. tensorflow笔记:多层CNN代码分析

    tensorflow笔记系列: (一) tensorflow笔记:流程,概念和简单代码注释 (二) tensorflow笔记:多层CNN代码分析 (三) tensorflow笔记:多层LSTM代码分析 ...

  8. ISD9160学习笔记05_ISD9160语音识别代码分析

    前言 语音识别是特别酷的功能,ISD9160的核心卖点就是这个语音识别,使用了Cybron VR 算法. 很好奇这颗10块钱以内的IC是如何实现人家百来块钱的方案.且听如下分析. 本文作者twowin ...

  9. 学习笔记:python3,代码。小例子习作(2017)

    http://www.cnblogs.com/qq21270/p/7634025.html 学习笔记:python3,一些基本语句(一些基础语法的代码,被挪到这里了) 日期和时间操作 http://b ...

随机推荐

  1. 《FPGA全程进阶---实战演练》第二十一章之 几种常用电平分析及特性

    TTL,CMOS以及LVTTL,LVCMOS TTL和CMOS是数字电路中两种常见的逻辑电平,LVTTL和LVCMOS是两者低电平版本.TTL是流控器件,输入电阻小,TTL电平器件速度快,驱动能力大, ...

  2. 访问网站出现EOF

    HTTP/0.0 503 Service Unavailable Date: Tuesday, 18-Apr-17 10:29:46 CST Keep-Alive: timeout=38 EOF 今天 ...

  3. checkbox批量删除功能

    参考:http://www.jb51.net/article/35338.htm 感谢作者 代码片段:jsp <c:forEach items="${infolist }" ...

  4. (笔记)Linux下的准确延时,#include <linux/delay.h>调用出错

    在编写应用层程序时,有时需要延时一下,这个时候该怎么办呢? 在内核代码中,我们经常会看到这样的头文件使用#include <linux/delay.h>,心想着直接调用这个就可以了吧!可是 ...

  5. 锐捷 Fat/Fit Ap切换

    工作中要使用锐捷的AP和AC进行组网.记录一下RG-AP220-E配置成瘦AP的方法. 使用console口连接,baudrate rate: 9600, 8n1 瘦AP:console密码是ruij ...

  6. 对C语言中指针的入门理解

    通过一个例子引出对指针的概念理解 1,例子 #include<stdio.h> int main(void) { ; //小张的身高 ; //小李的身高 ; //小王的身高 int *xi ...

  7. Linux-HA实战(3)— 基于Pacemaker搭建TFS Nameserver HA

    上篇文章简单介绍了下基于Heartbeat的虚拟IP配置,个人觉得简单易用已经能够应付大部分场景了.但是既然花时间研究HA,如果仅限于一个虚拟IP飘来飘去未免有点糊弄任务了,因此这篇文章打算介绍下基于 ...

  8. C#或者.NET下的强制垃圾回收办法

    转载 2011年03月16日 17:21:00 标签: c# / .net / button / object / stream / class 8185 今天来谈谈C#的GC,也就是垃圾回收机制,非 ...

  9. unity---各种资源对应的格式

    我们打包AssetBundle后,Unity3D会根据文件的后缀名将文件转换为特定的类型对象存储起来,我们后期获取时需要根据这些类型取出打包的数据,这里记录下不同后缀文件打包后的类型. 文本格式 支持 ...

  10. 【转】Castle Windsor之组件注册

    [转]Castle Windsor之组件注册 注册方式较多,大体有这么几种,学习得比较粗浅,先记录: 1.逐个注册组件 即对每个接口通过代码指定其实现类,代码: container.Register( ...