Locus Map - forum

Development => Other features => Topic started by: Matthias1912 on July 27, 2018, 10:04:35

Title: Audio Output over Bluetooth HFP
Post by: Matthias1912 on July 27, 2018, 10:04:35
Hi developer(s),

I do have a problem with the bluetooth audio mode. Since garmin kicked the navigon app for car navigation I like to use locus as a replacement. But my problem is that the audio output need to route over BT HFP so that my car radio can interrupt the radio for the navigation speech output. For now the only working mode is A2DP. But there is now interrupt mode, my car radio need to stay in Bluetooth music mode. In this mode it is impossible to listen music with the car radio when there is now navigation instruction. For that I wrote a little class to show you what I mean or how to solve this problem.

Can you please include the functionality for Bluetooth HFP, so that the navigation route instructions are played by speech output over Bluetooth HFP?

Best regards from Germany
Matthias


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.speech.tts.TextToSpeech;
import java.util.Calendar;
import java.util.Locale;
import static android.content.Context.AUDIO_SERVICE;

public class mySay
{
/* Please define the following in AndroidManifest.xml
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
*/

/* Using this class

Global define: private mySay _say;
in onCreate: _say = new mySay(this);
in functions: _say.say("Das ist eine Testausgabe", Locale.GERMANY, true, 2000, 2500);
*/

private Context context;

private TextToSpeech speech = null;
private boolean speech_init_finished = false;
private boolean speech_busy = false;
private boolean speech_SCO_connected = false;

public mySay(Context c)
{
context = c;

speech = null;
speech_init_finished = false;
speech_busy = false;
speech_SCO_connected = false;

speech = new TextToSpeech(context, new TextToSpeech.OnInitListener()
{
@Override
public void onInit(int status)
{
if (status == TextToSpeech.SUCCESS) speech_init_finished = true;
}
});
}

public boolean isInitialized()
{
return speech_init_finished;
}

public boolean isBusy()
{
return speech_busy;
}

public void close()
{
try { speech.stop(); }
catch (Exception ex) { }

try { speech.shutdown(); }
catch (Exception ex) { }

speech_init_finished = false;
speech = null;
}

private long get_timestamp_ms()
{
Calendar cal_today = Calendar.getInstance();
java.util.Date date_today = cal_today.getTime();
return date_today.getTime();
}

public boolean say(final String text, final Locale locale, final boolean use_HFP, final int audio_delay_HFP_ms, final int SCO_timeout_ms)
{
if (speech_busy) return false;
if (speech == null) return false;
speech_busy = true;

new Thread(new Runnable()
{
public void run()
{
BroadcastReceiver receiver = null;

try
{
if (speech_init_finished != true) throw new Exception();

speech.setSpeechRate(1.0f);
speech.setPitch(1.0f);
int result = speech.setLanguage(locale);
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) throw new Exception();

AudioManager am = (AudioManager)context.getSystemService(AUDIO_SERVICE);
int OLD_AUDIO_MODE = am.getMode();

// Check if HFP (SCO) is available
boolean SCO_available = false;
AudioDeviceInfo[] devices = am.getDevices (AudioManager.GET_DEVICES_OUTPUTS);
for (int i=0; i<devices.length; i++)
{
if (devices[i].getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) SCO_available = true;
}

if (use_HFP && SCO_available)
{
speech_SCO_connected = false;

receiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction() == AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
{
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) speech_SCO_connected = true;
else speech_SCO_connected = false;
}
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
context.registerReceiver(receiver, intentFilter);

am.setMode(AudioManager.MODE_IN_COMMUNICATION);        // MODE_IN_CALL, MODE_IN_COMMUNICATION
am.setBluetoothScoOn(true);
am.startBluetoothSco();
am.setSpeakerphoneOn(false);

am.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);

// wait for SCO Connected with a max. timeout of 2,5s
long timestamp_ms_Start = get_timestamp_ms();
while (speech_SCO_connected == false && (get_timestamp_ms() - timestamp_ms_Start) < SCO_timeout_ms)
{
// wait
try { Thread.sleep(100); }
catch (Exception ex) { }
}

if (speech_SCO_connected == true)
{
// additional delay to prevent missing the initial words
try { Thread.sleep(audio_delay_HFP_ms); }
catch (Exception ex) { }

speech.speak(text, TextToSpeech.QUEUE_FLUSH, null, "UtteranceIdTextToSpeechOutput");
while (speech.isSpeaking()) { } // warten bis das sprechen fertig ist
}

am.stopBluetoothSco();
am.setMode(OLD_AUDIO_MODE);
am.setSpeakerphoneOn(true);
}
else
{
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
speech.speak(text, TextToSpeech.QUEUE_FLUSH, null, "UtteranceIdTextToSpeechOutput");
while (speech.isSpeaking()) { } // warten bis das sprechen fertig ist
}
}
catch (Exception ex) { }

try
{
if (receiver != null) context.unregisterReceiver(receiver);
}
catch (Exception ex) { }

speech_busy = false;
}
}).start();

