標籤:
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", ""); } }}
Android 擷取 AudioRecord 麥克風音量大小並做選擇性發送