本文將對幾種音頻混音的方法進行詳細的介紹和比較,讀完之后你應該可以對混音有個基本的認識,針對不同情形知道應該采用哪種具體的處理方法了。
如果對音頻的一些基礎知識還不是很了解的建議先去閱讀一下上一篇文章:寫給小白的音頻認識基礎 。
混音的原理
音頻混音的原理: 空氣中聲波的疊加等價於量化的語音信號的疊加。
這句話可能有點拗口,我們從程序員的角度去觀察就不難理解了。下圖是兩條音軌的數據,將每個通道的值做線性疊加后的值就是混音的結果了。比如音軌A和音軌B的疊加,A.1 表示 A 音軌的 1 通道的值 AB03 , B.1 表示 B 音軌的 1 通道的值 1122 , 結果是 bc25,然后按照低位在前的方式排列,在合成音軌中就是 25bc,這里的表示都是 16 進制的。
直接加起來就可以了?事情如果這么簡單就好了。音頻設備支持的采樣精度肯定都是有限的,一般為 8 位或者 16 位,大一些的為 32 位。在音軌數據疊加的過程中,肯定會導致溢出的問題。為了解決這個問題,人們找了不少的辦法。這里我主要介紹幾種我用過的,並給出相關代碼實現和最終的混音效果對比結果。
線性疊加平均
這種辦法的原理非常簡單粗暴,也不會引入噪音。原理就是把不同音軌的通道值疊加之后取平均值,這樣就不會有溢出的問題了。但是會帶來的后果就是某一路或幾路音量特別小那么整個混音結果的音量會被拉低。
以下的的單路音軌的音頻參數我們假定為采樣頻率一致,通道數一致,通道采樣精度統一為 16 位。
其中參數 bMulRoadAudios 的一維表示的是音軌數,二維表示該音軌的音頻數據。
Java 代碼實現:
1 |
|
自適應混音
參與混音的多路音頻信號自身的特點,以它們自身的比例作為權重,從而決定它們在合成后的輸出中所占的比重。具體的原理可以參考這篇論文:快速實時自適應混音方案研究。這種方法對於音軌路數比較多的情況應該會比上面的平均法要好,但是可能會引入噪音。
Java 代碼實現:
1 |
|
多通道混音
在實際開發中,我發現上面的兩種方法都不能達到滿意的效果。一方面是和音樂相關,對音頻質量要求比較高;另外一方面是通過手機錄音,效果肯定不會太好。不知道從哪里冒出來的靈感,為什么不試着把不同的音軌數據塞到不同的通道上,讓聲音從不同的喇叭上同時發出,這樣也可以達到混音的效果啊!而且不會有音頻數據損失的問題,能很完美地呈現原來的聲音。
於是我開始查了一下 Android 對多通道的支持情況,對應代碼可以在android.media.AudioFormat中查看,結果如下:
1 |
public static final int CHANNEL_OUT_FRONT_LEFT = 0x4; |
一共支持 10 個通道,對於我的情況來說是完全夠用了。我們的耳機一般只有左右聲道,那些更多通道的支持是 Android 系統內部通過軟件算法模擬實現的,至於具體如何實現的,我也沒有深入了解,在這里我們知道這回事就行了。我們平時所熟知的立體聲,5.1 環繞等就是上面那些通道的組合。
1 |
int CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT; |
知道原理之后,實現起來非常簡單,下面是具體的代碼:
1 |
|
結果比較
線性疊加平均法雖然看起來很簡單,但是在音軌數量比較少的時候取得的效果可能會比復雜的自適應混音法要出色。
自適應混音法比較合適音軌數量比較多的情況,但是可能會引入一些噪音。
多通道混音雖然看起來很完美,但是產生的文件大小是數倍於其他的處理方法。
沒有銀彈,還是要根據自己的應用場景來選擇,多試一下。
下面是我錄的兩路音軌:
-
音軌一:
-
音軌二:
-
線性疊加平均法:
-
自適應混音法:
-
多通道混音:
采樣頻率、采樣精度和通道數不同的情況如何處理?
不同采樣頻率需要算法進行重新采樣處理,讓所有音軌在同一采樣率下進行混音,這個比較復雜,等有機會再寫篇文章介紹。
采樣精度不同比較好處理,向上取精度較高的作為基准即可,高位補0;如果是需要取向下精度作為基准的,那么就要把最大通道值和基准最大值取個倍數,把數值都降到最大基准數以下,然后把低位移除。
通道數不同的情況也和精度不同的情況相似處理。
參考資料
來源:https://yedaxia.me/Android-A-Good-MixAudioMethod/


