MMIO----Wav格式文件解析
DirectSound只支持Wav格式的音頻文件,在創建次緩沖區之前需要先確定播放的Wav音頻數據的格式。如果是從本地Wav文件播放,則需要先讀出它的數據格式。
1. Wav音頻格式布局
Wav是WAVE音頻格式文件的后綴名,WAVE音頻格式全稱是Waveform Audio File Format,又叫波形文件。這是一種微軟和IBM合理推出的音頻比特流文件格式,是RIFF(Resource Interchange File Format)文件格式的一種特殊應用。
RIFF使用一種叫做Chunk的數據塊來存儲各種信息,主要分為兩部分:RIFF Chunk、Other Chunk。
RIFF Chunk:
Chunk id: chunk標識符,值為FOURCC('R', 'I', 'F', 'F')組成的一個32位的無符號整數。
file size: 4字節大小,file type和data部分的數據大小總和。
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格式文件解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jquery 清空表单
- 下一篇: jQuery必知必熟基础知识