Android 音視頻技術之錄音獲取實時音量

1、實時音量相關基礎知識

說到獲取音量,你們首先想到的應該就是分貝(dB),分貝是一個相對單位(是一個比值,是一個數值,是一個純計數方法)。android

在音頻領域dB度量的是聲音的強度,其計算的公式以下:git

在上面的公式中,分子是測量值的聲壓,分母是參考值的聲壓(20微帕,人類所能聽到的最小聲壓)。github

 

在Android設備傳感器中,咱們能獲取到的物理值是振幅值,通常使用下面的公式來計算分貝值:數組

 

咱們從Android SDK中讀取了某段音頻數據的振幅後,取最大振幅或平均振幅(能夠用平方和平均,或絕對值的和平均),代入上述公式的A1。參考的振幅A0取值定爲1(這裏取1是爲了實現方便,如需更加精確建議拿一個標準分貝計作校準參考),做爲 Android 麥克風能聽到的最小的聲音振幅。這樣咱們就能夠計算出分貝值了。ide

2、Android 獲取實時音量

獲取音量以前,咱們必須先在AndroidManifest.xml文件裏面申請相應的權限:post

<uses-permission android:name="android.permission.RECORD_AUDIO" />

在Android SDK提供的API中,咱們能獲取到音頻方式有兩個:android.media.MediaRecorderandroid.media.AudioRecordui

1. MediaRecorder

MediaRecorder 是用來錄製一段完整的音視頻並寫入到文件系統中的API。經過它,咱們能很簡單的經過它的無參方法getMaxAmplitude來獲取一小段時間內音頻源數據中的最大振幅,此方法是不少錄音軟件計算音量等級所採用的辦法。(注:由於是取最大值,因此存在受到極端數據的影響而致使計算的分貝波動值較大的問題)。編碼

使用MediaRecorder.getMaxAmplitude返回的是0到32767範圍的16位整型。若是設置參考振幅爲1的話,那麼計算出來的分貝值域的正常範圍應該爲 0dB90.3dBspa

核心代碼: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秒
      }
  }

2. AudioRecord

此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

相關文章
相關標籤/搜索