谷歌语音识别超时。

32

我正在开发一款基于语音识别的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()

嗨@Hector,我在我的应用程序中遇到了相同的问题,是否有任何解决方案或解决时间延迟的方法。如果您已经解决了您的问题,请在评论中指导我如何解决这个问题。我在这里发布了我的问题http://stackoverflow.com/q/38179290/4657065。提前致谢。 - Anandapriyan S.D
这个问题似乎已经解决了,所以我在底部的条目应该不再必要。 - John Smith
1
对于不喜欢蜂鸣声的John Smith,您可以通过将STREAM_MUSIC的音量设置为零来摆脱它。当然,您应该保存原始值,并在暂停应用程序时恢复它。我在自己的语音识别应用程序中使用这个功能。我认为它可能仍在等待,因为它认为正在发出蜂鸣声:我在我的识别器中看到了一段死时间。实际上,Google应该提供一个选项来完全抑制蜂鸣声,而不是等待它。 - user3791713
6个回答

22

编辑 - 已在2016年8月发布的更新中修复 您可以测试beta版以确认。

这是Google“Now” V6.0.23.*版本发布时出现的错误,最新版本V6.1.28.*仍未解决该问题。

自从V5.11.34.*发布以来,Google实现的SpeechRecognizer一直存在着许多错误。

您可以使用这个代码片段复制其中的大部分错误。

您可以使用BugRecognitionListener绕过其中一些错误。

我已经向Now团队直接报告了这些错误,所以他们已经知道,但迄今为止还没有解决。Google Now没有外部bug跟踪器,因为它不是AOSP的一部分,所以很抱歉您无法收藏任何东西。

您详细描述的最新错误几乎使他们的实现无法使用,正如您正确指出的那样,用于控制语音输入时间的参数被忽略了。 根据文档

此外,根据识别器实现的不同,这些值可能没有任何作用。

我们应该预料到会出现这种情况......

如果您没有讲话或发出任何可检测到的声音,则识别将无限期地继续进行。

我目前正在创建一个项目来复制这个新错误和所有其他错误,我很快将其转发并在这里链接。

编辑 — 我希望我能够创建一个解决方法,通过检测部分或不稳定的结果来触发知道用户仍在说话。一旦他们停止,我可以在一定时间后手动调用 recognizer.stopListening()

不幸的是,stopListening()也是有问题的,并不能真正停止识别,因此没有解决方法。

对于上述尝试,销毁识别器并仅依赖到目前为止的部分结果(销毁识别器时未调用 onResults())无法产生可靠的实现,除非你只是 关键字识别

在 Google 修复该问题之前,我们无能为力。您唯一的出路是发送电子邮件至 apps-help@google.com 报告问题,并希望他们收到足够的反馈从而推动修复……


为什么不尝试使用cancel而不是stopListening呢? - Hector
@Hector cancel()会阻止onResults()被调用,因此您在那个时候仍然依赖于部分或不稳定的结果。恐怕与销毁控件相同的结果。 - brandall
我想到的解决方案(尽管它是为了加快获取结果的速度),就是请求部分结果,当我得到它们时,取消请求,因为它们比完整结果更快地返回。如果没有返回部分结果,则像往常一样等待完整结果。 - Hector
@Hector 在我的当前实现中,如果我在部分或不稳定的结果中检测到我需要的语音https://dev59.com/-pbfa4cB1Zd3GeqPv5mX#37033162,我也会取消识别。但是对于用户可能正在口述电子邮件等情况,仅依赖部分结果只能是一种解决方法,如果这些结果的规律性足以确定用户已停止讲话。在我的测试中,这并不成功,无法考虑将其作为生产解决方案。对于关键字识别,完全没有问题。 - brandall

9

注意!此功能仅在在线模式下可用。 启用语音识别模式并禁用部分结果:

intent.putExtra("android.speech.extra.DICTATION_MODE", true);
intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, false);

在听写模式下,SpeechRecognizer 仍将调用 onPartialResults(),但您应将这些部分视为最终结果。


它有效!我建议这个解决方案作为快速临时修复。 - Arkadiusz Cieśliński
这个对我有用!谢谢你!这是最好的解决方案。 - Dontreadonme
这对我没有解决问题。在读取段落时仍然会发生超时。 - Cognoscis
这个对当前实现没有影响。 - Zayid Mohammed

4

更新:

以防有人在设置语音识别方面遇到问题,您可以使用我创建的Droid Speech库来解决Android中的语音超时问题。


我的应用完全依赖于语音识别功能,而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);

3
我找到的最佳解决方案(在谷歌修复这个漏洞之前)是进入Google应用程序信息并点击“卸载更新”按钮。这将删除对此应用程序进行的所有更新,这直接影响语音识别器,基本上将其恢复为出厂状态。
** 可能是一个好主意停止自动更新,直到我们知道它已经被修复。 *** 注意:这仅适用于开发人员,显然如果您在商店中有一个应用程序,这将不会对您有所帮助。对不起...

这将删除用户在Google Now中的所有功能,因此需要向用户发出大警告。 - brandall
这将会禁用Android Wear的“Ok Google”(我试过了) :/ - John Smith

2

更新:根据我今天的测试,这个bug似乎已经得到解决,这不再是必需的步骤。如果以后再次出现问题,将其保留。从我的测试结果来看,语音超时功能正常工作。

好吧,我知道这很丑陋,但使用onPartialResults似乎能够解决问题(我了解onPartialResults的注意事项,但我已经尝试了几次,直到Google修复这个荒谬的错误之前,这个方法看起来行得通!)我还没有进行全面测试(一旦我在应用程序中使用它,我会返回测试结果),但我迫切需要一个解决方案。基本上,我使用onRmsChanged触发用户完成讲话,并假设当RmsDb下降到峰值以下且2秒内没有onPartialResults时,我们完成了讲话。

我不喜欢的一件事是销毁SR会发出双重uh-oh声。供参考和自己体验。请发布任何改进意见!

注意:如果您要重复使用此代码,请勿忘记重置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;
    }
}

恐怕 onRmsChanged() 也不能正常工作,这是我报告的一个错误之一 https://gist.github.com/brandall76/b911eb732f309dec68af351d74013851#file-speech_test-java-L44。有时在语音识别期间根本没有被调用。这不是每次都可以复制的,这使得它成为一种不可靠的解决方法... - brandall
我已经仔细检查过了,但确认onRmsChanged()对我来说绝对不总是被调用的 - 这是在许多不同的设备/版本/sdk变化上。如果您转到设备上Google应用程序的应用信息,您运行的是哪个版本? - brandall
6.0.23.21.arm. - 我刚刚将更新后的应用程序放在Play商店上了(不是免费的,也不想无耻地宣传),所以让我们拭目以待! - John Smith
非常令人沮丧的是,他们在发布之间明显没有测试SpeechRecognizer的影响...通过你的实现,在V5.11.34中onRmsChanged()更加棘手-因此,如果用户没有更新,他们可能需要一点提示。 - brandall
FYI - 这个问题即使在最新的 Google App beta 版本中仍然存在!!非常令人沮丧! - John Smith
显示剩余4条评论

0

仅限离线解决方案:

我遇到了相同的问题(Android 系统在通过 onEndOfSpeech() 触发后,花费25秒来产生语音转录结果)。

我尝试了以下代码并得到了成功:

Intent.putExtra
(
    RecognizerIntent.EXTRA_PREFER_OFFLINE,
    true
);

这个解决方案适用于我的应用程序,如果您不使用在线模式(我通过手机设置下载了语言包),它可能也适用于您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接