ARM技术william hill官网
直播中

twowinter

8年用户 29经验值
擅长:嵌入式技术 RF/无线
私信 关注
[经验]

【Nuvoton ISD9160语音识别试用体验】ISD9160音频解码代码分析

本帖最后由 twowinter 于 2016-11-22 12:48 编辑

## 前言

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

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

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

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


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

## 第一部分 App_StartPlay

  1. BOOL App_StartPlay(void)
  2. {
  3.                 // Initiate NuLiteEx audio decode lib with callback functions stored in g_asAppCallBack[0]
  4.                 NuLiteExApp_DecodeInitiate(&g_sApp.sNuLiteExAppDecode, (UINT8 *)&g_sApp.uTempBuf, 0);
  5.                
  6.                 // Start NuLiteEx decode lib to decode NuLiteEx file stored from address and played from audio channel 0.
  7.                 // And decode the first frame of PCMs.
  8.                 if ( NuLiteExApp_DecodeStartPlayByAddr(&g_sApp.sNuLiteExAppDecode, AUDIOROM_STORAGE_START_ADDR, 0) == FALSE )
  9.                         return FALSE;

  10.                 // Light playback led(PB9) for display status.
  11.                 OUT4(0);
  12.                
  13.                 // Start Ultraio Timer & HW pwm for UltraIO curve output
  14.                 ULTRAIO_START();
  15.                
  16.                 // Start to playback audio.
  17.                 Playback_StartPlay();
  18. }

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

### 重中之重 NuLiteExApp_DecodeStartPlayByAddr

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

  1. BOOL NuLiteExApp_DecodeStartPlayByAddr(S_NULITEEX_APP_DECODE *psNuLiteExAppDecode, UINT32 u32NuLiteExStorageStartAddr, UINT8 u8PlaybackChannel)
  2. {
  3.                 UINT16 u16SampleRate;
  4.                 // NuLiteEx解码库初始化对应的工作缓冲区,应用层传入temp缓存来方便解码库内部工作。另外根据传入的SPI地址从SPI取文件,获取采样率。
  5.                 // NuLiteEx decoder initiates work buffer and returns sample rate.
  6.                 if ( (u16SampleRate = NuLiteEx_DecodeInitiate(        (UINT8*)psNuLiteExAppDecode->au32DecodeWorkBuf,
  7.                                                                                                                 psNuLiteExAppDecode->pau8TempBuf,
  8.                                                                                                                 u32NuLiteExStorageStartAddr,
  9.                                                                                                                 g_asAppCallBack[psNuLiteExAppDecode->u8CallbackIndex].pfnReadDataCallback )) == 0 )
  10.                         return FALSE;        

  11.                 // 给Playback模块对接对应的工作缓冲区,方便其下一步播放。
  12.                 // Initiate and set output buffer variable(include frame size, buffer size etc.)
  13.                 Playback_SetOutputBuf(         &psNuLiteExAppDecode->sOutBufCtrl,
  14.                                                                 NULITEEXAPP_OUT_BUF_SIZE,
  15.                                                                 psNuLiteExAppDecode->i16OutBuf,
  16.                                                                 NULITEEXAPP_OUT_SAMPLES_PER_FRAME,
  17.                                                                 u16SampleRate );
  18.                
  19.                 // 工作缓冲区,置有效位。
  20.                 // Trigger active flag of output buffer for NuLiteEx decoding
  21.                 BUF_CTRL_SET_ACTIVE(&psNuLiteExAppDecode->sOutBufCtrl);

  22.                 // 工作缓冲区中的读写指针赋值。
  23.                 // Pre-decode one frame
  24.                 psNuLiteExAppDecode->sOutBufCtrl.u16BufWriteIdx = NULITEEXAPP_OUT_SAMPLES_PER_FRAME;
  25.                 if ( NuLiteExApp_DecodeProcess(psNuLiteExAppDecode) == FALSE )
  26.                 {
  27.                         BUF_CTRL_SET_INACTIVE(&psNuLiteExAppDecode->sOutBufCtrl);
  28.                         return FALSE;
  29.                 }
  30.                 psNuLiteExAppDecode->sOutBufCtrl.u16BufReadIdx = NULITEEXAPP_OUT_SAMPLES_PER_FRAME;
  31.                
  32.                 // 记录当前播放的channel,用来停止播放。
  33.                 // Record play channel index for stopping to play.
  34.                 psNuLiteExAppDecode->u8PlaybackChannel = u8PlaybackChannel;
  35.                 // 准备播放,把这里的循环缓冲区同playback共用。
  36.                 // Add audio codec into channel and preper to play codec.
  37.                 Playback_Add(psNuLiteExAppDecode->u8PlaybackChannel, &psNuLiteExAppDecode->sOutBufCtrl);

  38.                 return TRUE;
  39. }

        
