WAVE文件格式是微软用于存储多媒体文件的RIFF规范的一个子集。主要用于封装PCM格式的音频数据。RIFF文件一开始是一个文件头,后面跟着一系列Chunk块。WAVE文件通常只是一个RIFF文件,它有一个由两个SubChunk组成的“WAVE” Chunk——一个是指定数据格式的“fmt” SubChunk,另一个是包含实际样本数据的“data” SubChunk。如下图所示,为WAV文件的典型结构图。
典型的WAV格式从RIFF头开始,由两个subchunks组成:“fmt ”和“data”。"fmt " subchunk描述音频的数据格式,“data” subchunk由音频数据的大小和实际的音频数据组成。
各个字段含义如表述如下:
如下图所示,为一个WAV文件的解析实例:
注意点:
1、WAVE数据文件的默认字节顺序是little-endian。使用big-endian字节排序方案编写的文件具有标识符RIFX而不是RIFF;
2、音频采样数据必须以偶数字节边界结束;
3、8位样本存储为无符号字节,范围从0到255。16位样本存储为范围从-32768到32767的2补有符号整数。
4、在Wave数据流中可能存在额外的子信道。如果是这样,每个都将有一个char[4]SubChunkID、无符号长SubChunkSize和SubChunkSize数据量。
5、RIFF代表资源交换文件格式(Resource Interchange File Format.);
依据该协议,封装PCM数据代码如下:
1 #define WRITE_ONE_WORD(p, offset, data) do{ \ 2 p[offset++] = (char)((data)&0xFF); \ 3 p[offset++] = (char)((data >> 8)&0xFF); \ 4 p[offset++] = (char)((data >> 16)&0xFF); \ 5 p[offset++] = (char)((data >> 24)&0xFF); \ 6 }while(0) 7 #define WRITE_HALF_WORD(p, offset, data) do{ \ 8 p[offset++] = (char)((data)&0xFF); \ 9 p[offset++] = (char)((data >> 8)&0xFF);\ 10 }while(0) 11 12 int wav_mux(char *pcm, int len, int sampleRate, int channels, int bitsample, char **wav, int *wavlen) 13 { 14 int offset = 0; 15 char *pwav = NULL; 16 uint32_t u32Data = 0; 17 uint16_t u16Data = 0; 18 pwav = calloc(1, 8+36+len); 19 20 /* RIFF Chunk */ 21 pwav[offset++] = 'R'; //Chunk ID 22 pwav[offset++] = 'I'; 23 pwav[offset++] = 'F'; 24 pwav[offset++] = 'F'; 25 u32Data = 36+len; //Chunk Size 26 WRITE_ONE_WORD(pwav, offset, u32Data); 27 pwav[offset++] = 'W'; //Format 28 pwav[offset++] = 'A'; 29 pwav[offset++] = 'V'; 30 pwav[offset++] = 'E'; 31 32 /* fmt sub-Chunk */ 33 pwav[offset++] = 'f'; //SubChunk1ID 34 pwav[offset++] = 'm'; 35 pwav[offset++] = 't'; 36 pwav[offset++] = ' '; 37 u32Data = 16; //SubChunk1 Size 38 WRITE_ONE_WORD(pwav, offset, u32Data); 39 u16Data = 1; //AudioFormat PCM=1 40 WRITE_HALF_WORD(pwav, offset, u16Data); 41 u16Data = channels; //NumChannels Mono = 1, stereo = 2 42 WRITE_HALF_WORD(pwav, offset, u16Data); 43 u32Data = sampleRate; //SampleRate 44 WRITE_ONE_WORD(pwav, offset, u32Data); 45 u32Data = sampleRate * channels * bitsample / 8;//ByteRate = SampleRate * NumChannels * BitsPerSample/8 46 WRITE_ONE_WORD(pwav, offset, u32Data); 47 u16Data = 2; //BlockAlign = NumChannels * BitsPerSample/8 48 WRITE_HALF_WORD(pwav, offset, u16Data); 49 u16Data = 16; //BitsPerSample 50 WRITE_HALF_WORD(pwav, offset, u16Data); 51 52 /* data sub-Chunk */ 53 pwav[offset++] = 'd'; //SubChunk2ID 54 pwav[offset++] = 'a'; 55 pwav[offset++] = 't'; 56 pwav[offset++] = 'a'; 57 u32Data = len; //SubChunk2 Size 58 WRITE_ONE_WORD(pwav, offset, u32Data); 59 60 memcpy(&pwav[offset], pcm, len); 61 62 *wav = pwav; 63 *wavlen = offset + len; 64 return 0; 65 }