78

看起来 Google 已经为第三方应用程序提供了 Google Now 的离线语音识别功能。它被名为 Utter 的应用程序使用。

有没有人见过如何使用这个离线语音记录执行简单的语音命令的任何实现?您是否只使用常规 SpeechRecognizer API 并且它会自动工作?

4

8 回答 8

74

Google 确实在该搜索更新中悄悄启用了离线识别,但SpeechRecognizer 类中(目前)还没有可用的 API 或其他参数。{参见本文底部的编辑}该功能无需额外编码即可使用,但是需要正确配置用户的设备才能开始工作,这就是问题所在,我想为什么很多开发人员假设他们“缺少某些东西”。

此外,由于硬件限制,Google 已限制某些 Jelly Bean 设备使用离线识别。这适用于哪些设备没有记录,事实上,没有任何记录,因此为用户配置功能已被证明是一个反复试验的问题(对他们来说)。它适用于某些人——对于那些不适用的人,这是我提供给他们的“指南”。

  1. 确保默认的 Android 语音识别器设置为 Google 而不是三星/Vlingo
  2. 从 Google 语音搜索设置中卸载您已安装的所有离线识别文件
  3. 转到您的 Android 应用程序设置,看看您是否可以卸载 Google 搜索和 Google 语音搜索应用程序的更新。
  4. 如果您无法执行上述操作,请前往 Play 商店,看看您是否有该选项。
  5. 重新启动(如果您达到 2、3 或 4)
  6. 从 Play 商店更新 Google 搜索和 Google 语音搜索(如果您达到 3 或 4 或如果有更新可用)。
  7. 重启(如果你达到了 6)
  8. 安装英语英国离线语言文件
  9. 重启
  10. 使用彻底!有连接
  11. 切换到飞行模式并试一试
  12. 一旦它工作,其他语言的离线识别,如英语美国也应该开始工作。

编辑:暂时将设备区域设置更改为英国英语似乎也开始了这对某些人的工作。

一些用户报告说他们仍然需要重新启动多次才能开始工作,但他们最终都到达了那里,通常莫名其妙地知道触发器是什么,其中的关键在Google 搜索 APK内,所以不在公共领域或AOSP的一部分。

据我所知,谷歌在决定使用离线还是在线识别之前测试连接的可用性。如果连接最初可用但在响应之前丢失,Google 将提供连接错误,它不会退回到离线状态。附带说明一下,如果已发出对网络合成语音的请求,则如果失败,则不会提供任何错误——您会得到静默。

Google 搜索更新在 Google Now 中没有启用任何附加功能,事实上,如果您尝试在没有互联网连接的情况下使用它,它会出错。我提到这一点是因为我想知道该功能是否会像它出现的那样悄悄地被撤回,因此不应该在生产中依赖它。

如果您打算开始使用 SpeechRecognizer 类,请注意,有一个与之相关的相当大的错误,需要您自己的实现来处理。

无法专门请求offline = true,如果不操纵数据连接,就无法控制此功能。垃圾。您将收到数百封用户电子邮件,询问您为什么没有启用如此简单的功能!

编辑:自 API 级别 23 以来,添加了一个新参数EXTRA_PREFER_OFFLINE,Google 识别服务似乎确实遵守该参数。

希望以上有所帮助。

于 2013-07-16T10:52:36.297 回答
20

我想改进答案https://stackoverflow.com/a/17674655/2987828发送给其用户的指南,并带有图像。就是这样一句话“对于那些没有的人,这是我提供给他们的‘指南’。” 我想改进。

用户应单击这些图像中以蓝色突出显示的四个按钮:

转到您的 Android 应用程序设置,选择语言和输入, 编辑谷歌语音输入设置, 选择下载离线语音识别, 在所有选项卡中选择您的语言。

然后用户可以选择任何所需的语言。下载完成后,应断开网络连接,然后点击键盘上的“麦克风”按钮。

它对我有用(android 4.1.2),然后语言识别开箱即用,无需重新启动。我现在可以向终端仿真器的外壳发出指令了!在华硕的 padfone 2 上,离线速度比在线快两倍。

这些图像在 cc by-sa 3.0 下获得许可,需要归属于 stackoverflow.com/a/21329845/2987828;因此,您可以将这些图像与此属性一起添加到任何地方。

(这是 stackoverflow.com 上所有图像和文本的标准策略)

于 2014-01-24T10:09:59.593 回答
18

一个简单灵活的Android离线识别由开源语音识别工具包CMUSphinx实现。它完全离线工作,快速且可配置 例如,它可以连续监听关键字。

你可以在这里找到最新的代码和教程

2019 年更新:时间过得很快,CMUSphinx 不再那么准确了。我建议改用 Kaldi 工具包。演示在这里

于 2014-09-01T07:45:15.353 回答
7

简而言之,我没有实现,但有解释。

谷歌没有为第三方应用提供离线语音识别功能。离线识别只能通过键盘访问。Ben Randall(utter! 的开发者)在 Android Police 的一篇文章中解释了他的解决方法:

