4
votes

I have a bluetooth headset (which can play stereo music) connected to my android phone (Android 4.4.3). Now I want my code to play a stereo music and record audio from that headset, both at high sampling rates (44100). I followed the solutions in the following posts.

How to record sound using bluetooth headset

Capture Audio through Bluetooth Headset paired with Android Device

My basic code looks like this.

Permissions:

android.permission.WRITE_EXTERNAL_STORAGE
android.permission.RECORD_AUDIO
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.BROADCAST_STICKY
android.permission.BLUETOOTH

Code to turn on Bluetooth Sco:

m_amAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

public void turnOnBluetooth() {
    final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
              int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
              if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) {
                    System.err.println("bluetooth connected");
                    unregisterReceiver(this);
              } else if (AudioManager.SCO_AUDIO_STATE_DISCONNECTED == state) {
                    System.err.println("bluetooth disconnected");
              }
           }
        };

    registerReceiver(broadcastReceiver, new IntentFilter(
            AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));

    try {
        if (m_amAudioManager.isBluetoothScoAvailableOffCall()) {
            if (m_amAudioManager.isBluetoothScoOn()) {
                m_amAudioManager.stopBluetoothSco();
                m_amAudioManager.startBluetoothSco();
                System.err.println("Bluetooth SCO On!");
            } else {
                System.err.println("Bluetooth Sco Off!");
                m_amAudioManager.startBluetoothSco();
            }

        } else {
            System.err.println("Bluetooth SCO not available");
        }
    } catch (Exception e) {
        System.err.println("sco elsepart startBluetoothSCO " + e);
        unregisterReceiver(broadcastReceiver);
    }
}

Code to play a stereo music:

public void playMusic(){
    this.mediaPlayer = new MediaPlayer();
    this.mediaPlayer
            .setOnCompletionListener(new OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    mp.release();
                }
            });
    this.mediaPlayer.setDataSource(Environment.getExternalStorageDirectory().
         getAbsolutePath()+ "/"+ folderName + "/stereo.wav");
    // change type to STREAM_VOICE_CALL can partly solve the problem 
    // but reduces the quality of the music, which is critical in my case
    this.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    this.mediaPlayer.prepare();
    this.mediaPlayer.start();
}

Code to record audio:

public void recordAudio() {
    AudioRecorder recorder = new AudioRecord(
            audioSource,         // MediaRecorder.AudioSource.MIC
            RECORDER_SAMPLERATE, // 44100
            RECORDER_CHANNELS,   // AudioFormat.CHANNEL_IN_MONO
            RECORDER_AUDIO_ENCODING, // AudioFormat.ENCODING_PCM_16BIT 
            bufferSize           // obtained by AudioRecord.getMinBufferSize()
    );
    int i = recorder.getState();
    if (i == 1)
        recorder.startRecording();
    // then read bytes from the recorder
}

How here are the problems.

Case 1: if I call the following sequence

turnOnBluetooth();
playMusic();
recordAudio();

The music plays through the phone's speaker rather than the bluetooth headset. The recorder can record sound from the bluetooth headset's mic but at very low sampling rate (8kHz).

Case 2: if I do not call turnOnBluetooth(), i.e., execute the following sequence

playMusic();
recordAudio();

The music plays through the bluetooth headset now, but the recorder only records audio from the phone's built-in mic.

I also tried to change the mode of the AudioManager by

m_amAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);

and set the audio route by

m_amAudioManager.setSpeakerphoneOn(false);
m_amAudioManager.setBluetoothScoOn(true);

The result is the same to case 1. And if I set

m_amAudioManager.setBluetoothScoOn(false);

It repeats case 2.

I have worked on this for a few days and the above behavior is puzzling me a lot. Have I missed anything in audio settings? Or do I need more sophisticated control with the bluetooth headset's settings? Thanks for reading this and any suggestion is welcome. Thank you!

1

1 Answers

1
votes

This may be too late, but here it is my solution.

you need to setup the mediarecorder as follows

mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
mediaRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB);
mediaRecorder.setOutputFile(AudioSavePathInDevice);

in order to play on the bluetooth headset you need to turn on bluetoothsco and turnoff speakers

audioManager.setBluetoothScoOn(true);
audioManager.startBluetoothSco();

audioManager.setSpeakerphoneOn(false);
audioManager.setMode(audioManager.MODE_NORMAL);

for recording at other frequency that 8KHz you need to use the AudioRecorder class

new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT, audioBufferSize);