文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处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开发相关文章