Android(JellyBean)中的离线语音识别


78

看来Google似乎已从Google即时中为第三方应用程序提供了离线语音识别。名为Utter的应用程序正在使用它。

有没有人看到过如何使用此离线语音记录执行简单语音命令的任何实现?您是否仅使用常规的SpeechRecognizer API并自动运行?


1
因此,只要下载该语言,就不需要更改代码了吗?
Ruchir Baronia

Answers:


72

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

此外,由于硬件限制,Google限制了某些Jelly Bean设备使用脱机识别。适用于哪个设备的文档尚未记录,实际上没有文档记录,因此为用户配置功能已被证明是反复试验的问题(对于他们而言)。它可以立即起作用-对于那些不起作用的对象,这是我提供给他们的“指南”。

  1. 确保默认的Android语音识别器设置为Google而不是Samsung / Vlingo
  2. 从Google语音搜索设置中卸载所有已经安装的离线识别文件
  3. 转到“ Android应用程序设置”,然后查看是否可以卸载Google搜索和Google语音搜索应用程序的更新。
  4. 如果您无法执行上述操作,请前往Play商店查看是否有选择权。
  5. 重新启动(如果达到2、3或4)
  6. 从Play商店更新Google搜索和Google语音搜索(如果您达到3或4,或者仍然有可用的更新)。
  7. 重新启动(如果达到6)
  8. 安装英文UK脱机语言文件
  9. 重启
  10. 使用绝对!与连接
  11. 切换至飞行模式并尝试一下
  12. 一旦开始运行,其他语言(例如美国英语)的脱机识别也应开始工作。

编辑:临时将设备区域设置更改为English UK似乎也可以启动这项功能,以供某些用户使用。

一些用户报告说,在开始工作之前,他们仍然必须重新启动几次,但是他们最终都到了那里,通常是莫名其妙地是触发了​​什么,触发的关键在Google Search APK内,所以不在公共领域或AOSP的一部分。

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

Google搜索更新未在Google即时中启用任何其他功能,实际上,如果您尝试在没有互联网连接的情况下使用它,则会出错。我提到这一点,是因为我想知道该功能是否会像它看起来那样安静地撤回,因此不应在生产中依赖它。

请注意,如果您打算开始使用SpeechRecognizer类,则存在一个与之相关的主要错误,需要您自己的实现才能处理。

无法明确要求offline = true,使得在不操纵数据连接的情况下无法控制此功能。垃圾。您将收到数百封用户电子邮件,询问您为什么未启用如此简单的功能!

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

希望以上内容对您有所帮助。


这对我来说效果很好,并且很容易实现。我在这里以这个样本为起点。 jameselsey.co.uk/blogs/techblog/…–
rmooney

@brandall我想知道我是否可以选择进行识别的语言?离线语言文件现在支持我的语言(越南语)!我想创建一个应用程序,可以针对我的语言(越南语)进行离线语音识别!那可能吗??多谢!
truongnm

1
@truongmn-这有帮助吗?stackoverflow.com/q/10538791/1256219如果不是,请提出一个新问题并将其链接至我,我将看看是否能为您提供帮助
brandall 2014年

在运行Kitkat 4.4的Samsung Galaxy Grand Prime上,我必须从“应用程序管理器”中禁用Google搜索(和Google+)应用程序,然后打开“限制后台数据”(或确保我没有可用的连接),然后重新启用Google搜索(和Google+)应用程序(可能我还必须在禁用这些应用程序之前清除所有数据)。而当我尝试在启用那些应用程序的情况下打开“限制背景数据”时,则麦克风未显示在拨号器中。显然,在没有连接(或受限)的情况下重新启用将强制使用脱机。
谢尔比·摩尔


20

我想改进指南https://stackoverflow.com/a/17674655/2987828发送给用户的图像。这句话是“对于那些没有的人,这是我提供给他们的'指南'。” 我想改善的地方。

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

转到“ Android应用程序设置”,选择“语言”并输入, 编辑Google语音输入的设置, 选择下载离线语音识别, 在全部标签中选择您的语言。

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

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

这些图像已根据cc by-sa 3.0进行了许可,并注明出处stackoverflow.com/a/21329845/2987828; 因此,您可以将这些图片连同此归因一起添加到任何地方。

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


18

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

您可以在此处找到最新的代码和教程

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


1
我刚刚尝试过演示,效果很好。快速和容易使用。
Micer

2
嗨,CMUSphinx也可以使用印度口音吗?
路西法

1
@Kedarnath似乎很在他们的名单上,请参阅此处的民意调查:cmusphinx.sourceforge.net
Jerther

谢谢,现在就尝试!
赫尔曼

它对阿拉伯语有用吗?你知道阿拉伯语有用吗?
Youssef Sherif

7

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

Google并未将离线语音识别功能提供给第三方应用程序。离线识别只能通过键盘访问。Ben Randall(完全开发者!)在Android Police上的一篇文章中解释了他的解决方法:

我已经实现了自己的键盘,并在Google语音输入和用户默认键盘之间切换,该键盘带有不可见的编辑文本字段和透明的Activity以获取输入。脏!

这是唯一的方法,因为脱机语音键入只能由IME或系统应用程序触发(这是我的根本技巧)。其他类型的识别API…并未触发它,只是由于服务器错误而失败。…在解决方法上,我浪费了很多工作!但是至少我已经准备好实施了...

来自Utter!声称是第一个在果冻豆中使用离线语音识别的非IME应用程序


4
我认为这就是他说过的最新更新之前所做的事情。按照您的说法:“ Randall继续解释说,Utter!现在使用了SpeechRecognizer,该语言已更新,允许开发人员在一系列应用程序中使用脱机识别,而以前的脱机语音键入代码Recognizerintent需要有效的IME令牌。”
rmooney 2013年


2

我正在处理此问题,我注意到您需要为您的语言安装脱机软件包。我的语言设置为“Español(Estados Unidos)”,但是没有该语言的脱机软件包,因此当我关闭所有网络连接时,我从RecognizerIntent收到警报,提示无法访问Google,然后将语言更改为“英语(美国)”(因为我已经有了脱机软件包)并启动了刚刚可以解决的RecognizerIntent。

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


我能知道您使用了哪种设备。它是否支持而不是Google设备。像三星,华硕等。我正在研究它,并且离线不支持其他设备。
Rao's

1

通过直接下载文件并将它们手动安装在正确的位置,显然可以手动安装脱机语音识别。我想这只是绕过Google硬件要求的一种方法。但是,就我个人而言,我无需重新启动或执行任何操作,只需更改为英国并再次执行即可。


0

下面给出工作示例,

MyService.class

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

希望这对以后的人有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.