安卓音頻采集播放方法


Android音頻收集和播放()

一、文章說明(此文章轉載自簡書)

這篇文章主要講述的是Android中使用AudioRecord類和AudioTrack類來進行語音采集和播放相關的知識,在這篇文章中首先介紹的是有關聲音的一些概念性知識,然后介紹聲音的采集,之后再講述Android上回聲消除的相關步驟,最后介紹的是聲音的播放。

二、概念性知識點

在這里關於聲音的定義和產生就不再贅述了,如果有對這個感興趣的朋友可以去了解一下,下面介紹幾個聽過但是不是很清楚的概念。

麥克風降噪

大集都知道,在現在這個科技盛行的年代,在打電話時,即使一方的環境有些嘈雜,而在電話的另一方也能聽的清清楚楚,這主要靠的就是手機降噪技術的發展。目前所使用的手機不僅僅有一個麥克風,而是有2個甚至是3個(iPhone),這多出的麥克風就是降噪的關鍵。手機麥克風降噪圖請看圖一:

 

 
圖一 手機麥克風降噪:

 

下面以兩個麥克風的情況來說一下麥克風降噪,頂部和底部都各有一個。這兩個麥克風都很小,但是兩者的作用是有着非常明顯的區別,其中底部的麥克風是保證清晰通話的,而頂部的麥克風是用來消除噪音的。

由於頂部和底部在通話時和音源(發出聲音的源頭)的距離不同,所以兩個麥克風拾取的音量大小也是有不同的,利用這個差別,我們就可以過濾掉噪聲保留人聲了。在打電話時,兩個麥克風所拾取的背景噪聲音量是基本相同的,而記錄的人聲會有6dB左右的音量差。頂端麥收集噪聲后,通過解碼生成補償信號后就可以用來消除噪音了。

回聲

當聲投射到距離聲源有一段距離的大面積上時,聲能的一部分被吸收,而另一部分聲能要反射回來,如果聽者聽到由聲源直接發來的聲和由反射回來的聲的時間間隔超過十分之一秒(在15℃空氣中,距聲源至少17米處反射),它就能分辨出兩個聲音這種反射回來的聲叫“回聲”。如果聲速已知,當測得聲音從發出到反射回來的時間間隔,就能計算出反射面到聲源之間的距離。例如在室溫(20℃)時空氣中的聲速是343米每秒,所以站在聲源處的人要聽到回聲需要障礙物到聲源的距離至少17米。

回聲消除

 

很多時候在收集語音的同時也會播放着聲音,這就要在語音收集的時候就需要對采集的聲音進行回聲消除。當在語音收集的時候,如果還同時播放着聲音的話,就不能保證采集的聲音不包括正在播放的聲音,在這種情況下采集的語音即包括原聲又包括回聲,在這樣的惡性循環下,就會使得回聲越來越多,最后出現嗡鳴聲。

回聲消除就是在麥克風錄制外音的時候去除掉手機自身播放出來的聲音,這樣就將播放的聲音從采集的聲音中過濾出去,從而就避免了回聲的產生。圖二很好展示了回聲消除的機制。

 
圖二 回聲消除:
 
 

在近端,麥克風會采集到揚聲器播放出來的遠端聲音,假設這路聲音為y(n),當然由於需要將遠端傳來播放出來,我們當然能得到遠端傳來的聲音信號,假設這路聲音為x(n)。不難發現x(n)經過揚聲器的播放,然后經過空氣的傳播,最后被麥克風采集,然后變為y(n),x(n)和y(n)具有明顯的相關性。假設麥克風采集到的總聲音信號為z(n),這時候需要通過自適應濾波器根據x(n)找出z(n)中的y(n),然后從z(n)中過濾掉y(n)。

三、聲音采集

麥克風的原理是將將采集的聲音轉換為模擬電信號,之后將模擬電信號數字化,也就是用高低電平表示的信號(計算機能識別的信號),在Android中有一個AudioRecord類就能錄制語音,並將語音轉換為PCM數據,聲音在經過麥克風轉換為模擬電信號並最終又轉換為PCM數據,在轉換為PCM數據時就要依賴於三個參數,分別是:聲道數、采樣位數和采樣頻率。

聲道數

很好理解,有單聲道和立體聲之分,單聲道的聲音只能使用一個喇叭發聲(有的也處理成兩個喇叭輸出同一個聲道的聲音),立體聲的PCM可以使兩個喇叭都發聲(一般左右聲道有分工) ,更能感受到空間效果。

采樣位數

