Обход задержки инициализации двигателя Google TTS в Android

Я пробовал играть преобразования текста в речь объект, когда определенное событие запускается в телефоне.

однако я сталкиваюсь с проблемами с двигателем 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-около 6-8 секунд на моих устройствах.

Я читал на других сообщениях, что этой задержки можно избежать, используя другие двигатели TTS. Поскольку я всегда разрабатываю на своем телефоне Samsung, который имеет собственный собственный TTS, настроенный по умолчанию, я никогда не замечал этой проблемы, пока не проверил свое приложение на других брендовых телефонах, у которых по умолчанию настроен движок Google TTS. Но, я в идеале не хочу заставлять пользователей устанавливать другое приложение Вместе с моим собственным, и поэтому я хотел бы, чтобы это работало с default Google TTS Engine.

через некоторое ошибочное кодирование, которое я позже исправил, я понял, что если бы я мог сохранить объект TextToSpeech инициализированным заранее и not null - после инициализации, я мог бы, казалось бы, обойти эту задержку.

однако, поскольку есть необходимость отключить ресурс, как только мы закончим с ним, я не удается сохранить объект живым и инициализированным надолго, и я не знаю, когда инициализировать / выключить ресурс,так как мне технически нужен голос, чтобы играть в любое время конкретное событие происходит, который в основном будет, когда мое приложение не работает на телефоне.

Итак, мои вопросы следующие :

  1. можем мы как-то уменьшить или исключить задержку инициализации Google TTS Engine, программно или в противном случае?

  2. есть ли способ, с помощью которого я могу сохранить объект TextToSpeech живой и инициализированный во все времена как сказать, через службу? Или это плохой, ресурсоемкий дизайн?

  3. и с помощью static TextToSpeech объект правильный путь, для моих требований?

любые решения вместе с кодом будет оцененный.

обновление: Я подтвердил, что задержка связана исключительно С Google TTS engine, так как я пробовал использовать другие бесплатные и платные двигатели TTS, в которых мало или нет отставания. Но я все равно предпочел бы не иметь каких-либо сторонних зависимостей, если это возможно, и хотел бы сделать эту работу с Google TTS Engine.

обновление: Я, казалось бы, обошел эту проблему, привязав этот объект TTS к обслуживание и доступ к нему из сервиса. Служба липкая (если служба завершается из-за проблемы с памятью, ОС 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();
    }
}




}

1 ответов


Я разработчик приложения для Android Saiy. Это не бесстыдный плагин, это продемонстрировать, что я использую шаблон дизайна, который вы рассматриваете, и я "прошел", что вызвало ваш вопрос.

это свежо в моем сознании, так как я провел последний год, переписывая свой код и должен был уделять большое внимание окружающей проблеме.

  • можем ли мы как-то уменьшить или устранить задержку инициализации Google TTS Engine программно или иначе?

попросил подобный вопрос некоторое время назад и инициализация текста в речевой объект в фоновом потоке, где он не конкурирует с другими задачами, может немного уменьшить задержку (как я вижу, вы уже делаете в своем опубликованном коде).

вы также можете убедиться, что запрос говорить не задерживается дальше, выбрав встроенный голос, а не один зависит от сети:

в API 21+ проверьте параметры на Голос класс. Особенно getFeatures() где вы можете изучить задержку и требования к сети.

в API KEY_FEATURE_NETWORK_SYNTHESIS в false внутри ваших параметров.

независимо от вышеизложенного, двигатель Google TTS имеет самое длинное время инициализации любого из двигателей, которые я тестировал (все они, я думаю). Я считаю, что это просто потому, что они используют все доступные ресурсы на устройстве, чтобы обеспечить высокое качество голоса они могут.

из моего личного тестирования, эта задержка прямо пропорциональна оборудования устройства. Чем больше ОЗУ и производительность процессора, тем меньше время инициализации. То же самое можно сказать и о текущем состоянии устройства - я думаю, вы обнаружите, что после перезагрузки, где есть свободная память и Android не нужно будет убивать другие процессы, время инициализации будет уменьшенный.

В общем, кроме вышеупомянутого,нет, вы не можете уменьшить время инициализации.

  • есть ли способ, с помощью которого я могу сохранить объект TextToSpeech живым и инициализированным во все времена, например, через службу? Или это плохой, ресурсоемкий дизайн?

  • также использует статический объект TextToSpeech правильный путь, для моего требования?

Как вы заметили, способ избежать времени инициализации-оставаться привязанным к движку. Но есть и другие проблемы, которые вы, возможно, захотите рассмотреть, прежде чем делать это.

Если устройство находится в состоянии, в котором ему нужно освободить ресурсы, что является тем же состоянием, которое вызывает задержку расширенной инициализации, Android имеет все права на сбор мусора этой привязки. Если вы держите эту привязку в фоновом режиме, служба может быть убита, поставив вас на место.

кроме того, если вы остаетесь привязаны к движку, ваши пользователи будут видеть использование коллективной памяти в настройках приложения Android. Для многих, многих пользователей, которые неправильно считают (бездействие) использование памяти прямо пропорционально разряжению батареи, по моему опыту, это приведет к удалениям и плохим рейтингам приложений.

на момент написания статьи Google TTS привязан к моему приложению по стоимости 70МБ.

Если вы все еще хотите продолжить на этой основе, вы можете попытаться заставить Android установить приоритет вашего процесса и убить его последним - вы бы сделали это с помощью службы переднего плана. Это открывает еще одну банку червей, в которую я не буду входить.

эффективно, привязка к движку в службе и проверка того, что служба запущена, когда вы хотите, чтобы двигатель говорил, является "одноэлементным шаблоном". Создание статического двигателя в этой службе не будет служить никакой цели, которую я могу думать о.

вы можете ознакомиться здесь как я начинаю обрабатывать инициализацию TTS и связанные с этим проблемы, которые могут возникнуть - включая ЛАГ.

наконец-то, чтобы поделиться своим опытом о том, как я справился с вышеизложенным.

Я 'Google медленно инициализировать 'в верхней части моих" известных ошибок " и " FAQ " в приложении.

я отслеживаю время, необходимое для вызова двигателя onInit. Если это слишком долго я поднимаю уведомление пользователю и направляю их в FAQ, где им мягко советуют попробовать другой движок TTS.

Я запускаю фоновый таймер, который освобождает двигатель после периода бездействия. Это количество времени настраивается пользователем и поставляется с предупреждениями о задержке инициализации...

Я знаю, что выше не решает ваши проблемы, но, возможно, мои предложения успокоят ваших пользователей, что является отдаленной секундой для решения проблемы, но эй...

Я не сомневаюсь, что Google постепенно увеличит производительность инициализации-четыре года назад у меня была эта проблема с IVONA, которая в конечном итоге хорошо поработала над своим временем инициализации.