查看: 1594|回复: 1

[原创] 【经验分享】MIMXRT10xx SAI SDCard wave音乐播放

[复制链接]

该用户从未签到

656

主题

6312

帖子

0

超级版主

Rank: 8Rank: 8

积分
20021
最后登录
2024-4-25
发表于 2021-4-8 15:21:47 | 显示全部楼层 |阅读模式
本帖最后由 小恩GG 于 2021-4-8 15:23 编辑

【经验分享】MIMXRT10xx SAI SDCard wave音乐播放
一 文档简介

    上一篇RT10xx SAI模块基础与工程构建测试的经验分享介绍了关于RT1060 SAI模块的基础,工具以及基于MCUXPresso CFGtool的SAI工程构建,上篇文章的音乐播放主要使用ffmpeg工具转换音频格式,然后使用python脚本提取出具体的左右声道音频数据并以数组的形式放到RT内存中实现播放。

    本篇文章基于上篇构建的SAI工程, 添加了SD卡,fatfs系统文件,可以直接实现sd卡中的16bit 16K采样率双声道的wave文件的循环播放。因为文件放到外部SD卡,不需要截取时间,直接实现播放。如果wave格式不满足16bit 16K采样率双声道,依旧可以参考之前的文章,使用ffmpeg转换成对应格式的wave文件:

ffmpeg -i music.mp3 -aq 16 -ar 16000 -ac 2music.wav

二 代码实现

2.1 代码驱动添加

    代码基于SDK_2.9.2_EVK-MIMXRT1060,在上次SAI的基础上添加sdcard驱动,sd fatfs驱动,现在整体baremetal工程驱动情况如下:

Drivers 勾选:

cache,clock, common, dmamux, edma,gpio,i2c,iomuxc,lpuart,sai,sai_edma,sdhc, xip_device

Utilities勾选:

     Debug_console,lpuart_adapter,serial_manager,serial_manager_uart

Middleware勾选:

      File System->FAT File System->fatfs+sd,Memories

Board components 勾选:

      Xip_board

Abstraction Layer勾选:

      Codec,codec_wm8960_adapter,lpi2c_adapter

Software Components勾选:

      Codec_i2c,lists,wm8960