即采樣值或取樣值(就是將采樣樣本幅度量化)。它是用來衡量聲音波動變化的一個參數,也可以說是聲卡的分辨率。它的數值越大,分辨率也就越高,所發出聲音的能力越強。

在計算機中采樣位數一般有8位和16位之分,但有一點請大家注意,8位不是說把縱坐標分成8份,而是分成2的8次方即256份; 同理16位是把縱坐標分成2的16次方65536份。

采樣頻率

即取樣頻率,指每秒鍾取得聲音樣本的次數。采樣頻率越高,聲音的質量也就越好,聲音的還原也就越真實,但同時它占的資源比較多。由於人耳的分辨率很有限,太高的頻率並不能分辨出來。在16位聲卡中有22KHz、44KHz等幾級,其中,22KHz相當於普通FM廣播的音質,44KHz已相當於CD音質了,目前的常用采樣頻率都不超過48KHz。

既然知道了以上三個概念,就可以由下邊的公式得出PCM文件所占容量:

存儲量= (采樣頻率 · 采樣位數 · 聲道 · 時間)/8 (單位:字節數)。

四、Android聲音錄制

Android中使用AudioRecord和MediaRecorder進行音頻的錄制,這里介紹AudioRecord類的使用,有朋友對MediaRecorder感興趣的就去學習一下。根據上邊的介紹可知,需要給AudioRecord傳入采樣頻率、采樣位數和聲道數,除此之外還需要傳入兩個參數,一個是聲音源,一個是緩沖區大小。在這里緩沖區大小不能小於最小緩沖區大小(下面有介紹)。

權限

在Android中進行音頻的錄制是需要相應的權限的。在Android 6.0及以上要動態的申請權限。

音頻源

下面是Android所支持的音頻源:

/**默認聲音**/

public static final int DEFAULT = 0;

/**麥克風聲音*/

public static final int MIC = 1;

/**通話上行聲音*/

public static final int VOICE_UPLINK = 2;

/**通話下行聲音*/

public static final int VOICE_DOWNLINK = 3;

/**通話上下行聲音*/

public static final int VOICE_CALL = 4;

/**根據攝像頭轉向選擇麥克風*/

public static final int CAMCORDER = 5;

/**對麥克風聲音進行聲音識別,然后進行錄制*/

public static final int VOICE_RECOGNITION = 6;

/**對麥克風中類似ip通話的交流聲音進行識別,默認會開啟回聲消除和自動增益*/

public static final int VOICE_COMMUNICATION = 7;

/**錄制系統內置聲音*/

public static final int REMOTE_SUBMIX = 8;

在我的項目中音頻源來自於麥克風聲音。

緩沖區大小

設置完音頻源接下來就要設置緩沖區大小。麥克風采集到的音頻數據先放置在一個緩沖區里面,之后我們再從這個緩沖區里面讀取數據,從而獲取到麥克風錄制的音頻數據。在Android中不同的聲道數、采樣位數和采樣頻率會有不同的最小緩沖區大小,當AudioRecord傳入的緩沖區大小小於最小緩沖區大小的時候則會初始化失敗。大的緩沖區大小可以打開更為平滑的錄制效果,相應的也會帶來更大一點的延時。如下圖所示展示了緩沖區在音頻錄制和獲取中所處的位置: 

圖三 緩沖區大小:

 
 

這里提到的最小緩沖區的大小可以由下邊的代碼來獲得:

AudioRecord.getMinBufferSize(frequency, channelConfiguration,audioEncoding);

當獲取最小緩沖區大小失敗的時候,會返回相應的負數錯誤碼。

AudioRecord的初始化

下面的代碼是AudioRecord的初始化處理

public AudioRecord(int audioSource, int sampleRateInHz, intchannelConfig, int audioFormat, int bufferSizeInBytes) throwsIllegalArgumentException

這個構造方法中的參數上邊已經有了講解。當初始化失敗時也就是傳入的參數不匹配時就會拋出異常。如果想知道是否初始化成功的話可以獲取一下AudioRecord的一個狀態量,通過getState()方法可以獲取,當返回為STATE_UNINITIALIZED表示未成功初始化,當返回為STATE_INITIALIZED表示已經成功初始化了,初始化成功后就可以進行讀取緩沖區中的音頻數據的操作了。

讀取數據

AudioRecord使用read()方法進行數據的讀取操作,正如下面的這個方法:

public int read(@NonNull byte[] audioData, int offsetInBytes,int sizeInBytes) {

returnread(audioData, offsetInBytes, sizeInBytes, READ_BLOCKING);

}

當從緩沖區獲取音頻數據失敗時,會返回負數的錯誤碼。

