AudioLoop的主要代码都集成在main.c中
先来看main.c这个文件
/* Private define ------------------------------------------------------------*/ #define AUDIO_CHANNELS 1 #define AUDIO_SAMPLING_FREQUENCY 48000 #define AUDIO_IN_BUF_LEN (AUDIO_CHANNELS*AUDIO_SAMPLING_FREQUENCY/1000) #define AUDIO_OUT_BUF_LEN (AUDIO_IN_BUF_LEN*8) |
AUDIO_CHANNELS定义了麦克风输入使用了1个通道
AUDIO_SAMPLING_FREQUENCY定义了输入和输出的采样率都是48KHz
AUDIO_IN_BUF_LEN定义了输入缓冲区的数据长度是48(1*4800/1000)
AUDIO_OUT_BUF_LEN定义了输出缓冲区的数据长度是384(48*8)
/* Private variables ---------------------------------------------------------*/
uint16_t PCM_Buffer[AUDIO_IN_BUF_LEN]; volatile int16_t audio_out_buffer[AUDIO_OUT_BUF_LEN]; |
PCM_Buffer用来存放DFSDM外设从麦克风读回的数据
audio_out_buffer用来存放让PCM1774播放的数据
/* Configure Audio Output peripheral (SAI) and external DAC */
BSP_AUDIO_OUT_Init(PCM1774_0, &PCM1774_X_0_handle, NULL, 20, AUDIO_SAMPLING_FREQUENCY); BSP_AUDIO_OUT_SetVolume(PCM1774_X_0_handle, 20);
|
BSP_AUDIO_OUT_Init和BSP_AUDIO_OUT_SetVolume函数用来配置输出和通过I2C接口将配置信息发送至PCM1774 DAC
20是播放音量,范围是0~63
/* Configure Audio Input peripheral - DFSDM */ BSP_AUDIO_IN_Init(AUDIO_SAMPLING_FREQUENCY, 16, AUDIO_CHANNELS);
/* Start Microphone acquisition */ BSP_AUDIO_IN_Record(PCM_Buffer,0);
|
BSP_AUDIO_IN_Init配置麦克风的采样率和通道数
BSP_AUDIO_IN_Record用来启动录音
16在这个例程里无意义
/** * @brief Transfer Complete user callback, called by BSP functions. * @param None * @retval None */ void BSP_AUDIO_IN_TransferComplete_CallBack(void) { AudioProcess(); }
/** * @brief Half Transfer Complete user callback, called by BSP functions. * @param None * @retval None */ void BSP_AUDIO_IN_HalfTransfer_CallBack(void) { AudioProcess(); } |
录音通过2个回调函数通知AudioProcess()处理程序
BSP_AUDIO_IN_HalfTransfer_CallBack表示读取了一半数据
BSP_AUDIO_IN_TransferComplete_CallBack表示读取了全部数据
这里的一半和全部是指DFSDM的DMA数据长度的一半和全部
在SensorTile_audio.c的BSP_AUDIO_IN_Record函数里
DMA的长度实际上是PCM_Buffer[]长度的2倍(SENSORTILE_AudioIn_Handler.Sampling_Freq / 1000 * 2)
uint8_t BSP_AUDIO_IN_Record(uint16_t* pbuf, uint32_t size) { int32_t counter = 0; SENSORTILE_AudioIn_Handler.PCM_Data = pbuf;
for (counter = SENSORTILE_AudioIn_Handler.MicChannels; counter > 0; counter --) { if (HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&haudio_in_dfsdmfilter[counter-1], (int32_t*) RecBuff[counter-1], SENSORTILE_AudioIn_Handler.Sampling_Freq / 1000 * 2)) { return AUDIO_ERROR; } } return AUDIO_OK; } |
所以BSP_AUDIO_IN_HalfTransfer_CallBack和BSP_AUDIO_IN_TransferComplete_CallBack函数都返回一个完整的PCM_Buffer数组
所以2个函数都执行了AudioProcess()用来处理收到的音频数据
void AudioProcess(void) { /*for L4 PDM to PCM conversion is performed in hardware by DFSDM peripheral*/ static uint32_t IndexOut = 0; static uint32_t AudioOutActive = 0; uint32_t indexIn; |
AudioProcess函数内有2个静态变量IndexOut和AudioOutActive和一个局部变量indexIn
IndexOut用来索引输出audio_out_buffer缓冲区
AudioOutActive用来标记是否启动了音频输出
(2个静态变量在下次进入函数时只留上一次设置的数据)
indexIn用来读取PCM_Buffer数据时使用
for(indexIn=0;indexIn<AUDIO_IN_BUF_LEN;indexIn++) { audio_out_buffer[IndexOut++] = PCM_Buffer[indexIn]; audio_out_buffer[IndexOut++] = PCM_Buffer[indexIn]; } |
上边的for循环用来将整个PCM_Buffer的数据复制到audio_out_buffer
audio_out_buffer是一个双通道数组,奇数代表一个通道,偶数代表一个通道
可以看出两个通道使用了相同的数据
PCM_Buffer的长度是audio_out_buffer的1/8
所以要执行4次AudioProcess()函数才能填充到audio_out_buffer的一半
if(!AudioOutActive && IndexOut==AUDIO_OUT_BUF_LEN/2) { BSP_AUDIO_OUT_Play(PCM1774_X_0_handle,(uint16_t*)audio_out_buffer, AUDIO_OUT_BUF_LEN); AudioOutActive=1; }
|
当AudioOutActive不为1时也就是没有执行过BSP_AUDIO_OUT_Play函数时
和输出缓冲区的数据为整个缓冲区数据长度的一半时进入if语句
执行BSP_AUDIO_OUT_Play函数播放audio_out_buffer数组内的音频数据
这时SAI模块会从audio_out_buffer的第0个数据开始依次将数据发送到PCM1774 DAC
输出索引达到AUDIO_OUT_BUF_LEN时再从0开始反复循环
同时将AudioOutActive置1
在重启模块前不会再次进入上边的if判断也不会再执行BSP_AUDIO_OUT_Play函数
可以注意到if语句里判断的是IndexOut==AUDIO_OUT_BUF_LEN/2
而在BSP_AUDIO_OUT_Play函数中传递的长度却是AUDIO_OUT_BUF_LEN
audio_out_buffer的后半部分的数据不是还没有赋值吗?
现在是这样的
别忘了还有BSP_AUDIO_IN_TransferComplete_CallBack和BSP_AUDIO_IN_HalfTransfer_CallBack函数
它们还在不断的把数据通过AudioProcess的for循环把采集到的麦克风数据填充到audio_out_buffer数组
因为音频输入和输出的采样率是一样的
所以在SAI模块还没有播放到audio_out_buffer的1/2位置时后边的数据已经准备好了
当SAI模块播放audio_out_buffer的后半部分数据时前边的数据又更新了
if(IndexOut==AUDIO_OUT_BUF_LEN) { IndexOut=0; } } |
这个if用来判断IndexOut索引是否达到了audio_out_buffer缓冲区的结尾
如果到了再从audio_out_buffer的0处填充
就这样反反复复读取播放,我们就能听到麦克风传来的数据了
另外还有一点疑问
从DFSDM模块读取到PCM_Buffer的数据是int16_t,有符号数据
void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) { uint32_t i, j = 0; if (hdfsdm_filter == &haudio_in_dfsdmfilter[0]) { for(j=0; j < SENSORTILE_AudioIn_Handler.MicChannels; j ++) { for (i = 0; i < SENSORTILE_AudioIn_Handler.Sampling_Freq / 1000; i++) { SENSORTILE_AudioIn_Handler.HP_Filters[j].Z = ((RecBuff[j][i] >> 8) * AudioInVolume) >> 7; SENSORTILE_AudioIn_Handler.HP_Filters[j].oldOut = (0xFC * (SENSORTILE_AudioIn_Handler.HP_Filters[j].oldOut + SENSORTILE_AudioIn_Handler.HP_Filters[j].Z - SENSORTILE_AudioIn_Handler.HP_Filters[j].oldIn)) / 256; SENSORTILE_AudioIn_Handler.HP_Filters[j].oldIn = SENSORTILE_AudioIn_Handler.HP_Filters[j].Z; SENSORTILE_AudioIn_Handler.PCM_Data[i * SENSORTILE_AudioIn_Handler.MicChannels + j] =SaturaLH(SENSORTILE_AudioIn_Handler.HP_Filters[j].oldOut, -32760, 32760); } } BSP_AUDIO_IN_HalfTransfer_CallBack(); } }
|
从上边代码(灰底)可以看出,奇怪的是BSP_AUDIO_IN_Record函数里的参数和PCM_Buffer变量却定义成有符号的
另外audio_out_buffer定义为int16_t可是在BSP_AUDIO_OUT_Play函数的参数里却将它转换成无符号的(uint16_t*)audio_out_buffer
试着修改两个变量的符号类型对演示没产生明显影响
还希望有经验的大神给分析分析
引用: johnrey 发表于 2016-12-25 22:25
虾哥,这个代码外面的框框是怎么弄出来的?