return true;
}
}
Title: Re: Audio Output over Bluetooth HFP
Post by: Menion on July 31, 2018, 18:59:20
Good day Matthias,
thank you for a very complex part of TTS system source code. I think I got a point of your solution and most probably understand how it works. Anyway, I also have to say that Locus Map is made mainly for hike & bike usage where your solution has no benefit. For use in car, I see the main problem with additional permission, additional settings (something like "Enable support for BT HFP) that will be in app, and also inability to test such feature (because I do not have a car with BT support).
Usually, new ideas are placed on our help.locusmap.eu web site, but such a complicated idea will be lost there between around 1000 interesting, not yet implemented, ideas.
So sorry, I think that such improvement does not belong directly to scope & plans of Locus Map app.
Thanks for understanding.
Title: Re: Audio Output over Bluetooth HFP
Post by: undrwater on January 30, 2019, 22:50:22
I came here to request a similar feature (now "me too!"). I realize this post is old, but I want to make a case for increased navigation features.


I love Locus Map.  I'd like to use it for everything.
Title: Re: Audio Output over Bluetooth HFP
Post by: Menion on February 04, 2019, 18:09:24
Hi,
problem with such specific idea is as I wrote before: main app usage & inability to test such feature.

Anyway, I just made basic simple implementation, but I'll need from you to test it.

How to do it:
Title: Re: Audio Output over Bluetooth HFP
Post by: Matthias1912 on July 08, 2021, 21:16:34
Hi Menion,
sorry for my late answer, I got no email notification from your second post. Last week I ended up with the same problem (BT SCO) again and I found my own post 😉
So short answer, I tested your implementation and TTS over BT SCO works perfect. But you have to release the BT SCO channel after finishing the TTS, otherwise the channel stays open and it does not switch back to car music.
If you modify your code, will this modification be only available in Locus 4 or also to Locus Pro?
Title: Re: Audio Output over Bluetooth HFP
Post by: Menion on July 10, 2021, 22:26:49
Hello Matthias,
3 years waiting on the feedback, funny :).
I'm checking the code I made and for me, it looks all is done correctly. After every call "speak", code almost identical to your 'close()' method is called. My implementation is not identical because it is based on events from registered 'UtteranceProgressListener'. So, unfortunately, I do not know what should be done better, sorry.
Title: Re: Audio Output over Bluetooth HFP
Post by: Viajero Perdido on July 10, 2021, 23:25:01
As an aside, I appreciate how (Locus? Android?) turns down the music audio gently, lets the TTS speak, then turns the music back up, again gently (over maybe half a second).  It's much less intrusive that way.  :)
Title: Re: Audio Output over Bluetooth HFP
Post by: Menion on July 11, 2021, 20:16:53
@Viajero Perdido
thanks. It is always cooperation. Locus Map always informs the system that now wants to say something and also informs about speed being done. And it is on the currently running audio player, to use this information.

