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 }

 

posted on 2023-07-11 22:50  沉默的思想  阅读(90)  评论(0编辑  收藏  举报