一、引言
在蓝牙语音交互项目开发中,经常需要验证语音的采集情况,用来判断是录的音有问题还是上传有问题,还是解析有问题。所以特整理了一个支持蓝牙或手机Mic录音及播放的小工具,支持8K、16K采样率。
二、录音
Android录音有三种方式:
- 通过Intent调用系统的录音机进行录音:通过发送一个Intent,系统开启录音机进行录音,录音完成之后,在onActivityResult中返回录音文件的URI
-
使用MediaRecorder进行录音:MediaRecorder可用来录制音频和视频。在使用时,为了能够捕获音频,在实例化MediaRecorder之后,需要调用setAudioSource和setAudioEncoder方法
-
使用AudioRecord录制原始音频:AudioRecord类进行音频录制是三种音频录制方法中最为灵活的,它能直接得到录音的数据流,可以对数据流进行处理,从而实现更多有趣的功能
判断是否支持蓝牙录音:
private synchronized void checkSCOEnable() {
if (mAudioManager == null) {
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// Start listening for button presses
mAudioManager.registerMediaButtonEventReceiver(new ComponentName(getPackageName(), RemoteControlReceiver.class.getName()));
if (!mAudioManager.isBluetoothScoAvailableOffCall()) {
setTipText(R.string.tip_sys_nosupport_bluetooth_recording);
return;
}
setTipText(R.string.tip_sys_support_bluetooth_recording);
}
startBluetoothSCO();
}
设置SCO通道:
private void startBluetoothSCO() {
if (mAudioManager != null) {
if (mAudioManager.isBluetoothScoOn()) {
mAudioManager.stopBluetoothSco();
}
mAudioManager.startBluetoothSco();
}
}
private void stopBluetoothSCO() {
if (mAudioManager != null && mAudioManager.isBluetoothScoOn()) {
mAudioManager.setBluetoothScoOn(false);
mAudioManager.stopBluetoothSco();
mAudioManager.setMode(AudioManager.MODE_NORMAL);
}
}
开启、停止录音:
class StartRecordingListener implements View.OnClickListener {
@Override
public void onClick(View view) {
toggleStartRecording();
}
}
private void toggleStartRecording() {
if (mPlayer != null) {
setTipText(R.string.tip_cannot_start_sound_recording);
return;
}
if (mRecorder == null) {
startBluetoothSCO();
stopPlaySoundRecording();
startRecording();
} else {
stopRecording();
}
setStartRecordingStatus();
}
private void createRecordFile() {
String fileName = System.currentTimeMillis() + ".3gp";
mFileName = mFileDir + "/" + fileName;
mTvRecordFileAddr.setText(getString(R.string.tip_record_file_address, mFileName, DIR_NAME, fileName));
}
//record
private void startRecording() {
if (mRecorder != null) {
setTipText(R.string.tip_cannot_start_recording);
return;
}
createRecordFile();
setTipText(R.string.tip_start_recording);
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setOutputFile(mFileName);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioSamplingRate(mSamplingRate);//设置采样率
try {
mRecorder.prepare();
} catch (Exception e) {
// TODO: handle exception
Log.i(TAG, "prepare() failed!");
}
mRecorder.start();//开始录音
}
private void stopRecording() {
if (mRecorder == null) {
setTipText(R.string.tip_cannot_stop_recording);
return;
}
setTipText(R.string.tip_stop_recording);
if (mRecorder != null) {
mRecorder.release();
mRecorder = null;
}
}
三、录音文件播放
Android播放声音同样有三种方式:
- SoundPool播放音频:SoundPool支持多个音频文件同时播放(组合音频也是有上限的),延时短,比较适合短促、密集的场景,是游戏开发中音效播放的福音。SoundPool只适合短促的音效播放,不能用于长时间的音乐播放
-
AudioTrack:AudioTrack是管理和播放单一音频资源的类。它用于PCM音频流的回放,实现方式是通过write方法把数据push到AudioTrack对象
-
MediaPlayer:MediaPlayer确实强大而且方便使用,提供了对音频播放的各种控制,支持AAC、AMR、FLAC、MP3、MIDI、OGG、PCM等格式
这里,使用MediaPlayer进行录音的播放。该方法使用简单方便,只需要几句代码便可完成录音放播。
播放按键监听:
class PlayRecordingListener implements View.OnClickListener {
@Override
public void onClick(View view) {
togglePlayRecording();
}
}
private void togglePlayRecording() {
if (TextUtils.isEmpty(mFileName)) {
setTipText(R.string.tip_no_file_to_play);
return;
}
if (mRecorder != null) {
setTipText(R.string.tip_cannot_start_recording);
return;
}
if (mPlayer == null) {
stopBluetoothSCO();
playRecording();
} else {
startBluetoothSCO();
releaseRecording();
}
setPlayRecordingStatus();
}
播放音频:
private void playRecording() {
if (mPlayer != null) {
setTipText(R.string.tip_cannot_start_sound_recording);
return;
}
setTipText(R.string.tip_start_sound_recording);
mPlayer = new MediaPlayer();
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
setTipText(R.string.tip_complete_sound_recording);
stopPlaySoundRecording();
}
});
try {
mPlayer.setDataSource(mFileName);
mPlayer.prepare();
mPlayer.start();
} catch (NullPointerException e) {
setTipText(R.string.tip_no_file_to_play);
} catch (IOException e) {
Log.e(TAG, "播放失败");
}
}