In the case of this task we discuss with Matthias, I do not exactly know what am I doing to be trie  :). Anyway, I was heavily inspired by the code directly from Matthias. I make sense, it should work I believe and I have no option to test it ... and it's really a minor task where I do not want to spend too much time. So I'm probably done here ...
Title: Re: Audio Output over Bluetooth HFP
Post by: Matthias1912 on July 12, 2021, 14:22:55
Hi Menion,
Yes, 3 years, shame on me. I checked my code and you are right, there is no SCO release.
I will test my code with my car radio this evening.
I think I know what is missing I will inform you when I finished my test.
Title: Re: Audio Output over Bluetooth HFP
Post by: Matthias1912 on July 12, 2021, 19:04:47
Hi Menion,
so I tested my code. I do not know how much you are using from my sample. I got it working with the following changes.

100ms delay after each "speak" command
and after finishing speaking, I added a 100ms delay too and then I called "am.abandonAudioFocus(null);" to release the Audio Focus.


public boolean say(final String text, final Locale locale, final boolean use_HFP, final int audio_delay_HFP_ms, final int SCO_timeout_ms)
{
    if (speech_busy) return false;
    if (speech == null) return false;
    speech_busy = true;

    Toast.makeText(context, "active", Toast.LENGTH_SHORT).show();

    new Thread(new Runnable()
    {
        public void run()
        {
            BroadcastReceiver receiver = null;

            try
            {
                if (speech_init_finished != true) throw new Exception();

                speech.setSpeechRate(1.0f);
                speech.setPitch(1.0f);
                int result = speech.setLanguage(locale);
                if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) throw new Exception();

                AudioManager am = (AudioManager)context.getSystemService(AUDIO_SERVICE);
                int OLD_AUDIO_MODE = am.getMode();

                // Check if HFP (SCO) is available
                boolean SCO_available = false;
                AudioDeviceInfo[] devices = am.getDevices (AudioManager.GET_DEVICES_OUTPUTS);
                for (int i=0; i<devices.length; i++)
                {
                    if (devices[i].getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) SCO_available = true;
                }

                if (use_HFP && SCO_available)
                {
                    speech_SCO_connected = false;

                    receiver = new BroadcastReceiver()
                    {
                        @Override
                        public void onReceive(Context context, Intent intent)
                        {
                            if (intent.getAction() == AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
                            {
                                int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
                                if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) speech_SCO_connected = true;
                                else speech_SCO_connected = false;
                            }
                        }
                    };
                    IntentFilter intentFilter = new IntentFilter();
                    intentFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
                    context.registerReceiver(receiver, intentFilter);

                    am.setMode(AudioManager.MODE_IN_COMMUNICATION);        // MODE_IN_CALL, MODE_IN_COMMUNICATION
                    am.setBluetoothScoOn(true);
                    am.startBluetoothSco();
                    am.setSpeakerphoneOn(false);

                    am.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);

                    // wait for SCO Connected with a max. timeout of 2,5s
                    long timestamp_ms_Start = get_timestamp_ms();
                    while (speech_SCO_connected == false && (get_timestamp_ms() - timestamp_ms_Start) < SCO_timeout_ms)
                    {
                        // wait
                        try { Thread.sleep(100); }
                        catch (Exception ex) { }
                    }

                    if (speech_SCO_connected == true)
                    {
                        // additional delay to prevent missing the initial words
                        try { Thread.sleep(audio_delay_HFP_ms); }
                        catch (Exception ex) { }

                        speech.speak(text, TextToSpeech.QUEUE_FLUSH, null, "UtteranceIdTextToSpeechOutput");
                        try { Thread.sleep(100); } // delay ms
                        catch (Exception ex) { }
                        while (speech.isSpeaking()) { } // warten bis das sprechen fertig ist
                    }

                    am.stopBluetoothSco();
                    //am.setBluetoothScoOn(false);
                    am.setMode(OLD_AUDIO_MODE);
                    am.setSpeakerphoneOn(true);
                }
                else
                {
                    am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
                    speech.speak(text, TextToSpeech.QUEUE_FLUSH, null, "UtteranceIdTextToSpeechOutput");
                    try { Thread.sleep(100); } // delay ms
                    catch (Exception ex) { }
                    while (speech.isSpeaking()) { } // warten bis das sprechen fertig ist
                }

                try { Thread.sleep(100); } // delay ms
                catch (Exception ex) { }
                am.abandonAudioFocus(null);
            }
            catch (Exception ex) { }

            try
            {
                if (receiver != null) context.unregisterReceiver(receiver);
            }
            catch (Exception ex) { }

            speech_busy = false;

        }
    }).start();

    return true;
}


