详解wave头格式(尽可能详细并附代码)

参考网址一:http://blog.csdn.net/sshcx/archive/2007/05/01/1593923.aspx
参考网址二:http://apps.hi.baidu.com/share/detail/15909594

WAVE 文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。RIFF是英
文Resource Interchange File Format的缩写,每个WAVE文件的头四个字节便是"RIFF"。WAVE
文件由文件头和数据体两大部分组成。其中文件头又分为RIFF/WAV文件 标识段和声音数据格
式说明段两部分。
常见的声音文件主要有两种,分别对应于单声道(11.025KHz 采样率、8Bit的采样值)和
双声道(44.1KHz采样率、16Bit的采样值)。采样率是指:声音信号在"模→数"转换过程中单
位时间内采样的次数。 采样值是指每一次采样周期内声音模拟信号的积分值。
[!21ki@][@21ki!]
对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH);而对于双声道立体声
声音文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代表左右两个声道。
WAVE文件数据块包含以脉冲编码调制(PCM)格式表示的样本。WAVE文件是由样本组织而成
的。在单声道WAVE文件中,声道0代表左声道,声道1代表右声道。在多声道WAVE文件中,样本
是交替出现的。
参考网址一和二中都给出了wav的头定义,但有一个小区别,一中将这个44个字节作为一个
大的头,给定在一个结构体中,而二中则是按照四个不同的chunk处理的,所以就给了四个结构
体,后面我的代码也是根据二来写的,个人觉得这样比较清晰,不过这纯属个人感觉。
下面就是根据四个结构体写的代码,注意FACT不是必须的,可以不用写入头,不写的话就
是44个字节,写的话wave的头是56个字节。
一:源代码(参考二中也有比较详细的读wave头的代码)
#if !defined(_WAV_INFO_)
#define _WAV_INFO_
// 一些和声音数据相关的宏
#define SAMPLE_RATE 22050 // sample rate,每秒22050个采样点
#define QUANTIZATION 0x10 // 16bit量化,
#define BYTES_EACH_SAMPLE 0x2 // QUANTIZATION / 8, 所以每个采样点、
// 是short,占个2个字节
#define CHANNEL_NUN 0x1 // 单声道
#define FORMAT_TAG 0x1 // 线性PCM
// 一个wave file包括四个CHUNK,除了FACT之外,其它是必须的,并且第一个RIFF是整个文件的头,
// 所以别名为WAV_HEADER,而不是RIFF
/*------------------------Wave File Structure ------------------------------------ */
typedef struct RIFF_CHUNK{

char fccID[4]; // must be "RIFF"
unsigned long dwSize; // all bytes of the wave file subtracting 8,
// which is the size of fccID and dwSize
char fccType[4]; // must be "WAVE"
}WAVE_HEADER;
// 12 bytes
typedef struct FORMAT_CHUNK{
char fccID[4]; // must be "fmt "
unsigned long dwSize; // size of this struct, subtracting 8, which
// is the sizeof fccID and dwSize
unsigned short wFormatTag; // one of these: 1: linear,6: a law,7:u-law
unsigned short wChannels; // channel number
unsigned long dwSamplesPerSec; // sampling rate
unsigned long dwAvgBytesPerSec; // bytes number per second
unsigned short wBlockAlign; // 每样本的数据位数(按字节算), 其值为:通道
// 数*每样本的数据位值/8,播放软件需要一次处
// 理多个该值大小的字节数据, 以便将其值用于
// 缓冲区的调整每样本占几个字节:
// NumChannels * uiBitsPerSample/8
unsigned short uiBitsPerSample; // quantization
}FORMAT;
// 24 bytes
// The fact chunk is required for all new WAVE formats.
// and is not required for the standard WAVE_FORMAT_PCM files
// 也就是说,这个结构体目前不是必须的,一般当wav文件由某些软件转化而成,则包含该Chunk
// 但如果这里写了,则必须是如下的结构,并且在四个结构体中的位置也要放在第三
typedef struct {
char fccID[4]; // must be "fact"
unsigned long id; // must be 0x4
unsigned long dwSize; // 暂时没发现有啥用
}FACT;
// 12 bytes
// 数据结构
typedef struct {
char fccID[4]; // must be "data"
unsigned long dwSize; // byte_number of PCM data in byte
}DATA;
// 8 bytes
/*------------------------Wave File Structure ------------------------------------ */
void WriteWaveHeader(FILE *fpwav,long length)
{
WAVE_HEADER WaveHeader;
FORMAT WaveFMT;
DATA WaveData;

FACT WaveFact;
memset(&WaveHeader, 0, sizeof(WAVE_HEADER));
memcpy(WaveHeader.fccID, "RIFF", 4);
memcpy(WaveHeader.fccType, "WAVE", 4);
// dwSize 是整个wave文件的大小(字节数,但不包括不包括HEADER中的前面两个结构:
// HEADER.fccID和HEAD.dwSize)
// WaveHeader.dwSize = length + 0x24; // 如果不写入fact,就是36个字节,
// 44- 8 = 36个
WaveHeader.dwSize = length + 0x30; // 如果写入fact,就是48 个bytes
memset(&WaveFMT, 0, sizeof(FORMAT));
memcpy(WaveFMT.fccID, "fmt ", 4);
WaveFMT.dwSize = 0x10;
WaveFMT.dwSamplesPerSec = SAMPLE_RATE;
WaveFMT.dwAvgBytesPerSec = CHANNEL_NUN * SAMPLE_RATE * BYTES_EACH_SAMPLE;
WaveFMT.wChannels = CHANNEL_NUN;
WaveFMT.uiBitsPerSample = QUANTIZATION;
WaveFMT.wFormatTag = FORMAT_TAG;
WaveFMT.wBlockAlign = BYTES_EACH_SAMPLE;
memset(&WaveFact, 0, sizeof(FACT));
memcpy(WaveFact.fccID, "fact", 4);
WaveFact.dwSize = length; // 这个值不知道什么意思
WaveFact.id = 0x4;
memset(&WaveData, 0, sizeof(DATA));
memcpy(WaveData.fccID, "data", 4);
WaveData.dwSize = length;
fwrite(&WaveHeader, sizeof(WAVE_HEADER), 1, fpwav);
fwrite(&WaveFMT, sizeof(FORMAT), 1, fpwav);
fwrite(&WaveFact, sizeof(FACT), 1, fpwav); // fact不是必须的
fwrite(&WaveData, sizeof(DATA), 1, fpwav);
}
void WriteWavfile(FILE *fp, short *pSpeechData, int length)
{
//write the header of wav
WriteWaveHeader(fp, length * BYTES_EACH_SAMPLE);
//write the data
fwrite(pSpeechData, sizeof(short), length, fp);
}
#endif

