MMIO----Wav格式文件解析

Posted lgxZJ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 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只支持它。我们看下图:

技术分享图片 技术分享图片

在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. 运行结果

技术分享图片

完整代码见链接





以上是关于MMIO----Wav格式文件解析的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向使用 Python 解析 ELF 文件 ( Capstone 反汇编 ELF 文件中的机器码数据 | 创建反汇编解析器实例对象 | 设置汇编解析器显示细节 )(代码片段

如何解析 MPD 清单视频文件并获取图像适配集的片段?

片段(Java) | 机试题+算法思路+考点+代码解析 2023

微信小程序json数据如何处理?

我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段

我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段