精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

一招教你快速解析WAV文件格式

電子設計 ? 來源:互聯網 ? 作者:佚名 ? 2018-08-01 09:38 ? 次閱讀

STM32SD卡中讀取語音文件進行播放,因此需要對語音進行解碼,剛開始就一直使用Speex的音頻壓縮格式,最近發現,在進行語音格式轉換時,我們不能很好地分析spx格式音頻文件的文件頭,這樣就會導致語音的播放出現問題。由于WAV采用PCM編碼,音質也十分不錯,于是考慮用STM32對WAV格式音頻文件進行解碼,上周末開始找資料編程,其中也遇到了不少問題,不過功夫不負有心人,最終還是順利的跑起來了。先將資料和編程過程整理成本文,供大家一起學習和進步。

WAV文件格式是一種重要的用于存放聲音文件的文件格式,盡管現在有MP3,RAM等壓縮效率更高的聲音文件格式,并且廣泛被音樂文件所采用,但是又很多的應用程序仍然采用WAV文件格式。由于WAV文件沒有采用壓縮技術,所以它的文件很龐大,一般都在幾MB以上。但也正是因為沒有采用壓縮技術,聲音的采樣數據很容易被讀出來,便于用作其他的處理。

廢話不多說了,我們直接去解析WAV文件格式吧。

WAV格式符合RIFF(Resource interchange File Format)規范。所有的WAV都有一個頭文件,這個頭文件音頻流的編碼參數。


表1、WAV文件的文件頭

WAV-2.jpg

表2、WAV聲音文件的數據塊
接下來我們用已經編好的程序來讀取一個WAV文件的文件頭和數據塊,看看各個內容都表示什么含義。


圖1、WAV源文件


圖2、用WinHex軟件解析WAV

WAV-5.jpg

圖3、STM32讀取WAV的信息

頭文件樣例說明:

? “52 49 46 46”這個是Ascii字符“RIFF”,這部分是固定格式,表明這是一個WAVE文件頭。

? “24 33 AE 00”這個是我的WAV文件的數據大小,這個大小包括除了前面4個字節的所有字節,也就是等于文件總字節數減去8。得到圖3中的11416356。11416356+8=11416364Byte=10.88Mb。

? “57 41 56 45 66 6D 74 20”,也是Ascii字符“WAVEfmt”,這部分是固定格式。以后是PCMWAVEFORMAT部分。

? “10 00 00 00”,這是一個DWORD,對應數字16,這個對應定義中的PCMWAVEFORMAT部分的大小,可以看到后面的這個段內容正好是16個字節。當為16時,最后是沒有附加信息的,當為數字18時,最后多了兩個字節的附加信息。

? “01 00”,這是一個WORD,對應定義為編碼格式(WAVE_FORMAT_PCM格式用的就是這個)。

? “01 00”,這是一個WORD,對應數字1,表示聲道數為1,是個單聲道WAV,當值為2時為立體聲WAV。

? “22 56 00 00”對應數字22050,代表的是采樣頻率220505,采樣率(每秒樣本數)表示每個通道的播放速度。

? “44 AC 00 00”對應數字44100,代表的是每秒的數據量,波形音頻數據傳送數率,其值為通道數×每秒樣本數×每個樣本的數據位數/8。播放軟件利用此值可以估計緩沖區的大小。

? “02 00:”對應數字是2,表示塊對齊的內容。數據塊的調整數(按字節算),其值為通道數×每個樣本的數據位置/8.播放軟件需要一次處理多個改值大小的字節數據,以便將其值用于緩沖區的調整。

? “10 00”,此數值為16,采樣大小為16bits,每樣本數據位數,表示每個聲道中各個樣本的數據位數。如果有多個聲道,對每個聲道而言,樣本大小都一樣。

? “64 61 74 61”,這個是Ascii字符“data”,表示頭結束,開始數據區域。

? “00 33 AE 00”,十六進制數是“0xAE3300”,對應十進制11416320,是數據區的開頭以后的數據總數。

再往后就是真正的WAV文件數據體了,頭文件分析到此。

