0
点赞
收藏
分享

微信扫一扫

Pro Android学习笔记(一三二):Media Frameworks(7):AudioRecord进行录音


作者@恺风Wei。

MediaRecorder将录音写到文件中,但有时我们需要对录音进行处理后在写,或者我们并不需要些文件,只是需要对这些数据进行处理,例如在VoIP中,数据转换为RTP/RTCP流,传输对远端。这些情况可以采用AudioRecord,录音数据写在AudioRecord的一个内部存储中,我们从这个内置存贮中读取数据。

AudioRecord同样需要RECORD_AUDIO的权限。下面是一个小例子,界面只有一个TextView显示debug现象,小例子的代码如下:


Pro Android学习笔记(一三二):Media Frameworks(7):AudioRecord进行录音_回调函数

public class AudioRecordActivity extends Activity{
     private TextView tv = null; 
     private int mAudioBufferSize; 
     private int mAudioBufferSampleSize ; 
     private AudioRecord record = null; 
     private boolean inRecordMode = false; 
     
     @Override 
    protected void onCreate(Bundle savedInstanceState) { 
         … …  
         initAudioRecord(); //【1】进行初始化 
    }  
      
     @Override 
     protected void onResume() {  
        super.onResume(); 
         debug("onResume() is called"); 
         inRecordMode = true;   // 控制是否继续进行采用的boolean
         Thread thread = new Thread(new Runnable() {    //【2】在后台开始进行录音的采样            
             @Override 
             public void run() {  
                 getSamples(); 
             } 
         }); 
         thread.start(); 
     }  

    @Override  
     protected void onPause() {  
         inRecordMode = false; //【3】停止采样 
        super.onPause();        
    }  

     @Override 
     protected void onDestroy() {  
         if(record != null){ 
             record.release();  //【4】释放录音所需资源 
             debug("Released AudioRecord"); 
         } 
        super.onDestroy(); 
     }  

    //【步骤1】初始化AudioRecord对象 
     private void initAudioRecord(){ 
         try{            
             int sampleRate = 8000; 
             int channelConfig = AudioFormat.CHANNEL_IN_MONO; 
             int audioFormat = AudioFormat.ENCODING_PCM_16BIT;              
             mAudioBufferSampleSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
             mAudioBufferSize = 2 * mAudioBufferSampleSize; 
             
            /* 【1.1】获取AudioRecord的对象。  
              * 获取AudioRecord对象的构造函数需要音频来源、采用频率,channel(mono、stereo,left,right),audio编码格式,和内置buffer大小。我们可以通过静态函数AudioRecord.getMinBufferSize()计算最小所需的buffer大小,如果设备不支持这些设计,会返回<0的错误,例如ZTE N909不支持PCM 8bit的采样,会返回-2。这事buffer容量的下限,通常设置更大容量作为缓存,以确保有充值时间对所有辷数据都进行处理。对于构造AudioRecord对象,如果设备对参数不支持,会抛出IllegalArgumentException异常。*/
             record = new AudioRecord(MediaRecorder.AudioSource.MIC, 
                     sampleRate, channelConfig, audioFormat, mAudioBufferSize);           
            debug("Setup AudioRecord OK. buffsize = " + mAudioBufferSize);           

             /* 【1.2】查看AudioRecord对象的状态是否正常  */  
             int audioRecordState = record.getState(); 
             if( audioRecordState  != AudioRecord.STATE_INITIALIZED){ 
                debug("ERROR: not initialized state=" + audioRecordState);
                 finish(); //小例子直接关闭Activity 
            }else{ 
                debug("AudioRecord is initialized!");
             }            
             
        }catch(IllegalArgumentException  e){ 
             debug("[ERROR]" +e.toString()); 
             e.printStackTrace(); 
         } 
     }  
      
     //【步骤2】进行采用,在后台线程中进行 
     private void getSamples(){ 
        if(record == null) 
             return; 
        //【步骤2.1】开始进行采用,并坚持采样的状态是否正确。 
         record.startRecording();  
         int state = record.getRecordingState(); 
         if(state != AudioRecord.RECORDSTATE_RECORDING){ 
             debug("AudioRecord is not recording... state=" + state); 
             finish(); 
             return; 
         }  
   
         //【步骤2.2】通过read(),不断地从AudioRecord的内置录音数据buffer中读出数据
        // 由于我们采用16bits,因此在read()可以选择short[]而非byte[],read()对short[]和byte[]都支持,采用short[],一个元素就是一个frame数据。我们在创建AudioRecord对象是,提供的最后参数是int bufferSizeInBytes,在转换为short[]存储时,长度需要减半 
         short[] audioBuffer = new short[mAudioBufferSize/2]; 
        debug("AudioRecord is recording..."); 
         while(inRecordMode){ 
            /* 我们不能直接使用AudioRecord的内置buffer,需要将内容读出来。读的方式是block,由于是在后台线程,所有不会有影响 。这个read从某种程度有些类似流的读取,当读满指定长度内容时,就完成一次read()操作,然后对数据进行处理。我们需要在下一步buffer填满之前处理完所有的数据,否则会导致buffer溢出。本例,我们只是简单地打印前8个帧的数据 */
            int samplesRead = record.read(audioBuffer,0,audioBuffer.length); 
             debug("Got samples: " + samplesRead); 
             debug("Frist few sample values: " + 
                     audioBuffer[0] + "," + 
                     audioBuffer[1] + "," + 
                     audioBuffer[2] + "," + 
                     audioBuffer[3] + "," + 
                     audioBuffer[4] + "," + 
                     audioBuffer[5] + "," + 
                     audioBuffer[6] + "," + 
                     audioBuffer[7]     ); 
         } 
         // 【步骤3】停止采样 
        record.stop();         debug("AudioRecord has stoped recording....");         
     } 

    private void debug(final String info){ 
         this.runOnUiThread(new Runnable() {              
             @Override 
            public void run() { 
                 Log.i("WEI",info); 
                 tv.setText(info + "\n" + tv.getText());                
             } 
         }); 
     } 
} 
AudioRecord可以设置基于frame的回调函数。我们在例子增加,每1000次采样的定期回调函数和到达10000次采样的回调函数。相关代码如下:
 private AudioRecord.OnRecordPositionUpdateListener listener = 
         new AudioRecord.OnRecordPositionUpdateListener() {              
             @Override //定期回调函数
             public void onPeriodicNotification(AudioRecord recorder) { 
                 debug("【onPeriodicNotification】");                    
             }              
             @Override  //到期回调函数 
             public void onMarkerReached(AudioRecord recorder) { 
                 debug("【onMarkerReached】"); 
                 inRecordMode = false;  //停止录音 
             } 
         }; 

 private void initAudioRecord(){ 
         …… 
        record = new AudioRecord(MediaRecorder.AudioSource.MIC, 
                 sampleRate, channelConfig, audioFormat, mAudioBufferSize);     

         /* 设置监听器。【注意】这时基于帧的,和我们设置的采样buffer没有任何关系 。我们可以算一下时间,8000Hz的采用频率,10000个frames,所需的时间为1.25秒。我们还可以计算一下大概在读取第几次时触发相应,从之前的debug可以看到,samplesize长度为640,即可以有640个frame,读到16次时,为640*16/8000=1.28秒,触发发生在读第16次的过程中。*/
         record.setNotificationMarkerPosition(10000);  //markerInFrames 
        record.setPositionNotificationPeriod(1000);   //in frames 
        record.setRecordPositionUpdateListener(listener);         
         ……       
 }

 小例子代码在:Pro Android学习:media framworks小例子


举报

相关推荐

0 条评论