首頁 > 評(píng)測(cè) > 基于靈動(dòng)MM32F5270開發(fā)板播放MP3和WAV音頻文件
基于靈動(dòng)MM32F5270開發(fā)板播放MP3和WAV音頻文件
- [導(dǎo)讀]
- 本帖最后由 春嬌霹靂娃 于 2023-6-16 14:47 編輯 #申請(qǐng)?jiān)瓌?chuàng)# @21小跑堂 一、I2S簡(jiǎn)介 I2S(Inter-IC Sound)總線為集成在芯片內(nèi)的音頻總線,是飛利浦公司為數(shù)字音頻設(shè)備之間的音頻數(shù)據(jù)傳輸而制定的一種總
本帖最后由 春嬌霹靂娃 于 2023-6-16 14:47 編輯
#申請(qǐng)?jiān)瓌?chuàng)# @21小跑堂
一、I2S簡(jiǎn)介
I2S(Inter-IC Sound)總線為集成在芯片內(nèi)的音頻總線,是飛利浦公司為數(shù)字音頻設(shè)備之間的音頻數(shù)據(jù)傳輸而制定的一種總線標(biāo)準(zhǔn)。
MM32F5270系列MCU最多支持3個(gè)I2S接口,支持半雙工通信和全雙工通信,數(shù)據(jù)幀格式可配置為 16 位、24 位或 32 位。數(shù)據(jù)傳輸方向始終是MSB優(yōu)先,且每個(gè)I2S接口都支持DMA傳輸方式。MM32F5270系列的I2S支持飛利浦標(biāo)準(zhǔn)、MSB向左對(duì)齊標(biāo)準(zhǔn)、LSB向右對(duì)齊標(biāo)準(zhǔn)和PCM標(biāo)準(zhǔn)四種。
MM32F5270 是一款搭載了安謀科技 Arm China STAR-MC1 內(nèi)核的 MCU 產(chǎn)品,其工作頻率可達(dá) 120MHz,內(nèi)置多達(dá) 256KB Flash 和 192KB RAM。MM32F5270 相較于現(xiàn)有產(chǎn)品全面提升了性能、存儲(chǔ)容量、總線架構(gòu)和外設(shè)配置,旨在覆蓋更廣泛的工業(yè)、汽車和 IoT 應(yīng)用。
二、本文實(shí)現(xiàn)音頻播放的設(shè)計(jì)框架
本實(shí)驗(yàn)搭載在 PLUS-F5270 開發(fā)板,通過 SDSPI 組件實(shí)現(xiàn) FatFs 移植讀取音頻文件,分為 WAV 格式音頻文件解碼和 MP3 格式音頻文件通過 libmad 解碼。組件包含SDSPI、FatFS 和 Libmad 三部分組成,整個(gè)設(shè)計(jì)框圖如下圖:
三、實(shí)驗(yàn)環(huán)境
1.硬件環(huán)境:MM32F5277E9P開發(fā)板,SD卡及讀卡器
I2S 部分電路原理圖:
2.軟件環(huán)境:KEIL軟件,Tera Term 終端軟件,酷狗音樂
本實(shí)驗(yàn)整個(gè)設(shè)計(jì)思路為:
1.通過 SDSPI 接口操作 SD 卡,移植 FatFs 文件系統(tǒng)對(duì)音頻文件進(jìn)行識(shí)別,對(duì)音頻文件數(shù)據(jù)進(jìn)行讀取操作;
2.通過 DMA 搬運(yùn)的方式,將存儲(chǔ)在 SD 卡中的音頻文件(WAV 格式 / MP3 格式)通過 I2S 總線,將音頻數(shù)據(jù)發(fā)送給 CS4344 音頻數(shù)模轉(zhuǎn)換芯片,從而播放出音樂;
3.開啟 DMA 傳輸完成中斷和半傳輸完成中斷;
4.使用雙緩沖區(qū),實(shí)現(xiàn)流暢的音頻文件播放效果。
四、FatFS簡(jiǎn)介
1.FatFs official website: http://elm-chan.org/fsw/ff/00index_e.html
FatFS 負(fù)責(zé)管理和存儲(chǔ)文件信息的軟件機(jī)構(gòu),是一種在磁盤上組織文件的方法。其優(yōu)點(diǎn)有:
2.移植FatFS
適配ffconf.h和diskio.c文件,使編譯通過。
FatFS常用的API如下圖:
五、WAV格式音頻文件
1.WAV是 Waveform 的簡(jiǎn)寫,也叫波形文件,是一種可以存儲(chǔ)聲音波形的數(shù)字音頻格式。其具有真實(shí)記錄聲音波形的特點(diǎn),基本無數(shù)據(jù)壓縮,數(shù)據(jù)體量相對(duì)較大。典型的WAV文件格式如下圖:
WAV 文件采用的是 RIFF 格式結(jié)構(gòu),至少由 RIFF 塊、fmt 塊和 data 塊組成。每個(gè) RIFF 文件由若干個(gè)塊 (chunk) 組成,每個(gè)塊由塊標(biāo)識(shí)、塊長(zhǎng)度以及數(shù)據(jù)這三部分組成。塊標(biāo)識(shí)由 4 個(gè) ASCII 碼字符組成的,如果不滿 4 個(gè)字符則在右邊以空格來補(bǔ)齊。塊長(zhǎng)度由 4 個(gè)字節(jié)存儲(chǔ)空間,保存的是當(dāng)前塊數(shù)據(jù)的長(zhǎng)度,但不包含塊標(biāo)識(shí)和塊長(zhǎng)度字段,所以一個(gè)塊的實(shí)際長(zhǎng)度就是塊長(zhǎng)度字段的數(shù)值再加上 8 字節(jié)。
WAV文件格式實(shí)例解析如下:
2.WAV文件解碼
- 結(jié)構(gòu)體定義:wav_decode.h
- 結(jié)構(gòu)解析:
- 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)
- 播放實(shí)現(xiàn):void app_wav_play(void)
wav_decode_open 打開FatFS 中音頻文件:
- <font face="Arial"><span style="background-color: black;"><font color="#fffacd">/* open fatfs file. */
- bool wav_decode_open(wav_decode_obj_t * obj,char * file_name, uint8_t * buf, uint32_t buf_size){
- UINT fatfs_br = 0u;
- uint8_t offset = 0u;
- if (FR_OK != f_open(&(obj->fatfs_file), file_name, FA_READ) ){
- return false;}
- obj->fatfs_buf = buf;
- obj->fatfs_buf_size = buf_size;
- if (FR_OK != f_read(&(obj->fatfs_file), obj->fatfs_buf, WAV_DECODE_HEAD_BUFFER_SIZE, &fatfs_br)){
- return false;}
- chunk_riff_def_t *chunk_riff;
- chunk_fmt_def_t *chunk_fmt;
- chunk_fact_def_t *chunk_fact;
- chunk_data_def_t *chunk_data;
- chunk_riff = (chunk_riff_def_t *)(obj->fatfs_buf);
- chunk_fmt = (chunk_fmt_def_t *)(obj->fatfs_buf + 12);
- chunk_fact = (chunk_fact_def_t *)(obj->fatfs_buf + 12 + 8 + chunk_fmt->sub_chunk1_size);
- if((chunk_fact->chunk_id == 0x74636166) || (chunk_fact->chunk_id == 0x5453494C)){
- chunk_data = (chunk_data_def_t *)(obj->fatfs_buf + 12 + 8 + chunk_fmt->sub_chunk1_size + 8 + chunk_fact->chunk_size);
- offset = 12 + 8 + chunk_fmt->sub_chunk1_size + 8 + chunk_fact->chunk_size;}
- else{
- chunk_data = (chunk_data_def_t *)(obj->fatfs_buf + 12 + 8 + chunk_fmt->sub_chunk1_size);
- offset = 12 + 8 + chunk_fmt->sub_chunk1_size;}
- if(chunk_riff->format == 0x45564157 && chunk_data->sub_chunk2_id == 0x61746164){
- obj->audio_format = chunk_fmt->audio_format;
- obj->channel = chunk_fmt->num_of_channels;
- obj->sample_rate = chunk_fmt->sample_rate;
- obj->bits_per_sample = chunk_fmt->bits_per_sample;
- obj->bit_rate = chunk_fmt->byte_rate * 8;
- obj->block_align = chunk_fmt->block_align;
- obj->data_size = chunk_data->sub_chunk2_size;
- obj->data_start_offset = offset;
- obj->total_second = obj->data_size;
- }else{
- return false;}
- return true;
- }</font></span></font>
wav_decode_read 讀取音頻文件數(shù)據(jù):
- <font face="Arial">/* read fatfs file. */
- bool wav_decode_read(wav_decode_obj_t * obj,uint16_t * buf,uint32_t buf_len){
- UINT fat_br = 0u;
- if (FR_OK != f_read(&(obj->fatfs_file), buf, buf_len,&fat_br) ){
- return false;}
- obj->current_second+=fat_br;
- if((fat_br <= 0) || (fat_br < buf_len)){
- return false;}
- return true;
- }</font>
wav_decode_close 解碼數(shù)據(jù)結(jié)束:
- <font face="Arial">/* close fatfs file. */
- void wav_decode_close(wav_decode_obj_t * obj){
- f_close(&(obj->fatfs_file) );}
- </font>
app_wav_play 播放 WAV:
- <font face="Arial">/* play wave audio. */
- void app_wav_play(void){
- if( wav_decode_open(&wav_obj, file_path, file_buf, 10240) ){
- if( (wav_obj.bits_per_sample == 16) &&
- (wav_obj.channel == 2) &&
- (wav_obj.sample_rate > 44000) &&
- (wav_obj.sample_rate < 48100)){
- Audio_ClearStatus(AUDIO_STATUS_XFER_HALF | AUDIO_STATUS_XFER_DONE);
- wav_decode_read(&wav_obj, audio_buf[0], 5120);
- Audio_SetConf(44100, (uint16_t *)audio_buf[0], 2560);
- Audio_Enable(true);}}
- else{
- return;}
- uint8_t next_index = 1;
- while(1){
- while((Audio_GetStatus() & AUDIO_STATUS_XFER_HALF) != AUDIO_STATUS_XFER_HALF);
- Audio_ClearStatus(AUDIO_STATUS_XFER_HALF);
- if(next_index == 0){
- if(!wav_decode_read(&wav_obj,audio_buf[0], 5120)){
- break;}}
- else{
- if(!wav_decode_read(&wav_obj,audio_buf[1], 5120)){
- break;}}
- while((Audio_GetStatus() & AUDIO_STATUS_XFER_DONE) != AUDIO_STATUS_XFER_DONE);
- Audio_ClearStatus(AUDIO_STATUS_XFER_DONE);
- if(next_index == 0){
- Audio_SetConf(44100, (uint16_t *)audio_buf[0], 2560);
- next_index = 1;}
- else{
- Audio_SetConf(44100, (uint16_t *)audio_buf[1], 2560);
- next_index = 0;}
- Audio_Enable(true);}
- Audio_Enable(false);
- wav_decode_close(&wav_obj);}</font>
六、MP3音頻文件解碼
1.MP3 格式音樂文件 (Moving Picture Experts Group Audio Layer III (MPEG Audio Layer 3) ),經(jīng)過壓縮后的 MP3 文件數(shù)據(jù)由多個(gè)幀 (frame) 組成。幀是 MP3 文件的最小組成單位,每個(gè)幀由幀頭 、附加信息和聲音數(shù)據(jù)組成,幀的長(zhǎng)度由不同的 MP3 文件而不同,每個(gè)幀包含一段音頻的壓縮數(shù)據(jù),通過解碼庫(kù)解碼即可得到對(duì)應(yīng) PCM 音頻數(shù)據(jù)。
2.MP3解碼庫(kù)
適合在小型嵌入式控制器移植的有兩個(gè)開源 MP3 解碼庫(kù),libmad 解碼庫(kù)和 helix 解碼庫(kù),這兩個(gè)解碼庫(kù)都是以一幀為解碼單位,一次解碼一幀,其區(qū)別見下表:
Libmad | Helix |
|
|
3.libmad軟件解碼庫(kù)
本實(shí)驗(yàn)使用Libmad解碼庫(kù)進(jìn)行MP3解碼。libmad 是一個(gè)開源 MP3 解碼庫(kù),其對(duì) MP3 解碼算法做了很多優(yōu)化,性能較好,很多播放器如 mplayer、xmms 等都是使用這個(gè)開源庫(kù)進(jìn)行解碼的。Download from : https://downloads.sourceforge.net/mad/libmad-0.15.1b.tar.gz
“mad.h” 頭文件定義了 libmad 的數(shù)據(jù)結(jié)構(gòu)及 API 函數(shù),libmad 中的主要數(shù)據(jù)結(jié)構(gòu)體如下表所示:
4.移植libmad
將 libmad-0.15.1b 文件夾復(fù)制到工程目錄,將所有的 .c 文件加入工程,設(shè)置好包含路徑。
若編譯出現(xiàn) “FPM not select” 警告,在 KEIL 進(jìn)行宏定義配置,如下圖:
libmad 代碼中使用 DEBUG 預(yù)編譯作為調(diào)試開關(guān),需將其屏蔽
libmad 解碼出的數(shù)據(jù)為 24-bit PCM 數(shù)據(jù),需進(jìn)行 24-bit 至 16-bit 的轉(zhuǎn)換,使用 uint16_t scale(mad_fixed_t sample) 函數(shù)。
解碼用到三個(gè)數(shù)據(jù)結(jié)構(gòu),需將其定義為全局變量,防止??臻g不夠:
解碼的緩沖區(qū) audio buffer 要設(shè)置為全局變量,注意字節(jié)對(duì)齊的問題。
Libmad音頻解碼MP3也分為三個(gè)API完成:
酷狗音樂下載網(wǎng)址
https://www.kugou.com/yy/html/rank.html
附件
軟件工程源代碼:
MM32F5270原理圖:
- 本文系21ic原創(chuàng),未經(jīng)許可禁止轉(zhuǎn)載!
網(wǎng)友評(píng)論
- 聯(lián)系人:巧克力娃娃
- 郵箱:board@21ic.com
- 我要投稿
-
歡迎入駐,開放投稿
- NRF52810藍(lán)牙數(shù)字耳機(jī)找人定制
預(yù)算:¥30005天前
- 125KW模塊式PCS軟硬件外包開發(fā)
預(yù)算:¥1100000015小時(shí)前
- 12V汽車啟動(dòng)電源項(xiàng)目BMS設(shè)計(jì)
預(yù)算:¥50000023小時(shí)前
- 數(shù)據(jù)可視化軟件 開發(fā)
預(yù)算:¥5000023小時(shí)前
- PLC項(xiàng)目調(diào)試修改
預(yù)算:¥100001天前
- 起動(dòng)電機(jī)控制器開發(fā)
預(yù)算:¥1100001天前