WAV文件解析

作者: 安仔夏天勤奋 | 来源:发表于2018-07-16 11:53 被阅读337次

    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文件解析

        本文链接:https://www.haomeiwen.com/subject/bjuvpftx.html