常見的聲音文件主要有兩種,分別對應單聲道(11.025KHz采樣率、8Bit的采樣值)和雙聲道(44.1KHz采樣率、16Bit的采樣值)。采樣率是指:聲音信號在“模->數”轉換過程中單位時間內采樣的次數。采樣值是指每一次采樣周期內聲音模擬信號的積分值。

對于單聲道聲音文件,采樣數據位8位的短整數;而對于雙聲道立體聲聲音文件,每次采樣數據位一個16位的整數,高8為和低8位分別代表左右兩個聲道。
WAVE文件數據塊包含以脈沖編碼調制(PCM)格式表示樣本。WAVE文件是由樣本組織而成的。在單聲道WAVE文件中,聲道0代表左聲道,聲道1代表右聲道。在多聲道WAVE文件中,樣本是交替出現的。

PCM數據的存放方式:

樣本1 樣本2

8位單聲道 0聲道 0聲道

8位立體聲 0聲道(左)1聲道(右) 0聲道(左) 1聲道(右)

16位單聲道 0聲道低 0聲道高 0聲道低 0聲道高

16位立體聲 0聲道(左)低 0聲道(左)高 1聲道(右)低 1聲道(右)高

系統硬件組成比較簡單,可以分為液晶顯示,LED指示,USB輸入,SD卡,電源供電,音頻功放和按鍵等,如圖3-1所示:

圖3-1 系統組成框圖

SD卡電路:
SD卡采用SPI驅動。

USB電路:

采用SGM7222做轉換開關,識別ID的電壓值來選擇是作為IAP下載還是用于USB接口


音頻功放電路:

充電和系統電源:

程序編寫主要有三個部分:定時器初始化,DAC初始化,定時器中斷服務程序,WAV播放程序。

定時器初始化:

void Timerx_Init(u16 arr,u16 psc)

{

NVIC_InitTypeDef NVIC_InitStructure;

RCC->APB1ENR"=1<<1;//TIM3時鐘使能

TIM3->ARR=arr; //設定計數器自動重裝值

TIM3->

SC=psc; //預分頻器7200,得到10KHz的計數時鐘

TIM3->DIER"=1<<0; //允許更新中斷

TIM3->DIER|=1<<6; //允許觸發中斷

TIM3->CR1|=0x01; //使能定時器3

//優先級設置

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時鐘

RCC->APB1ENR|=RCC_APB1Periph_DAC;//使能DAC時鐘

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位右對齊數據

DAC->SWTRIGR|=0x01;//軟件啟動轉換

}

void DAC2_SetData(u16 data)

{

DAC->DHR12R2=data;//

DAC->DHR12R2=data;//通道2的12位右對齊數據

DAC->SWTRIGR|=0x02;//軟件啟動轉換

}

定時器中斷服務程序:

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錯誤標志

//if(Check_Ifo(wav_buf+12,"fmt "))return 3;//fmt錯誤標志

wav1.formart=Get_num(wav_buf+20,2);//格式類別

printf("\n\rwav1.formart = %d\n\r",wav1.formart);

wav1.CHnum=Get_num(wav_buf+22,2);//通道數

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);//音頻轉換數率

printf("\n\rwav1.speed = %ld\n\r",wav1.speed);

wav1.ajust=Get_num(wav_buf+32,2);//數據塊調速數

printf("\n\rwav1.ajust = %d\n\r",wav1.ajust);

wav1.SampleBits=Get_num(wav_buf+34,2);//樣本數據位數

printf("\n\rwav1.SampleBits = %d\n\r",wav1.SampleBits);

Bitnum=wav1.SampleBits;

//if(Check_Ifo(wav_buf+36,"data"))return 4;//數據標志錯誤

wav1.DATAlen=Get_num(wav_buf+40,4);//數據長度

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; //跳過頭信息

Timerx_Init(1000000/wav1.SampleRate,72); //定時器初始化

times=(wav1.DATAlen>>10)-1; //計算數據大小

for(i=0;i

{

while(!DACdone);//等待前面16384字節轉換完成 DACdone=0;

Res = f_read(&fwav, wav_buf, 16384, &BR);

while(!DACdone);// 等待前面16384字節轉換完成

DACdone=0;

Res = f_read(&fwav, wav_buf, 16384, &BR);//讀取數據

}

}

