標籤:wav audio
1、wav概述WAV為微軟公司(Microsoft)開發的一種音效檔格式,它符合RIFF(Resource Interchange File Format)檔案規格,用於儲存Windows平台的音頻資訊資源,被Windows平台及其應用程式所廣泛支援,該格式也支援MSADPCM,CCITT A LAW等多種壓縮運演算法,支援多種音頻數字,取樣頻率和聲道,標準格式化的WAV檔案和CD格式一樣,也是44.1K的取樣頻率,16位量化數字,因此在音效檔品質和CD相差無幾! WAV開啟工具是WINDOWS的媒體播放器。通常使用三個參數來表示聲音,量化位元,取樣頻率和採樣點振幅。量化位元分為8位,16位,24位三種,聲道有單聲道和立體聲之分,單聲道振幅資料為n*1矩陣點,立體聲為n*2矩陣點,取樣頻率一般有11025Hz(11kHz) ,22050Hz(22kHz)和44100Hz(44kHz) 三種,不過儘管音質出色,但在壓縮後的檔案體積過大!相對其他音頻格式而言是一個缺點,其檔案大小的計算方式為:WAV格式檔案所佔容量(B) = (取樣頻率 X量化位元X 聲道) X 時間 / 8 (位元組= 8bit) 每一分鐘WAV格式的音頻檔案的大小為10MB,其大小不隨音量大小及清晰度的變化而變化。支援WAV設計的手機主要為智能手機,如索尼愛立信P910和諾基亞N90以及採用Windows Moblie的多普達等手機還有微軟Windows Phone系列手機,而其它一些非智能手機的產品,如果宣傳支援WAV格式則多半屬於只是支援單聲道的。2、格式解析WAVE檔案是非常簡單的一種RIFF檔案,它的格式類型為"WAVE"。RIFF塊包含兩個子塊,這兩個子塊的ID分別是"fmt"和"data",其中"fmt"子塊由結構PCMWAVEFORMAT所組成,其子塊的大小就是sizeofof(PCMWAVEFORMAT),資料群組成就是PCMWAVEFORMAT結構中的資料。整個頭長度44byte.
標誌符(RIFF) |
餘下所有資料的長度 |
格式類型("WAVE") |
"fmt" |
PCMWAVEFORMAT的長度 |
PCMWAVEFORMAT |
"data" |
聲音資料大小 |
聲音資料 |
3、wav頭結構體定義
/* RIFF WAVE file struct. * For details see WAVE file format documentation * (for example at http://www.wotsit.org). */typedef struct WAV_HEADER_S{char riffType[4];//4byte,資源分頁檔標誌:RIFFunsigned int riffSize;//4byte,從下個地址到檔案結尾的總位元組數char waveType[4];//4byte,wav檔案標誌:WAVEchar formatType[4];//4byte,波形檔案標誌:FMT(最後一位空格符)unsigned intformatSize;//4byte,音頻屬性(compressionCode,numChannels,sampleRate,bytesPerSecond,blockAlign,bitsPerSample)所佔位元組數unsigned shortcompressionCode;//2byte,格式種類(1-線性pcm-WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM)unsigned short numChannels;//2byte,通道數unsigned int sampleRate;//4byte,採樣率unsigned int bytesPerSecond;//4byte,傳輸速率unsigned short blockAlign;//2byte,資料區塊的對齊,即DATA資料區塊長度unsigned short bitsPerSample;//2byte,採樣精度-PCM位寬char dataType[4];//4byte,資料標誌:dataunsigned int dataSize;//4byte,從下個地址到檔案結尾的總位元組數,即除了wav header以外的pcm data length}WAV_HEADER;
4、頭解析程式樣本wav.h
#ifndef __WAV_H__#define __WAV_H__#define debug(fmt...) do { printf("[%s::%d] ", __func__, __LINE__);printf(fmt); }while(0)/* RIFF WAVE file struct. * For details see WAVE file format documentation * (for example at http://www.wotsit.org). */typedef struct WAV_HEADER_S{char riffType[4];//4byte,資源分頁檔標誌:RIFFunsigned int riffSize;//4byte,從下個地址到檔案結尾的總位元組數char waveType[4];//4byte,wave檔案標誌:WAVEchar formatType[4];//4byte,波形檔案標誌:FMTunsigned int formatSize;//4byte,音頻屬性(compressionCode,numChannels,sampleRate,bytesPerSecond,blockAlign,bitsPerSample)所佔位元組數unsigned shortcompressionCode;//2byte,編碼格式(1-線性pcm-WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM)unsigned short numChannels;//2byte,通道數unsigned int sampleRate;//4byte,採樣率unsigned int bytesPerSecond;//4byte,傳輸速率unsigned short blockAlign;//2byte,資料區塊的對齊unsigned short bitsPerSample;//2byte,採樣精度char dataType[4];//4byte,資料標誌:dataunsigned int dataSize;//4byte,從下個地址到檔案結尾的總位元組數,即除了wav header以外的pcm data length}WAV_HEADER;typedef struct WAV_INFO_S{ WAV_HEADER header; FILE *fp; unsigned int channelMask;}WAV_INFO;#endif
wav.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include "wav.h"/* func: endian judge * return: 0-big-endian othes-<span style="color: rgb(51, 51, 51); font-family: 'Courier New';font-size:12px; line-height: 24px; text-indent: 24px;">little</span>-endian */int IS_LITTLE_ENDIAN(void) {int __dummy = 1;return ( *( (unsigned char*)(&(__dummy) ) ) );}unsigned int readHeader(void *dst, signed int size, signed int nmemb, FILE *fp) {unsigned int n, s0, s1, err;unsigned char tmp, *ptr;if ((err = fread(dst, size, nmemb, fp)) != nmemb) {return err;}if (!IS_LITTLE_ENDIAN() && size > 1) {//debug("big-endian \n");ptr = (unsigned char*)dst;for (n=0; n<nmemb; n++) { for (s0=0, s1=size-1; s0 < s1; s0++, s1--) { tmp = ptr[s0]; ptr[s0] = ptr[s1]; ptr[s1] = tmp; } ptr += size;}}else{//debug("<span style="color: rgb(51, 51, 51); font-family: 'Courier New';font-size:12px; line-height: 24px; text-indent: 24px;">little</span>-endian \n");} return err;}void dumpWavInfo(WAV_INFO wavInfo){debug("compressionCode:%d \n",wavInfo.header.compressionCode);debug("numChannels:%d \n",wavInfo.header.numChannels);debug("sampleRate:%d \n",wavInfo.header.sampleRate);debug("bytesPerSecond:%d \n",wavInfo.header.bytesPerSecond);debug("blockAlign:%d \n",wavInfo.header.blockAlign);debug("bitsPerSample:%d \n",wavInfo.header.bitsPerSample);}int wavInputOpen(WAV_INFO *pWav, const char *filename){ signed int offset; WAV_INFO *wav = pWav ; if (wav == NULL) { debug("Unable to allocate WAV struct.\n"); goto error; } wav->fp = fopen(filename, "rb"); if (wav->fp == NULL) { debug("Unable to open wav file. %s\n", filename); goto error; }/* RIFF標誌符判斷 */if (fread(&(wav->header.riffType), 1, 4, wav->fp) != 4) { debug("couldn't read RIFF_ID\n"); goto error; /* bad error "couldn't read RIFF_ID" */}if (strncmp("RIFF", wav->header.riffType, 4)) { debug("RIFF descriptor not found.\n") ; goto error;}debug("Find RIFF \n");/* Read RIFF size. Ignored. */ readHeader(&(wav->header.riffSize), 4, 1, wav->fp); debug("wav->header.riffSize:%d \n",wav->header.riffSize); /* WAVE標誌符判斷 */ if (fread(&wav->header.waveType, 1, 4, wav->fp) !=4) { debug("couldn't read format\n"); goto error; /* bad error "couldn't read format" */ } if (strncmp("WAVE", wav->header.waveType, 4)) { debug("WAVE chunk ID not found.\n") ; goto error; } debug("Find WAVE \n");/* fmt標誌符判斷 */ if (fread(&(wav->header.formatType), 1, 4, wav->fp) != 4) { debug("couldn't read format_ID\n"); goto error; /* bad error "couldn't read format_ID" */ } if (strncmp("fmt", wav->header.formatType, 3)) { debug("fmt chunk format not found.\n") ; goto error; } debug("Find fmt \n"); readHeader(&wav->header.formatSize, 4, 1, wav->fp); // Ignored debug("wav->header.formatSize:%d \n",wav->header.formatSize);/* read info */readHeader(&(wav->header.compressionCode), 2, 1, wav->fp);readHeader(&(wav->header.numChannels), 2, 1, wav->fp);readHeader(&(wav->header.sampleRate), 4, 1, wav->fp);readHeader(&(wav->header.bytesPerSecond), 4, 1, wav->fp);readHeader(&(wav->header.blockAlign), 2, 1, wav->fp);readHeader(&(wav->header.bitsPerSample), 2, 1, wav->fp);offset = wav->header.formatSize - 16; /* Wav format extensible */ if (wav->header.compressionCode == 0xFFFE) { static const unsigned char guidPCM[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }; unsigned short extraFormatBytes, validBitsPerSample; unsigned char guid[16]; signed int i; /* read extra bytes */ readHeader(&(extraFormatBytes), 2, 1, wav->fp); offset -= 2;if (extraFormatBytes >= 22) {readHeader(&(validBitsPerSample), 2, 1, wav->fp);readHeader(&(wav->channelMask), 4, 1, wav->fp);readHeader(&(guid), 16, 1, wav->fp);/* check for PCM GUID */for (i = 0; i < 16; i++) if (guid[i] != guidPCM[i]) break;if (i == 16) wav->header.compressionCode = 0x01;offset -= 22;}}debug("wav->header.compressionCode:%d \n",wav->header.compressionCode); /* Skip rest of fmt header if any. */ for (;offset > 0; offset--) { fread(&wav->header.formatSize, 1, 1, wav->fp); }#if 1 do { /* Read data chunk ID */if (fread(wav->header.dataType, 1, 4, wav->fp) != 4) {debug("Unable to read data chunk ID.\n");free(wav);goto error;} /* Read chunk length. */ readHeader(&offset, 4, 1, wav->fp);/* Check for data chunk signature. */if (strncmp("data", wav->header.dataType, 4) == 0) {debug("Find data \n");wav->header.dataSize = offset;break;}/* Jump over non data chunk. */for (;offset > 0; offset--) {fread(&(wav->header.dataSize), 1, 1, wav->fp);} } while (!feof(wav->fp)); debug("wav->header.dataSize:%d \n",wav->header.dataSize); #endif /* return success */ return 0;/* Error path */error: if (wav) { if (wav->fp) { fclose(wav->fp); wav->fp = NULL; } //free(wav); } return -1; }#if 0int main(int argc,char **argv){WAV_INFO wavInfo;char fileName[128];if(argc<2 || strlen(&argv[1][0])>=sizeof(fileName)){debug("argument error !!! \n");return -1 ;}debug("size : %d \n",sizeof(WAV_HEADER));strcpy(fileName,argv[1]);wavInputOpen(&wavInfo, fileName);return 0;}#endif
附:FIFF檔案知識點1. 簡介
RIFF全稱為
資源互換檔案格式(
ResourcesInterchange FileFormat),
RIFF檔案是windows環境下大部分多媒體檔案遵循的一種檔案結構,RIFF檔案所包含的資料類型由該檔案的副檔名來標識,能以RIFF檔案儲存體的資料包括:音訊視訊交錯格式格式資料(.AVI) 波形格式資料(.WAV) 位元影像格式資料(.RDI) MIDI格式資料(.RMI)調色盤格式(.PAL)多媒體電影(.RMN)動畫游標(.ANI)其它RIFF檔案(.BND)2. CHUNKchunk是組成RIFF檔案的基本單元,它的基本結構如下:struct chunk{u32 id; /* 塊標誌 */u32 size; /* 塊大小 */u8 dat[size]; /* 塊內容 */};id 由4個ASCII字元組成,用以識別塊中所包含的資料。如:‘RIFF‘,‘LIST‘,‘fmt‘,‘data‘,‘WAV‘,‘AVI‘等等,由於這種檔案結構最初是由Microsoft和IBM為PC機所定義,RIFF檔案是按照little-endian[2] 位元組順序寫入的。size(塊大小) 是儲存在data域中資料的長度,id與size域的大小則不包括在該值內。dat(塊內容) 中所包含的資料是以字(WORD)為單位排列的,如果該資料結構長度是奇數,則在最後添加一個空(NULL)位元組。chunk塊中有且僅有兩種類型塊:‘RIFF‘和‘LIST‘類型可以包含其他塊,而其它塊僅能含有資料。‘RIFF‘和‘LIST‘類型的chunk結構如下structchunk{u32 id; /* 塊標誌 */u32 size; /* 塊大小 *//*此時的dat = type + restdat */u32 type ; /* 類型 */u8 restdat[size] /* dat中除type4個位元組後剩餘的資料*/};可以看出,‘RIFF‘和‘LIST‘也是chunk,只是它的dat由兩部分組成type和restdat。type,由4個ASCII字元組成,代表RIFF檔案的類型,如‘WAV‘,‘AVI ‘;或者‘LIST‘塊的類型,如avi檔案中的列表‘hdrl‘,‘movi‘。restdat,dat中除type4個位元組後剩餘的資料,包括塊內容,包含若干chunk和‘LIST‘2.1 FOURCC 一個
FOURCC(
fourcharacter code)是一個佔4個位元組的資料,一般表示4個ASCII字元。在
RIFF檔案格式中,
FOURCC非常普遍,structchunk 中的id成員,‘LIST‘,‘RIFF‘的type成員,起始標識等資訊都是用
FOURCC表示的。
FOURCC一般是四個字元,如‘abcd‘這樣的形式,也可以三個字元包含一個空格,如‘abc‘這樣的形式。RIFF檔案的FileData部分由若干個‘LIST‘和chunk組成,而‘LIST‘的ListData又可以由若干個‘LIST‘和chunk組成,即‘LIST‘是可以嵌套的。‘RIFF‘,FileType,‘LIST‘,ListType,ChunkID都是FOURCC,即使用4位元組的ASIIC字元標識類型。FileSize,ListSize,ChunkSize為little-endian32-bit正整數,表示Type(只有‘RIFF‘,‘LIST‘chunk有Type)+Data一起的大小,注意它是little-endian表示,如:0x00123456,儲存地址由低到高,在little-endian系統中的儲存表示為0x56341200(位元組由低位到高位儲存),而在big-endian為0x00123456(位元組由高位到低位儲存)。32bit整數0x00123456儲存地址低--------->;高little-endian(位元組由低位到高位儲存)56341200big-endian(位元組由高位到低位儲存)00123456
wav音頻檔案頭解析