zoukankan      html  css  js  c++  java
  • 转wave 文件解析

    转 1

    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 文件的格式

    endian

    field name

    Size

     
    big ChunkID 4 文件头标识,一般就是" RIFF" 四个字母
    little ChunkSize 4 整个数据文件的大小,不包括上面ID和Size本身
    big Format 4 一般就是" WAVE" 四个字母
    big SubChunk1ID 4 格式说明块,本字段一般就是"fmt "
    little SubChunk1Size 4 本数据块的大小,不包括ID和Size字段本身
    little AudioFormat 2 音频的格式说明
    little NumChannels 2 声道数
    little SampleRate 4 采样率
    little ByteRate 4 比特率,每秒所需要的字节数
    little BlockAlign 2 数据块对齐单元
    little BitsPerSample 2 采样时模数转换的分辨率
    big SubChunk2ID 4 真正的声音数据块,本字段一般是"data"
    little SubChunk2Size 4 本数据块的大小,不包括ID和Size字段本身
    little Data N 音频的采样数据

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

    ChunkID 4bytes ASCII 码表示的“RIFF”。(0x52494646)
    ChunkSize 4bytes 36+SubChunk2Size,或是 
    4 + ( 8 + SubChunk1Size ) + ( 8 + SubChunk2Size ), 
    这是整个数据块的大小(不包括ChunkID和ChunkSize的大小)
    Format 4bytes ASCII 码表示的“WAVE”。(0x57415645)
         
    SubChunk1ID   新的数据块(格式信息说明块) 
    ASCII 码表示的“fmt ”——最后是一个空格。(0x666d7420)
    SubChunk1Size 4bytes 本块数据的大小(对于PCM,值为16)。
    AudioFormat 2bytes PCM = 1 (比如,线性采样),如果是其它值的话,则可能是一些压缩形式
    NumChannels 2bytes 1 => 单声道  |  2 => 双声道
    SampleRate 4bytes 采样率,如 8000,44100 等值
    ByteRate 4bytes 等于: SampleRate * numChannels * BitsPerSample / 8
    BlockAlign 2bytes 等于:NumChannels * BitsPerSample / 8
    BitsPerSample 2bytes 采样分辨率,也就是每个样本用几位来表示,一般是 8bits 或是 16bits
         
    SubChunk2ID 4bytes 新数据块,真正的声音数据 
    ASCII 码表示的“data ”——最后是一个空格。(0x64617461)
    SubChunk2Size 4bytes 数据大小,即,其后跟着的采样数据的大小。
    Data N bytes 真正的声音数据

    对于Data块,根据声道数和采样率的不同情况,布局如下(每列代表8bits):

    1. 8 Bit 单声道:

    采样1 采样2
    数据1 数据2

    2. 8 Bit 双声道

    采样1   采样2  
    声道1数据1 声道2数据1 声道1数据2 声道2数据2

    1. 16 Bit 单声道:

    采样1   采样2  
    数据1低字节 数据1高字节 数据1低字节 数据1高字节

    2. 16 Bit 双声道

    采样1      
    声道1数据1低字节 声道1数据1高字节 声道2数据1低字节 声道2数据1高字节
    采样2      
    声道1数据2低字节 声道1数据2高字节 声道2数据2低字节 声道2数据2高字节

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

    52 49 46 46 24 08 00 00 57 41 56 45 
    66 6d 74 20 10 00 00 00 01 00 02 00 
    22 56 00 00 88 58 01 00 04 00 10 00 
    64 61 74 61 00 08 00 00 00 00 00 00 
    24 17 1e f3 3c 13 3c 14 16 f9 18 f9 
    34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d 

    对应的分析如下图所示:

    untitled

    本文参考了:

    转2

    1.PCM Wave格式详解

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

    每个字段的涵义如下: ChunkID: 占4个字节,内容为“RIFF”的ASCII码(0x52494646),以大端(big endian)存储。
    ChunkSize: 4字节,存储整个文件的字节数(不包含ChunkID和ChunkSize这8个字节),以小端(little endian)方式存储。
    Format: 4字节,内容为“WAVE”的ASCII码(0x57415645),以大端存储。

    其中bigendian 主要有一个特征,在内存中对操作数的存储方式和从高字节到低字节。例如:0x1234,这样一个数,存储为:
    0x4000: 0x12
    0x4001: 0x34
    而小尾端littleendian是:
    0x4000: 0x34
    0x4001: 0x12
    用程序在区别的话,可以考虑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char *argv[])
    {
           union w
          {
           short int a;
           char b;
          }c;
          c.a=1;
          if( c.b==1 )  printf("little endian
    ");
          else printf("big endian
    ");
          system("PAUSE"); 
          return 0;
    }

    “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数读取数据的方式不同:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    // data 为读取到的采样点的值,speech为原始数据流,
    //对应于下面的"WAVE"格式文件的第二个子数据块“data”块的“Data”部分。
    for(i=0;i<NumSample;i++){
      if(BitsPerSample==8)
          data[i] = (int)*((char*)speech+i);
      else if(BitsPerSample==16)
          data[i] = (int)*((short*)speech+i);
      else if(BitsPerSample==32)
          data[i] = (int)*((int*)speech+i);
    }

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

    一个Wave文件头的实例

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

    1
    2
    3
    
    52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00 
    22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00 
    24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d
    则其个字段的解析如下图:

    C语言实现wave文件的读取

    这里给出一个用基本的C语言文件操作库函数实现的Wave文件读取的实例代码,可以跨Windows和Linux平台。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    // 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.
    ",fname);
          return NULL;
      }
      fseek(fp, 8, SEEK_SET);
      fread(str, sizeof(char), 7, fp);
      str[7] = '';
      if(strcmp(str,"WAVEfmt")){
          fprintf(stderr,"The file is not in WAVE format!
    ");
          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] = '';
      if(strcmp(str,"data")){
          fprintf(stderr,"Locating data start point failed!
    ");
          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!
    ");
          return NULL;
      }
      fseek(fp, 20+subchunk1size+8, SEEK_SET);
      fread(speech, 1, subchunk2size, fp);
    
      fclose(fp);
      return speech;
    }

    参考

    [1]WAVE PCM soundfile format: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ 
    [2]Resource Interchange File Format: http://en.wikipedia.org/wiki/Resource_Interchange_File_Format 
    [3]基于Visual C++6.0的声音文件操作: http://www.yesky.com/20030414/1663116_1.shtml

    Original Link: http://ibillxia.github.io/blog/2013/07/20/details-of-wave-format-and-reading-wave-files-in-C-language/
    Attribution - NON-Commercial - ShareAlike - Copyright © Bill Xia
  • 相关阅读:
    怪题
    reflow(回流)和repaint(重绘)
    typeof
    Apicloud——图片不适配屏幕解决方案
    (学习笔记二)Apicloud使用中的坑
    es6的10个新特性
    QQ刷屏脚本
    vue的组件绑定
    console控制台打印看板娘
    Canvas
  • 原文地址:https://www.cnblogs.com/jlmgary/p/6846061.html
Copyright © 2011-2022 走看看