else

{

printf("\n\rread file error : %d\n\r",Res);

}

f_close(&fwav);

}

return 0;

}

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • STM32
    +關注

    關注

    2266

    文章

    10876

    瀏覽量

    354931
  • WAVE
    +關注

    關注

    0

    文章

    22

    瀏覽量

    14826
  • WinHex
    +關注

    關注

    0

    文章

    5

    瀏覽量

    7650
收藏 人收藏

    評論

    相關推薦

    基于DWC2的USB驅動開發-UAC之WAV-PCM音頻文件格式詳解

    在做UAC,PWM音頻播放的項目,需要解析WAV格式文件,通過UAC發送,或接收PCM數據,驅動喇叭播放。這里對WAV文件格式相關內容進行整
    的頭像 發表于 07-27 09:02 ?2081次閱讀
    基于DWC2的USB驅動開發-UAC之<b class='flag-5'>WAV</b>-PCM音頻<b class='flag-5'>文件格式</b>詳解

    教你一招如何分辨PCB板層數

    教你一招如何分辨PCB板層數
    發表于 04-23 06:36

    教你一招如何去實現傅立葉變換算法?

    教你一招如何去實現傅立葉變換算法?
    發表于 04-30 06:05

    教你一招怎樣去選擇合適的CPLD

    CPLD有哪些優點?教你一招怎樣去選擇合適的CPLD
    發表于 05-06 09:48

    教你一招分分鐘實現頻譜儀帶寬的正確設置

    教你一招分分鐘實現頻譜儀帶寬的正確設置
    發表于 05-07 06:36

    教你一招如何去選擇射頻濾波器?

    教你一招如何去選擇射頻濾波器?
    發表于 05-28 07:05

    Hex文件格式是什么意思

    Hex文件格式解析Hex文件如果用特殊的程序來查看(般記事本就可以實現)。打開后可發現,整個文件以行為單位,每行以冒號開頭,內容全部為16
    發表于 01-12 06:17

    Hex文件格式解析

    hex文件格式是可以燒寫到單片機中,被單片機執行的文件格式,生成Hex文件的方式有很多種,可以通過不同的編譯器將C程序或者匯編程序編譯生成hex。Hex
    發表于 03-02 06:34

    如何光繪輸出AutoCAD文件格式

    教你光繪輸出AutoCAD文件格式
    發表于 06-07 15:16 ?36次下載

    數碼相機伴侶的文件格式

    數碼相機伴侶的文件格式              文件格式指的是數碼相機伴侶獨特的文件系統結構在
    發表于 12-23 10:05 ?864次閱讀

    GIF圖形文件格式文檔

    GIF圖形文件格式文檔 GIF圖形文件格式文檔 GIF圖形文件格式文檔
    發表于 05-24 10:53 ?2次下載

    GIF文件格式詳解

    GIF文件格式詳解 GIF文件格式詳解 GIF文件格式詳解
    發表于 05-24 10:53 ?2次下載

    一招讓手機屏立馬干凈

    手機屏幕油膩還滿是手印?教你一招,立馬干凈亮閃閃
    的頭像 發表于 08-27 17:26 ?4454次閱讀

    WAV文件格式詳解

    WAV是微軟公司開發的種音頻格式文件,用于保存Windows平臺的音頻信息資源,它符合資源互換文件格式(Resource Interchange File Format,RIFF)
    的頭像 發表于 10-21 09:52 ?5181次閱讀
    <b class='flag-5'>WAV</b><b class='flag-5'>文件格式</b>詳解

    哪些晶體、諧振器和振蕩器可旋轉180°使用?一招教你快速識別

    哪些晶體、諧振器和振蕩器可旋轉180°使用?一招教你快速識別
    的頭像 發表于 12-05 16:37 ?375次閱讀
    哪些晶體、諧振器和振蕩器可旋轉180°使用?<b class='flag-5'>一招</b>兒<b class='flag-5'>教你</b><b class='flag-5'>快速</b>識別