Android音頻開發(fā)之音頻采集的實現(xiàn)示例
在 Android 系統(tǒng)中,一般使用 AudioRecord 或者 MediaRecord 來采集音頻。
AudioRecord 是一個比較偏底層的API,它可以獲取到一幀幀 PCM 數(shù)據(jù),之后可以對這些數(shù)據(jù)進行處理。
而 MediaRecorder 是基于 AudioRecorder 的 API(最終還是會創(chuàng)建AudioRecord用來與AudioFlinger進行交互) ,它可以直接將采集到的音頻數(shù)據(jù)轉(zhuǎn)化為執(zhí)行的編碼格式,并保存。
直播技術(shù)采用的就是 AudioRecorder 采集音頻數(shù)據(jù)。
本文主要介紹例如 AudioRecord 進行音頻的采集。
基本API
獲取最小的緩沖區(qū)大小,用于存放 AudioRecord 采集到的音頻數(shù)據(jù)。
AudioRecord構(gòu)造方法
根據(jù)具體的參數(shù)配置,請求硬件資源創(chuàng)建一個可以用于采集音頻的 AudioRecord 對象。
參數(shù)描述:
音頻采集基本概念
音頻采集一般使用 AudioRecod或者 MediaRecord
音頻采集的來源是什么?
MediaRecorder.AudioSource.MIC 一般是麥克風
采樣率(單位:赫茲)
每秒鐘音頻采樣點個數(shù)(8000/44100Hz)
聲道
- AudioFormat.CHANNEL_IN_MONO 單聲道,一個聲道進行采樣
- AudioFormat.CHANNEL_IN_STEREO 雙聲道,兩個聲道進行采樣
音頻采樣精度
指定采樣的數(shù)據(jù)的格式和每次采樣的大小。
數(shù)據(jù)返回格式為 PCM 格式
每次采樣的位寬為 16bit
一般都采用這個 AudioFormat.ENCODING_PCM_16BIT(官方文檔表示,該采樣精度保證所有設備都支持)
比特率
每秒傳送的比特(bit)數(shù)。單位為 bps(Bit Per Second),比特率越高,傳送數(shù)據(jù)速度越快。
采樣率x采樣大小x聲道數(shù)
每秒鐘采樣的大小=16bit(位寬) 2(雙通道) 44100(每次采樣的次數(shù)hz) = 1411200b=1411.2kbps
比特率越大表示單位時間內(nèi)采樣的數(shù)據(jù)越多,傳輸?shù)臄?shù)據(jù)量越大。
audioResource
音頻采集的來源
audioSampleRate
音頻采樣率
channelConfig
聲道
audioFormat
音頻采樣精度,指定采樣的數(shù)據(jù)的格式和每次采樣的大小。
bufferSizeInBytes
AudioRecord 采集到的音頻數(shù)據(jù)所存放的緩沖區(qū)大小。
//設置采集來源為麥克風 private static final int AUDIO_RESOURCE = MediaRecorder.AudioSource.MIC; //設置采樣率為44100,目前為常用的采樣率,官方文檔表示這個值可以兼容所有的設置 private final static int AUDIO_SAMPLE_RATE = 44100; //設置聲道聲道數(shù)量為雙聲道 private final static int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO; //設置采樣精度,將采樣的數(shù)據(jù)以PCM進行編碼,每次采集的數(shù)據(jù)位寬為16bit。 private final static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)
開始采集
開始采集之后,狀態(tài)變?yōu)镽ECORDSTATE_RECORDING 。
public void startRecording ()
讀取錄制內(nèi)容,將采集到的數(shù)據(jù)讀取到緩沖區(qū)
方法調(diào)用的返回值的狀態(tài)碼:
情況異常:
1.ERROR_INVALID_OPERATION if the object wasn't properly initialized
2.ERROR_BAD_VALUE if the parameters don't resolve to valid data and indexes.
情況正常:the number of bytes that were read
public int read (ByteBuffer audioBuffer, int sizeInBytes) public int read (byte[] audioData, int offsetInBytes, int sizeInBytes) public int read (short[] audioData, int offsetInShorts, int sizeInShorts)
停止采集
停止采集之后,狀態(tài)變?yōu)?RECORDSTATE_STOPPED 。
public void stop ()
獲取AudioRecord的狀態(tài)
用于檢測AudioRecord是否確保了獲得適當?shù)挠布Y源。在AudioRecord對象實例化之后調(diào)用。
STATE_INITIALIZED 初始完畢
STATE_UNINITIALIZED 未初始化
public int getState ()
返回當前AudioRecord的采集狀態(tài)
public static final int RECORDSTATE_STOPPED = 1; 停止狀態(tài)
調(diào)用 void stop() 之后的狀態(tài)
public static final int RECORDSTATE_RECORDING = 3;正在采集
調(diào)用 startRecording () 之后的狀態(tài)
public int getRecordingState()
AudioRecord 采集音頻的基本流程
權(quán)限
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
構(gòu)造一個 AudioRecord 對象。
開始采集。
讀取采集的數(shù)據(jù)。
停止采集。
構(gòu)造一個 AudioRecord 對象
獲取 bufferSizeInBytes 值
bufferSizeInBytes 是 AudioRecord 采集到的音頻數(shù)據(jù)所存放的緩沖區(qū)大小。
注意:這個大小不能隨便設置,AudioRecord 提供對應的 API 來獲取這個值。
通過 bufferSizeInBytes 返回就可以知道傳入給 AudioRecord.getMinBufferSize 的參數(shù)是否支持當前的硬件設備。
if (AudioRecord.ERROR_BAD_VALUE == bufferSizeInBytes || AudioRecord.ERROR == bufferSizeInBytes) { throw new RuntimeException("Unable to getMinBufferSize"); } //bufferSizeInBytes is available...
開始采集
在開始錄音之前,首先要判斷一下 AudioRecord 的狀態(tài)是否已經(jīng)初始化完畢了。
//判斷AudioRecord的狀態(tài)是否初始化完畢 //在AudioRecord對象構(gòu)造完畢之后,就處于AudioRecord.STATE_INITIALIZED狀態(tài)了。 int state = audioRecord.getState(); if (state == AudioRecord.STATE_UNINITIALIZED) { throw new RuntimeException("AudioRecord STATE_UNINITIALIZED"); }
開始采集
audioRecord.startRecording(); //開啟線程讀取數(shù)據(jù) new Thread(recordTask).start();
讀取采集的數(shù)據(jù)
上面提到, AudioRecord 在采集數(shù)據(jù)時會將數(shù)據(jù)存放到緩沖區(qū)中,因此我們只需要創(chuàng)建一個數(shù)據(jù)流去從緩沖區(qū)中將采集的數(shù)據(jù)讀取出來即可。
創(chuàng)建一個 數(shù)據(jù)流 ,一邊從 AudioRecord 中讀取音頻數(shù)據(jù)到 緩沖區(qū) ,一邊將 緩沖區(qū) 中數(shù)據(jù)寫入到 數(shù)據(jù)流 。
因為需要使用IO操作,因此讀取數(shù)據(jù)的過程應該在子線程中執(zhí)行
//創(chuàng)建一個流,存放從AudioRecord讀取的數(shù)據(jù) File saveFile = new File(Environment.getExternalStorageDirectory(), "audio-record.pcm"); DataOutputStream dataOutputStream = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(saveFile))); private Runnable recordTask = new Runnable() { @Override public void run() { //設置線程的優(yōu)先級 android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIOR Log.i(TAG, "設置采集音頻線程優(yōu)先級"); final byte[] data = new byte[bufferSizeInBytes]; //標記為開始采集狀態(tài) isRecording = true; Log.i(TAG, "設置當前當前狀態(tài)為采集狀態(tài)"); //getRecordingState獲取當前AudioReroding是否正在采集數(shù)據(jù)的狀態(tài) while (isRecording && audioRecord.getRecordingState() == AudioRecord //讀取采集數(shù)據(jù)到緩沖區(qū)中,read就是讀取到的數(shù)據(jù)量 final int read = audioRecord.read(data, 0, bufferSizeInBytes); if (AudioRecord.ERROR_INVALID_OPERATION != read && AudioRecord.E //將數(shù)據(jù)寫入到文件中 dataOutputStream.write(buffer,0,read); } } } };
停止采集
/** * 停止錄音 */ public void stopRecord() throws IOException { Log.i(TAG, "停止錄音,回收AudioRecord對象,釋放內(nèi)存"); isRecording = false; if (audioRecord != null) { if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { audioRecord.stop(); Log.i(TAG, "audioRecord.stop()"); } if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) { audioRecord.release(); Log.i(TAG, "audioRecord.release()"); } } }
幾個小問題
采集數(shù)據(jù)之后,保存的文件為 audio-record.pcm ,這個文件并不能使用普通的播放器播放。它是一個原始的文件,沒有任何播放格式,因此就無法被播放器識別并播放。
上面的問題可以有兩種解決方法
- 使用 AudioTrack 播放 pcm 格式的音頻數(shù)據(jù)。
- 將 pcm 數(shù)據(jù)轉(zhuǎn)化為 wav 格式的數(shù)據(jù),這樣就可以被播放器識別。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- Android音頻錄制MediaRecorder之簡易的錄音軟件實現(xiàn)代碼
- Android提高之MediaPlayer播放網(wǎng)絡音頻的實現(xiàn)方法
- Android音頻可視化開發(fā)案例說明
- Android使用音頻信息繪制動態(tài)波紋
- Android音頻系統(tǒng)AudioTrack使用方法詳解
- Android音頻處理之通過AudioRecord去保存PCM文件進行錄制,播放,停止,刪除功能
- Android實現(xiàn)音頻條形圖效果(仿音頻動畫無監(jiān)聽音頻輸入)
- android downsample降低音頻采樣頻率代碼
- Android App中使用AudioManager類來編寫音頻播放器
相關(guān)文章
Android編程實現(xiàn)禁止StatusBar下拉的方法
這篇文章主要介紹了Android編程實現(xiàn)禁止StatusBar下拉的方法,涉及Android StatusBarManager相關(guān)屬性控制操作技巧,需要的朋友可以參考下2017-08-08Android AsyncTask實現(xiàn)機制詳細介紹及實例代碼
這篇文章主要介紹了Android AsyncTask實現(xiàn)機制詳細介紹及實例代碼的相關(guān)資料,這里附有示例代碼,幫助大家學習理解,需要的朋友可以參考下2016-12-12android studio節(jié)省C盤空間的配置方法
這篇文章主要介紹了android studio節(jié)省C盤空間的配置方法,本文給大家介紹的非常詳細,具有一定的參考借鑒價值 ,需要的朋友可以參考下2019-07-07Android獲取手機型號/系統(tǒng)版本號/App版本號等信息實例講解
本示例獲得手機型號,系統(tǒng)版本,App版本號等信息,具體實現(xiàn)如下,感興趣的朋友可以參考下哈2013-06-06