說到獲取音量,你們首先想到的應該就是分貝(dB),分貝是一個相對單位(是一個比值,是一個數值,是一個純計數方法)。android
在音頻領域dB度量的是聲音的強度,其計算的公式以下:git
在上面的公式中,分子是測量值的聲壓,分母是參考值的聲壓(20微帕,人類所能聽到的最小聲壓)。github
在Android設備傳感器中,咱們能獲取到的物理值是振幅值,通常使用下面的公式來計算分貝值:數組
咱們從Android SDK中讀取了某段音頻數據的振幅後,取最大振幅或平均振幅(能夠用平方和平均,或絕對值的和平均),代入上述公式的A1。參考的振幅A0取值定爲1(這裏取1是爲了實現方便,如需更加精確建議拿一個標準分貝計作校準參考),做爲 Android 麥克風能聽到的最小的聲音振幅。這樣咱們就能夠計算出分貝值了。ide
獲取音量以前,咱們必須先在AndroidManifest.xml文件裏面申請相應的權限:post
<uses-permission android:name="android.permission.RECORD_AUDIO" />
在Android SDK提供的API中,咱們能獲取到音頻方式有兩個:android.media.MediaRecorder 和 android.media.AudioRecord。ui
MediaRecorder 是用來錄製一段完整的音視頻並寫入到文件系統中的API。經過它,咱們能很簡單的經過它的無參方法getMaxAmplitude來獲取一小段時間內音頻源數據中的最大振幅,此方法是不少錄音軟件計算音量等級所採用的辦法。(注:由於是取最大值,因此存在受到極端數據的影響而致使計算的分貝波動值較大的問題)。編碼
使用MediaRecorder.getMaxAmplitude返回的是0到32767範圍的16位整型。若是設置參考振幅爲1的話,那麼計算出來的分貝值域的正常範圍應該爲 0dB 到 90.3dB。spa
核心代碼:3d
/** * 開始錄音 使用amr格式 */ public void startRecord() { // 開始錄音 /* ①Initial:實例化MediaRecorder對象 */ if (mMediaRecorder == null) mMediaRecorder = new MediaRecorder(); try { /* ②setAudioSource/setVedioSource */ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 設置麥克風 /* ②設置音頻文件的編碼:AAC/AMR_NB/AMR_MB/Default 聲音的(波形)的採樣 */ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); /* * ②設置輸出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 * ,H263視頻/ARM音頻編碼)、MPEG-四、RAW_AMR(只支持音頻且音頻編碼要求爲AMR_NB) */ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); /* ③準備 */ mMediaRecorder.setOutputFile(Environment.getExternalStorageDirectory().getPath() + File.separator + "111.amr"); mMediaRecorder.setMaxDuration(MAX_LENGTH); mMediaRecorder.prepare(); /* ④開始 */ mMediaRecorder.start(); updateMicStatus(); } catch (IllegalStateException | IOException e) { e.printStackTrace(); } } /** * 中止錄音 */ private void stopRecord() { if (mMediaRecorder == null) { return; } mMediaRecorder.stop(); mMediaRecorder.reset(); mMediaRecorder.release(); mMediaRecorder = null; } private void updateMicStatus() { if (mMediaRecorder != null) { double ratio = (double) mMediaRecorder.getMaxAmplitude() / 1; // 參考振幅爲 1 double db = 0;// 分貝 if (ratio > 1) { db = 20 * Math.log10(ratio); } Log.d(TAG, "計算分貝值 = " + db + "dB"); mHandler.postDelayed(mUpdateMicStatusTimer, 100); // 間隔取樣時間爲100秒 } }
此API相對MediaRecorder來講更偏底層一點,咱們可使用AudioRecord得到具體的音頻數據。
音源數據經過read(byte[] audioData, int offsetInBytes, int sizeInBytes)方法從緩衝區讀取到咱們傳入的字節數組audioData後,咱們即可以對其進行操做,如求平方和或絕對值的平均值。這樣能夠避免個別極端值的影響,使計算的結果更加穩定。求得平均值以後,若是是平方和則代入常數係數爲10的公式中,若是是絕對值的則代入常數係數爲20的公式中,算出分貝值。
核心代碼:
public void getNoiseLevel() { mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE_IN_HZ, AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE); new Thread(new Runnable() { @Override public void run() { mAudioRecord.startRecording(); short[] buffer = new short[BUFFER_SIZE]; while (isGetVoiceRun) { //r是實際讀取的數據長度,通常而言r會小於buffersize int r = mAudioRecord.read(buffer, 0, BUFFER_SIZE); long v = 0; // 將 buffer 內容取出,進行平方和運算 for (short value : buffer) { v += value * value; } // 平方和除以數據總長度,獲得音量大小。 double mean = v / (double) r; double volume = 10 * Math.log10(mean); Log.d(TAG, "分貝值 = " + volume + "dB"); synchronized (mLock) { try { mLock.wait(100); // 一秒十次 } catch (InterruptedException e) { e.printStackTrace(); } } } mAudioRecord.stop(); mAudioRecord.release(); mAudioRecord = null; } }).start(); }
github地址:https://github.com/renhui/RHAudioVolume/tree/master