WAV文件解析

WAV

         WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频信息资源,被Windows平台及其应用程序所广泛支持,该格式也支持MSADPCM,CCITT A LAW等多种压缩运算法,支持多种音频数字,取样频率和声道,标准格式化的WAV文件和CD格式一样,也是44.1K的取样频率,16位量化数字。
        通常使用三个参数来表示声音,量化位数,取样频率和采样点振幅。量化位数分为8位,16位,24位三种,声道有单声道和立体声之分,单声道振幅数据为n1矩阵点,立体声为n2矩阵点,取样频率一般有11025Hz(11kHz) ,22050Hz(22kHz)和44100Hz(44kHz) 三种,不过尽管音质出色,但在压缩后的文件体积过大!相对其他音频格式而言是一个缺点,其文件大小的计算方式为:WAV格式文件所占容量(B) = (取样频率 X量化位数X 声道) X 时间 / 8 (字节= 8bit) 每一分钟WAV格式的音频文件的大小为10MB,其大小不随音量大小及清晰度的变化而变化。
        WAV是最接近无损的音乐格式,所以文件大小相对也比较大。

WAV文件格式结构解析

        WAVE文件是非常简单的一种RIFF文件,它的格式类型为"WAVE"。RIFF块包含两个子块,这两个子块的ID分别是"fmt"和"data",其中"fmt"子块由结构PCMWAVEFORMAT所组成,其子块的大小就是sizeofof(PCMWAVEFORMAT),数据组成就是PCMWAVEFORMAT结构中的数据。
        首先是一个RIFF块,有块标识RIFF,指明该文件是符合RIFF标准的文件;接着是一个FourCC,WAVE,该文件为WAV文件;fmt块包含了音频的一些属性:采样率、码率、声道等;fact 块是一个可选块,不是PCM数据格式的需要该块;最后data块,则包含了音频的PCM数据。实际上,可以将一个WAV文件看着由两部分组成:文件头和PCM数据。WAV文件头各字段如下图所示:

[图片上传失败...(image-2d0fb3-1531713233046)]

[图片上传失败...(image-eaf3dc-1531713233046)]

FIFF文件知识点

        简介RIFF——全称为资源互换文件格式ResourcesInterchange FileFormat),RIFF文件是windows环境下大部分多媒体文件遵循的一种文件结构,RIFF文件所包含的数据类型由该文件的扩展名来标识,能以RIFF文件存储的数据包括:音频视频交错格式数据(.AVI) 波形格式数据(.WAV) 位图格式数据(.RDI) MIDI格式数据(.RMI)调色板格式(.PAL)多媒体电影(.RMN)动画光标(.ANI)其它RIFF文件(.BND)。

FIFF之CHUNK块

chunk是组成RIFF文件的基本单元,它的基本结构如下:

struct chunk{
    u32 id; /* 块标志 */
    u32 size; /* 块大小 */
    u8 dat[size]; /* 块内容 */
};

id ——由4个ASCII字符组成,用以识别块中所包含的数据。如:'RIFF','LIST','fmt','data','WAV','AVI'等等,由于这种文件结构最初是由Microsoft和IBM为PC机所定义,RIFF文件是按照little-endian[2] 字节顺序写入的。
size ——(块大小) 是存储在data域中数据的长度,id与size域的大小则不包括在该值内。
dat ——(块内容) 中所包含的数据是以字(WORD)为单位排列的,如果该数据结构长度是奇数,则在最后添加一个空(NULL)字节。

chunk块中有且仅有两种类型块:'RIFF'和'LIST'类型可以包含其他块,而其它块仅能含有数据。
'RIFF'和'LIST'类型的chunk结构如下:

structchunk{
    u32 id; /* 块标志 */
    u32 size; /* 块大小 */
    /*此时的dat = type + restdat */
    u32 type ; /* 类型 */
    u8 restdat[size] /* dat中除type4个字节后剩余的数据*/
};

可以看出,'RIFF'和'LIST'也是chunk,只是它的dat由两部分组成type和restdat。

type由4个ASCII字符组成,代表RIFF文件的类型,如'WAV','AVI ';或者'LIST'块的类型,如avi文件中的列表'hdrl','movi'。
restdat在dat中除type4个字节后剩余的数据,包括块内容,包含若干chunk和'LIST'

FIFF之FOURCC

        一个FOURCC(fourcharacter code)是一个占4个字节的数据,一般表示4个ASCII字符。在RIFF文件格式中,FOURCC非常普遍,structchunk 中的id成员,'LIST','RIFF'的type成员,起始标识等信息都是用FOURCC表示的。FOURCC一般是四个字符,如'abcd'这样的形式,也可以三个字符包含一个空格,如'abc'这样的形式。
        RIFF文件的FileData部分由若干个'LIST'和chunk组成,而'LIST'的ListData又可以由若干个'LIST'和chunk组成,即'LIST'是可以嵌套的。
        'RIFF',FileType,'LIST',ListType,ChunkID都是FOURCC,即使用4字节的ASIIC字符标识类型。
        FileSize,ListSize,ChunkSize为little-endian32-bit正整数,表示Type(只有'RIFF','LIST'chunk有Type)+Data一起的大小,注意它是little-endian表示,如:0x00123456,存储地址由低到高,在little-endian系统中的存储表示为0x56341200(字节由低位到高位存储),而在big-endian为0x00123456(字节由高位到低位存储)。32bit整数0x00123456存储地址低--------->;高little-endian(字节由低位到高位存储)56341200big-endian(字节由高位到低位存储)00123456

