實時錄音並顯示波形、頻譜

原文出處:http://vip.6to23.com/NowCan1/tech/snd_wave.htm    感謝作者分享他的成果!

本文講述如何實時錄音,以及將錄音波形頻譜實時顯示的方法。Windows提供了一個多媒體控制接口(MCI),用它可以錄音,很方便。但是這種方法不能實時給出錄音的原始數據,因此要顯示波形和頻譜都是不可能實現的。要達到實時的效果,就要使用Windows提供的另一套函數,即低級音頻函數。其中和錄音有關的是以waveIn開頭的一組函數。

    低級音頻函數的使用比較繁瑣,大致要有以下幾個步驟。

  1. 用waveInOpen打開設備,並設置回調。因爲要保證實時性,所以不能用查詢的方式,而必須設置回調。
  2. 爲設備分配足夠的內存做緩衝區,動態分配或靜態數組都可以。爲了保證實時性,程序用了雙緩衝技術,在處理一個緩衝區數據的同時另一個緩衝區用於錄音。爲了便於說明寫成Buffer1、Buffer2。
  3. 將Buffer1關聯到設備上去,waveInPrepareBuffer、waveInAddBuffer。
  4. 開始錄音,waveInStart
  5. 當驅動程序填滿這個緩衝區(Buffer1)時就會產生回調(消息爲WIM_DATA),這時立刻將Buffer2關聯到設備上繼續錄音,然後處理Buffer1,當驅動程序填滿Buffer2時又會產生回調,這是再將Buffer1關聯到設備上,而去處理Buffer2,如此反覆就使得錄音能夠實時的進行下去。
  6. 停止錄音,waveInStop
  7. 關閉設備,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

 
發佈了43 篇原創文章 · 獲贊 1 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章