Android 獲取 AudioRecord 麥克風音量大小並做選擇性發送


 extends:http://blog.csdn.net/alvinhuai/article/details/8955127,http://mikespook.com/2010/11/android-%E5%AE%9E%E6%97%B6%E8%8E%B7%E5%8F%96%E9%BA%A6%E5%85%8B%E9%A3%8E%E8%BE%93%E5%85%A5%E9%9F%B3%E9%87%8F%E7%9A%84%E4%BB%A3%E7%A0%81/

  前幾天做一個關於錄音並獲取音量大小的模塊,今天寫一個demo和大家分享。如果有各位有更好的方法可以留言提醒我,謝謝。

    首先錄音功能很容易實現,通過audiorecord或者mediarecorder都可以實現,如果要獲取錄音音量的大小,用audiorecord更加方便。實現錄音功能可以大致分為幾個步驟。一 初始化錄音設備audiorecord。 二 ,開啟一個線程實現錄音功能。 三 獲取錄音的音頻流對它的大小進行分析。四 將大小傳遞至主線程使UI做出相應的改變。

    首先初始化audiorecord 。AudioRecord (int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes) ,初始化需要五個參數,audiosource 是指錄制源在此我們選擇麥克風:MediaRecorder.AudioSource.MIC。  sampleRateInHz 默認采樣頻率,以赫茲為單位,官方文檔說44100 為目前所有設備兼容,但是如果用模擬器測試的話會有問題,所以有的也用8000。  channelConfig ,  描述音頻通道設置 CHANNEL_IN_MONO保證能在所有設備上工作。audioFormat 音頻流的格式,分為16bit 或8bit目前都支持的是ENCODING_PCM_16BIT.  bufferSizeInBytes 在錄制過程中,音頻數據寫入緩沖區的總數(字節)。 從緩沖區讀取的新音頻數據總會小於此值. 這個值一般通過getMinBufferSize來獲取。getMinBufferSize的參數可以參照audiorecord的構造函數。在oncreate中執行一下代碼。 

try {
                mMinibuffer = AudioRecord.getMinBufferSize(sampleRates,
                                AudioFormat.CHANNEL_IN_MONO,
                                AudioFormat.ENCODING_PCM_16BIT);
                if(mMinibuffer != AudioRecord.ERROR_BAD_VALUE){
                        mRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
                                      sampleRates[i],
                                      AudioFormat.CHANNEL_IN_MONO,
                                      AudioFormat.ENCODING_PCM_16BIT,
                                      mMinibuffer);
                }
            } catch (IllegalArgumentException e) {
                ;
            }

 

這是audiorecord的初始化,下面可以實現錄音

public class RecordThread extends Thread{
        private boolean mIsRun = false;
        public RecordThread(){
            super();
        }
        public void run(){
            super.run();
            MainActivity.this.mRecord.startRecording();
            byte[]  byte_buffer = new byte[mMinibuffer];
            mIsRun = true;
            while(mIsRun){
                int r = mRecord.read(byte_buffer,0,mMinibuffer);
                int mShortArrayLenght = r/2;
                short[] short_buffer = new short[mShortArrayLenght];
                short_buffer = byteArrayToShortArray(byte_buffer,mShortArrayLenght);
                int max =  0;
                if(r > 0){
                    for(int i=0; i<mShortArrayLenght; i++){
                        if(Math.abs(short_buffer[i]) > max){
                            max = Math.abs(short_buffer[i]);
                        }
                    }
                    Bundle mBundle = new Bundle();
                    mBundle.putInt(mSendData, max);
                    Message Msg = new Message();
                    Msg.what = RECORDSTATE;
                    Msg.setData(mBundle);
                    mHandler.sendMessage(Msg);
                }
            }
            MainActivity.this.mRecord.stop();
            mHandler.sendEmptyMessage(NULLBUFFER);
        }

        public void stopRecord(){
            mIsRun = false;
        }
    }

 