2.2 WAVE 头解析

  从上篇文章了解了WAV的头结构,所以需要播放wave文件,需要先解析头,知道wave文件的音频格式,音频数据大小,头解析代码如下:

  1. uint8_t Fun_Wave_Header_Analyzer(void)
  2. {
  3.     char * datap;
  4.     uint8_t ErrFlag = 0;


  5.     datap = strstr((char*)Wav_HDBuffer,"RIFF");
  6.      if(datap != NULL)
  7.      {
  8.              wav_header.chunk_size = ((uint32_t)*(Wav_HDBuffer+4)) + (((uint32_t)*(Wav_HDBuffer + 5)) << 8) + (((uint32_t)*(Wav_HDBuffer + 6)) << 16) +(((uint32_t)*(Wav_HDBuffer + 7)) << 24);
  9.              movecnt += 8;

  10.      }
  11.      else
  12.      {
  13.             ErrFlag = 1;
  14.         return ErrFlag;
  15.      }

  16.          datap = strstr((char*)(Wav_HDBuffer+movecnt),"WAVEfmt");
  17.          if(datap != NULL)
  18.          {
  19.                  movecnt += 8;
  20.                  wav_header.fmtchunk_size = ((uint32_t)*(Wav_HDBuffer+movecnt+0)) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 1)) << 8) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 2)) << 16) +(((uint32_t)*(Wav_HDBuffer +movecnt+ 3)) << 24);
  21.                  wav_header.audio_format = ((uint16_t)*(Wav_HDBuffer+movecnt+4) + (uint16_t)*(Wav_HDBuffer+movecnt+5));
  22.                  wav_header.num_channels = ((uint16_t)*(Wav_HDBuffer+movecnt+6) + (uint16_t)*(Wav_HDBuffer+movecnt+7));
  23.                  wav_header.sample_rate  = ((uint32_t)*(Wav_HDBuffer+movecnt+8)) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 9)) << 8) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 10)) << 16) +(((uint32_t)*(Wav_HDBuffer +movecnt+ 11)) << 24);
  24.                  wav_header.byte_rate    = ((uint32_t)*(Wav_HDBuffer+movecnt+12)) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 13)) << 8) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 14)) << 16) +(((uint32_t)*(Wav_HDBuffer +movecnt+ 15)) << 24);
  25.                  wav_header.block_align  = ((uint16_t)*(Wav_HDBuffer+movecnt+16) + (uint16_t)*(Wav_HDBuffer+movecnt+17));
  26.                  wav_header.bps          = ((uint16_t)*(Wav_HDBuffer+movecnt+18) + (uint16_t)*(Wav_HDBuffer+movecnt+19));

  27.                  movecnt +=(4+wav_header.fmtchunk_size);
  28.          }
  29.      else
  30.      {
  31.              ErrFlag = 1;
  32.         return ErrFlag;
  33.      }

  34.          datap = strstr((char*)(Wav_HDBuffer+movecnt),"LIST");
  35.          if(datap != NULL)
  36.          {

  37.                  movecnt += 4;

  38.                  wav_header.list_size = ((uint32_t)*(Wav_HDBuffer+movecnt+0)) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 1)) << 8) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 2)) << 16) +(((uint32_t)*(Wav_HDBuffer +movecnt+ 3)) << 24);
  39.                  movecnt +=(4+wav_header.list_size);

  40.          } //LIST not Must

  41.          datap = strstr((char*)(Wav_HDBuffer+movecnt),"data");
  42.          if(datap != NULL)
  43.          {

  44.                  movecnt += 4;

  45.                  wav_header.datachunk_size = ((uint32_t)*(Wav_HDBuffer+movecnt+0)) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 1)) << 8) + (((uint32_t)*(Wav_HDBuffer +movecnt+ 2)) << 16) +(((uint32_t)*(Wav_HDBuffer +movecnt+ 3)) << 24);
  46.                  movecnt += 4;

  47.                  ErrFlag = 0;
  48.          }
  49.      else
  50.      {
  51.              ErrFlag = 1;
  52.         return ErrFlag;
  53.      }

  54.          PRINTF("Wave audio format is %d\r\n",wav_header.audio_format);
  55.          PRINTF("Wave audio channel number is %d\r\n",wav_header.num_channels);
  56.          PRINTF("Wave audio sample rate  is %d\r\n",wav_header.sample_rate);
  57.          PRINTF("Wave audio byte rate is %d\r\n",wav_header.byte_rate);
  58.          PRINTF("Wave audio block align is %d\r\n",wav_header.block_align);
  59.          PRINTF("Wave audio bit per sample is %d\r\n",wav_header.bps);
  60.          PRINTF("Wave audio data size is %d\r\n",wav_header.datachunk_size);

  61.          return ErrFlag;

  62. }
复制代码


主要分为RIFF 4大部分解析:“RIFF”,“fmt”,“LIST”,“data”。“data”块后面紧跟的4个字节是整个音频左右声道数据长度,用于后续fatfs读取音频数据使用,并且记录音频数据指针相对位置,用于读取wave文件时,直接跳转到对应的数据区域获取音频数据。


