借用PortAudio採集和播放音頻

混音,顧名思義,就是把多個音源混合的過程,是一個很常見的應用。這兩天我也做了一個雙路混音器,當然,我沒有做多麼專業的音頻信號處理,只是一個簡單的混音,調節各路音量,並實現了一些音效處理。主要功能有:採集硬件設備,讀取wav文件,播放,混音,音量調節,音頻節奏、音調的調節,wav文件輸出。這麼多功能,我們不需要一個一個全部自己實現,有時候,藉助開源項目,尤其是比較成熟的開源項目,不但可以大大節省開發時間,還能使程序更加穩定。即便不能直接在自己的項目中使用,也能有借鑑意義。這個項目中我就使用了PortAudio,PortAudio是一個開源的、跨平臺的音頻IO庫,它主要提供了音頻採集和播放的接口,而且API非常簡單。大家可以嘗試一下。

  下圖就是該項目產品截圖:

  所有的功能呢,在界面上是一目瞭然了,其中有三個子窗口,是音頻數據經FFT(快速傅立葉)變換後顯示的頻譜圖,左右兩個分別是兩路音頻的,中間則是混音後的。這裏我和大家分享一下這個混音器的設計思路以及PortAudio的使用,希望有需求的朋友能夠有所借鑑。

 

首先是程序的邏輯架構圖

  

  PortAudio在項目中主要負責採集硬件設備和播放內存中的音頻Sample,其實在Windows上實現這種功能可以有多種方法,之前我也基於DirectShow做過,這次使用PortAudio主要也是想熟悉一下,將來如果要做其他平臺的應用時,可以直接拿來使用了。

  • AudioInput的主要功能是封裝一下音頻的輸入,包括硬件採集和文件讀取
  • AudioMixer管理AudioInput,並進行音量和各種音效處理
  • 鑑於文件操作比較費時,FileWritter的操作其實是放在單獨的線程中進行的。

 

PortAudio的封裝和使用

  PortAudio的API非常的簡單,基本上完成採集或播放的功能,只需要調用三個接口就可以了:Pa_OpenStreamPa_StartStreamPa_CloseStream。它們的原型如下

    

  首先,我們需要調用Pa_Initialize來進行PortAudio的初始化,然後設置好Pa_OpenStream的各項參數,然後就可以使用它了。調用Pa_StartStream之後,如果是採集,就可以從PortAudio讀取數據了,如果是播放,則只需要不斷的把要播放的音頻數據交給PortAudio就行了。而PortAudio和你的程序之間的數據的交互,其實是有兩種方式的,一種就是上面原型中提到的設置回調函數的形式,另一種就是調用Pa_ReadStreamPa_WriteStream這兩個函數,我推薦還是通過回調的形式,簡單嘛,這個最重要了:)。回調函數的原型也比較簡單,採集和播放都是一樣的。

  我們只需要在回調函數中操作inputBuffer或者outputBuffer即可,下面是我啓動前進行設置的代碼:

1. PortAudio的採集

2. PortAudio的播放

  有一點需要注意的是,framesPerBuffer的值,也就是在Pa_OpenStream中設置的參數值,這個數值就是outputBuffer或inputBuffer中音頻幀的個數,我這裏設置成了512,當然你也可以設置成其他的數值,不過不宜過小,否則會造成大量的異步回調,cpu是處理不過來的。這個數值再乘以你之前這隻的音頻幀Sample格式(我這裏是paFloat32)和音軌個數,就可以計算出outputBuffer或inputBuffer的大小,然後就可以操作音頻數據了,例如在採集的回調函數中這樣使用。

memcpy(audio_data, inputBuffer, framesPerBuffer * sizeof(float)*2);

 

混音算法

  實話實說,這個混音算法是我從網上找到的,不過效果還是挺不錯的,公式就是

C = A + B - (A * B >> 0x10)

  A和B就是兩路不同的音頻數據,C就是混音後的音頻數據,當然,處理後,還需要對C進行防止數據溢出的處理,否則,可能會有爆音。

如果是16bit音頻數據,就是:

if (C > 32767) C = 32767;
else if (C < -32768) C = -32768;

如果是float音頻數據,就是:

if (C > 1) C = 1;
else if (C < -1) C = -1;

  這個算法針對的是16bit的音頻採樣數據,我實驗的結果是:對float音頻採樣數據,同樣有不錯的效果。

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