這里是寫一個線程實現錄音功能。byte_buffer  保存錄制的音頻流,因為每次錄制次數很多,我暫時將每次錄音的最大值當作這次錄音的音量,然后通過handler將最大值返回給主線程。如果需要停止這個線程可以通過調用這個線程函數 stopRecord(); 然后我們通過每次得到的音量值,可以通過view的ondraw函數,將音量變化動態的畫出來。具體代碼不全貼出來的,主要講一下這個思想。 如果有人需要可以直接留言給我, 可以發給大伙。

public class BoschAudioClient extends Thread {

    static final int frequency = 44100;
    static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
    private final int socketVol = 2000;
    private final int cycle = 8;
    protected AudioRecord m_in_rec;
    protected int m_in_buf_size;
    protected byte[] m_in_bytes;
    protected boolean m_keep_running;
    protected LinkedList<byte[]> m_in_q;
    private int volTime = 0;
    private boolean flagVol = false;

    public  AudioClient() {

        m_in_buf_size = AudioRecord.getMinBufferSize(frequency,
                channelConfiguration, audioEncoding) * 2;

        m_in_rec = new AudioRecord(MediaRecorder.AudioSource.MIC,
                8000, channelConfiguration, audioEncoding,
                m_in_buf_size);

        m_in_bytes = new byte[m_in_buf_size];

        m_keep_running = true;
        m_in_q = new LinkedList<byte[]>();
    }

    public void run() {
        try {
            byte[] bytes_pkg;
            m_in_rec.startRecording();
            while (m_keep_running) {

                //從內存獲取數據
                int r = m_in_rec.read(m_in_bytes, 0, m_in_buf_size);
                bytes_pkg = m_in_bytes.clone();

                //發送socket數據
                if (m_in_q.size() >= 2) {
                    byte[] buff = m_in_q.removeFirst();
                    ByteString socketStr = ByteString.copyFrom(buff);
               }

                //音量
                int maxVol = getVolumeMax(r, bytes_pkg);
//                int v = getVolume(r, bytes_pkg);
                //如果音量大於最小值 打開開關
                if (maxVol > socketVol) {
                    flagVol = true;
                }

                //如果開關為打開狀態,開始計時,計時大約1個周期的時候 ,關閉開關,停止計時,停止發送數據
                if (flagVol) {
                    m_in_q.add(bytes_pkg);
                    volTime++;
                    if ((volTime / cycle) > 0) {
                        flagVol = false;
                        volTime = 0;
                    }
                }
        
            }

            m_in_rec.stop();
            m_in_rec = null;
            m_in_bytes = null;

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private int getVolume(int r, byte[] bytes_pkg) {
        //way 1
        int v = 0;
//      將 buffer 內容取出,進行平方和運算
        for (byte aBytes_pkg : bytes_pkg) {
            // 這里沒有做運算的優化,為了更加清晰的展示代碼
            v += aBytes_pkg * aBytes_pkg;
        }
//      平方和除以數據總長度,得到音量大小。可以獲取白噪聲值,然后對實際采樣進行標准化。
//      如果想利用這個數值進行操作,建議用 sendMessage 將其拋出,在 Handler 里進行處理。
        int volume = (int) (v / (float) r);
        return volume;
    }

    private int getVolumeMax(int r, byte[] bytes_pkg) {

        //way 2
        int mShortArrayLenght = r / 2;
        short[] short_buffer = byteArray2ShortArray(bytes_pkg, mShortArrayLenght);
        int max = 0;
        if (r > 0) {
            for (int i = 0; i < mShortArrayLenght; i++) {
                if (Math.abs(short_buffer[i]) > max) {
                    max = Math.abs(short_buffer[i]);
                }
            }
        }
        return max;
    }

    private short[] byteArray2ShortArray(byte[] data, int items) {
        short[] retVal = new short[items];
        for (int i = 0; i < retVal.length; i++)
            retVal[i] = (short) ((data[i * 2] & 0xff) | (data[i * 2 + 1] & 0xff) << 8);

        return retVal;
    }

    public void free() {
        m_keep_running = false;
        try {
            Thread.sleep(100);
        } catch (Exception e) {
            Log.d("sleep exceptions...\n", "");
        }
    }

}
 

 


免責聲明!

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



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