打印
[应用相关]

读STSW-STLKT01的AudioLoop例程 看看AudioLoop是如何把麦克风数据...

[复制链接]
413|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
elephant00|  楼主 | 2020-12-14 10:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

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

试着修改两个变量的符号类型对演示没产生明显影响

还希望有经验的大神给分析分析


使用特权

评论回复
沙发
wanduzi| | 2020-12-14 22:28 | 只看该作者
没用过这个东西。

使用特权

评论回复
板凳
两只袜子| | 2020-12-15 16:38 | 只看该作者

虾哥,这个代码外面的框框是怎么弄出来的?

使用特权

评论回复
地板
jcky001| | 2020-12-15 16:39 | 只看该作者
学习 马克 威武

使用特权

评论回复
5
guanjiaer| | 2021-1-9 14:38 | 只看该作者
main函数岂不是很庞大

使用特权

评论回复
6
heimaojingzhang| | 2021-1-9 14:40 | 只看该作者
把麦克风数据怎么了

使用特权

评论回复
7
keaibukelian| | 2021-1-9 14:41 | 只看该作者
解释的太详细了 非常清楚

使用特权

评论回复
8
labasi| | 2021-1-9 14:46 | 只看该作者
数据类型用对了非常重要

使用特权

评论回复
9
paotangsan| | 2021-1-9 14:56 | 只看该作者
整个程序的走向非常清晰

使用特权

评论回复
10
麻花油条| | 2021-2-1 11:23 | 只看该作者

解释的 非常清楚

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

926

主题

2514

帖子

4

粉丝