How to route MediaPlayer1 to wired headset and MediaPlayer2 to BT headset? - android

I'm trying to play two files at the same time and route one to wired headset and the other to the BT headset. Is this even possible? Any ideas how I can achieve this? I'm targeting OS 2.3 and greater.

You can try creating two MediaPlayers with different stream types:
btPlayer = new MediaPlayer(...);
wiredPlayer = new MediaPlayer(...);
...
btPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
wiredPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
...
Of cause you'll need to put extra code to redirect btPlayer to bluetooth headset. For this you'll need to use startBluetoothSco() and setBluetoothScoOn().
Also note, that audio can be redirected to bluetooth headset only on AudioManager.STREAM_VOICE_CALL. But if you are using a2dp bluetooth device, you can redirect audio to this device in AudioManager.STREAM_MUSIC stream as well.

Related

TextToSpeech audio routed to phone speaker instead of bluetooth headset

I have an app that uses android.speech.tts.TextToSpeech.speak() to readout messages.
The messages are only supposed to be readout to a connected bluetooth headset. So before I call the speak method I check AudioManager.isBluetoothA2dpOn();
Speak method implementation:
ttsOptions.putInt(TextToSpeech.Engine.KEY_PARAM_STREAM, AudioManager.STREAM_VOICE_CALL);
ttsOptions.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, 1.0f);//MAX
tts.speak(text, TextToSpeech.QUEUE_ADD, ttsOptions, utteranceID);
This behaviour was working as expected for about a year but lately it fails on Samsung devices only in a way that it routes the audio to the phone's speaker and the message are read out loud.
I managed to fix the routing issue with a hack of:
1.AudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
2.mAudioManager.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN);
3.mAudioManager.startBluetoothSco();
4.Play a brief audio file
If I do these 4 steps the audio routing is fixed and routed to the bluetooth headset.
Questions:
Why do Samsung devices route the audio to the phone speaker instead of the bluetooth device?
Any reliable way to check whether the audio will be routed to the headset instead of the AudioManager.isBluetoothA2dpOn();?

How to play audio through Bluetooth speaker even when headset is plugged in?

