3

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

Pozzo Apps
  • 1,819
  • 2
  • 20
  • 29
user3845389
  • 31
  • 1
  • 2
  • Hi, I encounter this problem too after so long time, can you tell me how to resolve it? Thanks. – mmm2006 Mar 22 '17 at 16:00

2 Answers2

4

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

Community
  • 1
  • 1
Wolfish
  • 930
  • 2
  • 7
  • 33
  • These permissions are already set, they are reported in the ending of my post (together with BLUETOOTH, BLUETOOTH_ADMIN, INTERNET, WRITE_EXTERNAL_STORAGE). Search my original post for "The Manifest asks permissions for:", thanks for your interesting though – user3845389 Jul 16 '14 at 15:07
  • 1
    @user3845389 Oh, sorry, I missed that. Too busy reading below about what your log contained. I have some sample code for B/T recording too, I'll post it as an edit. – Wolfish Jul 16 '14 at 15:10
  • Thanks for the snippet, it looks pretty much similar to the one in my post. Unfortunately, everything looks "Ok" (the Intent is called and the connection is on, otherwise we wouldn't see the "not null" message referring to the AudioRecord recorder), but the app will simply capture the audio from the device's own mic and not from the BT paired device...dunno why! Looks much an issue with the way I used the AudioRecord recorder maybe? – user3845389 Jul 16 '14 at 15:28
  • 1
    @user3845389 Wait, could you use `REMOTE_SUBMIX` in place of `MIC`? http://developer.android.com/reference/android/media/MediaRecorder.AudioSource.html#REMOTE_SUBMIX – Wolfish Jul 16 '14 at 15:40
  • 1
    @user3845389 Actually, now that I'm doing some research, I keep finding links to this [google post](https://code.google.com/p/android/issues/detail?id=60323). Maybe recording from bluetooth can't be done? – Wolfish Jul 16 '14 at 15:50
  • I've tried REMOTE_SUBMIX and VOICE_UPLINK, but these end up in LogCat exceptions: Error creating AudioRecord instance: initialization check failed, Error code -20 when initializating native AudioRecord object; and java.lang.RuntimeException: Error receiving broadcast Intent. MIC and DEFAULT won't get to exceptions but won't work with BT! – user3845389 Jul 16 '14 at 15:55
  • I have read the google post content. However, I'm trying to capture audio through BT not during a phone call, I need to capture it _out_ of a phone call, so I thought that the code response: "Can BT record from mic? true" was quite promising. – user3845389 Jul 17 '14 at 10:09
  • 1
    @user3845389 I get the context of when you're recording, I was just considering the fact that the code to do so simply might not exist based on that post. However, there is no evidence to support either yes or no. As for that code response, the impression I get from that is using an external device to record from the phone's mic, not vice versa. – Wolfish Jul 17 '14 at 13:14
  • 1
    @user3845389 additionally, I'm reading up on some apps that apparently already do what you're trying to accomplish, and apparently they don't work on every device. Maybe that's the problem you have? Unsupported hardware? – Wolfish Jul 17 '14 at 13:17
-2

code to voice recording from bluetooth headset

public class Recording {

static int count = 0;
static String Shared;
static String bFlag;
public static int TIMEOUT = 5000;
public static int COUNTDOWN_INTERVAL = 1000;
static Context context;

public static void checkAndRecord(Context context,
        OnBluetoothRecording BluetoothRecording, boolean resume) {

    // Check bluetooth flag And Bluetooth is ON or OFF
    if (getBluetoothFlag(context) && isBluetoothON()) {

        // Check for bluetooth and Record
        startBluetoothRecording(BluetoothRecording, resume, context);

    } else {

        // If Bluetooth is OFF Show Toast else Dont Show
        if (getBluetoothFlag(context) && !isBluetoothON()) {
            // false because recording not started
            Toast.makeText(context,
                    "Bluetooth is OFF. Recording from Phone MIC.",
                    Toast.LENGTH_SHORT).show();
            BluetoothRecording.onStartRecording(resume, false);
        } else {
            // false because recording not started
            BluetoothRecording.onStartRecording(resume, false);
        }
    }

}

private static void startBluetoothRecording(
        final OnBluetoothRecording BluetoothRecording,
        final boolean resume, Context context) {
    // TODO Auto-generated method stub

    final int MAX_ATTEPTS_TO_CONNECT = 3;
    final AudioManager audioManager = (AudioManager) context
            .getSystemService(Context.AUDIO_SERVICE);

    final CountDownTimer timer = getTimer(BluetoothRecording, audioManager,
            resume);

    context.registerReceiver(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) {
                // cancel Timer
                timer.cancel();
                context.unregisterReceiver(this);

                // pass through and true because
                // recording from bluetooth so set 8000kHz

                BluetoothRecording.onStartRecording(resume, true);

            } else if (AudioManager.SCO_AUDIO_STATE_DISCONNECTED == state) {
                if (count > MAX_ATTEPTS_TO_CONNECT) {
                    context.unregisterReceiver(this);
                    // Stop BluetoothSCO
                    audioManager.stopBluetoothSco();
                    // reset Counter
                    count = 0;
                    // stop timer
                    timer.cancel();
                    // false because still recording not started
                    BluetoothRecording.onStartRecording(resume, false);
                } else {
                    // Increment Disconnect state Count
                    count++;

                }
            }

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

    // Start the timer
    timer.start();
    audioManager.startBluetoothSco();

}

// set the Timeout
private static CountDownTimer getTimer(
        final OnBluetoothRecording BluetoothRecording,
        final AudioManager audioManager, final boolean resume) {
    // TODO Auto-generated method stub
    return new CountDownTimer(TIMEOUT, COUNTDOWN_INTERVAL) {

        @Override
        public void onTick(long millisUntilFinished) {
            // Do Nothing

        }

        @Override
        public void onFinish() {

            // stopBluetoothSCO() and start Normal Recording
            audioManager.stopBluetoothSco();

            // false because recording button is already clicked but still
            // not recording.
            BluetoothRecording.onStartRecording(resume, false);
        }
    };
}

// Return's the bluetooth state
private static boolean isBluetoothON() {

    BluetoothAdapter bluetoothAdapter = BluetoothAdapter
            .getDefaultAdapter();
    return bluetoothAdapter.isEnabled();
}

// Return's the bluetoothFlag state
private static boolean getBluetoothFlag(Context context) {

    // shared pref
    SharedPreferences sp = context.getSharedPreferences(Shared,
            Context.MODE_PRIVATE);
    return sp.getBoolean(bFlag, false);

}

}
Shivaraj Patil
  • 8,134
  • 4
  • 27
  • 55