package com.crosstales.RTVoice; //region Imports import android.annotation.TargetApi; import android.content.Context; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.speech.tts.TextToSpeech; import android.speech.tts.UtteranceProgressListener; import android.speech.tts.Voice; import android.util.Log; import java.io.File; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; //endregion /** * Acts as a handler for all TTS functions called by RT-Voice on Android. *
* Copyright 2016-2020 www.crosstales.com
*/
public class RTVoiceAndroidBridge {
//region Variables
public static final String VERSION = "2020.4.7";
//Context to instantiate TTS engine
private static Context appContext;
//TTS object
private static TextToSpeech tts;
//TTS engine is initialized
private static boolean initialized;
//TTS engine is currently busy
private static boolean working = false;
//pathname of the generated WAV file
private static String outputFile;
// Volume for native speaking
private static float nativeVolume = 1f;
// Set of all available Locales (SDK < 21)
private static Set
* Returns immediately
*
* @return the boolean signifying if the engine is busy or not
*/
public static boolean isWorking() {
return working;
}
/**
* Checks if the engine has been instantiated by calling the
* boolean "initialized".
*
* Returns immediately
*
* @return the boolean signifying if the engine has been instantiated or not
*/
public static boolean isInitialized() {
return initialized;
}
/**
* If the TTS engine is instantiated, shut it down and set boolean
* "initialized" to false.
* Log the result.
*
* Logs after the TTS engine has been shut down or immediately,
* if the TTS engine is not instantiated.
*/
public static void Shutdown() {
if (tts != null) {
tts.shutdown();
initialized = false;
if (DEBUG) Log.d(TAG, "TTS engine shutdown complete!");
} else {
Log.w(TAG, "tts is null!");
}
}
/**
* Starts the private task "speakNative".
*
* This method generates multiple logs in Log.d regarding its current state.
*
* @param speechText the text that is supposed to be read.
* @param rate the rate at which the text is supposed to be read.
* @param pitch the pitch that gets applied to the Locale/Voice reading the text.
* @param inpVolume the volume that gets applied to the Locale/Voice reading the text.
* @param voiceName the name of the Locale/Voice reading the text.
*/
public static void SpeakNative(String speechText, float rate, float pitch, float inpVolume, String voiceName) {
if (DEBUG)
Log.d(TAG, "SpeakNative called!");
working = true;
if (tts != null && initialized) {
if (DEBUG) Log.d(TAG, "TTS engine initialized!");
// if (tts.isSpeaking()) {
// StopNative();
// }
// if (!tts.isSpeaking()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Voice voiceResult = null;
if (voiceName != null) {
for (Voice voice : tts.getVoices()) {
if (voice != null && voiceName.equals((voice.getName()))) {
voiceResult = voice;
break;
}
}
}
if (voiceResult == null)
voiceResult = tts.getDefaultVoice();
if (voiceResult == null) {
tts.setLanguage(getLocaleFromString(voiceName));
} else {
tts.setVoice(voiceResult);
}
} else {
tts.setLanguage(getLocaleFromString(voiceName));
}
tts.setSpeechRate(rate);
tts.setPitch(pitch);
nativeVolume = inpVolume;
speakNative(speechText);
// } else {
// Log.e(TAG, "TTS-system is busy!");
// }
} else {
Log.e(TAG, "TTS-system not initialized!");
}
}
/**
* Checks if the TTS engine is busy. If it's busy, stop the engine.
*
* This method generates a log in Log.d on call and on exit.
*/
public static void StopNative() {
if (DEBUG)
Log.d(TAG, "RTVoiceAndroidBridge: StopNative called!");
if (!(tts == null)) {
//if (isWorking()) {
tts.stop();
if (DEBUG) Log.d(TAG, "RTVoiceAndroidBridge: TTS engine stopped!");
// } else {
// Log.w(TAG, "Can't stop the TTS engine as it's not busy!");
// }
} else {
Log.w(TAG, "Can't stop the TTS engine as there is no instance of it.");
}
}
/**
* Generates audio and starts the private task "generateAudio".
*
* This method generates multiple logs in Log.d regarding its current state.
*
* @param speechText the text that is supposed to be read.
* @param rate the rate at which the text is supposed to be read.
* @param pitch the pitch that gets applied to the Locale/Voice reading the text.
* @param voiceName the name of the Locale/Voice that is supposed to read the text.
* @param outputFile the target path
* @return Multiple Log.d entries, String with the .wav-File path
*/
public static String Speak(String speechText, float rate, float pitch, String voiceName, String outputFile) {
if (DEBUG)
Log.d(TAG, "Speak called!");
working = true;
String result = null;
if (tts != null && initialized) {
// if (!tts.isSpeaking()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Voice voiceResult = null;
if (voiceName != null) {
for (Voice voice : tts.getVoices()) {
if (voice != null && voiceName.equals((voice.getName()))) {
voiceResult = voice;
break;
}
}
}
if (voiceResult == null)
voiceResult = tts.getDefaultVoice();
if (voiceResult == null) {
tts.setLanguage(getLocaleFromString(voiceName));
} else {
tts.setVoice(voiceResult);
}
} else {
tts.setLanguage(getLocaleFromString(voiceName));
}
tts.setSpeechRate(rate);
tts.setPitch(pitch);
RTVoiceAndroidBridge.outputFile = outputFile;
result = generateAudio(speechText);
// } else {
// Log.e(TAG, "TTS-system is busy!");
// }
} else {
Log.e(TAG, "TTS-system not initialized!");
}
return result;
}
/**
* Checks if the TTS engine is initialized, then -
* - if SDK >= Lollipop:
* Looks for installed voices on the Android device and use their names to
* generate a for RTVoice readable list.
* - if SDK < Lollipop:
* Looks for installed locales on the Android device, check each if they
* have an available voice to them and use their names and languages to
* generate a for RTVoice readable list.
*
* It returns a String array when the tasks are done, not immediately.
*
* @return Multiple Log.d entries, String[] with the available voices/locales
*/
public static String[] GetVoices() {
String[] result = null;
if (tts != null && initialized) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Set