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格式则多半属于只是支持单声道的。
格式解析
WAVE文件是非常简单的一种RIFF文件,它的格式类型为"WAVE"。RIFF块包含两个子块,这两个子块的ID分别是"fmt"和"data",其中"fmt"子块由结构PCMWAVEFORMAT所组成,其子块的大小就是sizeofof(PCMWAVEFORMAT),数据组成就是PCMWAVEFORMAT结构中的数据。
整个头长度44byte.
标志符(RIFF) |
余下所有数据的长度 |
格式类型("WAVE") |
"fmt" |
PCMWAVEFORMAT的长度 |
PCMWAVEFORMAT |
"data" |
声音数据大小 |
声音数据 |
wav头结构体定义
- /* RIFF WAVE file struct.
- * For details see WAVE file format documentation
- * (for example at <a href="http://www.wotsit.org)." target="_blank">http://www.wotsit.org).</a> */
- typedef struct WAV_HEADER_S
- {
- char riffType[4]; //4byte,资源交换文件标志:RIFF
- unsigned int riffSize; //4byte,从下个地址到文件结尾的总字节数
- char waveType[4]; //4byte,wav文件标志:WAVE
- char formatType[4]; //4byte,波形文件标志:FMT(最后一位空格符)
- unsigned int formatSize; //4byte,音频属性(compressionCode,numChannels,sampleRate,bytesPerSecond,blockAlign,bitsPerSample)所占字节数
- unsigned short compressionCode;//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,数据标志:data
- unsigned int dataSize; //4byte,从下个地址到文件结尾的总字节数,即除了wav header以外的pcm data length
- }WAV_HEADER;
头解析程序示例
wav.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37#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 <a href="http://www.wotsit.org)." target="_blank">http://www.wotsit.org).</a> */ typedef struct WAV_HEADER_S { char riffType[4]; //4byte,资源交换文件标志:RIFF unsigned int riffSize; //4byte,从下个地址到文件结尾的总字节数 char waveType[4]; //4byte,wave文件标志:WAVE char formatType[4]; //4byte,波形文件标志:FMT unsigned int formatSize; //4byte,音频属性(compressionCode,numChannels,sampleRate,bytesPerSecond,blockAlign,bitsPerSample)所占字节数 unsigned short compressionCode;//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,数据标志:data unsigned 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230#include <stdio.h> #include <stdlib.h> #include <string.h> #include "wav.h" /* func : endian judge * return : 0-big-endian othes-little-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("little-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. %sn", filename); goto error; } /* RIFF标志符判断 */ if (fread(&(wav->header.riffType), 1, 4, wav->fp) != 4) { debug("couldn't read RIFF_IDn"); 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 formatn"); 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_IDn"); 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 0 int 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. CHUNK
chunk是组成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音频文件头解析的全部内容,更多相关wav音频文件头解析内容请搜索靠谱客的其他文章。
发表评论 取消回复