zoukankan      html  css  js  c++  java
  • MMIO----Wav格式文件解析

    DirectSound只支持Wav格式的音频文件,在创建次缓冲区之前需要先确定播放的Wav音频数据的格式。如果是从本地Wav文件播放,则需要先读出它的数据格式。

    1. Wav音频格式布局

    Wav是WAVE音频格式文件的后缀名,WAVE音频格式全称是Waveform Audio File Format,又叫波形文件。这是一种微软和IBM合理推出的音频比特流文件格式,是RIFF(Resource Interchange File Format)文件格式的一种特殊应用。

    RIFF使用一种叫做Chunk的数据块来存储各种信息,主要分为两部分:RIFF ChunkOther Chunk

    riff-format

    RIFF Chunk:

    • Chunk id: chunk标识符,值为FOURCC('R', 'I', 'F', 'F')组成的一个32位的无符号整数。
    • file size: 4字节大小,file typedata部分的数据大小总和。
    • file type: 用FOURCC标识的文件类型,对于Wav文件来说值为FOURCC('W', 'A', 'V', 'E')。
    • data: 该部分包含剩余的chunk块。

    Other Chunk按顺序包含3个部分:

    • Chunk id:这是每个chunk的标识符,是由4个字符组成一个FOURCC。
    • Chunk size:4字节值,表明该chunk块数据部分的大小。
    • Chunk data:数据部分,该数据可能为空,并且实际的数据会被填充到2字节边界

    对于Wav音频文件来说,常见并被称为标准格式的Wav格式的只包含“RIFF”、“fmt”和“data” chunk 块(今天我们不讨论格式标准,因此不深入探讨这方面),只有“data”子块中的data部分包含实际的音频数据,可以被用来播放。实际上整个RIFF文件只由一个“RIFF”块组成,而该“RIFF”又包含其他子块(例如“fmt”、“data”块)。由于Wav文件可能包含其他子块,因此在解析的时候不能假定这些字块的顺序。

    2.Wav音频数据格式

    Wav音频的数据格式信息包含在“fmt”块中,“fmt”字块的data部分就包含数据的格式信息。Wav文件多包含的是PCM( Pulse -code modulation)原始音频数据,即未经压缩的数据,但其实它可以包含多种数据格式,包括原始的和经过压缩的:

    • PCM

    • ADPCM

    • GSM

    • MP3

    • CELP

    • SBC

    • Truespeech

    我们这里只处理PCM原始数据,因为DirectSound只支持它。我们看下图:

    fmt-pic wave-struct

    在Windows平台上开发时,系统已经为我们定义好了数据格式用来存储Wav格式信息WAVEFORMATEX,可以看到这个结构体的字段除cbSize以外都是按顺序和“fmt”块中data部分的格式信息字段一一对应的,这极大地方便了我们对格式信息的处理。

    3. 读取Wav音频格式信息

    HMMIO mmioOpen(LPTSTR szFilename, LPMMIOINFO lpmmioinfo, DWORD dwOpenFlags);
    MMRESULT mmioAscend(HMMIO hmmio, LPMMCKINFO lpck, UINT wFlags);
    MMRESULT mmioDescend(HMMIO hmmio, LPMMCKINFO lpck, LPMMCKINFO lpckParent, UINT wFlags);
    LONG mmioRead(HMMIO hmmio, HPSTR pch, LONG cch);
    ...

    微软提供了mmio系列的函数来帮助我们操作RIFF文件,首先我们调用mmioOpen打开一个Wav文件并防止被其他程序写入:

    HMMIO mmioHandle = mmioOpenW(tempFilePath, NULL,
                                MMIO_READ |
                                MMIO_DENYWRITE |
                                MMIO_ALLOCBUF);
    if (mmioHandle == NULL) {
        throw std::exception("mmioOpon error!");
    }
    

    然后我们调用mmioDescend函数下降到RIFF块,通过在MMCKINFO结构体中设置file type为FOURCC('W', 'A', 'V', 'E'),我们表明我们想下降到的RIFF块里的文件类型是“WAVE”:

    MMCKINFO riffChunkInfo;
    riffChunkInfo.fccType = mmioFOURCC('W', 'A', 'V', 'E');
    DWORD as;
    if ((as = mmioDescend(mmioHandle, &riffChunkInfo, NULL, MMIO_FINDRIFF))
            !=
        MMSYSERR_NOERROR) {
    
        throw std::exception("mmioDescend error!");
    }
    

    因为“fmt”块是RIFF的子块,因此我们紧接着下降到“fmt”子块:

    MMCKINFO fmtSubChunkInfo;
    fmtSubChunkInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
    if (mmioDescend(mmioHandle, &fmtSubChunkInfo, &riffChunkInfo, MMIO_FINDCHUNK)
            !=
        MMSYSERR_NOERROR) {
    
        throw std::exception("mmioDescend error!");
    }
    

    然后我们调用mmioRead函数来读取格式信息到一个WAVEFORMATEX结构体中。调用mmioRead时,我们需要自己提供存放读取信息的地址,像这里我们提供的是一个栈上的变量地址:

    //  `fmt` chunk contains wave audio format described by WAVEFORMAT or WAVEFORMATEX
    if (mmioRead(mmioHandle, (HPSTR)&m_waveFormat, fmtSubChunkInfo.cksize)
            <= 0) {
    
        throw std::exception("mmioRead read fmt chunk error!");
    }
    

    4. 运行结果

    result

    完整代码见链接

  • 相关阅读:
    apache安全—用户访问控制
    hdu 3232 Crossing Rivers 过河(数学期望)
    HDU 5418 Victor and World (可重复走的TSP问题,状压dp)
    UVA 11020 Efficient Solutions (BST,Splay树)
    UVA 11922 Permutation Transformer (Splay树)
    HYSBZ 1208 宠物收养所 (Splay树)
    HYSBZ 1503 郁闷的出纳员 (Splay树)
    HDU 5416 CRB and Tree (技巧)
    HDU 5414 CRB and String (字符串,模拟)
    HDU 5410 CRB and His Birthday (01背包,完全背包,混合)
  • 原文地址:https://www.cnblogs.com/lgxZJ/p/8412803.html
Copyright © 2011-2022 走看看