原文出處:http://vip.6to23.com/NowCan1/tech/snd_wave.htm 感謝作者分享他的成果!
本文講述如何實時錄音,以及將錄音波形頻譜實時顯示的方法。Windows提供了一個多媒體控制接口(MCI),用它可以錄音,很方便。但是這種方法不能實時給出錄音的原始數據,因此要顯示波形和頻譜都是不可能實現的。要達到實時的效果,就要使用Windows提供的另一套函數,即低級音頻函數。其中和錄音有關的是以waveIn開頭的一組函數。
低級音頻函數的使用比較繁瑣,大致要有以下幾個步驟。
- 用waveInOpen打開設備,並設置回調。因爲要保證實時性,所以不能用查詢的方式,而必須設置回調。
- 爲設備分配足夠的內存做緩衝區,動態分配或靜態數組都可以。爲了保證實時性,程序用了雙緩衝技術,在處理一個緩衝區數據的同時另一個緩衝區用於錄音。爲了便於說明寫成Buffer1、Buffer2。
- 將Buffer1關聯到設備上去,waveInPrepareBuffer、waveInAddBuffer。
- 開始錄音,waveInStart
- 當驅動程序填滿這個緩衝區(Buffer1)時就會產生回調(消息爲WIM_DATA),這時立刻將Buffer2關聯到設備上繼續錄音,然後處理Buffer1,當驅動程序填滿Buffer2時又會產生回調,這是再將Buffer1關聯到設備上,而去處理Buffer2,如此反覆就使得錄音能夠實時的進行下去。
- 停止錄音,waveInStop
- 關閉設備,waveInClose
上面就是實時錄音的一個基本流程,產生回調時,其lParam就是一個指向WAVEHDR結構的指針,通過這個指針就可以得到原始數據(注意了:我這個程序只是單聲道8位的PCM格式,因此正好一個字節就是一個採樣點的值,對於其他格式的我就不知道了)。有了原始數據就可以很容易的畫出波形了,而頻譜也就是多一次FFT變換而已,沒什麼太難的。
這裏是我用LCC寫的例子,非常簡單,而且相信大家都可以很容易的移植到BCB上來。另外,我偷了一下懶,FFT用了我以前封裝到DLL中的函數(現在已經忘了怎麼寫了^_^),可以在此下載 (不要覺得奇怪,就是這個時鐘,將其中的DEMO.DLL拿來用就是了)。當然你也可以自己寫一個FFT放到程序裏去。
-*-*-PATCH-*-*-2002-06-11
上次那個程序還不好,原因是雙緩衝還不能滿足實時的要求,因此這次用了8緩衝循環技術。新的程序也是用LCC寫的。
開始時依次將0-6號緩衝區關聯到設備上,而7號緩衝區不關聯,然後開始錄音,這是驅動程序開始向0號緩衝區輸出數據,當0號緩衝區數據滿時產生回調,這時驅動程序會自動向1號緩衝區輸出數據,但這時應將7號緩衝區關聯到設備上去,然後處理0號緩衝區的數據。然後當1號緩衝區數據滿時又會產生回調,這時驅動程序會自動向2號緩衝區輸出數據,而將0號緩衝區關聯到設備上去。這樣如此反覆,始終保持1個緩衝區用於驅動輸出數據,1個緩衝區用於處理數據,6個緩衝區處於等待狀態。
而如果只有兩個緩衝區,就只能一個用於驅動輸出,一個用於處理數據。這樣在產生回調的時候就會出現沒有處於等待狀態的緩衝區的情況,這樣驅動程序就不能自動向緩衝區輸出數據。而要等waveInPrepareBuffer、waveInAddBuffer調用之後,這樣就會出現一個小小的間隙,而導致錄音不連續。
-*-*-PATCH-*-*-2002-06-11
只是我的個人觀點,有什麼不對的地方,還望指教。
-*-*-PATCH-*-*-2003-04-07
程序中有一個錯誤,在WIM_DATA的處理過程裏。
case WIM_DATA:waveInUnprepareHeader(hwi,pwhi,sizeof(WAVEHDR));//釋放錯誤。
nextWavHdr=(currWavHdr-1+MAX_INQUEU)%MAX_INQUEU;
currWavHdr=(currWavHdr+1)%MAX_INQUEU;
if(b_playing)
{
pwhi=&whis[nextWavHdr];
pwhi->dwFlags=0;
pwhi->dwLoops=0;
waveInPrepareHeader(hwi,pwhi,sizeof(WAVEHDR));
waveInAddBuffer(hwi,pwhi,sizeof(WAVEHDR));
}
當0號緩衝區數據滿時,應該釋放0號句柄。這第一次是對的,但是第二次當1號緩衝滿的時候,程序卻釋放了7號句柄。這裏是錯的,解決方法就是計算正確的句柄號。
或者,waveInUnprepareHeader(hwi,LPWAVEHDR(lParam),sizeof(WAVEHDR));
這樣就可以了,感謝flinming發現這個問題。
另外,《網絡語音傳輸》中的程序也有這個問題,請大家自己改吧。
-*-*-PATCH-*-*-2003-04-07