您好,登錄后才能下訂單哦!
這篇文章的內(nèi)容主要圍繞Android中怎么采集一幀音頻進(jìn)行講述,文章內(nèi)容清晰易懂,條理清晰,非常適合新手學(xué)習(xí),值得大家去閱讀。感興趣的朋友可以跟隨小編一起閱讀吧。希望大家通過這篇文章有所收獲!
Android SDK 提供了兩套音頻采集的API,分別是:MediaRecorder 和 AudioRecord,前者是一個更加上層一點(diǎn)的API,它可以直接把手機(jī)麥克風(fēng)錄入的音頻數(shù)據(jù)進(jìn)行編碼壓縮(如AMR、MP3等)并存成文件,而后者則更接近底層,能夠更加自由靈活地控制,可以得到原始的一幀幀PCM音頻數(shù)據(jù)。
如果想簡單地做一個錄音機(jī),錄制成音頻文件,則推薦使用 MediaRecorder,而如果需要對音頻做進(jìn)一步的算法處理、或者采用第三方的編碼庫進(jìn)行壓縮、以及網(wǎng)絡(luò)傳輸?shù)葢?yīng)用,則建議使用 AudioRecord,其實(shí) MediaRecorder 底層也是調(diào)用了 AudioRecord 與 Android Framework 層的 AudioFlinger 進(jìn)行交互的。
音頻的開發(fā),更廣泛地應(yīng)用不僅僅局限于本地錄音,因此,我們需要重點(diǎn)掌握如何利用更加底層的 AudioRecord API 來采集音頻數(shù)據(jù)(注意,使用它采集到的音頻數(shù)據(jù)是原始的PCM格式,想壓縮為mp3,aac等格式的話,還需要專門調(diào)用編碼器進(jìn)行編碼)。
1. AudioRecord 的工作流程
首先,我們了解一下 AudioRecord 的工作流程:
(1) 配置參數(shù),初始化內(nèi)部的音頻緩沖區(qū)
(2) 開始采集
(3) 需要一個線程,不斷地從 AudioRecord 的緩沖區(qū)將音頻數(shù)據(jù)“讀”出來,注意,這個過程一定要及時,否則就會出現(xiàn)“overrun”的錯誤,該錯誤在音頻開發(fā)中比較常見,意味著應(yīng)用層沒有及時地“取走”音頻數(shù)據(jù),導(dǎo)致內(nèi)部的音頻緩沖區(qū)溢出。
(4) 停止采集,釋放資源
2. AudioRecord 的參數(shù)配置
上面是 AudioRecord 的構(gòu)造函數(shù),我們可以發(fā)現(xiàn),它主要是靠構(gòu)造函數(shù)來配置采集參數(shù)的,下面我們來一一解釋這些參數(shù)的含義(建議對照著我的上一篇文章來理解):
(1) audioSource
該參數(shù)指的是音頻采集的輸入源,可選的值以常量的形式定義在 MediaRecorder.AudioSource 類中,常用的值包括:DEFAULT(默認(rèn)),VOICE_RECOGNITION(用于語音識別,等同于DEFAULT),MIC(由手機(jī)麥克風(fēng)輸入),VOICE_COMMUNICATION(用于VoIP應(yīng)用)等等。
(2) sampleRateInHz
采樣率,注意,目前44100Hz是唯一可以保證兼容所有Android手機(jī)的采樣率。
(3) channelConfig
通道數(shù)的配置,可選的值以常量的形式定義在 AudioFormat 類中,常用的是 CHANNEL_IN_MONO(單通道),CHANNEL_IN_STEREO(雙通道)
(4) audioFormat
這個參數(shù)是用來配置“數(shù)據(jù)位寬”的,可選的值也是以常量的形式定義在 AudioFormat 類中,常用的是 ENCODING_PCM_16BIT(16bit),ENCODING_PCM_8BIT(8bit),注意,前者是可以保證兼容所有Android手機(jī)的。
(5) bufferSizeInBytes
這個是最難理解又最重要的一個參數(shù),它配置的是 AudioRecord 內(nèi)部的音頻緩沖區(qū)的大小,該緩沖區(qū)的值不能低于一幀“音頻幀”(Frame)的大小,而前一篇文章介紹過,一幀音頻幀的大小計算如下:
int size = 采樣率 x 位寬 x 采樣時間 x 通道數(shù)
采樣時間一般取 2.5ms~120ms 之間,由廠商或者具體的應(yīng)用決定,我們其實(shí)可以推斷,每一幀的采樣時間取得越短,產(chǎn)生的延時就應(yīng)該會越小,當(dāng)然,碎片化的數(shù)據(jù)也就會越多。
在Android開發(fā)中,AudioRecord 類提供了一個幫助你確定這個 bufferSizeInBytes 的函數(shù),原型如下:
int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat);
不同的廠商的底層實(shí)現(xiàn)是不一樣的,但無外乎就是根據(jù)上面的計算公式得到一幀的大小,音頻緩沖區(qū)的大小則必須是一幀大小的2~N倍,有興趣的朋友可以繼續(xù)深入源碼探究探究。
實(shí)際開發(fā)中,強(qiáng)烈建議由該函數(shù)計算出需要傳入的 bufferSizeInBytes,而不是自己手動計算。
3. 音頻的采集線程
當(dāng)創(chuàng)建好了 AudioRecord 對象之后,就可以開始進(jìn)行音頻數(shù)據(jù)的采集了,通過下面兩個函數(shù)控制采集的開始/停止:
AudioRecord.startRecording();
AudioRecord.stop();
一旦開始采集,必須通過線程循環(huán)盡快取走音頻,否則系統(tǒng)會出現(xiàn) overrun,調(diào)用的讀取數(shù)據(jù)的接口是:
AudioRecord.read(byte[] audioData, int offsetInBytes, int sizeInBytes);
4. 示例代碼
我將 AudioRecord 類的接口簡單封裝了一下,提供了一個 AudioCapturer 類,可以到我的Github下載:https://github.com/Jhuster/Android/blob/master/Audio/AudioCapturer.java
這里也貼出來一份:
/* * COPYRIGHT NOTICE * Copyright (C) 2016, Jhuster <lujun.hust@gmail.com> * https://github.com/Jhuster/Android * * @license under the Apache License, Version 2.0 * * @file AudioCapturer.java * * @version 1.0 * @author Jhuster * @date 2016/03/10 */ import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import android.util.Log; public class AudioCapturer { private static final String TAG = "AudioCapturer"; private static final int DEFAULT_SOURCE = MediaRecorder.AudioSource.MIC; private static final int DEFAULT_SAMPLE_RATE = 44100; private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO; private static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; private AudioRecord mAudioRecord; private int mMinBufferSize = 0; private Thread mCaptureThread; private boolean mIsCaptureStarted = false; private volatile boolean mIsLoopExit = false; private OnAudioFrameCapturedListener mAudioFrameCapturedListener; public interface OnAudioFrameCapturedListener { public void onAudioFrameCaptured(byte[] audioData); } public boolean isCaptureStarted() { return mIsCaptureStarted; } public void setOnAudioFrameCapturedListener(OnAudioFrameCapturedListener listener) { mAudioFrameCapturedListener = listener; } public boolean startCapture() { return startCapture(DEFAULT_SOURCE, DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_CONFIG, DEFAULT_AUDIO_FORMAT); } public boolean startCapture(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat) { if (mIsCaptureStarted) { Log.e(TAG, "Capture already started !"); return false; } mMinBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz,channelConfig,audioFormat); if (mMinBufferSize == AudioRecord.ERROR_BAD_VALUE) { Log.e(TAG, "Invalid parameter !"); return false; } Log.d(TAG , "getMinBufferSize = "+mMinBufferSize+" bytes !"); mAudioRecord = new AudioRecord(audioSource,sampleRateInHz,channelConfig,audioFormat,mMinBufferSize); if (mAudioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) { Log.e(TAG, "AudioRecord initialize fail !"); return false; } mAudioRecord.startRecording(); mIsLoopExit = false; mCaptureThread = new Thread(new AudioCaptureRunnable()); mCaptureThread.start(); mIsCaptureStarted = true; Log.d(TAG, "Start audio capture success !"); return true; } public void stopCapture() { if (!mIsCaptureStarted) { return; } mIsLoopExit = true; try { mCaptureThread.interrupt(); mCaptureThread.join(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { mAudioRecord.stop(); } mAudioRecord.release(); mIsCaptureStarted = false; mAudioFrameCapturedListener = null; Log.d(TAG, "Stop audio capture success !"); } private class AudioCaptureRunnable implements Runnable { @Override public void run() { while (!mIsLoopExit) { byte[] buffer = new byte[mMinBufferSize]; int ret = mAudioRecord.read(buffer, 0, mMinBufferSize); if (ret == AudioRecord.ERROR_INVALID_OPERATION) { Log.e(TAG , "Error ERROR_INVALID_OPERATION"); } else if (ret == AudioRecord.ERROR_BAD_VALUE) { Log.e(TAG , "Error ERROR_BAD_VALUE"); } else { if (mAudioFrameCapturedListener != null) { mAudioFrameCapturedListener.onAudioFrameCaptured(buffer); } Log.d(TAG , "OK, Captured "+ret+" bytes !"); } } } } }
使用前要注意,添加如下權(quán)限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
感謝你的閱讀,相信你對“Android中怎么采集一幀音頻”這一問題有一定的了解,快去動手實(shí)踐吧,如果想了解更多相關(guān)知識點(diǎn),可以關(guān)注億速云網(wǎng)站!小編會繼續(xù)為大家?guī)砀玫奈恼拢?/p>
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。