二:数据存储结构:
根据声音文件的声道和量化数的不同,在头之后的数据存储有不同的格式,如下面:
http://apps.hi.baidu.com/share/detail/15909594
---------------------------------------------------------------------
| 单声道 | 取样1 | 取样2 | 取样3 | 取样4 |
| | --------------------------------------------------------
| 8bit 量化 | 声道0 | 声道0 | 声道0 | 声道0 |
---------------------------------------------------------------------
| 双声道 | 取样1 | 取样2 |
| |--------------------------------------------------------
| 8bit 量化 | 声道0(左) | 声道1(右) | 声道0(左) | 声道1(右) |
---------------------------------------------------------------------
| | 取样1 | 取样2 |
| 单声道 |--------------------------------------------------------
| 16bit 量化 | 声道0 | 声道0 | 声道0 | 声道0 |
| | (低位字节) | (高位字节) | (低位字节) | (高位字节) |
---------------------------------------------------------------------
| | 取样1 |
| 双声道 |--------------------------------------------------------
| 16bit 量化 | 声道0(左) | 声道0(左) | 声道1(右) | 声道1(右) |
| | (低位字节) | (高位字节) | (低位字节) | (高位字节) |
---------------------------------------------------------------------
图:Wav 的data 数据的bit 位置可能的几种形式
三:各种头示例
常用语音编码的WAVE 文件头格式剖析--各种编码
http://www.360doc.com/content/10/0812/10/722458_45443101.shtml
表1 8KHz 采样、16 比特量化的线性PCM 语音信号的WAVE 文件头格式表(共44 字节)
偏移地址 字节数 数据类型 内容 文件头定义为
00H 4 char "RIFF" char riff_id[4]="RIFF"
04H 4 long int 文件总长-8 long int size0=文总长-8
08H 8 char "WAVEfmt " char wave_fmt[8]
10H 4 long int 10 00 00 00H(PCM) long int size1=0x10
14H 2 int 01 00H int fmttag=0x01
16H 2 int int channel=1 或2
18H 4 long int 采样率 long int samplespersec
1CH 4 long int 每秒播放字节数 long int bytepersec
20H 2 int 采样一次占字节数 int blockalign=声道数*量化数/8
22H 2 int 量化数 int bitpersamples=8 或16
24H 4 char "data" char data_id="data"

