Тайм-аут распознавания речи Google
Я разрабатываю приложение для Android, основанное на распознавании речи.
До сегодняшнего дня все работало нормально и своевременно, например, я запускал распознаватель речи, говорил, и максимум через 1 или 2 секунды приложение получало результаты.
Это был ОЧЕНЬ приемлемый пользовательский опыт.
Тогда сегодня мне нужно подождать десять или более секунд, прежде чем станут доступны результаты распознавания.
Я попытался установить следующие EXTRAS, ни один из которых не имеет никакого заметного различия
RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS
RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS
RecognizerIntent.EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS
Я постоянно менял свое заявление, однако ни одно из этих изменений не было связано с распознавателем речи.
Можно ли использовать какой-либо метод, чтобы сократить время между переключением распознавателя речи с onBeginningOfSpeech()
в onResults()
?
Вот пример того, сколько времени это займет
07-01 17:50:20.839 24877-24877/com.voice I/Voice: onReadyForSpeech()
07-01 17:50:21.614 24877-24877/com.voice I/Voice: onBeginningOfSpeech()
07-01 17:50:38.163 24877-24877/com.voice I/Voice: onEndOfSpeech()
6 ответов
РЕДАКТИРОВАТЬ - По- видимому, было исправлено в предстоящем выпуске августа 2016 года. Вы можете протестировать бета-версию для подтверждения.
Это ошибка с выпуском Google 'Now' V6.0.23.* И сохраняется в последней версии V6.1.28.*
С момента выпуска V5.11.34.* Реализация Google SpeechRecognizer
был изведен с ошибками.
Вы можете использовать эту суть, чтобы повторить многие из них.
Вы можете использовать этот BugRecognitionListener, чтобы обойти некоторые из них.
Я сообщил об этом непосредственно команде Now, чтобы они знали, но пока ничего не исправлено. В Google Now нет внешнего баг-трекера, так как он не является частью AOSP, поэтому, боюсь, вы ничего не пометите.
Последняя ошибка, которую вы подробно описали, делает ее реализацию непригодной для использования, поскольку, как вы правильно заметили, параметры для управления временем речевого ввода игнорируются. Который согласно документации:
Кроме того, в зависимости от реализации распознавателя, эти значения могут не иметь никакого эффекта.
это то, что мы должны ожидать......
Распознавание будет продолжаться бесконечно, если вы не говорите и не издаете какого-либо обнаруживаемого звука.
В настоящее время я создаю проект для репликации этой новой ошибки и всех остальных, о которых я расскажу позже и со ссылками здесь в ближайшее время.
РЕДАКТИРОВАТЬ - Я надеялся, что смогу создать обходной путь, который использовал бы обнаружение частичных или нестабильных результатов в качестве триггера, чтобы узнать, что пользователь все еще говорит. Как только они остановились, я мог бы вручную позвонить recognizer.stopListening()
после определенного периода времени.
К несчастью, stopListening()
тоже сломан и фактически не останавливает распознавание, поэтому обходного пути нет.
Попытки вокруг вышеизложенного уничтожить распознаватель и полагаться только на частичные результаты вплоть до этой точки (при уничтожении распознавателя onResults()
не вызывается) не удалось создать надежную реализацию, если вы просто не нашли ключевые слова.
Мы ничего не можем сделать, пока Google не исправит это. Ваша единственная возможность - отправить электронное письмо по адресу apps-help@google.com с сообщением о проблеме и надеяться, что получаемый ими объем подтолкнет их.....
НОТА! это работает только в онлайн режиме. Включите режим диктовки и отключите частичные результаты:
intent.putExtra("android.speech.extra.DICTATION_MODE", true);
intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, false);
В режиме диктовки VoiceRecognizer все равно будет вызывать onPartialResults()
Однако вы должны рассматривать частичные как окончательные результаты.
ОБНОВИТЬ:
На случай, если у кого-то возникнут проблемы с настройкой распознавания речи, вы можете использовать библиотеку Droid Speech, которую я создал, чтобы преодолеть проблему истечения времени ожидания речи в Android.
Мое приложение полностью зависело от функции распознавания голоса, и Google сбросил бомбу. Судя по всему, я считаю, что это не будет исправлено, по крайней мере, в ближайшем будущем.
В настоящее время я нашел решение, позволяющее распознавать голоса Google так, чтобы результаты речи соответствовали ожиданиям.
Примечание: этот подход немного отличается от вышеупомянутых решений.
Основная цель этого метода - убедиться, что все слова, произнесенные пользователем, перехвачены в onPartialResults().
В обычных случаях, если пользователь произносит более одного слова в данном случае, время отклика слишком короткое, и частичные результаты чаще всего получают только первое слово, а не полный результат.
Таким образом, чтобы удостовериться, что каждое отдельное слово перехватывается в onPartialResults (), вводится обработчик для проверки задержки пользовательской паузы, а затем фильтрации результатов. Также обратите внимание, что массив результатов из onPartialResults () будет чаще всего содержать только один элемент.
SpeechRecognizer userSpeech = SpeechRecognizer.createSpeechRecognizer(this);
Intent speechIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
speechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
speechIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, this.getPackageName());
speechIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
speechIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, ModelData.MAX_VOICE_RESULTS);
Handler checkForUserPauseAndSpeak = new Handler();
Boolean speechResultsFound = false;
userSpeech.setRecognitionListener(new RecognitionListener(){
@Override
public void onRmsChanged(float rmsdB)
{
// NA
}
@Override
public void onResults(Bundle results)
{
if(speechResultsFound) return;
speechResultsFound = true;
// Speech engine full results (Do whatever you would want with the full results)
}
@Override
public void onReadyForSpeech(Bundle params)
{
// NA
}
@Override
public void onPartialResults(Bundle partialResults)
{
if(partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION).size() > 0 &&
partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION).get(0) != null &&
!partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION).get(0).trim().isEmpty())
{
checkForUserPauseAndSpeak.removeCallbacksAndMessages(null);
checkForUserPauseAndSpeak.postDelayed(new Runnable()
{
@Override
public void run()
{
if(speechResultsFound) return;
speechResultsFound = true;
// Stop the speech operations
userSpeech.destroy();
// Speech engine partial results (Do whatever you would want with the partial results)
}
}, 1000);
}
}
@Override
public void onEvent(int eventType, Bundle params)
{
// NA
}
@Override
public void onError(int error)
{
// Error related code
}
@Override
public void onEndOfSpeech()
{
// NA
}
@Override
public void onBufferReceived(byte[] buffer)
{
// NA
}
@Override
public void onBeginningOfSpeech()
{
// NA
}
});
userSpeech.startListening(speechIntent);
Лучшее решение, которое я нашел (до тех пор, пока Google не исправит ошибку), - это перейти к информации приложения Google App и нажать кнопку "Удалить обновления". Это удалит все обновления, сделанные для этого приложения, которые имеют прямое влияние на распознаватель речи, в основном возвращая его на заводские настройки.
** Вероятно, это хорошая идея, чтобы остановить автоматическое обновление, пока мы не узнаем, что это исправления. *** Примечание: это решение только для разработчиков, очевидно, если у вас есть приложение в магазине, это вам не поможет. Сожалею...
ОБНОВЛЕНИЕ: По состоянию на мое сегодняшнее тестирование, эта ошибка, похоже, окончательно устранена, и в этом больше нет необходимости. Оставив его на случай, если в будущем он снова обанкротится. Из моих тестов тайм-аут речи работает нормально.
Хорошо, я знаю, что это ОЧЕНЬ уродливо, но, похоже, он работает с использованием onPartialResults (я понимаю ошибки с onPartialResults, но я пробовал это несколько раз, и пока что Google не исправит эту нелепую ошибку!) Я не провел тщательного тестирования пока (я буду публиковать результаты, так как я буду использовать это в приложении), но я отчаянно нуждался в решении. По сути, я использую onRmsChanged, чтобы инициировать, что пользователь закончил говорить, предполагая, что когда RmsDb падает ниже пика и нет onPartialResults в течение 2 секунд, мы закончили.
Единственное, что мне не нравится в этом, - это то, что СР издает двойной гудок. FWIW и YMMV. Пожалуйста, оставьте любые улучшения!
ПРИМЕЧАНИЕ: если вы собираетесь использовать это несколько раз, не забудьте сбросить bBegin и fPeak! Также вам нужно будет воссоздать SR (либо onStartCommand, либо остановить и запустить сервис.)
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.ArrayList;
public class SpeechToTextService extends Service {
private String TAG = "STT";
float fPeak;
boolean bBegin;
long lCheckTime;
long lTimeout = 2000;
@Override
public void onCreate() {
super.onCreate();
bBegin = false;
fPeak = -999; //Only to be sure it's under ambient RmsDb.
final SpeechRecognizer sr = SpeechRecognizer.createSpeechRecognizer(getApplicationContext());
sr.setRecognitionListener(new RecognitionListener() {
@Override
public void onReadyForSpeech(Bundle bundle) {
Log.i(TAG, "onReadyForSpeech");
}
@Override
public void onBeginningOfSpeech() {
bBegin = true;
Log.i(TAG, "onBeginningOfSpeech");
}
@Override
public void onRmsChanged(float rmsDb) {
if(bBegin) {
if (rmsDb > fPeak) {
fPeak = rmsDb;
lCheckTime = System.currentTimeMillis();
}
if (System.currentTimeMillis() > lCheckTime + lTimeout) {
Log.i(TAG, "DONE");
sr.destroy();
}
}
//Log.i(TAG, "rmsDB:"+rmsDb);
}
@Override
public void onBufferReceived(byte[] buffer) {
Log.i(TAG, "onBufferReceived");
}
@Override
public void onEndOfSpeech() {
Log.i(TAG, "onEndOfSpeech");
}
@Override
public void onError(int error) {
Log.i(TAG, "onError:" + error);
}
@Override
public void onResults(Bundle results) {
ArrayList data = results.getStringArrayList(
SpeechRecognizer.RESULTS_RECOGNITION);
String sTextFromSpeech;
if (data != null) {
sTextFromSpeech = data.get(0).toString();
} else {
sTextFromSpeech = "";
}
Log.i(TAG, "onResults:" + sTextFromSpeech);
}
@Override
public void onPartialResults(Bundle bundle) {
lCheckTime = System.currentTimeMillis();
ArrayList data = bundle.getStringArrayList(
SpeechRecognizer.RESULTS_RECOGNITION);
String sTextFromSpeech;
if (data != null) {
sTextFromSpeech = data.get(0).toString();
} else {
sTextFromSpeech = "";
}
Log.i(TAG, "onPartialResults:" + sTextFromSpeech);
}
@Override
public void onEvent(int eventType, Bundle params) {
Log.i(TAG, "onEvent:" + eventType);
}
});
Intent iSRIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
iSRIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
iSRIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
iSRIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, getPackageName());
iSRIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "en-US");
iSRIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "en-US");
sr.startListening(iSRIntent);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Решение только для оффлайн:
Я столкнулся с той же проблемой (системе Android потребовалось 25 секунд, чтобы получить расшифровку речи через onPartialResults()
после onEndOfSpeech()
срабатывает.
Я пробовал следующий код, и он работал:
Intent.putExtra
(
RecognizerIntent.EXTRA_PREFER_OFFLINE,
true
);
Это решение работает для моего приложения и может работать для вас, если вы не используете онлайн-режим (я загрузил языковой пакет через настройки телефона).