Android視訊編輯器(五)音訊編解碼、從視訊中分離音訊、音訊混音、音訊音量調節等
阿新 • • 發佈:2019-02-12
上面程式碼,就是一個歸一化混音演算法的java實現,而我們在過程中將原始是byte資料轉換成了short資料的原因就是為了提高精度,從而讓混音效果更好。核心原理就是上面的公式。 當然因為音訊的原始資料其實是非常多的,為了提升效率,最好使用jni實現混音相關演算法,這樣就可以實現一個效果較好的混音演算法了。專案裡面已新增相關實現,可以進行測試查閱。 但是混音時,有一個問題需要注意一下,就是音訊是存在單聲道和雙聲道,立體聲的區別的。我們讀取音訊的資訊的時候,可以看到他們是哪種聲道的,單聲道(mono),雙聲道(stereo),其實stereo應該叫立體聲,但是我查閱資料得到的資訊是,大部分的android手機其實是不支援立體聲錄音的,android平臺的很多立體聲其實只是單純的雙聲道,因為這涉及到非常底層的知識了,我也不太瞭解這一點,有知道的朋友,還望不惜賜教。這裡在進行混音的時候,不同的聲道會出現問題,因為不同的聲道資料同樣的時間戳,播放的資料量是不同的,但是我們這裡混音是按照資料量來混的,所以一個單聲道和一個多聲道的音訊直接混音的話,就會出現混音失敗。那麼如何解決這個問題呢? 其實我們可以通過將mono轉成stereo的方法來解決這個問題,居然的實現很簡單,如下程式碼/** * 歸一化混音 * */ public static byte[] normalizationMix(byte[][] allAudioBytes){ if (allAudioBytes == null || allAudioBytes.length == 0) return null; byte[] realMixAudio = allAudioBytes[0]; //如果只有一個音訊的話,就返回這個音訊資料 if(allAudioBytes.length == 1) return realMixAudio; //row 有幾個音訊要混音 int row = realMixAudio.length /2; // short[][] sourecs = new short[allAudioBytes.length][row]; for (int r = 0; r < 2; ++r) { for (int c = 0; c < row; ++c) { sourecs[r][c] = (short) ((allAudioBytes[r][c * 2] & 0xff) | (allAudioBytes[r][c * 2 + 1] & 0xff) << 8); } } //coloum第一個音訊長度 / 2 short[] result = new short[row]; //轉成short再計算的原因是,提供精確度,高階的混音軟體據說都是這樣做的,可以測試一下不轉short直接計算的混音結果 for (int i = 0; i < row; i++) { int a = sourecs[0][i] ; int b = sourecs[1][i] ; if (a <0 && b<0){ int i1 = a + b - a * b / (-32768); if (i1 > 32767){ result[i] = 32767; }else if (i1 < - 32768){ result[i] = -32768; }else { result[i] = (short) i1; } }else if (a > 0 && b> 0){ int i1 = a + b - a * b / 32767; if (i1 > 32767){ result[i] = 32767; }else if (i1 < - 32768){ result[i] = -32768; }else { result[i] = (short) i1; } }else { int i1 = a + b ; if (i1 > 32767){ result[i] = 32767; }else if (i1 < - 32768){ result[i] = -32768; }else { result[i] = (short) i1; } } } return toByteArray(result); } public static byte[] toByteArray(short[] src) { int count = src.length; byte[] dest = new byte[count << 1]; for (int i = 0; i < count; i++) { dest[i * 2 +1] = (byte) ((src[i] & 0xFF00) >> 8); dest[i * 2] = (byte) ((src[i] & 0x00FF)); } return dest; }
for (int i = 0; i < monoBytes.length; i += 2) { stereoBytes[i*2+0] = monoBytes[i]; stereoBytes[i*2+1] = monoBytes[i+1]; stereoBytes[i*2+2] = monoBytes[i]; stereoBytes[i*2+3] = monoBytes[i+1]; }