Я попытался воспроизвести объект TextToSpeech, когда в телефоне запускается определенное событие.
Тем не менее, я столкнулся с проблемами с Google TTS по умолчанию, установленным на большинстве телефонов. На данный момент я воспроизвожу текст сразу после того, как объект TextToSpeech инициализирован и завершение работы ресурса, как только будет завершена речь, в соответствии со следующим кодом:
public class VoiceGenerator {
private Context context = null;
private static TextToSpeech voice = null;
public VoiceGenerator(Context context)
{
this.context = context;
}
public void voiceInit(String text)
{
try {
if (voice == null) {
new Thread(new Runnable() {
@Override
public void run() {
voice = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
@Override
public void onInit(final int status) {
try {
if (status != TextToSpeech.ERROR) {
voice.setLanguage(Locale.US);
Log.d("VoiceTTS", "TTS being initialized");
HashMap p = new HashMap<String, String>();
p.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "ThisUtterance");
//Speaking here
voice.speak(text, TextToSpeech.QUEUE_ADD, p);
voice.setOnUtteranceProgressListener(new UtteranceProgressListener() {
@Override
public void onStart(String utteranceId) {
}
@Override
public void onDone(String utteranceId) {
Log.d("VoiceTTS", "TTS being released");
clearTtsEngine();
}
@Override
public void onError(String utteranceId) {
}
});
}
} catch (Exception e) {
clearTtsEngine();
Log.d("ErrorLog", "Error occurred while voice play");
e.printStackTrace();
}
}
});
}
}).start();
}
}
catch(Exception e)
{
clearTtsEngine();
Log.d("ErrorLog","Error occurred while voice play");
e.printStackTrace();
}
}
public static void clearTtsEngine()
{
if(voice!=null)
{
voice.stop();
voice.shutdown();
voice = null;
}
}
}
Однако проблема, с которой я столкнулся, - это конечная сумма задержки, связанная с инициализацией Google TTS Engine - около 6-8 секунд на моих устройствах.
Я прочитал на других сообщениях, что эту задержку можно избежать, используя другие двигатели TTS. Поскольку я всегда развиваюсь на своем телефоне Samsung, у которого есть собственный собственный TTS, настроенный по умолчанию, я никогда не замечал эту проблему, пока не проверил мое приложение на других фирменных телефонах, у которых двигатель Google TTS настроен по умолчанию. Но я в идеале не хочу заставлять пользователей устанавливать другое приложение вместе с моим собственным, и поэтому я хотел бы, чтобы это работало с самим Google TTS Engine по умолчанию.
Через некоторое ошибочное кодирование, которое я позже исправил, я понял, что если бы я мог предварительно инициализировать объект TextToSpeech и всегда не null - после инициализации я мог бы, похоже, обойти эту задержку.
Однако, поскольку есть необходимость отключить ресурс, как только мы закончим с ним, я не смогу сохранить объект в ожидании и инициализироваться надолго, и я не знаю, когда инициализировать/выключать ресурс, поскольку я технически нужен голос для воспроизведения в любое время, когда происходит конкретное событие, которое в основном было бы, когда мое приложение не было открыто на телефоне.
Итак, мои вопросы таковы:
-
Можно ли как-то уменьшить или устранить задержку инициализации Google TTS Engine, программно или иначе?
-
Есть ли способ, с помощью которого я могу сохранить объект TextToSpeech живой и инициализированный во все времена, например, через службу? Или это будет плохой, ресурсоемкий дизайн?
-
Также используется статический объект TextToSpeech - правильный путь, для моих требований?
Любые решения вместе с кодом будут оценены.
Обновление: Я подтвердил, что задержка связана исключительно с движком Google TTS, так как я пытался использовать другие бесплатные и платные двигатели TTS, в которых мало или вообще нет задержки. Но я по-прежнему предпочитаю не иметь зависимостей третьих сторон, если это возможно, и хотел бы сделать эту работу с Google TTS Engine.
ОБНОВЛЕНИЕ: Я, похоже, обошел эту проблему, связывая этот объект TTS с сервисом и получая его от службы. Служба STICKY (если услуга завершается из-за проблемы с памятью, ОС Android перезапустит службу, когда память снова будет доступна) и настроен на перезагрузку при перезагрузке устройства.
Служба только инициализирует объект TTS и не выполняет никакой другой работы. Я явно не останавливаю службу, позволяя ей работать как можно дольше. Я определил объект TTS как статический, так что я могу получить к нему доступ из других классов моего приложения.
Хотя это, кажется, работает потрясающе хорошо, я обеспокоен тем, что это может привести к проблемам с памятью или батареей (в моей конкретной ситуации, когда служба обрабатывает только инициализацию объекта, а затем остается бездействующей). Есть ли какие-либо проблемы в моем дизайне или могут быть сделаны какие-либо дополнительные улучшения/проверки для моего дизайна?
Файл манифеста:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name="activity.MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name="services.BroadcastReceiverOnBootComplete"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<service android:name="services.TTSService"></service>
Код BroadcastReceiver:
public class BroadcastReceiverOnBootComplete extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) {
Intent serviceIntent = new Intent(context, TTSService.class);
context.startService(serviceIntent);
}
}
}
Код TTSService:
public class TTSService extends Service {
private static TextToSpeech voice =null;
public static TextToSpeech getVoice() {
return voice;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
// not supporting binding
return null;
}
public TTSService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
Log.d("TTSService","Text-to-speech object initializing");
voice = new TextToSpeech(TTSService.this,new TextToSpeech.OnInitListener() {
@Override
public void onInit(final int status) {
Log.d("TTSService","Text-to-speech object initialization complete");
}
});
}
catch(Exception e){
e.printStackTrace();
}
return Service.START_STICKY;
}
@Override
public void onDestroy() {
clearTtsEngine();
super.onDestroy();
}
public static void clearTtsEngine()
{
if(voice!=null)
{
voice.stop();
voice.shutdown();
voice = null;
}
}
}
Измененный код VoiceGenerator:
public class VoiceGenerator {
private TextToSpeech voice = null;
public VoiceGenerator(Context context)
{
this.context = context;
}
public void voiceInit(String text)
{
try {
if (voice == null) {
new Thread(new Runnable() {
@Override
public void run() {
voice = TTSService.getVoice();
if(voice==null)
return;
voice.setLanguage(Locale.US);
HashMap p = new HashMap<String, String>();
p.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "ThisUtterance");
voice.speak(text, TextToSpeech.QUEUE_ADD, p);
voice.setOnUtteranceProgressListener(new UtteranceProgressListener() {
@Override
public void onStart(String utteranceId) {
}
@Override
public void onDone(String utteranceId) {
}
@Override
public void onError(String utteranceId) {
}
});
}
}).start();
}
}
catch(Exception e)
{
Log.d("ErrorLog","Error occurred while voice play");
e.printStackTrace();
}
}
}