现在的位置: 首页 > 综合 > 正文

wav文件解析

2013年10月02日 ⁄ 综合 ⁄ 共 3984字 ⁄ 字号 评论关闭

前两天在公司做了个wav文件的播放器和波形显示的东东,回来之后忽然想起一直没有做过总结,趁现在还有空赶紧整理一下...

一.WAV格式文件头
1.综述:
wav文件是由四个大块构成的,按照从前到后的顺序依次为RIFF WAVE Chunk,Format Chunk,Fact Chunk(可选),Data Chunk.具体如下图所示:

------------------------------------------------
  | RIFF WAVE Chunk |
  | ID = 'RIFF' |
  | RiffType = 'WAVE' |
------------------------------------------------
  | Format Chunk |
  | ID = 'fmt ' |    (此处为四个字节,最后面一个是空格)
------------------------------------------------
       | Fact Chunk(optional) |
  | ID = 'fact' |
------------------------------------------------
  | Data Chunk |
  | ID = 'data' |
------------------------------------------------
除了Fact Chunk外,其他三个Chunk都是必须的,并且都有自己的ID,放在块的开始位置,后面紧跟的四个字节是本块所占的大小(出去ID和Size本身所占的四个字节),低字节表示地位,高字节表示高位...

2.详细介绍:
①RIFF WAVE Chunk
  ==================================
  | |所占字节数| 具体内容 |
  ==================================
  | ID | 4 Bytes | 'RIFF' |
  ----------------------------------
  | Size | 4 Bytes | |
  ----------------------------------
  | Type | 4 Bytes | 'WAVE' |
  ----------------------------------
以RIFF作为标识,后面紧跟size,该size的大小是整个WAVE文件大小减去8个字节,然后是Type字段,为"WAVE"表示是Wav文件,结构体定义如下:

struct RIFF_HEADER
     {
  char szRiffID[4]; // 'R','I','F','F'
  DWORD dwRiffSize;
  char szRiffFormat[4]; // 'W','A','V','E'
  };

②Format Chunk
  ====================================================================
  | | 字节数 | 具体内容 |
  ====================================================================
  | ID | 4 Bytes | 'fmt ' |
  --------------------------------------------------------------------
  | Size | 4 Bytes | 数值为16或18,18则最后又附加信息 |
  -------------------------------------------------------------------- ----
  | FormatTag | 2 Bytes | 编码方式,一般为0x0001 | |
  -------------------------------------------------------------------- |
  | Channels | 2 Bytes | 声道数目,1--单声道;2--双声道 | |
  -------------------------------------------------------------------- |
  | SamplesPerSec | 4 Bytes | 采样频率 | |
  -------------------------------------------------------------------- |
  | AvgBytesPerSec| 4 Bytes | 每秒所需字节数 | |===> WAVE_FORMAT
  -------------------------------------------------------------------- |
  | BlockAlign | 2 Bytes | 数据块对齐单位(每个采样需要的字节数) | |
  -------------------------------------------------------------------- |
  | BitsPerSample | 2 Bytes | 每个采样需要的bit数 | |
  -------------------------------------------------------------------- |
  | | 2 Bytes | 附加信息(可选,通过Size来判断有无) | |
  -------------------------------------------------------------------- ----
以"fmt "作为标识,一般长度为16个字节,如果是18个字节则有附加信息,写在最后两个字节上,定义结构体如下:

struct WAVE_FORMAT
  {
  WORD wFormatTag;
  WORD wChannels;
  DWORD dwSamplesPerSec;
  DWORD dwAvgBytesPerSec;
  WORD wBlockAlign;
  WORD wBitsPerSample;
  };
  struct FMT_BLOCK
  {
  char szFmtID[4]; // 'f','m','t',' '
  DWORD dwFmtSize;
  WAVE_FORMAT wavFormat;
  };

③Fact Chunk(可选字段)
  ==================================
  | |所占字节数| 具体内容 |
  ==================================
  | ID | 4 Bytes | 'fact' |
  ----------------------------------
  | Size | 4 Bytes | 数值为4 |
  ----------------------------------
  | data | 4 Bytes | |
  ----------------------------------
在标准WAVE文件中是可有可无的字段,一般当wav文件是某些软件合成的话则包含该字段.定义结构体如下:

struct FACT_BLOCK
  {
  char szFactID[4]; // 'f','a','c','t'
  DWORD dwFactSize;
  };
PS:数据区域不在头部的定义当中...

⑤Data Chunk
  ==================================
  | |所占字节数| 具体内容 |
  ==================================
  | ID | 4 Bytes | 'data' |
  ----------------------------------
  | Size | 4 Bytes | |
  ----------------------------------
  | data | | |
  ----------------------------------
这是真正存储语音数据的地方,以"data"为标识,Size是数据区域的大小,紧接着就是语音数据.定义结构体如下:

struct DATA_BLOCK
  {
  char szDataID[4]; // 'd','a','t','a'
  DWORD dwDataSize;
  };
根据Format Chunk里面声道数和位数的大小,读取WAVE数据时候还要看具体的内容,总之数据区不管由几个字节表示一个采样点,都是地位表示低字节,高位表示高字节...

二.波形表示

①数据密集时候表示波形算法:

数据很密集的情况下,在一个区域内有很多个点,这些点肯定可以连成一条线,在这种情况下,取这段区域的最大值和最小值然后用平滑曲线连接起来,就可以得到大体的波形了...

②数据疏散时候表示波形:

在屏幕上画出每个点的位置,然后用GDI的平滑曲线函数连接各个点,就可得到类似Adobe Audition的显示波形

③每个采样点和屏幕位置的对应:

我只试着处理了8位和16位单声道的数据,现说明如下:

8位采样的用short型的变量存储的话范围就是0-255,0表示屏幕的最上端,而255表示屏幕的最下端,所以就可以用rect.bottom - perData*rect.Height()/255来计算屏幕点的位置了...

16位采样的用short型变量存储就会出现溢出,但是恰好可以利用它的溢出来解决问题,由于16位的unsigned 类型表示范围是0-65535,所以用short存储的时候就会出现范围是-32767到32768之间其中负值表示屏幕中线以上的数据,而正值是屏幕中线以下的位置所以计算公式改为:

rect.top + rect.Height()/2 + perData*rect.Height()/65535

画出曲线来基本和Adobe Audition画出的波形类似...

抱歉!评论已关闭.