ARM技术william hill官网
直播中

twowinter

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

【Nuvoton ISD9160语音识别试用体验】ISD9160音频编码(录音)代码分析

## 前言

录音例程涉及了录音和播放两大块内容,上篇笔记说了播放,这篇就来说说录音这块,也就是音频编码这部分功能。

上篇笔记中的这段话太装逼了,我决定再复制下,嘿嘿。

"我的锤子便签中有上个月记下的一句话,“斯蒂芬·平克说,写作之难,在于把网状思考,用树状结构,体现在线性展开的语句里。”这篇代码解析也有类似的困难,代码的网状结构,如何用文章这种线性载体来体现。"

跟上篇笔记的做法一样,还是挑出了主干,来讲下自己的理解。另外在文章最后添加了一个模块拓扑图来帮助消化。

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


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

## 第一部分 App_StartRec

App_StartRec是由按键触发的,也就是按键按下去就开始录音。

  1. BOOL App_StartRec(void)
  2.         {
  3.                 S_AUDIOCHUNK_HEADER sAudioChunkHeader;
  4.                
  5.                 // 这回就不是Decode了,改用Encode。
  6.                 // Initiate NuLiteEx audio encode lib with temp buffer provided for lib.
  7.                 NuLiteExApp_EncodeInitiate(&g_sApp.sNuLiteExAppEncode, (UINT8 *)&g_sApp.uTempBuf);
  8.                
  9.                 // Start to encode NuLiteEx data with sample rate, bit per frame and saved data information into audio chunk header.
  10.                 if (NuLiteExApp_EncodeStart(&g_sApp.sNuLiteExAppEncode, &sAudioChunkHeader, ADC_SAMPLE_RATE, E_NULITEEX_ENCODE_BPS_10) == FALSE)
  11.                         return FALSE;
  12.                
  13.                 // SPIFlash utility function provide encode data write into SPIFlash.
  14.                 // detail info please refer "SPIFlashUtil.h"
  15.                 SPIFlahUtil_StartWriteEncodeData(&sAudioChunkHeader, AUDIOROM_STORAGE_START_ADDR, NULL);
  16.                
  17.                 // Light record led for display status.
  18.                 OUT5(0);
  19.                                                                
  20.                 // Start to record PCM data into buffer for produc NuLiteEx encode data.
  21.                 Record_StartRec();
  22.                
  23.                 return TRUE;       
  24.         }

可以看到App_StartRec主要牵扯了NuLiteExApp_EncodeStart和Record两部分子函数。

### 重中之重NuLiteExApp_EncodeStart

NuLiteExApp_EncodeStart(&g_sApp.sNuLiteExAppEncode, &sAudioChunkHeader, ADC_SAMPLE_RATE, E_NULITEEX_ENCODE_BPS_10);照例对代码做了中文注解方便消化。

