After I use the Android AudioManager to set SCO ON and speakerphone ON, then I return it back to where it started, the media stream will then stay on earpiece instead of the phones speaker. Here is where I make the changes to route a stream through the SCO speakerphone (which all works great)
if (am2.isBluetoothScoAvailableOffCall()) {
am2.startBluetoothSco();
}
if(!am2.isSpeakerphoneOn()){
speakerPhoneWasOn = false;
am2.setSpeakerphoneOn(true);
}
myHash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_VOICE_CALL));
am2.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
Then I try to change it all back after my message is read (I am using TTS)
if(SMSstream == 1){
if (am2.isBluetoothScoAvailableOffCall()) {
am2.stopBluetoothSco();
}
if(!speakerPhoneWasOn){
am2.setSpeakerphoneOn(false);
}
}
else{
}
am2.setMode(OLD_AUDIO_MODE);
am2.abandonAudioFocus(null);
But it stays with the phone earpiece instead of the speaker until I reboot. I saw a few post that had the opposite problem but none with this issue. I have a Droid 3 with Android 2.3.4.
You can see the whole project and source code here: http://code.google.com/p/a2dpvolume/
OK, I finally fixed this problem. Its not pretty. There is some sort of bug in the AudioManager I believe. Even after abandoning focus it would leave the device streams in a mess. Unless the last TTS was read over the music stream just before abandoning focus, it would route streams wrong, mute streams, etc. So, I just have it read a single period if a SMS had been read over any stream except the music stream before abandoning focus. I also cleaned up the order I called things in, and fixed some of the phone mode, etc after reading a TTS. Strangely it seems to work well.
Related
I make a player that should play only through headphones (wired or Bluetooth), but not through the speaker.
If you turn off the headphones during playback, the player automatically pauses.
When (Bluetooth) headphones are connected back and I get the ACTION_AUDIO_STATE_CHANGED event, I resume playback. But for a few seconds, the sound goes through the builtin speaker and only then goes to the headphones. I think to fix this by setting the device to output. I can get the device id from AudioManager. But how to pass it to OpenSL ES?
Or maybe there is a way to completely prevent playback through the builtin speaker?
Or another way to solve this problem?
P.S. Santa, where is you when you are so needed? Help me, please!
Maybe you could listen to this other type of events with a BroadcastReceiver:
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED -> {
state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1)
when (state) {
BluetoothA2dp.STATE_CONNECTED -> {
// Do something
}
BluetoothA2dp.STATE_DISCONNECTED -> {
// Do something else
}
}
}
I am trying to record both Uplink and Downlink voice using Android. Regardless the law and everything, i am already aware, so please do not put comments related to the law.
The code below works fine, except when i mute the microphone, it wont record the downlink voice.
I am using Android 8.1. I've tried using a third party app called ACR on the same device, and it works fine, while i am muted, it still records the downlink voice.
val audioManager = applicationContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val maximumVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL)
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, maximumVolume, 0)
val audioSource = MediaRecorder.AudioSource.MIC
val mediaRecorder = MediaRecorder()
mediaRecorder.apply {
setAudioSource(audioSource)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
setAudioChannels(audioChannels)
setAudioSamplingRate(audioSamplingRate)
setAudioEncodingBitRate(audioEncodingBitRate)
setOutputFile(path)
prepare()
start()
This is not an issue. You set the MediaRecorder to use MIC as input, so if you MUTE the microphone it's obliviously that the input signat is lost/muted. When you use "downlink" word I expected to see a different input source as VOICECALL or DOWNLINK instead of MIC. Trying to record a voicecall using the MIC it's wrong in my opinion because: (1) you have to set max volume to speaker and redirect the voicecall through it (2) while recording a voicecall from the MIC the caller hears ALL what it happens around your device and all what you're saying to other people (3) this method records much noise and echoes. The right way is to record from VOICECALL but most of new devices (using newer Android version) prevents to record from this source and allows it only at System Apps. ACR uses a workaround by calling hidden API methods, but this method could stop stop work at any time due to Android updates.
I am trying to identify how to route a very short audio stream (a notification) to a bluetooth headphone that is already paired with the device, while the device is ringing.
When I play any audio at any time, it is routed to the bluetooth device, no problem.
But if I try to start playing the audio when receiving an android.intent.action.PHONE_STATE, in RINGING state, the audio is not routed as expected.
I can see that the AudioManager's setBluetoothA2dpOn method has became deprecated, but I actually tried it but is seems has no effect.
I have tried the MediaRouter object, but I can see that MediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_AUDIO) points to the RouteInfo of the Bluetooth device while the device is ringing, and the AudioManager.isBluetoothA2dpOn is true.
So, can any one tell me why the audio route is like this in the ringing moment? is there any way to force the audio to be routed to the Bluetooth device in such case?
[UPDATED]
I have tried again today and I have discovered something that may be the cause of the problem.
I have created a BroadcaseReceiver to detect the change in the android.intent.action.PHONE_STATE. if an intent is received and the state is currently ringing, check for AudioManager's mode and you will find it is MODE_NORMAL. but few seconds later the phone will start actually ringing and the mode is going to be changed into MODE_RINGTONE. trying to manually set the mode using the method setMode(AudioManager.MODE_NORMAL) is useless then, the state remains MODE_RINGTONE even after setting it to MODE_NORMAL.
Now, I think the cause of the problem is that in the MODE_RINGTONE mode, all the streams are directed to the phone speaker and here there is no way offered by the android system to change the mode.
I think the Media player realease the bluetooth connection when the phone is ringing. You can try to obtain the bluetooth audio connection and see if Media player now play through your obtained connection. You can use my class at my answer Using the Android RecognizerIntent with a bluetooth headset and see if it works. The audio in the class is Sco only.
As stated in the JavaDoc for the StartBluetoothSco method:
Note that the phone application always has the priority on the usage of the SCO connection for telephony. If this method is called while the phone is in call it will be ignored. Similarly, if a call is received or sent while an application is using the SCO connection, the connection will be lost for the application and NOT returned automatically when the call ends.`
I tried to start sco then play a music clip in normal mode, it played to the Bluetooth headset without problems, although I couldnot stop the microphone that caused the input stream plays to the headset. I then tried to call my target phone from another phone while the music is still playing, I found that the stream has got redirected automatically to the phone speaker. After the ringing mode is finished, the stream did not get redirected again to the Bluetooth headset and I think that behavior is normal according to what is stated in the JavaDoc above.
My guessing is that Android tries to protect the ringing and in-call modes as possible in order not to allow any unwanted interference from applications. In other words, when in ringing mode then no sound is going to be played to the headset until the call is accepted, and you cannot even change the AudioManager mode from ringing to another mode, your call for mode setter will be ignored.
I have tried the AudioTrack instead of MediaPlayer, but that makes no difference.
I have then tried the TextToSpeech engine like this:
in the main activity, initialize on create:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textToSpeech=new TextToSpeech(this, this);
textToSpeech.setLanguage(Locale.US);
textToSpeech.addEarcon("[wwww]", "XXXXXXXXXXXXXXXXXXXXXXX", R.raw.a);
}
in the broadcast receiver class when rining starting the Bluetooth utility class and adding the below to the onScoAudioConnected method
textToSpeech.playEarcon("[wwww]", TextToSpeech.QUEUE_FLUSH, null);
This did not work too.
I have tested my app: it starts playing a song by getting incoming call on external speaker with enough volume to make person on another side to listen what we play on our side.
But when I answer a call, the playing song stops. I want the song to be playing during call so the person on the other side can hear it.
I would appreciate any suggestion from anyone if they has also faced this problem or know a solution.
That's because while you're in a call, media playback routing will follow the voice call routing. And the default output routing for voice calls if you don't have any accessories attached is to use the earpiece.
You could try waiting for the phone state to switch to MODE_IN_CALL, and then use setSpeakerPhoneOn to change the output routing to use the loudspeaker. Note that this will also route the voice call audio to the loudspeaker, not just the media audio.
EDIT: You could try using the stream type ENFORCED_AUDIBLE (integer value 7) for your media playback. However, it might not work across all devices / all Android versions.
My Android tutorial states that I can explicitly tell the TTS engine which stream to use:
For music playback:
params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_MUSIC));
And for phone calls:
params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_VOICE_CALL));
My understanding is that audio routing to a Bluetooth headset works such that STREAM_MUSIC goes to A2DP (aka "media audio" in Android Bluetooth settings) and STREAM_VOICE_CALL goes to HSP (aka "phone audio" in Android Bluetooth settings).
But regardless whether I use STREAM_MUSIC or STREAM_VOICE_CALL in my little application, the audio always goes for some reason to A2DP.
What am I am doing wrong? Is there a way to route TTS output to headset's HSP profile?
I got this working for the most part, on most devices. Here is the part that starts the TTS on the voice call stream using Bluetooth SCO instead of A2DP.
if (mTtsReady) {
myHash = new HashMap<String, String>();
myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "A2DP_Vol");
OLD_AUDIO_MODE = am2.getMode();
if(SMSstream == 1){
if (am2.isBluetoothScoAvailableOffCall()) {
am2.startBluetoothSco();
}
if(!am2.isSpeakerphoneOn()){
speakerPhoneWasOn = false;
am2.setSpeakerphoneOn(true);
}
myHash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_VOICE_CALL));
am2.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
else{
am2.requestAudioFocus(null, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
myHash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_MUSIC));
}
new CountDownTimer(SMS_DELAY, SMS_DELAY/2) {
#Override
public void onFinish() {
try {
mTts.speak(str, TextToSpeech.QUEUE_ADD,
myHash);
} catch (Exception e) {
Toast.makeText(application,
R.string.TTSNotReady,
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
#Override
public void onTick(long arg0) {
}
}.start();
}
Now I just have a problem getting the stream to revert back when done. It all works fine to read TTS. It will pause any music, play the TTS, and then resume music fine. However, when I exit the app later the stream for media now plays through the phone earpiece until I reboot. I posted that question here: Audio stream stays on earpiece after using AudioManager
You can see my whole project here: http://code.google.com/p/a2dpvolume/
If your headset is compatible with the a2dp profile, then using AudioManager.STREAM_MUSIC and hence playing audio through the stream should get the job done.
I would also add, that if you are currently in a call, and you play audio through the voice stream, then any headset (a2dp or otherwise) can hear the audio. Unfortunately, you need to be in a call.
Unfortunately I have found that setting the mode to MODE_IN_CALL does nothing.
To sum it up:
If all that you are trying to do is play music (when not in a call), then use the AudioManager.STREAM_MUSIC and if the headset is A2DP compatible, then it will hear the music.
Also, take a look at AudioManager.isBluetoothA2dpOn(), to make sure that the system thinks that your headeset is plugged in.
Yes it is possible to play the TTS (text-to-speech) output through a non A2DP headset like jroal says by using SCO.
Use AudioManager startBluetoothSco to enable SCO. Then listen for AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED broadcast with the EXTRA_SCO_AUDIO_STATE set to SCO_AUDIO_STATE_CONNECTED.
If you now do TextToSpeech to the STREAM_VOICE_CALL stream, it will end up in the headset (even the cheap non A2DP devices).