STM32從SD卡中讀取語(yǔ)音文件進(jìn)行播放,因此需要對(duì)語(yǔ)音進(jìn)行解碼,剛開(kāi)始就一直使用Speex的音頻壓縮格式,最近發(fā)現(xiàn),在進(jìn)行語(yǔ)音格式轉(zhuǎn)換時(shí),我們不能很好地分析spx格式音頻文件的文件頭,這樣就會(huì)導(dǎo)致語(yǔ)音的播放出現(xiàn)問(wèn)題。由于WAV采用PCM編碼,音質(zhì)也十分不錯(cuò),于是考慮用STM32對(duì)WAV格式音頻文件進(jìn)行解碼,上周末開(kāi)始找資料和編程,其中也遇到了不少問(wèn)題,不過(guò)功夫不負(fù)有心人,最終還是順利的跑起來(lái)了。先將資料和編程過(guò)程整理成本文,供大家一起學(xué)習(xí)和進(jìn)步。
WAV文件格式是一種重要的用于存放聲音文件的文件格式,盡管現(xiàn)在有MP3,RAM等壓縮效率更高的聲音文件格式,并且廣泛被音樂(lè)文件所采用,但是又很多的應(yīng)用程序仍然采用WAV文件格式。由于WAV文件沒(méi)有采用壓縮技術(shù),所以它的文件很龐大,一般都在幾MB以上。但也正是因?yàn)闆](méi)有采用壓縮技術(shù),聲音的采樣數(shù)據(jù)很容易被讀出來(lái),便于用作其他的處理。
廢話(huà)不多說(shuō)了,我們直接去解析WAV文件格式吧。
WAV格式符合RIFF(Resource interchange File Format)規(guī)范。所有的WAV都有一個(gè)頭文件,這個(gè)頭文件音頻流的編碼參數(shù)。
表1、WAV文件的文件頭
表2、WAV聲音文件的數(shù)據(jù)塊
接下來(lái)我們用已經(jīng)編好的程序來(lái)讀取一個(gè)WAV文件的文件頭和數(shù)據(jù)塊,看看各個(gè)內(nèi)容都表示什么含義。
圖1、WAV源文件
圖2、用WinHex軟件解析WAV
圖3、STM32讀取WAV的信息
頭文件樣例說(shuō)明:
? “52 49 46 46”這個(gè)是Ascii字符“RIFF”,這部分是固定格式,表明這是一個(gè)WAVE文件頭。
? “24 33 AE 00”這個(gè)是我的WAV文件的數(shù)據(jù)大小,這個(gè)大小包括除了前面4個(gè)字節(jié)的所有字節(jié),也就是等于文件總字節(jié)數(shù)減去8。得到圖3中的11416356。11416356+8=11416364Byte=10.88Mb。
? “57 41 56 45 66 6D 74 20”,也是Ascii字符“WAVEfmt”,這部分是固定格式。以后是PCMWAVEFORMAT部分。
? “10 00 00 00”,這是一個(gè)DWORD,對(duì)應(yīng)數(shù)字16,這個(gè)對(duì)應(yīng)定義中的PCMWAVEFORMAT部分的大小,可以看到后面的這個(gè)段內(nèi)容正好是16個(gè)字節(jié)。當(dāng)為16時(shí),最后是沒(méi)有附加信息的,當(dāng)為數(shù)字18時(shí),最后多了兩個(gè)字節(jié)的附加信息。
? “01 00”,這是一個(gè)WORD,對(duì)應(yīng)定義為編碼格式(WAVE_FORMAT_PCM格式用的就是這個(gè))。
? “01 00”,這是一個(gè)WORD,對(duì)應(yīng)數(shù)字1,表示聲道數(shù)為1,是個(gè)單聲道WAV,當(dāng)值為2時(shí)為立體聲WAV。
? “22 56 00 00”對(duì)應(yīng)數(shù)字22050,代表的是采樣頻率220505,采樣率(每秒樣本數(shù))表示每個(gè)通道的播放速度。
? “44 AC 00 00”對(duì)應(yīng)數(shù)字44100,代表的是每秒的數(shù)據(jù)量,波形音頻數(shù)據(jù)傳送數(shù)率,其值為通道數(shù)×每秒樣本數(shù)×每個(gè)樣本的數(shù)據(jù)位數(shù)/8。播放軟件利用此值可以估計(jì)緩沖區(qū)的大小。
? “02 00:”對(duì)應(yīng)數(shù)字是2,表示塊對(duì)齊的內(nèi)容。數(shù)據(jù)塊的調(diào)整數(shù)(按字節(jié)算),其值為通道數(shù)×每個(gè)樣本的數(shù)據(jù)位置/8.播放軟件需要一次處理多個(gè)改值大小的字節(jié)數(shù)據(jù),以便將其值用于緩沖區(qū)的調(diào)整。
? “10 00”,此數(shù)值為16,采樣大小為16bits,每樣本數(shù)據(jù)位數(shù),表示每個(gè)聲道中各個(gè)樣本的數(shù)據(jù)位數(shù)。如果有多個(gè)聲道,對(duì)每個(gè)聲道而言,樣本大小都一樣。
? “64 61 74 61”,這個(gè)是Ascii字符“data”,表示頭結(jié)束,開(kāi)始數(shù)據(jù)區(qū)域。
? “00 33 AE 00”,十六進(jìn)制數(shù)是“0xAE3300”,對(duì)應(yīng)十進(jìn)制11416320,是數(shù)據(jù)區(qū)的開(kāi)頭以后的數(shù)據(jù)總數(shù)。
再往后就是真正的WAV文件數(shù)據(jù)體了,頭文件分析到此。
常見(jiàn)的聲音文件主要有兩種,分別對(duì)應(yīng)單聲道(11.025KHz采樣率、8Bit的采樣值)和雙聲道(44.1KHz采樣率、16Bit的采樣值)。采樣率是指:聲音信號(hào)在“模->數(shù)”轉(zhuǎn)換過(guò)程中單位時(shí)間內(nèi)采樣的次數(shù)。采樣值是指每一次采樣周期內(nèi)聲音模擬信號(hào)的積分值。
對(duì)于單聲道聲音文件,采樣數(shù)據(jù)位8位的短整數(shù);而對(duì)于雙聲道立體聲聲音文件,每次采樣數(shù)據(jù)位一個(gè)16位的整數(shù),高8為和低8位分別代表左右兩個(gè)聲道。
WAVE文件數(shù)據(jù)塊包含以脈沖編碼調(diào)制(PCM)格式表示樣本。WAVE文件是由樣本組織而成的。在單聲道WAVE文件中,聲道0代表左聲道,聲道1代表右聲道。在多聲道WAVE文件中,樣本是交替出現(xiàn)的。
PCM數(shù)據(jù)的存放方式:
樣本1 樣本2
8位單聲道 0聲道 0聲道
8位立體聲 0聲道(左)1聲道(右) 0聲道(左) 1聲道(右)
16位單聲道 0聲道低 0聲道高 0聲道低 0聲道高
16位立體聲 0聲道(左)低 0聲道(左)高 1聲道(右)低 1聲道(右)高
系統(tǒng)硬件組成比較簡(jiǎn)單,可以分為液晶顯示,LED指示,USB輸入,SD卡,電源供電,音頻功放和按鍵等,如圖3-1所示:
圖3-1 系統(tǒng)組成框圖
SD卡電路:
SD卡采用SPI驅(qū)動(dòng)。
USB電路:
采用SGM7222做轉(zhuǎn)換開(kāi)關(guān),識(shí)別ID的電壓值來(lái)選擇是作為IAP下載還是用于USB接口
音頻功放電路:
充電和系統(tǒng)電源:
程序編寫(xiě)主要有三個(gè)部分:定時(shí)器初始化,DAC初始化,定時(shí)器中斷服務(wù)程序,WAV播放程序。
定時(shí)器初始化:
void Timerx_Init(u16 arr,u16 psc)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC->APB1ENR"=1<<1;//TIM3時(shí)鐘使能
TIM3->ARR=arr; //設(shè)定計(jì)數(shù)器自動(dòng)重裝值
TIM3->
SC=psc; //預(yù)分頻器7200,得到10KHz的計(jì)數(shù)時(shí)鐘
TIM3->DIER"=1<<0; //允許更新中斷
TIM3->DIER|=1<<6; //允許觸發(fā)中斷
TIM3->CR1|=0x01; //使能定時(shí)器3
//優(yōu)先級(jí)設(shè)置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
DAC初始化:
#include "dac.h"
extern u16 digital;
void MyDAC_Init(void)//DAC channel1 Configuration
{
unsigned int tmpreg1=0,tmpreg2=0;
RCC->APB2ENR|=1<<2;//使能PORTA時(shí)鐘
RCC->APB1ENR|=RCC_APB1Periph_DAC;//使能DAC時(shí)鐘
GPIOA->CRL&=0XFFF0FFFF;
GPIOA->CRL|=0X00040000;//PA4浮空輸入
tmpreg1=DAC->CR;//Get the DAC CR value
tmpreg1&=~(CR_CLEAR_Mask<
tmpreg2=(DAC_Trigger_Software|DAC_WaveGeneration_None|DAC_LFSRUnmask_Bits8_0|DAC_OutputBuffer_Enable);
tmpreg1|=tmpreg2<
DAC->CR=tmpreg1;//Write to DAC CR
DAC->CR|=CR_EN_Set<
DAC1_SetData(0x000);
#if 0
tmpreg1=DAC->CR;//Get the DAC CR value
tmpreg1&=~(CR_CLEAR_Mask<
tmpreg1|=tmpreg2<
DAC->CR=tmpreg1;
DAC->CR|=CR_EN_Set<
DAC2_SetData(0x000);
#endif
}
void DAC1_SetData(u16 data)
{
DAC->DHR12R1=data;//通道1的12位右對(duì)齊數(shù)據(jù)
DAC->SWTRIGR|=0x01;//軟件啟動(dòng)轉(zhuǎn)換
}
void DAC2_SetData(u16 data)
{
DAC->DHR12R2=data;//
DAC->DHR12R2=data;//通道2的12位右對(duì)齊數(shù)據(jù)
DAC->SWTRIGR|=0x02;//軟件啟動(dòng)轉(zhuǎn)換
}
定時(shí)器中斷服務(wù)程序:
void TIM3_IRQHandler(void)
{
u16 temp;
if(TIM3->SR&0X0001)//溢出中斷
{
if(CHanalnum==1)//單聲道
{
if(Bitnum==8)//8位精度
{
DAC->DHR12R1=wav_buf[DApc]*10/volume;
DAC->DHR12R2=wav_buf[DApc]*10/volume;
DAC->SWTRIGR |=0x01;
DApc++;
}
else if(Bitnum==16)
{
temp=(((u8)(wav_buf[DApc+1]-0x80)<<4)|(wav_buf[DApc]>>4))*10/volume;
DAC->DHR12L1=temp;
DAC->DHR12L2=temp;
DAC->SWTRIGR|=0x01;
DApc+=2;
}
}
else if(CHanalnum==2)
{
if(Bitnum==8)
{
DAC->DHR12R1=wav_buf[DApc]*10/volume;
DApc++;
DAC->DHR12R2=wav_buf[DApc]*10/volume;
DApc++;
DAC->SWTRIGR|=0x01;
}
else if(Bitnum==16)
{ DAC->DHR12L1=(((u8)(wav_buf[DApc+1]-0x80)<<4)|(wav_buf[DApc]>>4))*10/volume; DApc+=2; DAC->DHR12L2=(((u8)(wav_buf[DApc+1]-0x80)<<4)|(wav_buf[DApc]>>4))*10/volume;
DApc+=2;
DAC->SWTRIGR|=0x01;
}
}
if(DApc==16384)
{
DApc=0;
DACdone=1;
}
}
TIM3->SR&=~(1<<0);
}
WAV初始化:
u8 WAV_Init(u8* wav_buf)
{
if(Check_Ifo(wav_buf,"RIFF"))
return 1;
wav1.wavlen=Get_num(wav_buf+4,4);
printf("\n\rwav1.wavlen = %ld\n\r",wav1.wavlen);
//if(Check_Ifo(wav_buf+8,"WAVE"))return 2;//WAVE錯(cuò)誤標(biāo)志
//if(Check_Ifo(wav_buf+12,"fmt "))return 3;//fmt錯(cuò)誤標(biāo)志
wav1.formart=Get_num(wav_buf+20,2);//格式類(lèi)別
printf("\n\rwav1.formart = %d\n\r",wav1.formart);
wav1.CHnum=Get_num(wav_buf+22,2);//通道數(shù)
printf("\n\rwav1.CHnum = %d\n\r",wav1.CHnum);
CHanalnum=wav1.CHnum;
wav1.SampleRate=Get_num(wav_buf+24,4);//采樣率
printf("\n\rwav1.SampleRate = %ld\n\r",wav1.SampleRate);
wav1.speed=Get_num(wav_buf+28,4);//音頻轉(zhuǎn)換數(shù)率
printf("\n\rwav1.speed = %ld\n\r",wav1.speed);
wav1.ajust=Get_num(wav_buf+32,2);//數(shù)據(jù)塊調(diào)速數(shù)
printf("\n\rwav1.ajust = %d\n\r",wav1.ajust);
wav1.SampleBits=Get_num(wav_buf+34,2);//樣本數(shù)據(jù)位數(shù)
printf("\n\rwav1.SampleBits = %d\n\r",wav1.SampleBits);
Bitnum=wav1.SampleBits;
//if(Check_Ifo(wav_buf+36,"data"))return 4;//數(shù)據(jù)標(biāo)志錯(cuò)誤
wav1.DATAlen=Get_num(wav_buf+40,4);//數(shù)據(jù)長(zhǎng)度
printf("\n\rwav1.DATAlen = %d\n\r",wav1.DATAlen);
if(wav1.wavlen<0x100000)
{
printf("\n\rwav1.wavlen = %dkb\n\r",(wav1.wavlen)>>10);
}
else
{
printf("\n\rwav1.wavlen = %dMb\n\r",(wav1.wavlen)>>20);
}
if(wav1.formart==1)
printf("\n\rWAV PCM\n\r");
if(wav1.CHnum==1)
printf("\n\rsingle\n\r");
else
printf("\n\rstereo\n\r");
printf("\n\rwav1.SampleRate = %dkHz\n\r",(wav1.SampleRate)/1000);
printf("\n\rwav1.speed = %dbps\n\r",(wav1.speed)/1000);
printf("\n\rwav1.SampleBits = %dbit\n\r",wav1.SampleBits);
return 0;
}
u8 Check_Ifo(u8* pbuf1,u8* pbuf2)
{
u8 i;
for(i=0;i<4;i++)
if(pbuf1!=pbuf2)
return 1;
return 0;
}
u32 Get_num(u8* pbuf,u8 len)
{
u32 num;
if(len==2)num=(pbuf[1]<<8)|pbuf[0];
else if(len==4)num=(pbuf[3]<<24)|(pbuf[2]<<16)|(pbuf[1]<<8)|pbuf[0];
return num;
}
WAV播放:
u8 Playwav(char *file)
{
FIL fwav;
FRESULT Res;
UINT BR;
unsigned char i;
unsigned int times;
Res = f_open(&fwav, file, FA_OPEN_EXISTING | FA_READ);
if(Res != FR_OK)
{
printf("\n\ropen file error : %d\n\r",Res);
}
else
{
Res = f_read(&fwav, wav_buf, sizeof(wav_buf), &BR); /* Read a chunk of src file */
if(Res==FR_OK)
{
WAV_Init(wav_buf);
DACdone=0;
DApc=44; //跳過(guò)頭信息
Timerx_Init(1000000/wav1.SampleRate,72); //定時(shí)器初始化
times=(wav1.DATAlen>>10)-1; //計(jì)算數(shù)據(jù)大小
for(i=0;i
{
while(!DACdone);//等待前面16384字節(jié)轉(zhuǎn)換完成 DACdone=0;
Res = f_read(&fwav, wav_buf, 16384, &BR);
while(!DACdone);// 等待前面16384字節(jié)轉(zhuǎn)換完成
DACdone=0;
Res = f_read(&fwav, wav_buf, 16384, &BR);//讀取數(shù)據(jù)
}
}
else
{
printf("\n\rread file error : %d\n\r",Res);
}
f_close(&fwav);
}
return 0;
}
-
STM32
+關(guān)注
關(guān)注
2283文章
10986瀏覽量
361297 -
WAVE
+關(guān)注
關(guān)注
0文章
22瀏覽量
14917 -
WinHex
+關(guān)注
關(guān)注
0文章
5瀏覽量
7741
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
基于DWC2的USB驅(qū)動(dòng)開(kāi)發(fā)-UAC之WAV-PCM音頻文件格式詳解

Hex文件格式是什么意思
Hex文件格式解析
數(shù)碼相機(jī)伴侶的文件格式
WAV文件格式詳解

EE-110:ELF和DWARF文件格式快速入門(mén)

評(píng)論