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


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

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

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

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

 

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

 

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

二、Android 獲取實時音量

獲取音量之前,我們必須先在AndroidManifest.xml文件里面申請相應的權限:

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

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

1. MediaRecorder

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

使用MediaRecorder.getMaxAmplitude返回的是0到32767范圍的16位整型。如果設置參考振幅為1的話,那么計算出來的分貝值域的正常范圍應該為 0dB90.3dB

核心代碼:

  /**
     * 開始錄音 使用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-4、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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM