轉自:http://www.cnblogs.com/haibindev/archive/2011/12/07/2277366.html 混音,顧名思義,就是把多個音源混合的過程,是一個很常見的應用。這兩天我也作了一個雙路混音器,固然,我沒有作多麼專業的音頻信號處理,只是一個簡單的混音,調節各路音量,並實現了一些音效處理。主要功能有:採集硬件設備,讀取wav文件,播放,混音,音量調節,音頻節奏、音調的調節,wav文件輸出。這麼多功能,咱們不須要一個一個所有本身實現,有時候,藉助開源項目,尤爲是比較成熟的開源項目,不但能夠大大節省開發時間,還能使程序更加穩定。即使不能直接在本身的項目中使用,也能有借鑑意義。這個項目中我就使用了PortAudio,PortAudio是一個開源的、跨平臺的音頻IO庫,它主要提供了音頻採集和播放的接口,並且API很是簡單。你們能夠嘗試一下。html
下圖就是該項目產品截圖:算法
全部的功能呢,在界面上是一目瞭然了,其中有三個子窗口,是音頻數據經FFT(快速傅立葉)變換後顯示的頻譜圖,左右兩個分別是兩路音頻的,中間則是混音後的。這裏我和你們分享一下這個混音器的設計思路以及PortAudio的使用,但願有需求的朋友可以有所借鑑。架構
首先是程序的邏輯架構圖異步
PortAudio在項目中主要負責採集硬件設備和播放內存中的音頻Sample,其實在Windows上實現這種功能能夠有多種方法,以前我也基於DirectShow作過,此次使用PortAudio主要也是想熟悉一下,未來若是要作其餘平臺的應用時,能夠直接拿來使用了。函數
AudioInput的主要功能是封裝一下音頻的輸入,包括硬件採集和文件讀取 AudioMixer管理AudioInput,並進行音量和各類音效處理 鑑於文件操做比較費時,FileWritter的操做實際上是放在單獨的線程中進行的。線程
PortAudio的封裝和使用設計
PortAudio的API很是的簡單,基本上完成採集或播放的功能,只須要調用三個接口就能夠了:Pa_OpenStream, Pa_StartStream, Pa_CloseStream。它們的原型以下htm
首先,咱們須要調用Pa_Initialize來進行PortAudio的初始化,而後設置好Pa_OpenStream的各項參數,而後就可使用它了。調用Pa_StartStream以後,若是是採集,就能夠從PortAudio讀取數據了,若是是播放,則只須要不斷的把要播放的音頻數據交給PortAudio就好了。而PortAudio和你的程序之間的數據的交互,實際上是有兩種方式的,一種就是上面原型中提到的設置回調函數的形式,另外一種就是調用Pa_ReadStream和Pa_WriteStream這兩個函數,我推薦仍是經過回調的形式,簡單嘛,這個最重要了:)。回調函數的原型也比較簡單,採集和播放都是同樣的。blog
咱們只須要在回調函數中操做inputBuffer或者outputBuffer便可,下面是我啓動前進行設置的代碼:接口
PortAudio的採集
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音頻採樣數據,一樣有不錯的效果。
+++++++++++++++++++++++++++++++++++++++++++++++++++++