Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件(学习笔记)
關于 AudioRecord
Android SDK 提供了兩套音頻采集的API,分別是:MediaRecorder 和 AudioRecord,前者是一個更加上層一點的API,它可以直接把手機麥克風錄入的音頻數(shù)據(jù)進行編碼壓縮(如AMR、MP3等)并存成文件,而后者則更接近底層,能夠更加自由靈活地控制,可以得到原始的一幀幀PCM音頻數(shù)據(jù)。
如果想簡單地做一個錄音機,錄制成音頻文件,則推薦使用 MediaRecorder,而如果需要對音頻做進一步的算法處理、或者采用第三方的編碼庫進行壓縮、以及網(wǎng)絡傳輸?shù)葢?#xff0c;則建議使用 AudioRecord,其實 MediaRecorder 底層也是調用了 AudioRecord 與 Android Framework 層的 AudioFlinger 進行交互的。
音頻的開發(fā),更廣泛地應用不僅僅局限于本地錄音,因此,我們需要重點掌握如何利用更加底層的 AudioRecord API 來采集音頻數(shù)據(jù)(注意,使用它采集到的音頻數(shù)據(jù)是原始的PCM格式,想壓縮為mp3,aac等格式的話,還需要專門調用編碼器進行編碼)。
AudioRecord 的參數(shù)配置
-
audioSource:音頻采集的輸入源
可選的值以常量的形式定義在 MediaRecorder.AudioSource 類中,常用的值包括:
- 1. DEFAULT (默認)
- 2. VOICE_RECOGNITION (用于語音識別,等同于 DEFAULT )
- 3. MIC (由手機麥克風輸入)
- 4. VOICE_COMMUNICATION (用于 VoIP 應用)等等
-
sampleRateInHz: 采樣率
目前 44100Hz 是唯一可以保證兼容所有 Android 手機的采樣率。
-
channelConfig: 通道數(shù)
可選的值以常量的形式定義在 AudioFormat 類中,常用的是
- 1. CHANNEL_IN_MONO (單通道)
- 2. CHANNEL_IN_STEREO(雙通道)
-
audioFormat: 數(shù)據(jù)位寬
可選的值以常量的形式定義在 AudioFormat 類中,常用的是( 1 是可以保證兼容所有Android手機的)
- 1. ENCODING_PCM_16BIT(16bit)
- 2. ENCODING_PCM_8BIT(8bit)
-
bufferSizeInBytes:AudioRecord 內部的音頻緩沖區(qū)的大小
該緩沖區(qū)的值不能低于一幀“音頻幀”(Frame)的大小:
int size = 采樣率 x 位寬 x 采樣時間 x 通道數(shù)
采樣時間一般取 2.5ms~120ms 之間,由廠商或者具體的應用決定,我們其實可以推斷,每一幀的采樣時間取得越短,產(chǎn)生的延時就應該會越小,當然,碎片化的數(shù)據(jù)也就會越多。
在Android開發(fā)中,AudioRecord 類提供了一個幫助你確定這個 bufferSizeInBytes 的函數(shù),原型如下:
int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat);
不同的廠商的底層實現(xiàn)是不一樣的,但無外乎就是根據(jù)上面的計算公式得到一幀的大小,音頻緩沖區(qū)的大小則必須是一幀大小的2~N倍。實際開發(fā)中,強烈建議由該函數(shù)計算出需要傳入的 bufferSizeInBytes,而不是自己手動計算。
流程
-
設置所有 AudioRecord 參數(shù)
// 音頻輸入-麥克風private final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;// 采樣頻率 一般共分為 22.05KHz、44.1KHz、48KHz 三個等級// 44100 是目前的標準,但是某些設備仍然支持 22050,16000,11025private final static int AUDIO_SAMPLE_RATE = 44100;// 聲道 單聲道private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO;// 編碼private final static int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;// 緩沖區(qū)字節(jié)大小private int bufferSizeInBytes = 0;// 錄音對象private AudioRecord audioRecord; -
創(chuàng)建 AudioRecord 并且獲取緩沖區(qū)字節(jié)大小
/*** 創(chuàng)建默認的錄音對象** @param fileName 文件名*/public void createDefaultAudio(String fileName) {// 獲得緩沖區(qū)字節(jié)大小,AudioRecord 能接受的最小的 buffer 大小bufferSizeInBytes = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING);// 創(chuàng)建默認的錄音對象并且設置一系列配置audioRecord = new AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING, bufferSizeInBytes);this.fileName = fileName;} -
創(chuàng)建 buffer (用于保存新的聲音數(shù)據(jù)),設置 buffer 大小(錄制聲音數(shù)據(jù)容量大小)
// new 一個 byte 數(shù)組用來存一些字節(jié)數(shù)據(jù),大小為緩沖區(qū)大小byte[] audiodata = new byte[bufferSizeInBytes]; -
開始錄音
audioRecord.startRecording(); // 標志位, 用于控制停止數(shù)據(jù)流讀寫循環(huán) isRecording = true; -
開始采集,一邊從AudioRecord中讀取聲音數(shù)據(jù)到初始化的buffer,一邊將buffer中數(shù)據(jù)導入數(shù)據(jù)流
// 開個子線程將錄音的數(shù)據(jù)放入pcm文件new Thread(new Runnable() {@Overridepublic void run() {writeDataTOFile(listener);}}).start();// 如何將音頻寫入文件是重點,我寫個偽代碼,說明這個代碼運行順序// 首先創(chuàng)建 pcm 文件,得到他的 FileOutputStream,然后不斷循環(huán) AudioRecord 通過 read 將錄音的數(shù)據(jù)放入字節(jié)數(shù)組里,// 當錄音結束的時候要記得停止這個循環(huán)FileOutputStream fos = null;int readsize = 0;try {File file = new File(currentFileName);if (file.exists()) {file.delete();}// 建立一個可存取字節(jié)的文件fos = new FileOutputStream(file);} catch (IllegalStateException e) {Log.e("AudioRecorder", e.getMessage());throw new IllegalStateException(e.getMessage());} catch (FileNotFoundException e) {Log.e("AudioRecorder", e.getMessage());}while (isRecording) {readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos != null) {try {fos.write(audiodata);} catch (IOException e) {Log.e("AudioRecorder", e.getMessage());}}} -
關閉數(shù)據(jù)流
修改標志位:isRecording 為false,上面的while循環(huán)就自動停止了,數(shù)據(jù)流也就停止流動了,Stream也就被關閉了。
isRecording = false; -
停止錄音
只要AudioRecord.stop就可以暫停錄音了,然后當繼續(xù)錄音時在AudioRecord.start就好了,但是要另創(chuàng)建一個pcm記錄,當錄音結束時我們要將這些pcm一起轉換為一個wav,
至于pcm轉換為wav的代碼是固定的
if (null != audioRecord) {audioRecord.stop();audioRecord.release();audioRecord = null;recordingThread = null; }
停止錄音之后,注意要釋放資源。
流程走向
- buffer 從 AudioRecord 中拉取聲音數(shù)據(jù)
- 創(chuàng)建一個數(shù)據(jù)流,開啟一個線程,一邊從 AudioRecord 中讀取數(shù)據(jù),一邊將數(shù)據(jù)導入到數(shù)據(jù)流中
參考文獻
- Android音頻開發(fā)(2):如何采集一幀音頻
- Android 音視頻開發(fā)(二):使用 AudioRecord 采集音頻PCM并保存到文件
- Android 音視頻深入
總結
以上是生活随笔為你收集整理的Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件(学习笔记)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 文档管理利器--云脉文档自动分类快速检索
- 下一篇: Android音视频学习系列(五) —