### 也很重要的Playback_StartPlay

  1. void Playback_StartPlay(void)
  2. {
  3.                 INT16 *pi16PcmBuf;
  4.                
  5.                 if( s_u8PlayCtrl == PLAYBACK_NOACTION ) // 这个s_u8PlayCtrl是playback模块内部处理的。
  6.                 {
  7.                         #if ( PLAYBACK_CHANNEL_COUNT > 1)
  8.                         pi16PcmBuf = g_ai16DACSamples;
  9.                         #else
  10.                         pi16PcmBuf = &g_psDacBufCtrl->pi16Buf[g_psDacBufCtrl->u16BufReadIdx];// PCM数据缓冲区复制。
  11.                         #endif
  12.                         
  13.                         #if ((APU_FILTER_ENABLE == 1)&&(APU_UPSAMPLE == 2))
  14.                         NuDACFilterEx_Up2Initial(g_au8Up2WorkBuf);
  15.                         #elif ((APU_FILTER_ENABLE == 1)&&(APU_UPSAMPLE == 4))
  16.                         NuDACFilterEx_Up4Initial(g_au8Up4WorkBuf);
  17.                         #endif
  18.                         g_u8AppCtrl|=APPCTRL_PLAY;
  19.                         s_u8PlayCtrl |= PLAYBACK_START;
  20.                         #if (APU_ENABLE)
  21.                         {
  22.                                 UINT8 u8Count;
  23.                                 
  24.                                 for( u8Count = 0; u8Count < 8; u8Count ++)
  25.                                         g_ai16DACSamples[u8Count] = 0;                //Clear virtual buffer
  26.                         }
  27.                         #endif
  28.                         
  29.                         Playback_ResetChannelVolume(0);
  30.                         
  31.                         SPK_Start(); // 这里头开始调用DPWM来播放DPWM->DATA,DPWM_START_PLAY(DPWM);
  32.                         
  33.                         #if (APU_PDMA_ENABLE)
  34.                         PdmaCtrl_Start(APU_PDMA_CH, (uint32_t *)pi16PcmBuf, (uint32_t *)&DPWM->DATA, 8);// 将PCM缓冲数据传到DPWM->DATA中。
  35.                         #endif
  36.                         
  37.                 }
  38. }

## 第二部分 App_ProcessPlay

App_ProcessPlay只调用了如下这个函数
        
  1. // Continue decode NuLiteEx data to produce PCMs for audio playback.
  2. if ( NuLiteExApp_DecodeProcess(&g_sApp.sNuLiteExAppDecode) == TRUE )


        
这个函数拆解进去是这样:
        
  1. BOOL NuLiteExApp_DecodeProcess(S_NULITEEX_APP_DECODE *psNuLiteExAppDecode)
  2. {
  3.                 INT16 *pi16OutBuf;
  4.                
  5.                 // 环形缓冲区非激活状态,这个只有在应用层置位(按键停止、或者启动失败等情况)
  6.                 if (BUF_CTRL_IS_INACTIVE(&psNuLiteExAppDecode->sOutBufCtrl))
  7.                         return FALSE;
  8.                
  9.                 // 环形缓冲区还有未读数据
  10.                 if ( Playback_NeedUpdateOutputBuf(&psNuLiteExAppDecode->sOutBufCtrl) )
  11.                 {
  12.                         // 由核心库来判断这个文件是否解析完了
  13.                         // Check end of file
  14.                         if(NuLiteEx_DecodeIsEnd((UINT8*)psNuLiteExAppDecode->au32DecodeWorkBuf))
  15.                         {
  16.                                 // Trigger inactive flag of output buffer to stop NuLiteEx decoding
  17.                                 BUF_CTRL_SET_INACTIVE(&psNuLiteExAppDecode->sOutBufCtrl);
  18.                                 // Use to represnt no active(or end) of decoding
  19.                                 psNuLiteExAppDecode->sOutBufCtrl.u16SampleRate = 0;
  20.                                 return FALSE;
  21.                         }

  22.                         // Record output data buffer pointer(for duplicate & process)
  23.                         pi16OutBuf = (PINT16)&psNuLiteExAppDecode->sOutBufCtrl.pi16Buf[psNuLiteExAppDecode->sOutBufCtrl.u16BufWriteIdx];
  24.                         
  25.                         // 核心库继续发挥其巨大作用,开足马力读取文件中PCM数据转到缓冲区。
  26.                         NuLiteEx_DecodeProcess(        (UINT8*)psNuLiteExAppDecode->au32DecodeWorkBuf,
  27.                                                                         psNuLiteExAppDecode->pau8TempBuf,
  28.                                                                         pi16OutBuf,
  29.                                                                         g_asAppCallBack[psNuLiteExAppDecode->u8CallbackIndex].pfnReadDataCallback,
  30.                                                                         g_asAppCallBack[psNuLiteExAppDecode->u8CallbackIndex].pfnUserEventCallback);

  31.                         // PlayBack依旧共享这个缓冲区,准备对数据进行进一步处理
  32.                         // Update write index of output buffer and avoid buffer overflow
  33.                         Playback_UpdateOutputBuf(&psNuLiteExAppDecode->sOutBufCtrl);

  34.                         // Duplicate data into buffer for using duplication callback function.
  35.                         if ( psNuLiteExAppDecode->u8CtrlFlag&(NULITEEXAPP_CTRL_DUPLICATE_TO_BUF|NULITEEXAPP_CTRL_DUPLICATE_TO_FUNC) )
  36.                         {
  37.                                 if ( psNuLiteExAppDecode->u8CtrlFlag & NULITEEXAPP_CTRL_DUPLICATE_TO_BUF )
  38.                                         BufCtrl_WriteWithCount(psNuLiteExAppDecode->psDuplicateOutBufCtrl, NULITEEXAPP_OUT_SAMPLES_PER_FRAME, pi16OutBuf );
  39.                                 else
  40.                                         psNuLiteExAppDecode->pfnDuplicateFunc(NULITEEXAPP_OUT_SAMPLES_PER_FRAME, pi16OutBuf);
  41.                         }               
  42.                 }        
  43.                 return TRUE;
  44. }

## 总结

源码拓扑结构




回帖(1)

黄叶凡

2017-1-5 20:17:05
收益颇多  谢谢楼主
举报

更多回帖

发帖
×
20
完善资料,
赚取积分