Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - undrwater

Pages: [1]
1
Other features / Audio Output over Bluetooth HFP
« 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

Code: [Select]
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;
}
}
The following users thanked this post: undrwater

Pages: [1]