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

78

看起来谷歌已经使第三方应用程序可以使用Google Now的离线语音识别功能。 名称为Utter的应用程序正在使用它。

是否有人看到如何使用这个离线语音识别做简单语音命令的实现?您是否只是使用常规SpeechRecognizer API,然后它会自动工作?


1
只要我们下载了语言,就不需要改变我们的代码了吗? - Ruchir Baronia
8个回答

74

谷歌在那次搜索更新中悄悄启用了离线识别,但是(目前)SpeechRecognizer类中没有可用的API或额外的参数。请参见本帖子底部的编辑然而,该功能可以不需要额外的编码即可使用,但是用户的设备需要正确配置才能开始工作,这就是问题所在,我想这也是很多开发人员认为他们“缺少某些东西”的原因。

此外,由于硬件限制,谷歌已经限制了某些果冻豆设备使用离线识别。具体适用于哪些设备并未记录在案,实际上,没有任何文件记录,因此为用户配置功能已被证明是一种试错(对他们来说)。对于一些人来说,它可以立刻起作用-对于那些不能的人,这就是我提供给他们的“指南”。

  1. 确保默认的Android语音识别器设置为Google而不是Samsung/Vlingo
  2. 卸载您已经从Google Voice Search设置中安装的任何离线识别文件
  3. 转到Android应用程序设置,看看是否可以卸载Google Search和Google Voice Search应用程序的更新。
  4. 如果您无法完成上述操作,请转到Play Store查看是否有该选项。
  5. 重新启动(如果您完成了2、3或4)
  6. 从Play商店更新Google Search和Google Voice Search(如果您完成了3或4,或者无论如何都有更新)。
  7. 重新启动(如果您完成了6)
  8. 安装英国离线语言文件
  9. 重新启动
  10. 使用utter!连接
  11. 切换到飞机模式并尝试一下
  12. 一旦它开始工作,其他语言的离线识别,例如美国英语,也应该开始工作。

编辑:暂时将设备区域设置为英国英语似乎也会启动此功能。

一些用户报告说,他们仍然需要重启多次才能开始工作,但他们最终都会成功,通常不清楚触发器是什么,关键在于Google Search APK中,因此不公开或不属于AOSP的一部分。
据我所知,谷歌会在决定使用离线或在线识别之前测试连接的可用性。如果一开始有连接但在响应之前丢失了连接,则谷歌会提供一个连接错误,而不会回退到离线。另外,如果已经请求了网络合成语音,则如果失败,不会提供任何错误消息-您将得到静音。
Google Search更新并没有在Google Now中启用任何其他功能,实际上,如果您尝试在没有互联网连接的情况下使用它,它会出现错误。我提到这一点是因为我想知道这种能力是否会像它出现的那样悄悄撤回,因此不能依赖它进行生产。
如果您打算开始使用SpeechRecognizer类,请注意,与其相关的一个相当重大错误需要您自己实现来处理。
无法明确请求offline = true,使得在没有操纵数据连接的情况下控制该功能成为不可能。糟糕。您会收到数百封用户电子邮件,询问您为什么没有启用如此简单的功能!
编辑:自API级别23以来,已添加了一个新参数EXTRA_PREFER_OFFLINE,Google识别服务似乎会遵守该参数。
希望以上内容能有所帮助。

这对我非常有效,而且实现起来非常容易。我使用这个示例作为起点。http://www.jameselsey.co.uk/blogs/techblog/android-how-to-implement-voice-recognition-a-nice-easy-tutorial/ - rmooney
@brandall,我想知道我是否可以选择进行识别的语言?离线语言文件现在支持我的语言(越南语)!我想创建一个可以离线语音识别我的语言(越南语)的应用程序!这可能吗?非常感谢! - truongnm
1
@truongmn - 这个有帮助吗?https://dev59.com/t2kv5IYBdhLWcg3wghIi 如果没有,请提出一个新问题并链接给我,我会看看能否帮忙。 - brandall
在我的三星Galaxy Grand Prime上运行的Kitkat 4.4系统中,我不得不从应用程序管理器中禁用Google搜索(和Google+)应用程序,然后打开“限制后台数据”(或确保我没有可用的连接),然后重新启用Google搜索(和Google+)应用程序(可能我还必须在禁用它们之前清除这些应用程序的所有数据)。而当我尝试在启用这些应用程序的情况下打开“限制后台数据”时,麦克风就不会显示在拨号器中。显然,无连接(或受限制)的重新启用强制使用离线模式。 - Shelby Moore III
嗨,请帮我解决这个问题:https://dev59.com/G-k6XIcBkEYKwwoYEf4H - Rao's
只要我们下载了语言,就不需要改变我们的代码了吗? - Ruchir Baronia

