首页 > 评测 > 基于灵动MM32F5270开发板播放MP3和WAV音频文件

基于灵动MM32F5270开发板播放MP3和WAV音频文件

  
  • 作者:
  • 来源:
  • [导读]
  • 本帖最后由 春娇霹雳娃 于 2023-6-16 14:47 编辑 #申请原创# @21小跑堂 一、I2S简介 I2S(Inter-IC Sound)总线为集成在芯片内的音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总

本帖最后由 春娇霹雳娃 于 2023-6-16 14:47 编辑

#申请原创# @21小跑堂

一、I2S简介
I2S(Inter-IC Sound)总线为集成在芯片内的音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准。

MM32F5270系列MCU最多支持3个I2S接口,支持半双工通信和全双工通信,数据帧格式可配置为 16 位、24 位或 32 位。数据传输方向始终是MSB优先,且每个I2S接口都支持DMA传输方式。MM32F5270系列的I2S支持飞利浦标准、MSB向左对齐标准、LSB向右对齐标准和PCM标准四种。

MM32F5270 是一款搭载了安谋科技 Arm China STAR-MC1 内核的 MCU 产品,其工作频率可达 120MHz,内置多达 256KB Flash 和 192KB RAM。MM32F5270 相较于现有产品全面提升了性能、存储容量、总线架构和外设配置,旨在覆盖更广泛的工业、汽车和 IoT 应用。

二、本文实现音频播放的设计框架
本实验搭载在 PLUS-F5270 开发板,通过 SDSPI 组件实现 FatFs 移植读取音频文件,分为 WAV 格式音频文件解码和 MP3 格式音频文件通过 libmad 解码。组件包含SDSPI、FatFS 和 Libmad 三部分组成,整个设计框图如下图:



三、实验环境


1.硬件环境:MM32F5277E9P开发板,SD卡及读卡器



I2S 部分电路原理图:


2.软件环境:KEIL软件,Tera Term 终端软件,酷狗音乐

本实验整个设计思路为:
1.通过 SDSPI 接口操作 SD 卡,移植 FatFs 文件系统对音频文件进行识别,对音频文件数据进行读取操作;
2.通过 DMA 搬运的方式,将存储在 SD 卡中的音频文件(WAV 格式 / MP3 格式)通过 I2S 总线,将音频数据发送给 CS4344 音频数模转换芯片,从而播放出音乐;
3.开启 DMA 传输完成中断和半传输完成中断;
4.使用双缓冲区,实现流畅的音频文件播放效果。

四、FatFS简介
1.FatFs official website: http://elm-chan.org/fsw/ff/00index_e.html
FatFS 负责管理和存储文件信息的软件机构,是一种在磁盘上组织文件的方法。其优点有:
  • C 语言编写,支持 FAT12, FAT16 和 FAT32
  • 支持多种存储媒介,有独立的缓冲区,可对多个文件进行读写
  • 可裁剪的文件系统
  • 免费开源,专门为小型嵌入式系统设计

2.移植FatFS
适配ffconf.h和diskio.c文件,使编译通过。


FatFS常用的API如下图:


五、WAV格式音频文件
1.WAV是 Waveform 的简写,也叫波形文件,是一种可以存储声音波形的数字音频格式。其具有真实记录声音波形的特点,基本无数据压缩,数据体量相对较大。典型的WAV文件格式如下图:

WAV 文件采用的是 RIFF 格式结构,至少由 RIFF 块、fmt 块和 data 块组成。每个 RIFF 文件由若干个块 (chunk) 组成,每个块由块标识、块长度以及数据这三部分组成。块标识由 4 个 ASCII 码字符组成的,如果不满 4 个字符则在右边以空格来补齐。块长度由 4 个字节存储空间,保存的是当前块数据的长度,但不包含块标识和块长度字段,所以一个块的实际长度就是块长度字段的数值再加上 8 字节。

WAV文件格式实例解析如下:


2.WAV文件解码

 

  • 结构体定义:wav_decode.h
  • 结构解析:
  • bool wav_decode_open(wav_decode_obj_t * obj, char * file_name, uint8_t * buf, uint32_t buf_size)
  • bool wav_decode_read(wav_decode_obj_t * obj, uint16_t * buf, uint32_t buf_len)
  • void wav_decode_close(wav_decode_obj_t * obj)
  • 播放实现:void app_wav_play(void)