WAV文件头

        在WAV的文件头中有三种chunk,分别为:RIFF,fmt,data,然后是音频的格式信息Wave_format。在RIFF chunk的后面是一个4字节非FOURCC:WAVE,表示该文件为WAV文件。另外,Wave_format的构造函数只需要三个参数:声道数、采样率和量化精度,关于音频的其他信息都可以使用这三个数值计算得到。
整个头长度44byte。顺序分别如下:

  1. 标志符(RIFF)
  2. 余下所有数据的长度
  3. 格式类型("WAVE")
  4. "fmt"
  5. PCMWAVEFORMAT的长度
  6. PCMWAVEFORMAT
  7. "data"
  8. 声音数据大小
  9. 声音数据

写入WAV文件头文件

        写WAV文件过程,首先是填充文件头信息,对于Wave_format只需要四个参数:声道数、采样率、量化精度和音频数据总长度,将文件头信息写入后,紧接这写入PCM数据就完成了WAV文件的写入。


/**
     * @param out            wav音频文件流
     * @param totalAudioLen  不包括header的音频数据总长度
     * @param longSampleRate 采样率,也就是录制时使用的频率
     * @param channels       audioRecord的频道数量
    * @param channels       audioRecord的量化精度
     * @throws IOException 写文件错误
     */
    private void writeWavFileHeader(FileOutputStream out, long totalAudioLen, long longSampleRate,
                                    int channels,int audioFormat) throws IOException {
        byte[] header = generateWavFileHeader(totalAudioLen, longSampleRate, channels,audioFormat);
        out.write(header, 0, header.length);
    }

      /**
     * @param totalAudioLen  不包括header的音频数据总长度
     * @param longSampleRate 采样率,也就是录制时使用的频率
     * @param channels       audioRecord的频道数量
     * @param channels       audioRecord的量化精度
     */
    private byte[] generateWavFileHeader(long totalAudioLen, long longSampleRate, int channels,int audioFormat) {
        long totalDataLen = totalAudioLen + 36;
        long byteRate = longSampleRate * 2 * channels;
        byte[] header = new byte[44];
        header[0] = 'R'; // RIFF
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        //文件长度  4字节文件长度,这个长度不包括"RIFF"标志(4字节)和文件长度本身所占字节(4字节),即该长度等于整个文件长度 - 8
        header[4] = (byte) (totalDataLen & 0xff);
        header[5] = (byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (byte) ((totalDataLen >> 24) & 0xff);
        //fcc type:4字节 "WAVE" 类型块标识, 大写
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        //FMT Chunk   4字节 表示"fmt" chunk的开始,此块中包括文件内部格式信息,小写, 最后一个字符是空格
        header[12] = 'f'; // 'fmt '
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';//过渡字节
        //数据大小  4字节,文件内部格式信息数据的大小,过滤字节(一般为00000010H)
        header[16] = 16;
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        //编码方式 10H为PCM编码格式   FormatTag:2字节,音频数据的编码方式,1:表示是PCM 编码
        header[20] = 1; // format = 1
        header[21] = 0;
        //通道数  Channels:2字节,声道数,单声道为1,双声道为2
        header[22] = (byte) channels;
        header[23] = 0;
        //采样率,每个通道的播放速度
        header[24] = (byte) (longSampleRate & 0xff);
        header[25] = (byte) ((longSampleRate >> 8) & 0xff);
        header[26] = (byte) ((longSampleRate >> 16) & 0xff);
        header[27] = (byte) ((longSampleRate >> 24) & 0xff);
        //音频数据传送速率,采样率*通道数*采样深度/8
        //4字节,音频数据传送速率, 单位是字节。其值为采样率×每次采样大小。播放软件利用此值可以估计缓冲区的大小
        //byteRate = sampleRate * (bitsPerSample / 8) * channels
        header[28] = (byte) (byteRate & 0xff);
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);
        // 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数
        header[32] = (byte) (2 * channels);
        header[33] = 0;
        //每个样本的数据位数
        //2字节,每个声道的采样精度; 譬如 16bit 在这里的值就是16。如果有多个声道,则每个声道的采样精度大小都一样的;
        header[34] = audioFormat;
        header[35] = 0;
        //Data chunk
        //ckid:4字节,数据标志符(data),表示 "data" chunk的开始。此块中包含音频数据,小写;
        header[36] = 'd';
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        //音频数据的长度,4字节,audioDataLen = totalDataLen - 36 = fileLenIncludeHeader - 44
        header[40] = (byte) (totalAudioLen & 0xff);
        header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
        header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
        header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
        return header;
    }

你可能感兴趣的:(WAV文件解析)