參數選擇

眾所周知Android的廠商非常多,這就給開發者們制造了很多的麻煩,最為迫切解決的也就是兼容性的問題,在這方面Android推薦在錄音時使用的參數為:

sampleRateInHz = 44100;//采樣率

channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;//音道數

audioFormat = AudioFormat.ENCODING_PCM_16BIT//采樣位數

五、Android回聲消除

在Android中回聲消除可以通過三種方式進行處理:1、通過VOICE_COMMUNICATION模式進行錄音,自動實現回聲消除;2、利用Android自身帶的AcousticEchoCanceler進行回聲消除處理;3、使用第三方庫(Speex、Webrtc)進行回聲消除處理(在項目中我所使用的就是Speex進行回聲處理的)。

通過第一種方式進行回聲消除,需要將AudioManager設置模式為MODE_IN_COMMUNICATION,還需要將麥克風打開。有一點需要特別注意,音頻采樣率必須設置8000或者16000,通道數必須設為1個(別問為什么,這就是規定)。

AudioManager audioManager =(AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);

audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);

audioManager.setSpeakerphoneOn(true);

使用AcousticEchoCanceler過程比較簡單,錄制聲音的時候可以通過AudioRecord得到AudioSessionId,在創建AudioTrack的時候也可以傳入一個AudioSessionId,這時候將這個統一的AudioSessionId傳入AcousticEchoCanceler,那么AcousticEchoCanceler將根據之前講過的回聲消除的原理進行回聲消除。代碼如下:

 

 
AcousticEchoCanceler回聲消除代碼:
 

當使用Speex或者Webrtc第三方庫進行回聲消除的時候,需要將采集到的音頻數據傳入作為源數據,需要將此刻播放的音頻數據傳入作為參考數據,然后還需要傳入一個延時間隔,這樣第三方庫就能工作,從而得到回聲消除后的聲音。因為播放的聲音需要傳播,而且麥克風采集聲音還有相應的緩沖區,因此需要傳入一個延時間隔。關於Speex和Webrtc在github上能找到相應的Android ndk庫。有興趣的朋友可以實際動手進行測試一下。

六、Android聲音播放

前面介紹了Android的音頻錄制,下面介紹一下音頻的播放,同樣Android有兩個音頻播放的類MediaPlayer和AudioTrack,這里介紹AudioTrack類,其實MediaPlayer在framwork層也實例化了AudioTrack來進行解碼生成PCM,然后代理給AudioTrack。

StreamType(指定在流的類型)

這個在構造AudioTrack的第一個參數中使用。這個參數和Android中的AudioManager有關系,涉及到手機上的音頻管理策略。Android將系統的聲音分為以下幾類常見的:

STREAM_ALARM:警告聲

STREAM_MUSCI:音樂聲,例如music等

STREAM_RING:鈴聲

STREAM_SYSTEM:系統聲音

STREAM_VOCIE_CALL:電話聲音

模式類型

這個在構造AudioTrack的最后一個參數中使用。AudioTrack中有MODE_STATIC和MODE_STREAM兩種分類。STREAM方式表示由用戶通過write方式把數據一次一次得寫到audiotrack中。這種方式的缺點就是JAVA層和Native層不斷地交換數據,效率損失較大。而STATIC方式表示是一開始創建的時候,就把音頻數據放到一個固定的buffer,然后直接傳給audiotrack,后續就不用一次次得write了。AudioTrack會自己播放這個buffer中的數據。這種方法對於鈴聲等體積較小的文件比較合適。

AudioTrack的初始化

下面的代碼是AudioTrack的初始化處理

public AudioTrack(int streamType,int sampleRateInHz, int channelConfig,

int audioFormat,int bufferSizeInBytes,int mode)throws IllegalArgumentException

{}這個構造方法中的參數上邊已經有了講解。當初始化失敗時也就是傳入的參數不匹配時就會拋出異常。

寫入數據

AudioTrack使用write()方法將數據寫入到audiotrack中,正如下面的這個方法:

public int write(byte[] audioData,int offsetInBytes, int

sizeInBytes) {}offsetInBytes是指要播放的數據是從參數audioData的哪個地方開始。

七、總結

好了,這個第一篇就寫到這里了,希望朋友們能對Android錄音和播放有一定的了解。后續還會更新。

八、相關連接

www.jianshu.com/p/2cb75a71009f

www.jianshu.com/p/bee958826a9e


轉載出處:


作者:熊熊熊孩子
鏈接:https://www.jianshu.com/p/af7787b409a2
來源:簡書


免責聲明!

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



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