wav_decode_open 打开FatFS 中音频文件:

  1. <font face="Arial"><span style="background-color: black;"><font color="#fffacd">/* open fatfs file. */
  2. bool wav_decode_open(wav_decode_obj_t * obj,char * file_name, uint8_t * buf, uint32_t buf_size){
  3.      UINT fatfs_br = 0u;
  4.      uint8_t offset = 0u;
  5.      if (FR_OK != f_open(&(obj->fatfs_file), file_name, FA_READ) ){
  6.          return false;}
  7.      obj->fatfs_buf = buf;
  8.      obj->fatfs_buf_size = buf_size;
  9.      if (FR_OK != f_read(&(obj->fatfs_file), obj->fatfs_buf, WAV_DECODE_HEAD_BUFFER_SIZE, &fatfs_br)){
  10.          return false;}
  11.     chunk_riff_def_t *chunk_riff;
  12.     chunk_fmt_def_t  *chunk_fmt;
  13.     chunk_fact_def_t *chunk_fact;
  14.     chunk_data_def_t *chunk_data;
  15.  
  16.     chunk_riff = (chunk_riff_def_t *)(obj->fatfs_buf);
  17.     chunk_fmt  = (chunk_fmt_def_t  *)(obj->fatfs_buf + 12);
  18.     chunk_fact = (chunk_fact_def_t *)(obj->fatfs_buf + 12 + 8 + chunk_fmt->sub_chunk1_size);
  19.  
  20.     if((chunk_fact->chunk_id == 0x74636166) || (chunk_fact->chunk_id == 0x5453494C)){
  21.         chunk_data = (chunk_data_def_t *)(obj->fatfs_buf + 12 + 8 + chunk_fmt->sub_chunk1_size + 8 + chunk_fact->chunk_size);
  22.         offset = 12 + 8 + chunk_fmt->sub_chunk1_size + 8 + chunk_fact->chunk_size;}
  23.     else{
  24.         chunk_data = (chunk_data_def_t *)(obj->fatfs_buf + 12 + 8 + chunk_fmt->sub_chunk1_size);
  25.         offset = 12 + 8 + chunk_fmt->sub_chunk1_size;}
  26.  
  27.     if(chunk_riff->format == 0x45564157 && chunk_data->sub_chunk2_id == 0x61746164){
  28.          obj->audio_format    = chunk_fmt->audio_format;
  29.          obj->channel         = chunk_fmt->num_of_channels;
  30.          obj->sample_rate     = chunk_fmt->sample_rate;
  31.          obj->bits_per_sample = chunk_fmt->bits_per_sample;
  32.          obj->bit_rate        = chunk_fmt->byte_rate * 8;
  33.          obj->block_align     = chunk_fmt->block_align;
  34.  
  35.          obj->data_size  = chunk_data->sub_chunk2_size;
  36.          obj->data_start_offset = offset;
  37.          obj->total_second = obj->data_size;
  38.     }else{
  39.         return false;}
  40.      return true;
  41. }</font></span></font>
复制代码

wav_decode_read 读取音频文件数据:

  1. <font face="Arial">/* read fatfs file. */
  2. bool wav_decode_read(wav_decode_obj_t * obj,uint16_t * buf,uint32_t buf_len){
  3.      UINT fat_br = 0u;
  4.      if (FR_OK != f_read(&(obj->fatfs_file), buf, buf_len,&fat_br) ){
  5.          return false;}
  6.      obj->current_second+=fat_br;
  7.  
  8.      if((fat_br <= 0) || (fat_br < buf_len)){
  9.      return false;}
  10.      return true;
  11. }</font>
复制代码

wav_decode_close 解码数据结束:

  1. <font face="Arial">/* close fatfs file. */
  2. void wav_decode_close(wav_decode_obj_t * obj){
  3.     f_close(&(obj->fatfs_file) );}
  4. </font>
复制代码

app_wav_play 播放 WAV:

  1. <font face="Arial">/* play wave audio. */
  2. void app_wav_play(void){
  3.     if( wav_decode_open(&wav_obj, file_path, file_buf, 10240) ){
  4.         if( (wav_obj.bits_per_sample == 16) &&
  5.             (wav_obj.channel == 2) &&
  6.             (wav_obj.sample_rate > 44000) &&
  7.             (wav_obj.sample_rate < 48100)){
  8.             Audio_ClearStatus(AUDIO_STATUS_XFER_HALF | AUDIO_STATUS_XFER_DONE);
  9.             wav_decode_read(&wav_obj, audio_buf[0], 5120);
  10.  
  11.             Audio_SetConf(44100, (uint16_t *)audio_buf[0], 2560);
  12.             Audio_Enable(true);}}
  13.     else{
  14.         return;}
  15.  
  16.     uint8_t next_index = 1;
  17.  
  18.     while(1){
  19.         while((Audio_GetStatus() & AUDIO_STATUS_XFER_HALF) != AUDIO_STATUS_XFER_HALF);
  20.         Audio_ClearStatus(AUDIO_STATUS_XFER_HALF);
  21.         if(next_index == 0){
  22.             if(!wav_decode_read(&wav_obj,audio_buf[0], 5120)){
  23.                 break;}}
  24.         else{
  25.             if(!wav_decode_read(&wav_obj,audio_buf[1], 5120)){
  26.                 break;}}
  27.  
  28.         while((Audio_GetStatus() & AUDIO_STATUS_XFER_DONE) != AUDIO_STATUS_XFER_DONE);
  29.         Audio_ClearStatus(AUDIO_STATUS_XFER_DONE);
  30.  
  31.         if(next_index == 0){
  32.             Audio_SetConf(44100, (uint16_t *)audio_buf[0], 2560);
  33.             next_index = 1;}
  34.         else{
  35.             Audio_SetConf(44100, (uint16_t *)audio_buf[1], 2560);
  36.             next_index = 0;}
  37.         Audio_Enable(true);}
  38.  
  39.     Audio_Enable(false);
  40.     wav_decode_close(&wav_obj);}</font>