2.3 SD卡音频数据播放

    这里定义了audioBuff[4* 512]数组,用来读取sd卡wave文件数据存放,并且提取数组已经读好的音频数据送给SAI EMDA播放,直到播放完成数据已经和音频数据大小相等。

  1. void SDWave_play()
  2. {
  3.         sai_transfer_t xfer;

  4.     uint32_t i            = 0;
  5.     FRESULT error;
  6.         UINT bytesRead;


  7.     txindex     = 0;
  8.     rxindex     = 0;
  9.     emptyBlock  = 0;
  10.     fullBlock   = 0;
  11.     sdReadCount =  0;

  12.     memset(audioBuff, 0, BUFFER_SIZE * BUFFER_NUM);
  13.     f_open(&g_fileObject, wav_file, (FA_READ));
  14.     if (f_lseek(&g_fileObject, movecnt))
  15.     {
  16.         PRINTF("Set file pointer position failed. \r\n");
  17.     }

  18.     for (i = 0; i < BUFFER_NUM; i++)
  19.     {
  20.         error = f_read(&g_fileObject, (void *)(audioBuff + i * BUFFER_SIZE), BUFFER_SIZE, (UINT *)&bytesRead);
  21.         if ((error) || (bytesRead != BUFFER_SIZE))
  22.         {
  23.             PRINTF("Read file failed. \r\n");
  24.             return;
  25.         }

  26.         sdReadCount++;
  27.         fullBlock++;
  28.     }

  29.     /* Wait for playback finished */
  30.     while (istxFinished != true)
  31.     {
  32.         if ((emptyBlock > 0) && (sdReadCount < (wav_header.datachunk_size/BUFFER_SIZE)))
  33.         {
  34.             error = f_read(&g_fileObject, (void *)(audioBuff + rxindex * BUFFER_SIZE), BUFFER_SIZE, (UINT *)&bytesRead);
  35.             if ((error) || (bytesRead != BUFFER_SIZE))
  36.             {
  37.                 PRINTF("Read file failed. \r\n");
  38.                 f_close(&g_fileObject);

  39.                 return;
  40.             }

  41.             rxindex = (rxindex + 1U) % BUFFER_NUM;
  42.             emptyBlock--;
  43.             fullBlock++;
  44.             sdReadCount++;
  45.         }

  46.         if (fullBlock > 0)
  47.         {
  48.             xfer.data = audioBuff + txindex * BUFFER_SIZE;
  49.             xfer.dataSize = BUFFER_SIZE;
  50.             txindex   = (txindex + 1U) % BUFFER_NUM;

  51.             if (kStatus_Success == SAI_TransferSendEDMA(DEMO_SAI, &SAI1_SAI_Tx_eDMA_Handle, &xfer))
  52.             {
  53.                     fullBlock--;
  54.             }
  55.         }
  56.     }
  57.     f_close(&g_fileObject);
  58.     PRINTF("\r\nPlayback is finished!\r\n");
  59. }

  60. void callback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
  61. {

  62.     sendCount++;
  63.     emptyBlock++;

  64.     if (sendCount == (wav_header.datachunk_size / BUFFER_SIZE))
  65.     {
  66.         istxFinished = true;
  67.         sendCount = 0;

  68.     }

  69. }
复制代码
Callback记录每次512字节发完,并且判断发送的数据是否已经到了全部的wav音频数据大小。
三 测试结果

    准备一个wave文件,16bit 16K采样率双通道的文件,名称为music.wav, 放到已经做过fat32格式化的sd卡中,插入MIMXRT1060-EVK J39中,运行代码并部分,打印信息如下:

Please insert a card into board.


Card inserted.


Make file system......The time may be long ifthe card capacity is big.

SAIwav module test!

MUSICPLAY Start!

Wave audio format is 1

Wave audio channel number is 2

Wave audio sample rate  is 16000

Wave audio byte rate is 64000

Wave audio block align is 4

Wave audio bit per sample is 16

Wave audio data size is 2728440


Playback is begin!


Playback is finished!

同时,在J12音频接口接上耳机或者功放喇叭,可以听到你的美妙音乐。

附件为mcuxpresso10.3.0的工程代码,以及一个wave音频样例文件。

MIMXRT1062_SAI_sdcardwav.zip (1.45 MB, 下载次数: 22)
回复

使用道具 举报

该用户从未签到

0

主题

6

帖子

0

中级会员

Rank: 3Rank: 3

积分
452
最后登录
2023-5-31
发表于 2022-12-5 13:08:49 | 显示全部楼层
奇怪,我怎么没有声音呢
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /4 下一条

Archiver|手机版|小黑屋|恩智浦技术社区

GMT+8, 2024-4-26 08:08 , Processed in 0.105508 second(s), 20 queries , MemCache On.

Powered by Discuz! X3.4

Copyright © 2001-2024, Tencent Cloud.

快速回复 返回顶部 返回列表