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

本文介绍如何在Android应用中使用AudioRecord进行录音,并详细解释了录音数据的处理过程,包括初始化、采样、读取数据及回调机制。通过一个简单的例子展示了录音数据的基本操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei

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

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

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小例子

相关链接:我的Android开发相关文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值