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 试着修改两个变量的符号类型对演示没产生明显影响 还希望有经验的大神给分析分析
|