我已经实现了自己的键盘,并在 Google Voice Typing 和用户默认键盘之间切换,使用不可见的编辑文本字段和透明的 Activity 来获取输入。肮脏的黑客!

这是唯一的方法,因为离线语音输入只能由 IME 或系统应用程序触发(这是我的 root hack)。另一种类型的识别 API……没有触发它,只是因为服务器错误而失败。......在解决方法上浪费了我很多工作!但至少我已经为实施做好了准备……

来自欧特!声称是第一个在 Jelly Bean 中使用离线语音识别的非 IME 应用程序

于 2013-07-12T14:41:48.467 回答
3

通过在离线时使用 onPartialResults 和在线时使用 onResults,我成功地实现了具有离线功能的语音服务。

于 2015-06-09T13:58:11.833 回答
2

我正在处理这个问题,我注意到您需要为您的语言安装离线包。我的语言设置是“Español (Estados Unidos)”,但该语言没有离线包,所以当我关闭所有网络连接时,我收到来自 RecognizerIntent 的警报,说无法访问 Google,然后我将语言更改为“英语(美国)”(因为我已经有离线包)并启动了它刚刚解决的识别器意图。

键:语言设置 == 离线语音识别器包

于 2014-04-08T22:55:16.547 回答
1

显然可以通过直接下载文件并手动将它们安装在正确的位置来手动安装离线语音识别。我想这只是绕过谷歌硬件要求的一种方法。但是,就我个人而言,我不必重新启动或任何其他操作,只需更改为 UK 并再次返回即可。

于 2014-05-25T02:31:41.317 回答
0

下面给出了工作示例,

我的服务类

public class MyService extends Service implements SpeechDelegate, Speech.stopDueToDelay {

  public static SpeechDelegate delegate;

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    //TODO do something useful
    try {
      if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
        ((AudioManager) Objects.requireNonNull(
          getSystemService(Context.AUDIO_SERVICE))).setStreamMute(AudioManager.STREAM_SYSTEM, true);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

    Speech.init(this);
    delegate = this;
    Speech.getInstance().setListener(this);

    if (Speech.getInstance().isListening()) {
      Speech.getInstance().stopListening();
    } else {
      System.setProperty("rx.unsafe-disable", "True");
      RxPermissions.getInstance(this).request(permission.RECORD_AUDIO).subscribe(granted -> {
        if (granted) { // Always true pre-M
          try {
            Speech.getInstance().stopTextToSpeech();
            Speech.getInstance().startListening(null, this);
          } catch (SpeechRecognitionNotAvailable exc) {
            //showSpeechNotSupportedDialog();

          } catch (GoogleVoiceTypingDisabledException exc) {
            //showEnableGoogleVoiceTyping();
          }
        } else {
          Toast.makeText(this, R.string.permission_required, Toast.LENGTH_LONG).show();
        }
      });
    }
    return Service.START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
    //TODO for communication return IBinder implementation
    return null;
  }

  @Override
  public void onStartOfSpeech() {
  }

  @Override
  public void onSpeechRmsChanged(float value) {

  }

  @Override
  public void onSpeechPartialResults(List<String> results) {
    for (String partial : results) {
      Log.d("Result", partial+"");
    }
  }

  @Override
  public void onSpeechResult(String result) {
    Log.d("Result", result+"");
    if (!TextUtils.isEmpty(result)) {
      Toast.makeText(this, result, Toast.LENGTH_SHORT).show();
    }
  }

  @Override
  public void onSpecifiedCommandPronounced(String event) {
    try {
      if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
        ((AudioManager) Objects.requireNonNull(
          getSystemService(Context.AUDIO_SERVICE))).setStreamMute(AudioManager.STREAM_SYSTEM, true);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    if (Speech.getInstance().isListening()) {
      Speech.getInstance().stopListening();
    } else {
      RxPermissions.getInstance(this).request(permission.RECORD_AUDIO).subscribe(granted -> {
        if (granted) { // Always true pre-M
          try {
            Speech.getInstance().stopTextToSpeech();
            Speech.getInstance().startListening(null, this);
          } catch (SpeechRecognitionNotAvailable exc) {
            //showSpeechNotSupportedDialog();

          } catch (GoogleVoiceTypingDisabledException exc) {
            //showEnableGoogleVoiceTyping();
          }
        } else {
          Toast.makeText(this, R.string.permission_required, Toast.LENGTH_LONG).show();
        }
      });
    }
  }


  @Override
  public void onTaskRemoved(Intent rootIntent) {
    //Restarting the service if it is removed.
    PendingIntent service =
      PendingIntent.getService(getApplicationContext(), new Random().nextInt(),
        new Intent(getApplicationContext(), MyService.class), PendingIntent.FLAG_ONE_SHOT);

    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    assert alarmManager != null;
    alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service);
    super.onTaskRemoved(rootIntent);
  }
}

更多细节,

https://github.com/sachinvarma/Speech-Recognizer

希望这对将来的人有所帮助。

于 2018-06-21T12:50:34.460 回答