wav是一种无损的音频文件格式,所有的WAV都有一个文件头,包含了音频流的编码参数,即支持非压缩的PCM编码方式,也支持常见的压缩编码格式。
当WAV文件采用PCM编码方式时,PCM文件和WAV文件的区别只在于是否有文件头,下面介绍一下wav文件的文件头。
偏移 |
字节数 | 数据 |
字段名称 | 字段说明 |
00H | 4 | 字符 | 文档标识 | 大写字符串"RIFF",标明该文件为有效的 RIFF 格式文档。 |
04H | 4 | 长整型数 | 文件数据长度 | 从下一个字段首地址开始到文件末尾的总字节数。该字段的数值加 8 为当前文件的实际长度。 |
08H | 4 | 字符 | 文件格式类型 | 所有 WAV 格式的文件此处为字符串"WAVE",标明该文件是 WAV 格式文件。 |
0CH | 4 | 字符 | 格式块标识 | 小写字符串,"fmt "。 |
10H | 4 | 长整型数 | 格式块长度。 | 其数值不确定,取决于编码格式。可以是 16、 18 、20、40 等。(PCM 编码为16) |
14H | 2 | 整型数 | 编码格式代码。 | 常见的 WAV 文件使用 PCM 脉冲编码调制格式,该数值通常为 1。 |
16H | 2 | 整型数 | 声道个数 | 单声道为 1,立体声或双声道为 2 |
18H | 4 | 长整型数 | 采样频率 | 每个声道单位时间采样次数。常用的采样频率有 11025, 22050 和 44100 kHz。 |
1CH | 4 | 长整型数 | 数据传输速率, | 该数值为:声道数×采样频率×每样本的数据位数/8。 |
20H | 2 | 整型数 | 数据块对齐单位 | 采样帧大小。该数值为:声道数×位数/8。 |
22H | 2 | 整型数 | 采样位数 | 存储每个采样值所用的二进制数位数。常见的位数有 4、8、12、16、24、32 |
24H | 2 | 整型数 | 扩展区长度 | 22(根据格式块长度有关) |
26H | 4 | 字符 | 数据块标识 | data |
2aH | 4 | 长整型数 | 数据块长度 | 数据块的长度 |
2eH |
当使用C程序读写wav格式数据时,先要把头文件的内容读取了,并判断文件的是否有损坏(可以通过文件长度判断)。
首先定义头文件的结构体:
typedef struct WAV_Format {
char ChunkID[4]; /* "RIFF" */
int ChunkSize; /* 36 + Subchunk2Size */
char Format[4]; /* "WAVE" */
/* sub-chunk "fmt" */
char Subchunk1ID[4]; /* "fmt " */
int Subchunk1Size; /* 16 for PCM */
short AudioFormat; /* PCM = 1*/
short NumChannels; /* Mono = 1, Stereo = 2, etc. */
int SampleRate; /* 8000, 44100, etc. */
int ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */
short BlockAlign; /* = NumChannels * BitsPerSample/8 */
short BitsPerSample; /* 8bits, 16bits, etc. */
/* sub-chunk "data" */
char Subchunk2ID[4]; /* "data" */
int Subchunk2Size; /* data size */
}WaveHead;
编写函数读取.wav文件的文件头,并返回文件头长度,数据块的数据长度,指向数据块的指针。
/*****************************************************************************
*@fn : read wave head and return wave head len
*@input : wh ,WaveHead point
:filename , wave file path point
*@output: fn ,file point of data chunk
: flen ,data len
*@return: head_len,wave head len
*@author:
*@data :
*******************************************************************************/
int WaveReadHead(WaveHead *wh, char *filename,FILE *fn, int *flen)
{
FILE *fd = NULL;
if((fd = fopen(filename, "rb")) == NULL)
{
printf("WaveReadHead(): cannot open file %s\n",filename);
}
int head_len = 0;
fseek(fd, 0L, SEEK_END);
*flen = ftell(fd);
fseek(fd,0L, SEEK_SET);
if (1!= fread(wh->ChunkID,4,1,fd)) return -1;
head_len += sizeof(int);
if (1!= fread(&wh->ChunkSize,4,1,fd)) return -1;
head_len += sizeof(int);
if (1!= fread(wh->Format,4,1,fd)) return -1;
head_len += 4*sizeof(char);
if (1!= fread(wh->Subchunk1ID,4,1,fd)) return -1;
head_len += sizeof(int);
if (1!= fread(&wh->Subchunk1Size,4,1,fd)) return -1;
head_len += sizeof(int);
if (1!= fread(&wh->AudioFormat,2,1,fd)) return -1;
head_len += sizeof(short);
if (1!= fread(&wh->NumChannels,2,1,fd)) return -1;
head_len += sizeof(short);
if (1!= fread(&wh->SampleRate,4,1,fd)) return -1;
head_len += sizeof(int);
if (1!= fread(&wh->ByteRate,4,1,fd)) return -1;
head_len += 4*sizeof(char);
if (1!= fread(&wh->BlockAlign,2,1,fd)) return -1;
head_len += sizeof(short);
if (1!= fread(&wh->BitsPerSample,2,1,fd)) return -1;
head_len += sizeof(short);
int i = 0;
char *str;
while (i>(wh->Subchunk1Size - 16))
{
if(1 != fread(str,1,1,fd)) return -1;
}
head_len += (wh->Subchunk1Size - 16);
if(1!= fread(wh->Subchunk2ID,4,1,fd)) return -1;
head_len +=4;
if(1!= fread(&wh->Subchunk2Size,4,1,fd)) return -1;
head_len +=4;
*flen -=head_len;
if(strncmp(wh->ChunkID,"RIFF",4)!=0|| strncmp(wh->Subchunk1ID,"fmt",3)!=0||
strncmp(wh->Format,"WAVE",4)!=0|| strncmp(wh->Subchunk2ID,"data",4)!=0||
wh->ChunkSize- wh->Subchunk2Size != head_len-8)
{
printf("Wave head read err!\n");
if (fd) fclose(fd);
}
fn = fd;
return(head_len);
}
编写一个测试函数。
int main()
{
FILE *fp = NULL;
WaveHead wh;
int flen = 0;
int head_len = 0;
char *filename ="0_0_0_0_1_1_1_1.wav";
if(0!=( head_len = WaveReadHead(&wh,filename,fp,&flen)));
printf("head_len: %d",head_len);
}
使用kaldi yesno 文件“0_0_0_0_1_1_1_1.wav”作为测试,程序运行结果如下: