Capture Audio through Bluetooth Headset paired with Android Device

user3845389 picture user3845389 · Jul 16, 2014 · Viewed 8.6k times · Source

I am trying to capture audio from a Bluetooth Headset paired with an Android Device.

Following is the relevant code:

Intent in=null;
final int bufferSize=BufferElements2Rec*BytesPerElement;

final BroadcastReceiver brr=new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context,Intent intent)
    {
        int state=intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,-1);
        Log.d(labelApp,"Audio SCO State = "+state);
        if(AudioManager.SCO_AUDIO_STATE_CONNECTED==state)
        {
            Log.d(labelApp,"Entered and Starting Recording");
            //recorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT,
            //        RECORDER_SAMPLERATE, RECORDER_CHANNELS,
            //        RECORDER_AUDIO_ENCODING, bufferSize);
            recorder = new AudioRecord(android.media.MediaRecorder.AudioSource.MIC,
                RECORDER_SAMPLERATE, RECORDER_CHANNELS,
                RECORDER_AUDIO_ENCODING, bufferSize);
            if(recorder==null)
            {
                Log.d(labelApp,"null");
            }
            else
            {
                Log.d(labelApp,"not null");
            }
            recorder.startRecording();
            recordingThread=new Thread(new Runnable()
            {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    writeAudioDataToFile();
                }

            },"AudioRecorder Thread");
            recordingThread.start();

            Log.d(labelApp,"Launched Recording Thread");
        }
    }
};

try
{
    Log.d(labelApp,"Initializing BT");
    am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
    //am.setMode(AudioManager.MODE_IN_CALL);

    //in=registerReceiver(brr,new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
    //in=registerReceiver(brr,new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
    Log.d(labelApp,"Starting Bluetooth");
    am.setStreamSolo(AudioManager.MODE_IN_CALL, true);
    am.setBluetoothScoOn(true);
    am.setMode(AudioManager.MODE_IN_CALL);
    am.startBluetoothSco();
    Log.d(labelApp,"Can BT record from mic? "+am.isBluetoothScoAvailableOffCall());
    //in=registerReceiver(brr,new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
    in=registerReceiver(brr,new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
    // The following line makes the audio go to hell
    //am.setMode(AudioManager.MODE_IN_CALL);


    //am.setStreamSolo(AudioManager.MODE_IN_CALL, true);
    Log.d(labelApp,"Everything initializated");
    Log.d(labelApp,"Recorder is...");
}
catch(Exception e)
{
    Log.e(labelApp,"exception",e);
    writeStack(e);
}

try
{
        Log.d(labelApp,"Initializing BT");
        am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
        //am.setMode(AudioManager.MODE_IN_CALL);

        //in=registerReceiver(brr,new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
        //in=registerReceiver(brr,new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
        Log.d(labelApp,"Starting Bluetooth");
        am.setStreamSolo(AudioManager.MODE_IN_CALL, true);
        am.setBluetoothScoOn(true);
        am.setMode(AudioManager.MODE_IN_CALL);
        am.startBluetoothSco();
        Log.d(labelApp,"Can BT record from mic? "+am.isBluetoothScoAvailableOffCall());
        //in=registerReceiver(brr,new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
        in=registerReceiver(brr,new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
        // The following line makes the audio go to hell
        //am.setMode(AudioManager.MODE_IN_CALL);


        //am.setStreamSolo(AudioManager.MODE_IN_CALL, true);
        Log.d(labelApp,"Everything initializated");
        Log.d(labelApp,"Recorder is...");
}
catch(Exception e)
{
        Log.e(labelApp,"exception",e);
        writeStack(e);
}

The Manifest asks permissions for:

  • WRITE_EXTERNAL_STORAGE
  • RECORD_AUDIO
  • INTERNET
  • MODIFY_AUDIO_SETTINGS
  • BROADCAST_STICKY
  • BLUETOOTH
  • BLUETOOTH_ADMIN

The typical Filtered LogCat output per the app is:

  1. Initializing BT
  2. Starting Bluetooth
  3. Can BT record from mic? true
  4. Everything initialized
  5. Recorder is...
  6. Audio SCO State = 2
  7. Audio SCO State = 1
  8. Entered and Starting Recording
  9. not null
  10. Launched Recording Thread

When the am.startBluetoothSco(); is invoked, I can hear a brief noise on the BT device, but then the app simply gets the audio from the Android Device's mic in place of the BT's one.

Any hint on what am I missing/doing wrong?

Thanks in advance for the attention

Answer

Wolfish picture Wolfish · Jul 16, 2014

Most importantly, you need to set
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

in the manifest file. Without it, there will be no error message but the B/T state will refuse to change to connected.

Other relevant permissions include:

<uses-permission android:name="android.permission.RECORD_AUDIO"/> 
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> 
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>

Edit: In addition to the comments, here is some sample code that I have used before:

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

brr(new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
        Log.d(TAG, "Audio SCO state: " + state);

        if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) { 
            /* 
             * Now the connection has been established to the bluetooth device. 
             * Record audio or whatever (on another thread).With AudioRecord you can record with an object created like this:
             * new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
             * AudioFormat.ENCODING_PCM_16BIT, audioBufferSize);
             *
             * After finishing, don't forget to unregister this receiver and
             * to stop the bluetooth connection with am.stopBluetoothSco();
             */
            unregisterReceiver(this);
        }

    }
}, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));

Log.d(TAG, "starting bluetooth");
am.startBluetoothSco();

Credits for this code go to user Stephan