您好,歡迎進入深圳市穎特新科技有限公司官方網(wǎng)站!
錄音例程涉及了錄音和播放兩大塊內(nèi)容,這篇筆記就先來說說播放,暫且先擊破解碼這部分功能。
我的錘子便簽中有上個月記下的一句話,“斯蒂芬·平克說,寫作之難,在于把網(wǎng)狀思考,用樹狀結(jié)構(gòu),體現(xiàn)在線性展開的語句里。”這篇代碼解析也有類似的困難,代碼的網(wǎng)狀結(jié)構(gòu),如何用文章這種線性載體來體現(xiàn)。我盡量挑出了主干,來講解自己的理解。另外在文章最后添加了一個模塊拓撲圖來幫助消化。
我還是建議大家還是多琢磨下源碼,代碼的事還是讓代碼來說話,筆記是一個輔助的概括梳理。
本文作者twowinter,轉(zhuǎn)載請注明:http://blog.csdn.net/iotisan/
查看代碼主邏輯,主要是App_StartPlay和App_ProcessPlay這兩個函數(shù)。下面就分別進行分析。
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兩部分子函數(shù)。
由于對音頻編解碼這塊比較陌生,我還是給對應(yīng)代碼做了中文注解方便消化。
BOOL NuLiteExApp_DecodeStartPlayByAddr(S_NULITEEX_APP_DECODE *psNuLiteExAppDecode, UINT32 u32NuLiteExStorageStartAddr, UINT8 u8PlaybackChannel) { UINT16 u16SampleRate; // NuLiteEx解碼庫初始化對應(yīng)的工作緩沖區(qū),應(yīng)用層傳入temp緩存來方便解碼庫內(nèi)部工作。另外根據(jù)傳入的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模塊對接對應(yīng)的工作緩沖區(qū),方便其下一步播放。 // 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 ); // 工作緩沖區(qū),置有效位。 // Trigger active flag of output buffer for NuLiteEx decoding BUF_CTRL_SET_ACTIVE(&psNuLiteExAppDecode->sOutBufCtrl); // 工作緩沖區(qū)中的讀寫指針賦值。 // 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; // 準備播放,把這里的循環(huán)緩沖區(qū)同playback共用。 // Add audio codec into channel and preper to play codec. Playback_Add(psNuLiteExAppDecode->u8PlaybackChannel, &psNuLiteExAppDecode->sOutBufCtrl); return TRUE; }
void Playback_StartPlay(void) { INT16 *pi16PcmBuf; if( s_u8PlayCtrl == PLAYBACK_NOACTION ) // 這個s_u8PlayCtrl是playback模塊內(nèi)部處理的。 { #if ( PLAYBACK_CHANNEL_COUNT > 1) pi16PcmBuf = g_ai16DACSamples; #else pi16PcmBuf = &g_psDacBufCtrl->pi16Buf[g_psDacBufCtrl->u16BufReadIdx];// PCM數(shù)據(jù)緩沖區(qū)復(fù)制。 #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(); // 這里頭開始調(diào)用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緩沖數(shù)據(jù)傳到DPWM->DATA中。 #endif } }
App_ProcessPlay只調(diào)用了如下這個函數(shù)
BOOL NuLiteExApp_DecodeProcess(S_NULITEEX_APP_DECODE *psNuLiteExAppDecode) { INT16 *pi16OutBuf; // 環(huán)形緩沖區(qū)非激活狀態(tài),這個只有在應(yīng)用層置位(按鍵停止、或者啟動失敗等情況) if (BUF_CTRL_IS_INACTIVE(&psNuLiteExAppDecode->sOutBufCtrl)) return FALSE; // 環(huán)形緩沖區(qū)還有未讀數(shù)據(jù) 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 = 0; return FALSE; } // Record output data buffer pointer(for duplicate & process) pi16OutBuf = (PINT16)&psNuLiteExAppDecode->sOutBufCtrl.pi16Buf[psNuLiteExAppDecode->sOutBufCtrl.u16BufWriteIdx]; // 核心庫繼續(xù)發(fā)揮其巨大作用,開足馬力讀取文件中PCM數(shù)據(jù)轉(zhuǎn)到緩沖區(qū)。 NuLiteEx_DecodeProcess( (UINT8*)psNuLiteExAppDecode->au32DecodeWorkBuf, psNuLiteExAppDecode->pau8TempBuf, pi16OutBuf, g_asAppCallBack[psNuLiteExAppDecode->u8CallbackIndex].pfnReadDataCallback, g_asAppCallBack[psNuLiteExAppDecode->u8CallbackIndex].pfnUserEventCallback); // PlayBack依舊共享這個緩沖區(qū),準備對數(shù)據(jù)進行進一步處理 // 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; }
源碼拓撲結(jié)構(gòu)
掃碼關(guān)注我們
傳真:0755-82591176
郵箱:vicky@yingtexin.net
地址:深圳市龍華區(qū)民治街道民治大道973萬眾潤豐創(chuàng)業(yè)園A棟2樓A08