I have my phone connected to a Bluetooth speaker and the headphones plugged in. Now I'd like to play audio through the Bluetooth speaker.
When I set the audio stream to AudioManager.STREAM_MUSIC it just plays over the headphones.
It doesn't matter if it plays on the headphones as well but I need it to play on the Bluetooth speaker.
How is this possible? The app SoundAbout manages to do that so there must be a way.
EDIT: When I plug in the headphones and only afterwards connect to the Bluetooth speakers all audio plays through the Bluetooth speakers which I want. But I can't expect the user to find that out and before having to show them a complicated message I'd rather find out a way to make the sound always play through BT speakers when connected to some.
Thanks
(Note this is not the same question as this: How to Play audio through speaker even when headset is plugged in?
I want it to play on Bluetooth speakers, not on the integrated speaker of the phone.)
Solution
Suppose you already tested STREAM_RING on your new instance of media player and not directly setting stream type, and it didn't work out, You need a correct profile for your bluetooth device.
Take a look at this article
Read the "Implementing HAL" section, there is alot of source for different profile that you may be able to use.
There is also an easy solution which is to change your device profile to HEADSET in your getServiceConnected() method, it will turn into a Stay connected device but the output will become mono! As I recall, Which is a shame for speakers, A2DP also may not be supported in some hardwares and still interrupted by wire headsets.
I suggest to create a new profile and use it, a little bit tricky working with HAL but will worth it,
Sorry that I can not provide a source code for you at the moment.
If you have your routing logic within your application then based on that you can decide on which output the audio to be played.
I have a test app written for the exact purpose.
My Github Link
You can also route audio as you want based on a requirement.You can refer this github link for routing
Bluetooth connection may work with below state is true .
After receive BluetoothA2dp.STATE_CONNECTED, you can play music as normal.
Java Code Examples for android.bluetooth.BluetoothA2dp.STATE_CONNECTED
public BluetoothHandsfree(Context context, CallManager cm) {
mCM = cm;
mContext = context;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
boolean bluetoothCapable = (adapter != null);
mHeadset = null; // nothing connected yet
mA2dp = new BluetoothA2dp(mContext);
mA2dpState = BluetoothA2dp.STATE_DISCONNECTED;
mA2dpDevice = null;
mA2dpSuspended = false;
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mStartCallWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
TAG + ":StartCall");
mStartCallWakeLock.setReferenceCounted(false);
mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
TAG + ":VoiceRecognition");
mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
mLocalBrsf = BRSF_AG_THREE_WAY_CALLING |
BRSF_AG_EC_NR |
BRSF_AG_REJECT_CALL |
BRSF_AG_ENHANCED_CALL_STATUS;
if (sVoiceCommandIntent == null) {
sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (mContext.getPackageManager().resolveActivity(sVoiceCommandIntent, 0) != null &&
BluetoothHeadset.isBluetoothVoiceDialingEnabled(mContext)) {
mLocalBrsf |= BRSF_AG_VOICE_RECOG;
}
mBluetoothPhoneState = new BluetoothPhoneState();
mUserWantsAudio = true;
mPhonebook = new BluetoothAtPhonebook(mContext, this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
cdmaSetSecondCallState(false);
if (bluetoothCapable) {
resetAtState();
}
}
please find below links : with sample codes it may help you.
Java Code Examples for android.bluetooth.BluetoothHeadset
Programmatically connect to paired Bluetooth speaker and play audio
You need to instantiate a new object of class MediaPlayer and use the following method on it
amediaplayer.setAudioStreamType(AudioManager.STREAM_RING)
Do not forget to check authorization to use bluetooth, you are unable to send anything to speaker via bluetooth without user privilege as you know.
Audiomanager overrides and routes audio to the latest connected device(either wired headset or bluetooth headset). In android, we do not have any option to override this setting unless it is a system app and route the audio wherever we wish to route. However, you can use reflection apis and override this setting. Audiomanager suspends bluetooth connection route(if already connected)if wired headset is connected and vice versa. You can look at the code here.
Hence using reflection apis you can toggle bluetooth audio route by invoking this method.

Android device as a receiver for A2DP profile

Basically, what I am trying to do right now is use an android device as an A2DP receiver and when pairing established, android plays sound that is received from a transmitter. I am worrying that if I use STP profile, it may cause delay of streaming. So, I want to use A2DP but is this possible to use an android device as a receiver of A2DP? and how to do it?
Since Android L the BlueDriod stack does support A2DP sink, but it is disabled by default.
To enable it do the following:
/* Enable bluetooth av sink. */
#define BTA_AV_SINK_INCLUDED TRUE
in /external/bluetooth/bluedroid/include/bt_target.h.
This enables sink support in the bluetooth stack.
Also you have to do this change:
<!-- Enable sink support. -->
<bool name="profile_supported_a2dp_sink">true</bool>
in /packages/apps/Bluetooth/res/values/config.xml. This enables the particular UI.
Now you can pair your devices and start streaming. Unfortunately you will hear no sound although you'll receive the packets. The reason is that there is no audio route for A2DP sink. In the commit message of this patch https://android-review.googlesource.com/#/c/98161/ you can find a sample implementation on how to fix this.
Here is a list of these changes:
https://android-review.googlesource.com/#/c/97832/
https://android-review.googlesource.com/#/c/97853/
https://android-review.googlesource.com/#/c/97833/
https://android-review.googlesource.com/#/c/98130/
https://android-review.googlesource.com/#/c/98131/
Yes. It is possible. I have done it in JB.
Android internally uses "Bluedroid" stack from Broadcomm for Bluetooth. Previously this stack did not have support for A2DP Sink Role (Which you mentioned as receiver). From Lollipop release, the A2DP Sink role profile has been added in Bluedroid.
But, it is not enabled to be used by framework/upper layer (Application).
You need to make changes in framework to enable it or 'use' it.
You may refer to the following files and relevant files in Android source code to enable it.
audio.h - put a new audio source
audio_policy.conf - put a new input source for a2dp 'inputs'
AudioManager.java
AudioPolicyManagerBase.cpp
AudioService.java
AudioSystem.java
BluetoothA2dp.java
MediaRecorder.java
A2DPStateMachine.java
etc. and implement it (this file list is not comprehensive, but you can figure it out if you have experience in relevant field).
When any stream connection is established, you will get callback in a2dp state machine and from there you have to start a thread to read the decoded PCM bytes from the 'new' audio source and send it to your media player.
SBC codec to PCM decoding will be done at the 'bluedroid' sbc decoder layer.
Build it and flash it to your phone and enjoy music.
EDIT:
Also, you may have make changes in A2DP SDP record in Bluedroid stack to advertise the A2DP Sink role.
You may-not be able to do it manually between 2 phones because to stream one device needs to be A2DP sink and other other A2DP source, Phones are typically only Source devices (Source of the stream that can stream to sink devices) , Sinks are Headsets or Bluetooth speakers.

How to capture or reroute navigation voice stream

There is a problem when using Google Navigation on Android with a Bluetooth device. Navigation sends the audio for the turn-by-turn instructions over the A2DP stream. I have a Motorola T605 Bluetooth car kit and it supports HFP and A2DP. I often just listen to the old fashioned car radio though. In this scenario I never hear turn-by-turn directions because my live A2DP stream is being sent to the radio AUX input but I am listening to the radio instead. My app has a feature where it reads out SMS messages to me. I have it set up so you can pick the stream you want to use for this. I prefer using AudioManager.STREAM_VOICE_CALL since it uses a dedicated separate speaker on the T605.
I have looked into a few ways to handle this:
1) Reroute notifications to Bluetooth SCO instead of A2DP. I have not fund a way to do this. I am able to send TTS messages I create over this path and it works great (I have an SMS reader built into my app). I tried activating SCO but the turn-by-turn instructions stream moves to the phone internal speaker instead of the voice call stream like I would prefer.
2) Capture the navigation messages and echo them back over the SCO path. I can't find a way to intercept or capture the navigation stream though. Does Google Navigation use the TTS engine or its own methods to create the voice output? It would be great to just get the directions in a text string since I could easily send that to the TTS engine and route it where I want like I do with SMS strings.
You can see my app source here: http://code.google.com/p/a2dpvolume/
Any thoughts?
Unfortunately I think you're SOOL when it comes to rerouting the navigation messages to BT SCO. I would expect the navigation app to use either the TTS or NOTIFICATION stream type to play the messages, and both of those stream types follow the MEDIA routing strategy (for NOTIFICATIONs that is at least typically true when there's no active voice call).
Although the behavior is up to each vendor to decide, my guess is that you'll find that in most implementations streams that follow the MEDIA routing strategy won't ever be routed to BT SCO, except during an ongoing voice call that is routed to BT SCO.
The best you could do in terms of routing is to force the routing to the loudspeaker, or to "anything but A2DP" (which means wired headset if one is attached, or the loudspeaker otherwise).
Here's how you could do that (I haven't verified that this works on every phone out there):
Class audioSystemClass = Class.forName("android.media.AudioSystem");
Method setForceUse = audioSystemClass.getMethod("setForceUse",
int.class,
int.class);
// 1 == FOR_MEDIA, 10 == FORCE_NO_BT_A2DP (FORCE_SPEAKER would be 1).
setForceUse.invoke(null, 1, 10);

