Android录音与播放测试



一、引言

在蓝牙语音交互项目开发中,经常需要验证语音的采集情况,用来判断是录的音有问题还是上传有问题,还是解析有问题。所以特整理了一个支持蓝牙或手机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, "播放失败");
        }
    }