混音演算法

一、最簡單的混音演算法 現在一般的軟體混音演算法是對輸入的音頻數據進行線性疊加, 即: (1) 或者疊加以後再取平均值: (2) 其中, m 為輸入音頻流的個數, n 為一幀的樣本數目, ·[i] 為一幀中的第i 個樣本, ·[j] 為第j 個音頻流, 所以, output[i] 為混音後的一幀中第i 個樣本, input[j][i] 為第j 個輸入音頻流當前幀的第i 個樣本(若經過編碼則輸入音頻流應在混音前通過解碼等還原成線性的PCM音頻流).通常的語音數據為16 bit(或者更少, 如8 bit), 即可以用C 語言中的short 類型表示, 其取值範圍是?32768 ≤ 採樣值≤ 32767, 可以預想到多個音頻流直接線性疊加以後就有可能溢出, 所以式(1) 最後的結果可能會有溢出, 產生噪音. 兩個連續平滑的波形疊加, 其結果也應該是平滑的. 所以產生噪音的地方就是由疊加溢出的地方引入的. 我們需要採用濾波來處理這些溢出部分, 改善由於溢出所造成的質量下降.為了解決溢出的問題, 一個常用的方法就是使用更多的位數來表示音頻數據的一個樣本, 在混音完畢以後,再使用一些演算法來降低其振幅, 使其分布在16 bit 所能表示的範圍之內, 這種方法叫做歸一化(Normalize). 通常使用32 bit 來表示線性疊加以後的數據, 也就是C 語言中的int 類型, 實現簡單, 運算也比較快, 更能滿足很多路音頻同時進行混音的需要. 式(2)對疊加的和值作平均, 解決了溢出的問題, 但是混音以後的聲音會總體衰減,特別是某一路音頻流的能量與其他路音頻流的能量反差很大的情況下, 音量非常小, 效果非常不理想. 進行濾波處理的另外一種常用方法就是「箝位」, 當發生上溢時, 箝位以後的值為所能表示的最大值, 當發生下溢時, 箝位後的值為所能表示的最小值, 如式(3) 所示: (3) 其中, sample 為疊加以後的樣本值, 為 32 bit, MAX 是最大輸出值, 這裡為32767, MIN 為最小輸出值, 為?32768. 現在很多現有的論文和系統都是採用箝位方法, 因為實現簡單, 快速, 效率很高. 但是可以看出, 這種箝位方法相當於在最大和最小的臨界值處切強行切斷波形, 非常生硬, 會造成較大的波形失真, 聽覺上引起如嘈雜, 出現突發刺耳的爆破音等.採用時域疊加作為基本的處理手段, 由於數字音頻信號存在量化上限和下限的問題,則因疊加運算肯定會造成結果溢出. 通常的處理手段是進行溢出檢測, 然後再進行飽和運算(如 4 式的方法), 即超過上限的結果被置為上限值, 超過下限的值置為下限值. 這種運算本身破壞了語音信號原有的時域特徵, 從而引入了雜訊. 這就是出現爆破聲和語音不連續現象的原因. 同時, 隨著參與混音的人數增加, 出現溢出的頻率也不斷上升, 所以這類方法存在一個上限, 而且這個上限值很低, 實驗證明, 採用這種時域直接疊加的方式進行混音, 一般不能突破 4 路輸入音頻流的限制, 否則將無法分辨語音流的內容了.二、改進的混音演算法 混音的時候, 還需要屏蔽某一路的本地音頻數據, 這樣就不會聽到本地的聲音, 只能聽到其他 n ? 1 路的聲音, 也就是說, 對於第 t 路音頻, 要發送給這個終端的混音後的數據如式(4): (4) 以下提出的演算法主要思想就是使用一個衰減因子, 對音頻數據進行衰減, 衰減因子會隨著數據而變化. 當溢出時, 衰減因子比較小, 使溢出的音頻數據衰減以後處於臨界值以內, 當沒有溢出時, 衰減因子會慢慢增加, 盡量保持數據的平滑變化. 而不是對於整幀使用同一個衰減因子來進行, 這是不同於式(2) 和式(3) 的地方, 既保證了整體的聲強不至於衰減太快, 又保證了較小的失真度. 演算法如下所述:1. f 初始化為1.2. 對於一幀中的樣本按順序處理: (a) output[i] = mixing[i] × f. (b) 如果output[i] > MAX, 求得最大的 f0 滿足 output[i] × f0 < MAX, 然後 f = f0, output[i] = MAX. (c) 如果output[i] < MIN, 求得最大的 f0 滿足 output[i]×f0 > MIN, 然後 f = f0, output[i] = MIN.3. 如果f < 1, 則f = f + STEPSIZE. 繼續處理下一幀, 轉2. 其中f 為衰減因子, f0 為新的衰減因子; mixing[] 為所有音頻流的某一幀線性疊加值, 實際實現的時候如式(4) 所示; output[] 為歸一化以後的輸出幀. MAX 為正的最大值; MIN 為負的最大值. STEPSIZE 為f 變化的步長, 通常取為 (1 ? f)/16 或者 (1 ? f)/32. 特別的, 就是在衰減以後的值溢出的情況下, 求新的衰減因子 f0 的方法不同, 新的 f0 需要滿足 output[i] × f0 < MAX 或者 output[i] × f0 > MIN, 而不是直接使用mixing[i]. 也就是說, 使用衰減以後的值output[i] 來計算f0, 而不是原始值mixing[i], 這樣將使得衰減因子的變化更為平滑. 用數學來表達, S 為溢出的一個樣本值, 在S × f 仍然溢出的情況下, 可以比較一下計算出來的新衰減因子的大小:假設是上溢, forig 是原始演算法計算出的新的衰減因子, 則f`orig < (MAX/S) , 我們改進的演算法得出的新衰減因子 f`new < (MAX/(S×f)) , 因為 S > (S × f), 所以(MAX/S) < (MAX/(S×f)) , 那麼 f`new 很大程度上要大於f`orig. 衰減因子大了(更接近1), 相鄰的數據變化不會特別大, 所以跳躍的現象不會特別明顯. 上述改進過的混音方案在實測中,與常規的混音演算法(直接線性疊加的方式)比較, 常規演算法在混入 4 路音頻流的結果, 已經明顯聽到背景噪音, 波形會突變失真, 出現比較輕微的爆破音, 少量出現語音不可以辨認的情況; 如果混入 5 路或者 5 路以上的音頻流, 則輸出的音頻流質量已經不能從聽覺上接受, 語聲模糊並且爆破音明顯, 噪音大, 難以辨別語音內容. 採用衰減因子的方式進行調整以後, 混入 4 路音頻流從聽覺上基本感覺不到背景噪音, 混入 5 路的情況下, 仍然能清晰辨別各路的語音內容, 不出現爆破音; 混入 6 路到 9 路的情況下仍然能保證語音質量, 不會發生突變的爆破音, 能夠滿足視頻會議的要求. 從演算法執行效率上看, 與常規的混音演算法比較, 其時間複雜度並沒有增加而具有同等的時間複雜度, 只是調和係數法在計算過程中疊加時需要進行一次額外的乘法運算(如上述演算法描述的 2.a), 並且發生溢出的情況下需要重新計算新的調和係數(整數除法運算), 最後在演算法的第三步需要進行一次加法運算(浮點數加法). 因為涉及的數值不會很大, 同時音頻流的數據量較之視頻等要小得很多, 在視頻會議的應用中, 採用調和係數方法進行混音完全在 MCU 承載的能力範圍內, 實測與常規混音演算法比較, 格式為 linear PCM raw, 16 bit, 單聲道, 採樣率為8000 HZ, 時間30 秒, 幀長30 毫秒的情況下, 其差別不會超過 17ms , 並不會由此產生很大的延遲, 其實時性仍然得到保證, 而從混音的質量來說較常規混音演算法要好很多.
推薦閱讀:

資源 | 目標檢測演算法精彩集錦(含圖像和視頻等)
運籌學教授葉蔭宇:作為 AI 基石,優化演算法如何在實際中應用?
計步器演算法是如何實現的?
程序員如何通過《演算法導論》學習?這本書適不適合演算法基礎薄弱的程序員?
如何理解量子糾纏對量子演算法的影響?

TAG:演算法 | 混音 | 算法 |