Android OpenSL ES 開發:OpenSL ES利用SoundTouch實現PCM音頻的變速和變調


緣由

OpenSL ES 學習到現在已經知道 OpenSL ES 不僅能播放和錄制PCM音頻數據,還能改變聲音大小、設置左聲道或右聲道播放、還能變速播放,可謂是播放音頻的王者。但是變速有一點不好的就是,雖然播放音頻的速度變了,但是相應的音調也隨之變了,這樣的用戶體驗就不那么好了。所以就想到了用開源的SoundTouch來實現PCM音頻變速和變調,OpenSL ES只是單純的播放PCM數據就可以了。

實現

1、移植SoundTouch(Android)

下載SoundTouch源碼,當前最新是:v2.0.1

在項目jni文件夾中創建include和SoundTouch文件夾,並把下載好的SoundTouch里面的include和SoundTouch的源碼拷貝進去就可以了,目錄結構如下:

 2、用SoundTouch轉碼PCM源文件

 因為SoundTouch默認是float(32bit)格式的數據,這里需要先改成short(16bit)的格式。打開STTypes.h文件,修改如下代碼:

再注釋掉下面這句,不然編譯不通過(for x86模擬器):

這樣SoundTouch里面處理PCM數據就是用的16bit的數據了。

3、SoundTouch使用流程

3.1 添加命名空間,並創建SoundTouch指針變量

using namespace soundtouch;
SoundTouch *soundTouch;

3.2 設置SoundTouch參數

    soundTouch = new SoundTouch();
    soundTouch->setSampleRate(44100);//設置采樣率,此處為44100,根據實際情況可變
    soundTouch->setChannels(2);//聲道,此處為立體聲
    soundTouch->setPitch(1);//變調不變速,如0.5、1.0、1.5等
    soundTouch->setTempo(1);//變速不變調,如0.5、1.0、2.0等 

3.3 向SoundTouch中傳入獲取到的PCM數據,使用:putSamples函數

size = fread(pcm_buffer, 1, 4096 * 2, pcmFile);
soundTouch->putSamples((const SAMPLETYPE *) pcm_buffer, size / 4);

這里,pcm_buffer是u_int16_t *類型的,也就是說和SoundTouch處理的PCM數據位數是一致的(16bit),所以可以直接傳入SoundTouch中。putSamples的第一個參數就是PCM數據指針,第二個參數是采樣點的個數,由於是2聲道16bit(2byte),所以PCM數據的采樣點個數為:num = 大小(size)/ (2 * 2)。

3.4 獲取SoundTouch輸出的PCM數據:使用receiveSamples函數

num = soundTouch->receiveSamples(sd_buffer, size / 4);

這里,receiveSamples的第一個參數是SoundTouch(變速或變調)處理后的PCM數據存放的內存地址,第二個參數是可能的最大采樣個數,可以和putSamples保持一致,其中sd_buffer是SAMPLETYPE * 類型的,記得要提前分配好內存大小,最后返回值就是SoundTouch處理后的PCM里面所包含的采樣個數,由於可能有緩存,所以應循環讀取receiveSamples,直到返回值為0為止。

3.5 OpenSL ES播放SoundTouch處理后的PCM音頻數據

(*pcmBufferQueue)->Enqueue(pcmBufferQueue, sd_buffer, size * 4);

由於size是采樣個數,所以sd_buffer的大小是:size * 2(聲道) * 2(16bit==2字節)。

這樣,我們聽到的聲音就是通過SoundTouch轉碼過后的了,如:變速不變調,變調不變速,變速又變調都可以自己設置。

思維發散

FFmpeg解碼得到的PCM數據(uint_8 *)利用SoundTouch轉碼

這里要處理的就是把uint_8 *(8bit)的數據轉換成short(16bit)的數據格式。這里其實就是做bit的位運算,原理如下如:

轉換代碼如下:

for (int i = 0; i < size / 2 + 1; i++)
                {
                    sd_buffer[i] = (pcm_buffer[i * 2] | (pcm_buffer[i * 2 + 1] << 8));
                }
                soundTouch->putSamples((const SAMPLETYPE *) pcm_buffer, size / 4); 

后續操作和16bit的一樣不變。

總結

雖然是簡單的移植SoundTouch到Android來播放PCM數據,但是還是讓我們了解到了數據在內存中怎么排列的,然后可以怎么操作最小單位的bit來達到我們的要求。

參考資料

OpenSL ES利用SoundTouch實現PCM音頻的變速和變調

參考源碼

SoundTouch_OpenSL_Android 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM