31

音声認識に基づく Android アプリケーションを開発しています。

今日まで、すべてが適切にタイムリーに機能していました。たとえば、音声認識エンジンを起動して話すと、最大 1 ~ 2 秒以内にアプリケーションが結果を受け取りました。

非常に満足できるユーザー エクスペリエンスでした。

それから今日、認識結果が得られるまで 10 秒以上待たなければなりません。

次の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()
4

6 に答える 6

22

編集- 2016 年 8 月の今後のリリースで修正されたようです。ベータ版をテストして確認できます。

これは、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にメールで問題を報告し、彼らが受け取った量が彼らに微調整を与えることを期待することです.....

于 2016-07-01T17:23:26.613 に答える
9

ノート!これはオンライン モードでのみ機能します。 ディクテーション モードを有効にし、部分的な結果を無効にします。

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

ディクテーション モードでは、speechRecognizer は引き続き呼び出しonPartialResults()ますが、パーシャルを最終結果として扱う必要があります。

于 2016-07-22T20:31:05.793 に答える
4

アップデート:

念のため、音声認識の設定に問題がある場合は、Android の音声タイムアウトの問題を解決するために構築したDroid Speech ライブラリを使用できます。


私のアプリは完全に音声認識機能に依存していましたが、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);
于 2016-07-14T20:52:52.473 に答える
2

更新: 今日のテストの時点で、このバグは最終的に解決されたようで、これはもはや必要ありません。将来また壊れた時のために残しておきます。私のテストでは、音声タイムアウトは正常に機能しています。

わかりました、これは非常に醜いことを知っていますが、onPartialResults を使用して動作するようです (onPartialResults の落とし穴は理解していますが、これを数回試してみましたが、Google がこのばかげたバグを修正するまでは何とかなります!) 徹底的にテストしていませんまだ(これをアプリで使用するので、結果を投稿します)しかし、私は解決策を切望していました. 基本的に、RmsDb がピークを下回り、onPartialResults が 2 秒間ない場合、ユーザーが話し終わったことをトリガーするために onRmsChanged を使用しています。

これについて私が気に入らないことの 1 つは、SR を破壊すると、うーん、というビープ音が 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;
    }
}
于 2016-07-07T17:10:00.363 に答える