Can you please change this in locus pro code, so I can test it again? I will not wait another 3 years again, I promise.  ;)
Title: Re: Audio Output over Bluetooth HFP
Post by: Menion on July 12, 2021, 22:52:39
Hello Matthias,

hehe :). Thanks for the updated code. Your Thread.sleep(100); calls are a really little bit ... crazy. As I wrote before, I use a different approach for handling the TTS speech lifecycle ...


                tts.setOnUtteranceProgressListener(object : UtteranceProgressListener() {

                    override fun onStart(utteranceId: String) {
                        onPlaybackStarted(utteranceId)
                    }

                    override fun onDone(utteranceId: String) {
                        onPlaybackDone(utteranceId)
                    }

                    override fun onError(utteranceId: String) {
                        Logger.e("onError($utteranceId)")

                        // disable focus in all cases
                        AudioUtils.disableAudioFocus()
                    }
                })


where 'onPlaybackDone' firstly disable audioFocus and then clear BT SCO references. So all is already there and set up.

I do not know where is the problem, sorry. I'm definitely not a BT expert, but from what I read now, maybe using STREAM_VOICE_CALL as an output stream should help here? Android Javadoc is quite precise here (https://developer.android.com/reference/android/media/AudioManager#startBluetoothSco()).
Title: Re: Audio Output over Bluetooth HFP
Post by: Matthias1912 on July 13, 2021, 00:54:08
Hi Menion,
thanks for your answer. Yes of course, the Thread.sleep method is quick and dirty, but it was only for demonstration. Can you please share some more code to see how you do it and to find the differences. Or maybe you are willing to create a short app?

I want to understand your implementation so that I can help because I need this functionality. I think the setOnUtteranceProgressListener is defined in the onInit part where my speech_init_finished = true is set. But what is behind your onPlaybackStarted and onPlaybackDone functions? How do you start the tts? Where do you set the AudioManager / SCO parameter? Are you using the same tts implementation for the SCO and "normal music channel" parts (switching by a variable like mine: "use_HFP")?
Title: Re: Audio Output over Bluetooth HFP
Post by: Menion on July 13, 2021, 08:07:15
These are good questions. Anyway, I'm sorry, but this task is really far beyond what Locus Map should do & take care of. Output to BT devices is handled by the system and except in special cases, the app should not need to worry about it. You are the first who need something like this and I have so many other important tasks to do .. sorry.

If this functionality does not work correctly, I!ll rather remove it not to confuse other users.

Thanks for understanding Matthias!
Title: Re: Audio Output over Bluetooth HFP
Post by: Matthias1912 on July 13, 2021, 13:48:53
Hi Menion,
sorry but I cannot agree with you. Your SCO code is almost ready, it is now fine tuning.

I think I am not the only one want to have this feature. The most people don't talk to the programmers they just move to another app. Osmand for example has this feature but I'm a locus user since the first hour and I like to see that locus has this feature too.

By the way, I changed my code to an event version. I tried to assume what is in your functions. There is only one sleep command left, and this is only for a timeout condition.



package com.example.mysay;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import java.util.Calendar;
import java.util.Locale;
import static android.content.Context.AUDIO_SERVICE;