g_sApp.sNuLiteExAppEncode是全局变量,涉及编码库的操作。sAudioChunkHeader是一个临时变量,用来做音频块头部信息。

  1. BOOL NuLiteExApp_EncodeStart( S_NULITEEX_APP_ENCODE *psNuLiteExAppEncode, S_AUDIOCHUNK_HEADER *psAudioChunkHeader,
  2.                                                                   UINT16 u16SampleRate, enum eNuLiteExEncodeBPS eBitPerSample)
  3.         {
  4.                 if ( (eBitPerSample > NULITEEXAPP_ENCODE_MAX_BITRATE) || (u16SampleRate == 0) )
  5.                         return FALSE;
  6.                 else
  7.                 {
  8.                         psAudioChunkHeader->u16SmplRate = u16SampleRate;
  9.                         psAudioChunkHeader->u32BitPerFrame = eBitPerSample;
  10.                 }
  11.                
  12.                 // 将全局变量申请的内存传入给编码库,方便其工作执行。将音频块头部传入,编码库最后的输出结果会体现在这里。另外的采样率信息是输入变量,辅助生成最后的音频块。
  13.                 // NuLiteEx encoder initiates work buffer.
  14.                 // Set bit rate and sample rate information for audio chunk header.
  15.                 NuLiteEx_EncodeInitiate((UINT8 *)psNuLiteExAppEncode->au32WorkBuf, psNuLiteExAppEncode->pau8TempBuf,
  16.                         psAudioChunkHeader, (enum eNuLiteExEncodeBPS)psAudioChunkHeader->u32BitPerFrame, psAudioChunkHeader->u16SmplRate);
  17.                                
  18.                 // Reset encode buffer read index and write index
  19.                 psNuLiteExAppEncode->sEncodeBufCtrl.u16BufWriteIdx = 0;
  20.                 psNuLiteExAppEncode->sEncodeBufCtrl.u16BufReadIdx = 0;
  21.                
  22.                 // Set Encoded frame size, Storage Utility will refer to this size to write data.
  23.                 psNuLiteExAppEncode->sEncodeBufCtrl.u16FrameSize =  (psAudioChunkHeader->u32BitPerFrame)>>3;
  24.                 psNuLiteExAppEncode->sEncodeBufCtrl.u16BufCount = (psNuLiteExAppEncode->sEncodeBufCtrl.u16FrameSize)*NULITEEXAPP_ENCODE_BUF_COUNT;
  25.                
  26.                 // 这一步很关键,设置录音模块部分的缓存。
  27.                 // Set input buffer size, PCM buffer pointer, frame size and sample rate.
  28.                 Record_SetInBufRecord(  &psNuLiteExAppEncode->sInBufCtrl,
  29.                                                                 NULITEEXAPP_IN_BUF_SIZE,
  30.                                                                 psNuLiteExAppEncode->i16InBuf,
  31.                                                                 NULITEEX_ENCODE_SAMPLE_PER_FRAME,
  32.                                                                 psAudioChunkHeader->u16SmplRate);
  33.                
  34.         // 都要做这一步操作,录音模块的操作顺序就是这样:SetInBufRecord ->Add -> StartRec。
  35.                 // Set application input buffer to record(ADC) output buffer.
  36.                 Record_Add(&psNuLiteExAppEncode->sInBufCtrl, psAudioChunkHeader->u16SmplRate);
  37.                
  38.                 return TRUE;
  39.         }

       
### 也很重要的Record_StartRec

上面说录音模块的操作顺序就是这样:SetInBufRecord ->Add -> StartRec。这就来了。
硬件PDMA这里头涉及一个关键函数,PDMA会把ADC数据直接放到s_pi16AdcBuf里头。

  1. void Record_StartRec(void)
  2.         {
  3.                
  4.                 g_u8AppCtrl |= APPCTRL_RECORD;

  5.                 #if (ADC_FILTER_ENABLE == 1)
  6.                 NoiseFilter_ResetIIR2();
  7.                 s_pi16AdcBuf = g_ai16ADCSamples;
  8.                 #else
  9.                 // 主要是缓冲区的处理
  10.                 if ( BUF_CTRL_ISNOT_CALLBACK(g_psAdcBufCtrl))       
  11.                         s_pi16AdcBuf = &g_psAdcBufCtrl->pi16Buf[g_psAdcBufCtrl->u16BufWriteIdx];
  12.                 else
  13.                         s_pi16AdcBuf = g_ai16ADCSamples;
  14.                 #endif
  15.                
  16.                 // 采用了硬件PDMA的方式
  17.                 #if (ADC_PDMA_ENABLE == 1)       
  18.                 PdmaCtrl_Start(ADC_PDMA_CH, (uint32_t *)&ADC->DAT, (uint32_t *)s_pi16AdcBuf, 8);
  19.                 #endif

  20.                 MIC_Start();
  21.         }


## 第二部分 App_ProcessRec
这个部分调用了这个关键函数。SPIFlashUtil_WriteEncodeData。主线程会在大部分时间执行SPIFlashUtil_4KPagePartialWriting,将缓冲逐步写入到SPI FLASH中。

另一个关键函数是NuLiteExApp_EncodeProcess。这是NuLiteEx库的一个编码处理应用。

  1. // Keep encode PCM buffer data to NuLiteEx lib.
  2.         NuLiteExApp_EncodeProcess(&g_sApp.sNuLiteExAppEncode);


其在内部调用了NuLiteEx_EncodeProcess,这是NuLiteEx库的API。


       
## 总结
源码拓扑结构



更多回帖

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