28H 4 long int 采样数据字节数 long int size2=文长-44
2CH 到文尾 char 采样数据
表2 8KHz 采样、8 比特A 律量化的PCM 语音信号的WAVE 文件头格式表(共58 字节)
偏移地址 字节数 数据类型 内容 文件头定义为
00H 4 char "RIFF" char riff_id[4]="RIFF"
04H 4 long int 文件总长-8 long int size0=文总长-8
08H 8 char "WAVEfmt " char wave_fmt[8]
10H 4 long int 12000000H(ALAW) long int size1=0x12
14H 2 int 06 00H int fmttag=0x06
16H 2 int 声道数 int channel=1 或2
18H 4 long int 采样率 long int samplespersec
1CH 4 long int 每秒播放字节数 long int bytepersec
20H 2 int 采样一次占字节数 int blockalign=0x01
22H 4 long int 量化数 long int bitpersamples=8
26H 4 char "fact" char wave_fact="fact"
2AH 8 char 0400000000530700H 定 char temp
32H 4 char "data" char wave_data="data"
36H 4 long int 采样数据字节数 lont int size2=文长-58
表3 8KHz 采样、8 比特U 律量化的PCM 语音信号的WAVE 文件头格式表(共58 字节)
偏移地址 字节数 数据类型 内容 文件头定义为
00H 4 char "RIFF" char riff_id[4]="RIFF"
04H 4 long int 文件总长-8 long int size0=文总长-8
08H 8 char "WAVEfmt " char wave_fmt[8]
10H 4 long int 12000000H(ULAW) long int size1=0x12
14H 2 int 07 00H int fmttag=0x07
16H 2 int 声道数 int channel=1 或2
18H 4 long int 采样率 long int samplespersec
1CH 4 long int 每秒播放字节数 long int bytepersec
20H 2 int 采样一次占字节数 int blockalign=0x01

定 char temp
32H 4 char "data" char wave_data="data"
36H 4 long int 采样数据字节数 lont int size2=文长-58
表4 ADPCM 语音编码后的WAVE 文件头格式表(共90 字节)
偏移地址 字节数 数据类型 内容 文件头定义为
00H 4 char "RIFF" char riff_id[4]="RIFF"
04H 4 long int 文件总长-8 long int size0=文总长-8
08H 8 char "WAVEfmt " char wave_fmt[8]
10H 4 long int 32000000H(ADPCM) long int size1=0x32
14H 2 int 02 00H int fmttag=0x02
16H 2 int 声道数 int channel=1 或2
18H 4 long int 采样率 long int samplespersec
1CH 4 long int 每秒播放字节数 long int bytepersec
20H 2 int 采样一次占字节数 int blockalign=声道数*量化数/8
22H 2 int 量化数 int bitpersamples=4
24H 34 char 固定字节 char temp1
46H 4 char "fact" char wave_fact="fact"
4AH 8 char 0400000004930600H 定 char temp2
52H 4 char "data" char wave_data="data"
56H 4 long int 采样数据字节数 lont int size2=文长-90
5AH 到文尾 采样数据
表5 GSM 语音编码后的WAVE 文件头格式表(共60 字节)
偏移地址 字节数 数据类型 内容 文件头定义为
00H 4 char "RIFF" char riff_id[4]="RIFF"
04H 4 long int 文件总长-8 long int size0=文总长-8
08H 8 char "WAVEfmt " char wave_fmt[8]
10H 4 long int 14000000H(GSM) long int size1=0x14
14H 2 int 31 00H int fmttag=0x31