public class mySay
{
/* Please define the following in AndroidManifest.xml
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
*/

/* Using this class

Global define: private mySay _say;
in onCreate: _say = new mySay(this);
in onDestroy: _say.TTS_Close();
in functions: _say.say("Das ist eine Testausgabe", Locale.GERMANY, true);
*/

private Context context;

public class TTS_Params_STRUCT
{
public TextToSpeech speech;
public AudioManager am;
public BroadcastReceiver receiver;
public boolean use_SCO;
public boolean SCO_available;
public boolean SCO_connected;
public Locale locale;
public int OLD_AUDIO_MODE;
}
public TTS_Params_STRUCT TTS_Params = new TTS_Params_STRUCT();

private long get_timestamp_ms()
{
Calendar cal_today = Calendar.getInstance();
java.util.Date date_today = cal_today.getTime();
return date_today.getTime();
}

public mySay(Context c)
{
context = c;

onPlaybackInit();
}

private void onPlaybackInit()
{
try
{
TTS_Params.speech = new TextToSpeech(context, new TextToSpeech.OnInitListener()
{
@Override
public void onInit(int status)
{
if (status == TextToSpeech.SUCCESS)
{
TTS_Params.speech.setOnUtteranceProgressListener(new UtteranceProgressListener()
{
@Override
public void onStart(String utteranceId) {
onPlaybackStarted(utteranceId);
}

@Override
public void onDone(String utteranceId) {
onPlaybackDoneOrError(utteranceId);
}

@Override
public void onError(String utteranceId) {
onPlaybackDoneOrError(utteranceId);
}
});

TTS_Params.receiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction() == AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
{
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
switch (state)
{
case AudioManager.SCO_AUDIO_STATE_CONNECTING:
break;
case AudioManager.SCO_AUDIO_STATE_CONNECTED:
TTS_Params.SCO_connected = true;
break;
case AudioManager.SCO_AUDIO_STATE_DISCONNECTED:
case AudioManager.SCO_AUDIO_STATE_ERROR:
TTS_Params.SCO_connected = false;
break;
default:
break;
}
}
}
};

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
context.registerReceiver(TTS_Params.receiver, intentFilter);
}
}
});

TTS_Params.am = (AudioManager)context.getSystemService(AUDIO_SERVICE);

TTS_Params.use_SCO = false;
TTS_Params.SCO_connected = false;
TTS_Params.locale = Locale.getDefault();
TTS_Params.OLD_AUDIO_MODE = -1;

// Check if HFP (SCO) is available
TTS_Params.SCO_available = false;
AudioDeviceInfo[] devices =  TTS_Params.am.getDevices (AudioManager.GET_DEVICES_OUTPUTS);
for (int i=0; i<devices.length; i++)
{
if (devices[i].getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) TTS_Params.SCO_available = true;
}
}
catch (Exception ex) { }
}

public void TTS_Close()
{
try { TTS_Params.speech.stop(); }
catch (Exception ex) { }

try { TTS_Params.speech.shutdown(); }
catch (Exception ex) { }

try { context.unregisterReceiver(TTS_Params.receiver); }
catch (Exception ex) { }

TTS_Params.speech = null;
}

private void onPlaybackStarted(String utteranceId)
{
// onPlaybackStarted is called simultaneously from android with the TTS.speak(...) command
// if you want to use SCO, this function isn't waiting for SCO connected
// all the SCO function calls must be started bevore speak command is called
// -> all this is done in the function say(..)
}

private void onPlaybackDoneOrError(String utteranceId)
{
try
{
TTS_Params.am.stopBluetoothSco();
TTS_Params.am.setBluetoothScoOn(false);
if (TTS_Params.OLD_AUDIO_MODE != -1) TTS_Params.am.setMode(TTS_Params.OLD_AUDIO_MODE);
//TTS_Params.am.setSpeakerphoneOn(true);

TTS_Params.am.abandonAudioFocus(null);
}
catch (Exception ex) { }
}