On android how to reroute audio from a2dp

I have a Samsung vibrant and I am connecting to to my car using bluetooth. In the samsung music app there is a button to route the audio to via bluetooth or via phone.
Anyone know how there were able create this functionality. I looked at the sdk and I see
ROUTE_BLUETOOTH_A2DP
This constant is deprecated. Do not
set audio routing directly, use
setSpeakerphoneOn(),
setBluetoothScoOn() methods instead.
Routing audio output to bluetooth A2DP
Constant Value: 16 (0x00000010)
but as you can see it is listed as "deprecated", i see the option for setBluetoothScoOn, but not an equivalent to for setting a2dp on.
My end goal would be to create a widget that allows me to turn on and off outing to the a2dp. So I can turn it on when I want to stream music and turn it off when I want to use navigator, but listen to music or the radio at the same time.
Here's what you are looking for: (not sure if this was available at the time of your question...)
setBluetoothA2dpOn();
But it's also deprecated.
My guess would be to use this instead:
audioManager.startBluetoothSco();
audioManager.setBluetoothScoOn(true);
And to stop routing:
audioManager.setBluetoothScoOn(false);
audioManager.stopBluetoothSco();
audioManager is a instance of the AudioManager.
Here's a reference to AudioManager: AudioManager
Hope this helps, cheers

Categories

Resources