DirectSound只支持Wav格式的音频文件,在建立次缓冲区以前须要先肯定播放的Wav音频数据的格式。若是是从本地Wav文件播放,则须要先读出它的数据格式。git
Wav是WAVE音频格式文件的后缀名,WAVE音频格式全称是Waveform Audio File Format,又叫波形文件。这是一种微软和IBM合理推出的音频比特流文件格式,是RIFF(Resource Interchange File Format)文件格式的一种特殊应用。github
RIFF使用一种叫作Chunk的数据块来存储各类信息,主要分为两部分:RIFF Chunk、Other Chunk。函数
RIFF Chunk:布局
file type
和data
部分的数据大小总和。Other Chunk按顺序包含3个部分:code
对于Wav音频文件来讲,常见并被称为标准格式的Wav格式的只包含“RIFF”、“fmt”和“data” chunk 块(今天咱们不讨论格式标准,所以不深刻探讨这方面),只有“data”子块中的data部分包含实际的音频数据,能够被用来播放。实际上整个RIFF文件只由一个“RIFF”块组成,而该“RIFF”又包含其余子块(例如“fmt”、“data”块)。因为Wav文件可能包含其余子块,所以在解析的时候不能假定这些字块的顺序。orm
Wav音频的数据格式信息包含在“fmt”块中,“fmt”字块的data部分就包含数据的格式信息。Wav文件多包含的是PCM( Pulse -code modulation)原始音频数据,即未经压缩的数据,但其实它能够包含多种数据格式,包括原始的和通过压缩的:blog
ADPCM开发
Truespeechget
咱们这里只处理PCM原始数据,由于DirectSound只支持它。咱们看下图:it
在Windows平台上开发时,系统已经为咱们定义好了数据格式用来存储Wav格式信息WAVEFORMATEX
,能够看到这个结构体的字段除cbSize之外都是按顺序和“fmt”块中data部分的格式信息字段一一对应的,这极大地方便了咱们对格式信息的处理。
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!"); }
完整代码见连接