本帖最后由 小恩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文件的音频格式,音频数据大小,头解析代码如下:
- uint8_t Fun_Wave_Header_Analyzer(void)
- {
- char * datap;
- uint8_t ErrFlag = 0;
- datap = strstr((char*)Wav_HDBuffer,"RIFF");
- if(datap != NULL)
- {
- 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);
- movecnt += 8;
- }
- else
- {
- ErrFlag = 1;
- return ErrFlag;
- }
- datap = strstr((char*)(Wav_HDBuffer+movecnt),"WAVEfmt");
- if(datap != NULL)
- {
- movecnt += 8;
- 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);
- wav_header.audio_format = ((uint16_t)*(Wav_HDBuffer+movecnt+4) + (uint16_t)*(Wav_HDBuffer+movecnt+5));
- wav_header.num_channels = ((uint16_t)*(Wav_HDBuffer+movecnt+6) + (uint16_t)*(Wav_HDBuffer+movecnt+7));
- 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);
- 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);
- wav_header.block_align = ((uint16_t)*(Wav_HDBuffer+movecnt+16) + (uint16_t)*(Wav_HDBuffer+movecnt+17));
- wav_header.bps = ((uint16_t)*(Wav_HDBuffer+movecnt+18) + (uint16_t)*(Wav_HDBuffer+movecnt+19));
- movecnt +=(4+wav_header.fmtchunk_size);
- }
- else
- {
- ErrFlag = 1;
- return ErrFlag;
- }
- datap = strstr((char*)(Wav_HDBuffer+movecnt),"LIST");
- if(datap != NULL)
- {
- movecnt += 4;
- 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);
- movecnt +=(4+wav_header.list_size);
- } //LIST not Must
- datap = strstr((char*)(Wav_HDBuffer+movecnt),"data");
- if(datap != NULL)
- {
- movecnt += 4;
- 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);
- movecnt += 4;
- ErrFlag = 0;
- }
- else
- {
- ErrFlag = 1;
- return ErrFlag;
- }
- PRINTF("Wave audio format is %d\r\n",wav_header.audio_format);
- PRINTF("Wave audio channel number is %d\r\n",wav_header.num_channels);
- PRINTF("Wave audio sample rate is %d\r\n",wav_header.sample_rate);
- PRINTF("Wave audio byte rate is %d\r\n",wav_header.byte_rate);
- PRINTF("Wave audio block align is %d\r\n",wav_header.block_align);
- PRINTF("Wave audio bit per sample is %d\r\n",wav_header.bps);
- PRINTF("Wave audio data size is %d\r\n",wav_header.datachunk_size);
- return ErrFlag;
- }
复制代码
主要分为RIFF 4大部分解析:“RIFF”,“fmt”,“LIST”,“data”。“data”块后面紧跟的4个字节是整个音频左右声道数据长度,用于后续fatfs读取音频数据使用,并且记录音频数据指针相对位置,用于读取wave文件时,直接跳转到对应的数据区域获取音频数据。
2.3 SD卡音频数据播放
这里定义了audioBuff[4* 512]数组,用来读取sd卡wave文件数据存放,并且提取数组已经读好的音频数据送给SAI EMDA播放,直到播放完成数据已经和音频数据大小相等。
- void SDWave_play()
- {
- sai_transfer_t xfer;
- uint32_t i = 0;
- FRESULT error;
- UINT bytesRead;
- txindex = 0;
- rxindex = 0;
- emptyBlock = 0;
- fullBlock = 0;
- sdReadCount = 0;
- memset(audioBuff, 0, BUFFER_SIZE * BUFFER_NUM);
- f_open(&g_fileObject, wav_file, (FA_READ));
- if (f_lseek(&g_fileObject, movecnt))
- {
- PRINTF("Set file pointer position failed. \r\n");
- }
- for (i = 0; i < BUFFER_NUM; i++)
- {
- error = f_read(&g_fileObject, (void *)(audioBuff + i * BUFFER_SIZE), BUFFER_SIZE, (UINT *)&bytesRead);
- if ((error) || (bytesRead != BUFFER_SIZE))
- {
- PRINTF("Read file failed. \r\n");
- return;
- }
- sdReadCount++;
- fullBlock++;
- }
- /* Wait for playback finished */
- while (istxFinished != true)
- {
- if ((emptyBlock > 0) && (sdReadCount < (wav_header.datachunk_size/BUFFER_SIZE)))
- {
- error = f_read(&g_fileObject, (void *)(audioBuff + rxindex * BUFFER_SIZE), BUFFER_SIZE, (UINT *)&bytesRead);
- if ((error) || (bytesRead != BUFFER_SIZE))
- {
- PRINTF("Read file failed. \r\n");
- f_close(&g_fileObject);
- return;
- }
- rxindex = (rxindex + 1U) % BUFFER_NUM;
- emptyBlock--;
- fullBlock++;
- sdReadCount++;
- }
- if (fullBlock > 0)
- {
- xfer.data = audioBuff + txindex * BUFFER_SIZE;
- xfer.dataSize = BUFFER_SIZE;
- txindex = (txindex + 1U) % BUFFER_NUM;
- if (kStatus_Success == SAI_TransferSendEDMA(DEMO_SAI, &SAI1_SAI_Tx_eDMA_Handle, &xfer))
- {
- fullBlock--;
- }
- }
- }
- f_close(&g_fileObject);
- PRINTF("\r\nPlayback is finished!\r\n");
- }
- void callback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
- {
- sendCount++;
- emptyBlock++;
- if (sendCount == (wav_header.datachunk_size / BUFFER_SIZE))
- {
- istxFinished = true;
- sendCount = 0;
- }
- }
复制代码 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音频样例文件。
|