节数 long int bytepersec
20H 8 char 4100000002004001H 定 char temp1
28H 8 char 6661637404000000H 定 char temp2
30H 4 char 40 E2 05 00H 定 char temp3
34H 4 char "data" char wave_data="data"
38H 4 long int 采样数据字节数 lont int size2=文长-60
3CH 到文尾 采样数据
表6 SBC 语音编码后的WAVE 文件头格式表(共58 字节)
偏移地址 字节数 数据类型 内容 文件头定义为
00H 4 char "RIFF" char riff_id[4]="RIFF"
04H 4 long int 文件总长-8 long int size0=文总长-8
08H 8 char "WAVEfmt " char wave_fmt[8]
10H 4 long int 12000000H(SBC) long int size1=0x12
14H 2 int 71 00H int fmttag=0x71
16H 2 int 声道数 int channel=1 或2
18H 4 long int 采样率 long int samplespersec
1CH 4 long int 每秒播放字节数 long int bytepersec
20H 2 int 采样一次占字节数 int blockalign=0x25
22H 4 long int 量化数 long int bitpersamples=16
26H 4 char "fact" char wave_fact="fact"
2AH 8 char 0400000076280400H 定 char temp
32H 4 char "data" char wave_data="data"
36H 4 long int 采样数据字节数 lont int size2=文长-59
表7 CELP 语音编码后的WAVE 文件头格式表(共58 字节)
偏移地址 字节数 数据类型 内容 文件头定义为
00H 4 char "RIFF" char riff_id[4]="RIFF"
04H 4 long int 文件总长-8 long int size0=文总长-8
08H 8 char "WAVEfmt " char wave_fmt[8]

10H 4 long int 12000000H(CELP) long int size1=0x12
14H 2 int 70 00H int fmttag=0x70
16H 2 int 声道数 int channel=1 或2
18H 4 long int 采样率 long int samplespersec
1CH 4 long int 每秒播放字节数 long int bytepersec
20H 2 int 采样一次占字节数 int blockalign=0x0C
22H 4 long int 量化数 long int bitpersamples=16
26H 4 char "fact" char wave_fact="fact"
2AH 8 char 0400000060520700H 定 char temp
32H 4 char "data" char wave_data="data"
36H 4 long int 采样数据字节数 lont int size2=文长-58
WAVEFORMATEX
typedef struct{WORD wFormatTag; WORD nChannels; DWORD nSamplesPerSec; DWORD
nAvgBytesPerSec; WORD nBlockAlign; WORD wBitsPerSample; WORD cbSize; }
WAVEFORMATEX;
具体参数解释如下:
wFormatTag:波形数据的格式,定义在MMREG.H 文件中
nChannels:波形数据的通道数:单声道或立体声
nSamplesPerSec:采样率,对于PCM 格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1
kHz 等
nAvgBytesPerSec:数据率,对于PCM 格式的波形数据,数据率等于采样率乘以每样点字节数
nBlockAlign:每个样点字节数
wBitsPerSample:采样精度,对于PCM 格式的波形数据,采样精度为8 或16
cbSize:附加格式信息的数据块大小
概念2、定义设备头结构
WAVEHDR 定义了指向波形数据缓冲区的设备头。
WAVEHDR

typedef struct { LPSTR lpData; DWORD dwBufferLength; DWORD dwBytesRecorded;
DWORD dwUser; DWORD dwFlags; DWORD dwLoops; struct wavehdr_tag * lpNext;
DWORD reserved; } WAVEHDR;
lpData:波形数据的缓冲区地址
dwBufferLength:波形数据的缓冲区地址的长度
dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度
dwUser:用户数据
dwFlags:波形数据的缓冲区的属性
dwLoops:播放循环的次数,仅用于播放控制中
lpNext 和reserved 均为保留值
注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已
经存在的,我们只需要对其进行赋值即可。

猜你喜欢

转载自mysuperbaby.iteye.com/blog/1463525