复制代码


六、MP3音频文件解码

1.MP3 格式音乐文件 (Moving Picture Experts Group Audio Layer III (MPEG Audio Layer 3) ),经过压缩后的 MP3 文件数据由多个帧 (frame) 组成。帧是 MP3 文件的最小组成单位,每个帧由帧头 、附加信息和声音数据组成,帧的长度由不同的 MP3 文件而不同,每个帧包含一段音频的压缩数据,通过解码库解码即可得到对应 PCM 音频数据。

2.MP3解码库

适合在小型嵌入式控制器移植的有两个开源 MP3 解码库,libmad 解码库和 helix 解码库,这两个解码库都是以一帧为解码单位,一次解码一帧,其区别见下表:

Libmad Helix
  • 高精度的MPEG音频解码库
  • 支持MPEG-1、MPEG-2、MPEG-2.5标准
  • 提供24 bit PCM输出
  • 用定点运算模拟浮点运算,不需要处理器有浮点运算功能
  • 对MP3解码中关键部分采用优化算法
  • 软件库结构清晰,易于开发和使用
。。。
  • 支持MPEG-1、MPEG-2、MPEG-2.5标准
  • 支持定点和浮点运算
  • 支持可变位恒率、恒定位速率及立体声、单声道音频格式
。。。


3.libmad软件解码库
本实验使用Libmad解码库进行MP3解码。
libmad 是一个开源 MP3 解码库,其对 MP3 解码算法做了很多优化,性能较好,很多播放器如 mplayer、xmms 等都是使用这个开源库进行解码的。Download from : https://downloads.sourceforge.net/mad/libmad-0.15.1b.tar.gz

“mad.h” 头文件定义了 libmad 的数据结构及 API 函数,libmad 中的主要数据结构体如下表所示:



4.移植libmad
将 libmad-0.15.1b 文件夹复制到工程目录,将所有的 .c 文件加入工程,设置好包含路径。



若编译出现 “FPM not select” 警告,在 KEIL 进行宏定义配置,如下图:


libmad 代码中使用 DEBUG 预编译作为调试开关,需将其屏蔽


libmad 解码出的数据为 24-bit PCM 数据,需进行 24-bit 至 16-bit 的转换,使用 uint16_t scale(mad_fixed_t sample) 函数。

解码用到三个数据结构,需将其定义为全局变量,防止栈空间不够:
  • struct mad_frame Frame;
  • struct mad_synth Synth;
  • struct mad_stream Stream;

解码的缓冲区 audio buffer 要设置为全局变量,注意字节对齐的问题。

Libmad音频解码MP3也分为三个API完成:
  1. bool mp3_decode_open(mp3_decode_obj_t * obj, char * file_name, uint8_t * buf, uint32_t buf_size);
  2. bool mp3_decode_read(mp3_decode_obj_t * obj, uint16_t * buf, uint32_t buf_len);
  3. void mp3_decode_close(mp3_decode_obj_t * obj);
复制代码
最终主函数实现WAV和MP3的播放
  1. #include "board_init.h"
  2.  
  3. #include "ff.h"
  4. #include "mp3_decode.h"
  5. #include "audio_port.h"
  6. #include "wav_decode.h"
  7.  
  8. FATFS   fs;
  9. DIR     dir;
  10. char file_path[BOARD_FILE_PATH_MAX_LEN];
  11. const char str_dir_path[] = BOARD_AUDIO_DIR_PATH;
  12.  
  13. /* audio buffer. */
  14. __attribute__((aligned(4))) uint16_t audio_buf[2][5120];
  15. __attribute__((aligned(4))) uint16_t out_buf[2][5120];
  16.  
  17. /* file buffer. */
  18. __attribute__((aligned(4))) uint8_t file_buf[10240];
  19.  
  20. __attribute__((aligned(4))) mp3_decode_obj_t mp3_obj;
  21. __attribute__((aligned(4))) wav_decode_obj_t wav_obj;
  22.  
  23. bool app_fatfs_init(void);
  24. void app_get_next_audio(void);
  25. void app_mp3_play(void);
  26. void app_wav_play(void);
  27.  
  28. int main(void){
  29.     BOARD_Init();
  30.     printf("audio player.\r\n");
  31.  
  32.     app_fatfs_init();
  33.     while (1){
  34.         app_get_next_audio();
  35.  
  36.         /* play mp3 audio. */
  37.         if (strstr(file_path, ".mp3") != NULL || strstr(file_path, ".MP3")) /* mp3 file. */{
  38.             app_mp3_play();
  39.             printf("done.\r\n");}
  40.  
  41.  
复制代码
最后播放音乐的视频



酷狗音乐下载网址

https://www.kugou.com/yy/html/rank.html

附件
软件工程源代码:
plus-f5270_audio_player_20239615.zip (7.87 MB)

MM32F5270原理图:
PLUS-F5270 原理图 V1.2.1.4.pdf (521.64 KB)

 

  • 本文系21ic原创,未经许可禁止转载!

网友评论