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!"); }
完整代碼見連接