public void say(final String text, Locale locale, boolean use_SCO)
{
// use SCO = true (HFP Mode = CALL Mode)
// use SCO = false (A2DP Mode = Music Mode)
TTS_Params.use_SCO = use_SCO;

TTS_Params.locale = locale;

new Thread(new Runnable()
{
public void run()
{
try
{
TTS_Params.speech.setSpeechRate(1.0f);
TTS_Params.speech.setPitch(1.0f);
int result = TTS_Params.speech.setLanguage(TTS_Params.locale);
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED)
{
TTS_Params.speech.setLanguage(Locale.getDefault());
}

TTS_Params.OLD_AUDIO_MODE = TTS_Params.am.getMode();

if (TTS_Params.use_SCO && TTS_Params.SCO_available)
{
TTS_Params.am.setMode(AudioManager.MODE_IN_CALL);        // MODE_IN_CALL, MODE_IN_COMMUNICATION
TTS_Params.am.setBluetoothScoOn(true);
TTS_Params.am.startBluetoothSco();
//TTS_Params.am.setSpeakerphoneOn(false);

TTS_Params.am.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);

long timeout = get_timestamp_ms() + 2000; // Wait for SCO is connected (comes from Broadcast Event) but wait max 2s
while (!TTS_Params.SCO_connected && get_timestamp_ms() < timeout)
{
try { Thread.sleep(100); } // Sleep to save CPU power during waiting
catch (Exception ex) { }
}
}
else
{
TTS_Params.am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}

TTS_Params.speech.speak(text, TextToSpeech.QUEUE_FLUSH, null, "UtteranceIdTextToSpeechOutput1234");
}
catch (Exception ex) { }
}
}).start();
}
}

Title: Re: Audio Output over Bluetooth HFP
Post by: Menion on July 13, 2021, 14:29:16
Hello Matthias,
most of the users (I as well) do not know what is SCO. I had to search and read about it. Why? Because nobody wants to deal with such low-level systems of BT communication. Android does it well ... it takes care automatically of most of the stuff regards BT communication.

I still do not perfectly understand, why the current system, where Android devices picks the optimal methods of communication with the receiver, is not enough.

Anyway, thanks for the update. I've quickly compared your previous, current and my own implementation, and what was missing was 'isBluetoothScoOn = false' call. I've also, based on your change, removed  'isSpeakerphoneOn = true/false' call. You may give it a try in the next version. Thanks.
Title: Re: Audio Output over Bluetooth HFP
Post by: Matthias1912 on August 02, 2021, 12:06:53
Hi Menion,

your modification Is not working. It is like before. (SCO enabled successfully, speach comes successfully but after finishing speaking there is no SCO release). I think you are not calling "abandonAudioFocus(null)" from your AudioManager instance. Can you please look inside my function "onPlaybackDoneOrError". When I do not call "abandonAudioFocus(null)" I have the same behavior like your implementation.

For testing, you can use almost every Bluetooth communication headset. All headsets have HFP support (using SCO). With enabled SCO support (of your code), your headset use this interrupt channel and you can test your implementation.

Test condition for SCO without car radio are similar to A2DP normal music audio modus:
run amazon music or other music player in background and call your SCO speech output. It should interrupt (stop) music, speech output should appear and after finishing speaking the music should come back.

When this SCO release problem is solved, your implementation will work perfectly.

Kind regards
Matthias
Title: Re: Audio Output over Bluetooth HFP
Post by: Menion on August 04, 2021, 22:20:57
Hello Matthias,
oki, you are right, it does not work correctly even with a basic BT headset. I've spent another few hours with testing a few more options and .. this is the end of this story. It simply does not work. I'm quite sure I do all correctly, even `abandonAudioFocus` is called correctly based on logs, but still, some part is missing. So in the next version, support for SCO will be completely removed because it is still not a reliable feature. Thanks for understanding.
Menion
Title: Re: Audio Output over Bluetooth HFP
Post by: Matthias1912 on August 05, 2021, 01:54:45
Hi, mhh, what should I say. This is a hard decision.

I gave you 2 working solutions and spent some hours too. I do not know how your code looks like but I think I can help you if you let me, but you made such a secret about your text to speech part. But ok, I have to accept your decision.

Just as an hint, locus is now an abonnement version and I think this is a base feature (against your opinion) if you want to use it as a car navi too. If you are willing to add this feature somehow in the future you will get a new gold user.

Thanks for trying.

Kind regards
Matthias