本帖最后由 734774645 于 2017-6-8 14:41 编辑
前言录音例程涉及了录音和播放两大块内容,这篇笔记就先来说说播放,暂且先击**码这部分功能。 我的锤子便签中有上个月记下的一句话,“斯蒂芬·平克说,写作之难,在于把网状思考,用树状结构,体现在线性展开的语句里。”这篇代码解析也有类似的困难,代码的网状结构,如何用**这种线性载体来体现。我尽量挑出了主干,来讲解自己的理解。另外在**最后添加了一个模块拓扑图来帮助消化。 我还是建议大家还是多琢磨下源码,代码的事还是让代码来说话,笔记是一个辅助的概括梳理。 本文作者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, 0);
- // 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, 0) == FALSE )
- return FALSE;
- // Light playback led(PB9) for display status.
- OUT4(0);
- // 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 )) == 0 )
- 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 = 0; u8Count < 8; u8Count ++)
- g_ai16DACSamples[u8Count] = 0; //Clear virtual buffer
- }
- #endif
- Playback_ResetChannelVolume(0);
- 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, 8);// 将PCM缓冲数据传到DPWM->DATA中。
- #endif
- }
- }
|