20
我希望添加图片来改进答案 https://dev59.com/Q2Mm5IYBdhLWcg3wbunb#17674655 发送给用户的“指南”。我想改进的是这句话:“对于那些没有的人,这是我提供给他们的'指南'。”
用户应该点击这些图像中突出显示为蓝色的四个按钮: Go to your Android Application Settings, select Languages and input, edit Settings of Google Voice typing, select Download Offline speech recognition, select your languages in the ALL tab. 然后,用户可以选择任何所需语言。下载完成后,应断开网络连接,然后点击键盘上的“麦克风”按钮。
它对我有效(Android 4.1.2),然后语言识别即可直接使用,无需重启。现在我可以向终端模拟器的shell输入命令!在ASUS的padfone 2上,离线速度比在线速度快两倍。
这些图片采用署名-相同方式共享 3.0 许可协议授权,需要注明来源于 stackoverflow.com/a/21329845/2987828。因此,您可以在任何地方添加这些图片及其归属。 (这是 stackoverflow.com 上所有图片和文字的标准政策)

18
使用开源语音识别工具CMUSphinx,可以在Android手机上实现简单灵活的离线语音识别。它完全离线运行,速度快且可配置,例如可以持续监听关键词。您可以在这里找到最新代码和教程。 2019年更新: 时间过得很快,CMUSphinx的准确性不再那么高。我建议尝试使用Kaldi工具包。演示可以在这里找到。

1
我刚刚尝试了演示,它运行得非常好。快速且易于使用。 - Micer
2
你好,CMUSphinx也适用于印度口音的英语吗? - Lucifer
1
@Kedarnath,看起来这是他们的首要任务,可以在这里查看投票结果:http://cmusphinx.sourceforge.net/。 - Jerther
谢谢,我现在正在尝试! - Hermandroid
它适用于阿拉伯语吗?你知道有哪些适用于阿拉伯语的吗? - Yousof Sharief
@YoussefSherif 是的,它适用于阿拉伯语,请联系我获取详细信息! - Nikolay Shmyrev

7
简而言之,我没有实现,只有解释。
谷歌没有向第三方应用程序提供离线语音识别。离线识别只能通过键盘访问。utter!的开发者Ben Randall在Android Police的一篇文章中解释了他的解决方法:
“我实现了自己的键盘,并使用一个不可见的编辑文本字段和透明的Activity在Google Voice Typing和用户默认键盘之间切换来获取输入。这是唯一的方法,因为离线语音打字只能由IME或系统应用程序触发(这是我的根hack)。另一种类型的识别API...不能触发它,只是失败并显示服务器错误。...对于我来说,浪费了很多工作来解决这个问题!但至少我已经准备好实施了...”
来自Utter!声称是Jelly Bean中第一个使用离线语音识别的非IME应用程序

4
在最新的更新之前,我以为那就是他说他过去经常做的事情。根据你所引用的话:“Randall接着解释说,Utter!现在使用的是SpeechRecognizer,该应用程序已经得到更新,使得开发者可以在各种应用中使用离线语音识别技术,而先前的离线语音打字代码 Recognizerintent 需要有效的 IME 令牌。” - rmooney

3

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


请问我能了解更多吗?我在这里发布了相关的错误:https://dev59.com/G-k6XIcBkEYKwwoYEf4H - Rao's

2
我注意到您需要安装离线包来使用语音识别功能。我的语言设置是"Español (Estados Unidos)",但是该语言没有相应的离线包,所以当我关闭所有网络连接后,RecognizerIntent提示无法连接Google。后来我将语言设置更改为"English (US)"(因为我已经安装了离线包),并重新启动了RecognizerIntent,这个问题就得到了解决。
关键词:语言设置 == 离线语音识别包

我可以知道你使用的是哪个设备吗?它是否支持非谷歌设备,比如三星、华硕等等。我正在处理这个问题,离线模式不支持其他设备。 - 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

希望这能对未来的某个人有所帮助。

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