本文就经常见的一种格式PCM(44字节)的Wav文件头进行分析
一.解析文件头
表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 采样数据
举例说明:kugoo下载的一首wav文件:魏三 抹去泪水 wav.wav(大小14,703,980 字节,时长2:46),文件头如下:
1.地址00H-03H,值为“RIFF”标志;
2.地址04H-07H,值为“64 5D E0 00”,存储的是文件大小刨去8字节后的值,注意这个是little-endian的,也就是高地址存低位,地地址存高位,所以Size=00E05D64H=14703972字节,比文件总大小少8个字节,这8个字节就是00H-07H。
3.地址08H-0FH,就是“WAVEfmt ”标记。
4.地址10H-13H,fmt格式的块大小,这种格式时是“10 00 00 00”,也是little-endian的,即块大小为16,也有可能为18,这时最后多了2个字节的附加信息。。其他格式的可能是20。
5.地址14H-15H,“01 00”,也是little-endian的,标记编码方式,一般为0x0001。
6.地址16H-17H,“01 00”,也是little-endian,标记声道数,这里值为1。注意1代表单声道,2代表双声道。
7.地址18H-1BH,“44 AC 00 00”,也是little-endian,标记采样频率,这里为441000Hz。
8.地址1CH-1FH,“88 58 01 00”每秒所需的字节数,也是little-endian,bytepersec=00015888H=88200(字节)。【加上点自己的看法 88200=频率*采样一次占的字节数=44100*2,我觉得这种关系是存在的,虽然很多资料上没提到)。
9.地址20H-21H,“02 00”,采样一次占字节数 ,有些地方也叫数据块对齐单位,也是little-endian的,这里是两个字节。声道数*量化数/8=1*16/8=2(字节)。
10.地址22H-23H,“10 00”,量化数,也就是每个采样需要的bit数,也是little-endian的,所以这里是16位。
11.地址24H-27H,“64 61 74 61”,就是“data”了。
12.地址28H-2BH,“40 5D E0 00”,存储的是文件大小刨去44字节后的值,这个也是little-endian的,Size=00E05D40H=14703936字节。
二、编程方法
1.计算文件播放时长
文件播放时长=(文件总长度-文件头长度)/每秒所需的字节数。如上例 duration=(14,703,980 -44)/88200=166.7s,这个在kugoo的制作铃声功能下可以查询的到。
2.按时间点切割文件(只精确到秒已用程序实现过,精确到0.1s理论上也是可以,但是没有用程序去实现)
2.1 切割文件的前N秒为一个新文件
第一步,计算N秒的偏移量,SetOff=N*每秒所需的字节数
第二步,算出新文件的大小,修改文件头的两个size值。
第三步,新的文件头以二进制形式写入到新文件,紧接着根据偏移量把原文件中的第45字节到(setOff-1)字节写入到新文件。
2.2 窃取中间某个时间段为一个新文件
这里可以根据2.1的步骤进行,同样的要修改文件头。