WAVE 文件格式分析

         WAVE 文件作为多媒体中使用的声音波形文件格式之一,它是以RIFF(Resource Interchange File Format)格式为标准的。每个WAVE文件的头四个字节便是“RIFF”。WAVE 文件由文件头和数据体两大部分组成。其中文件头又分为 RIFF/WAV 文件标识段和声音数据格式说明段两部分。WAVE文件各部分内容及格式见后文。

         常见的声音文件主要有两种,分别对应于单声道(11.025KHz 采样率、8Bit 的采样值)和双声道(44.1KHz 采样率、16Bit 的采样值)。采样率是指:声音信号在“模→数”转换过程中单位时间内采样的次数。采样值是指每一次采样周期 
内声音模拟信号的积分值。

         对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH);而对于双声道立体声声音文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代表左右两个声道。

        WAVE 文件数据块包含以脉冲编码调制(PCM)格式表示的样本。WAVE 文件是由样本组织而成的。在单声道 WAVE 文件中,声道0代表左声道,声道1代表右声道。在多声道WAVE文件中,样本是交替出现的。

        WAVE 文件除了前面一小段文件头对数据组织进行说明之外,Data 块就是声音的原始采样数据,WAVE 文件虽然可以压缩,但一般都使用不压缩的格式。44.1KHz 采样率、16Bit的分辨率、双声道,所以WAVE可以保存音质要求非常高的声音文件,CD 采用的也是这种格式,声音方面的专家或是音乐发烧友们应该非常熟悉。但这种文件的体积也非常大,以 44.1KHz 16bit 双声道的数据为例,一分钟的声音数据量为:4100*2byte*2channel*60s/1024/1024=10.09M 。所以不合适在网上传送。

下面我们具体地分析 WAVE 文件的格式:

WAVE 文件格式分析_第1张图片

以下是对各个字段的详细解说:

WAVE 文件格式分析_第2张图片

对于Data块,根据声道数和采样率的不同情况:

下面我们看一个具体的例子,声音文件如下:

WAVE 文件格式分析_第3张图片

对应的分析如下图所示:

WAVE 文件格式分析_第4张图片

1.PCM Wave格式详解

WAVE文件格式是微软RIFF(Resource Interchange File Format,资源交换文件标准)的一种,是针对于多媒体文件存储的一种文件格式和标准。 一般而言,RIFF文件由文件头和数据两部分组成,一个WAVE文件由一个“WAVE”数据块组成,这个“WAVE”块又由一个”fmt”子数据块和一个“data”子 数据块组成,也称这种格式为“Canonical form”(权威/牧师格式),如下图所示:

WAVE 文件格式分析_第5张图片

“WAVE”格式由两个子数据块构成:“fmt”块和“data”块,其中“fmt”块的详细解释如下: Subchunk1ID: 占4个字节,内容为“fmt ”的ASCII码(0x666d7420),以大端存储。
Subchunk1Size: 占4个字节,存储该子块的字节数(不含前面的Subchunk1ID和Subchunk1Size这8个字节),以小端方式存储。
AudioFormat:占2个字节,以小端方式存储,存储音频文件的编码格式,例如若为PCM则其存储值为1,若为其他非PCM格式的则有一定的压缩。
NumChannels: 占2个字节,以小端方式存储,通道数,单通道(Mono)值为1,双通道(Stereo)值为2,等等。
SampleRate: 占4个字节,以小端方式存储,采样率,如8k,44.1k等。
ByteRate: 占4个字节,以小端方式存储,每秒存储的bit数,其值=SampleRate * NumChannels * BitsPerSample/8
BlockAlign: 占2个字节,以小端方式存储,块对齐大小,其值=NumChannels * BitsPerSample/8
BitsPerSample: 占2个字节,以小端方式存储,每个采样点的bit数,一般为8,16,32等。
接下来是两个可选的扩展参数:
ExtraParamSize: 占2个字节,表示扩展段的大小。
ExtraParams: 扩展段其他自定义的一些参数的具体内容,大小由前一个字段给定。

其中,对于每个采样点的bit数,不同的bit数读取数据的方式不同:

WAVE 文件格式分析_第6张图片

“WAVE”格式文件的第二个子数据块是“data”,其个字段的详细解释如下:
Subchunk2ID: 占4个字节,内容为“data”的ASCII码(0x64617461),以大端存储。
Subchunk2Size: 占4个字节,内容为接下来的正式的数据部分的字节数,其值=NumSamples * NumChannels * BitsPerSample/8
Data: 真正的语音数据部分。

一个Wave文件头的实例

设一个wave文件的前72个字节的十六进制内容如下(可以使用Ultra Edit等工具查看wave文件头):

则其个字段的解析如下图:

WAVE 文件格式分析_第7张图片

使用C语言文件操作库函数实现的Wave文件读取的实例代码:

#include 
#include 
#include 

// define Wave format structure
typedef struct tWAVEFORMATEX
{
    short wFormatTag;         /* format type */
    short nChannels;          /* number of channels (i.e. mono, stereo...) */
    unsigned int nSamplesPerSec;     /* sample rate */
    unsigned int nAvgBytesPerSec;    /* for buffer estimation */
    short nBlockAlign;        /* block size of data */
    short wBitsPerSample;     /* number of bits per sample of mono data */
    short cbSize;             /* the count in bytes of the size of */
                                    /* extra information (after cbSize) */
} WAVEFORMATEX, *PWAVEFORMATEX;

char* wavread(char *fname, WAVEFORMATEX *wf);

int main(){
  char fname[] = "test.wav";
  char *speech;
  WAVEFORMATEX wf;
  
  speech = wavread(fname, &wf);
  // afterward processing...
  
  return 0;
}

// read wave file
char* wavread(char *fname, WAVEFORMATEX *wf){
  FILE* fp;
  char str[32];
  char *speech;
  unsigned int subchunk1size; // head size
  unsigned int subchunk2size; // speech data size

  // check format type
  fp = fopen(fname,"r");
  if(!fp){
      fprintf(stderr,"Can not open the wave file: %s.\n",fname);
      return NULL;
  }
  fseek(fp, 8, SEEK_SET);
  fread(str, sizeof(char), 7, fp);
  str[7] = '\0';
  if(strcmp(str,"WAVEfmt")){
      fprintf(stderr,"The file is not in WAVE format!\n");
      return NULL;
  }
  
  // read format header
  fseek(fp, 16, SEEK_SET);
  fread((unsigned int*)(&subchunk1size),4,1,fp);
  fseek(fp, 20, SEEK_SET);
  fread(wf, subchunk1size, 1, fp);
  
  // read wave data
  fseek(fp, 20+subchunk1size, SEEK_SET);
  fread(str, 1, 4, fp);
  str[4] = '\0';
  if(strcmp(str,"data")){
      fprintf(stderr,"Locating data start point failed!\n");
      return NULL;
  }
  fseek(fp, 20+subchunk1size+4, SEEK_SET);
  fread((unsigned int*)(&subchunk2size), 4, 1, fp);
  speech = (char*)malloc(sizeof(char)*subchunk2size);
  if(!speech){
      fprintf(stderr, "Memory alloc failed!\n");
      return NULL;
  }
  fseek(fp, 20+subchunk1size+8, SEEK_SET);
  fread(speech, 1, subchunk2size, fp);

  fclose(fp);
  return speech;
}

 

你可能感兴趣的:(C++,音视频)