speech_to_text

pub package build status codecov

一个库,可公开设备特定的语音识别功能。

此插件包含一组类,可轻松在 Flutter 中使用底层平台的语音识别功能。它支持 Android、iOS 和 Web。此库的目标用例是命令和短语,而不是连续语音转换或一直收听。

近期更新

5.4.2 支持 Android 上的蓝牙耳机,这需要新的权限,请参阅下面的权限部分。请注意,蓝牙权限会向用户请求,升级时用户可能需要手动设置权限或清除缓存以强制重新请求。

5.3.0 修复了 Web 支持的一个长期存在的问题,并改进了 iOS 上的错误处理。从 5.2.0+ 开始,Android 必须使用 compileSdkVersion 31

5.0.0 得益于 @deJong-IT 的工作,改进了 iOS 上的音频处理。它还增加了一个新的 done 状态,在监听会话完成后且插件已完成设备上的音频子系统工作后发送。这有助于协调多个音频插件。

4.2.0 版本显著加快了 iOS 上的监听启动速度(约 500 毫秒),并将空安全作为默认发布版本。

注意:欢迎提供任何测试设备的反馈。

使用方法

要识别麦克风的文本,请导入包并像这样调用插件

最小化

import 'package:speech_to_text/speech_to_text.dart' as stt;

    stt.SpeechToText speech = stt.SpeechToText();
    bool available = await speech.initialize( onStatus: statusListener, onError: errorListener );
    if ( available ) {
        speech.listen( onResult: resultListener );
    }
    else {
        print("The user has denied the use of speech recognition.");
    }
    // some time later...
    speech.stop()

完整的 Flutter 示例

import 'package:flutter/material.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  SpeechToText _speechToText = SpeechToText();
  bool _speechEnabled = false;
  String _lastWords = '';

  @override
  void initState() {
    super.initState();
    _initSpeech();
  }

  /// This has to happen only once per app
  void _initSpeech() async {
    _speechEnabled = await _speechToText.initialize();
    setState(() {});
  }

  /// Each time to start a speech recognition session
  void _startListening() async {
    await _speechToText.listen(onResult: _onSpeechResult);
    setState(() {});
  }

  /// Manually stop the active speech recognition session
  /// Note that there are also timeouts that each platform enforces
  /// and the SpeechToText plugin supports setting timeouts on the
  /// listen method.
  void _stopListening() async {
    await _speechToText.stop();
    setState(() {});
  }

  /// This is the callback that the SpeechToText plugin calls when
  /// the platform returns recognized words.
  void _onSpeechResult(SpeechRecognitionResult result) {
    setState(() {
      _lastWords = result.recognizedWords;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Speech Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
              padding: EdgeInsets.all(16),
              child: Text(
                'Recognized words:',
                style: TextStyle(fontSize: 20.0),
              ),
            ),
            Expanded(
              child: Container(
                padding: EdgeInsets.all(16),
                child: Text(
                  // If listening is active show the recognized words
                  _speechToText.isListening
                      ? '$_lastWords'
                      // If listening isn't active but could be tell the user
                      // how to start it, otherwise indicate that speech
                      // recognition is not yet ready or not supported on
                      // the target device
                      : _speechEnabled
                          ? 'Tap the microphone to start listening...'
                          : 'Speech not available',
                ),
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed:
            // If not yet listening for speech start, otherwise stop
            _speechToText.isNotListening ? _startListening : _stopListening,
        tooltip: 'Listen',
        child: Icon(_speechToText.isNotListening ? Icons.mic_off : Icons.mic),
      ),
    );
  }
}

初始化一次

initialize 方法在每个应用程序会话中只需要调用一次。之后可以使用 listenstartstopcancel 与插件进行交互。后续调用 initialize 会被忽略,这很安全,但意味着 onStatusonError 回调在首次调用 initialize 后无法重置。因此,每个应用程序应只有一个插件实例。SpeechToTextProvider 是创建单个实例并在多个小部件中轻松重用它的方法之一。

权限

使用此插件的应用程序需要用户权限。

iOS

将以下键添加到您的 Info.plist 文件中,该文件位于 <project root>/ios/Runner/Info.plist

  • NSSpeechRecognitionUsageDescription – 描述您的应用程序为何使用语音识别。在可视化编辑器中称为隐私 – 语音识别使用说明
  • NSMicrophoneUsageDescription – 描述您的应用程序为何需要访问麦克风。在可视化编辑器中称为隐私 – 麦克风使用说明

Android

将录音权限添加到您的 AndroidManifest.xml 文件中,该文件位于 <project root>/android/app/src/main/AndroidManifest.xml

  • android.permission.RECORD_AUDIO – 此权限对于访问麦克风是必需的。
  • android.permission.INTERNET – 此权限是必需的,因为语音识别可能使用远程服务。
  • android.permission.BLUETOOTH – 此权限是必需的,因为语音识别连接时可以使用蓝牙耳机。
  • android.permission.BLUETOOTH_ADMIN – 此权限是必需的,因为语音识别连接时可以使用蓝牙耳机。
  • android.permission.BLUETOOTH_ADMIN – 此权限是必需的,因为语音识别连接时可以使用蓝牙耳机。

    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

Android SDK 30 或更高版本

如果您面向 Android SDK,即您将 targetSDKVersion 设置为 30 或更高版本,那么您需要在 AndroidManifest.xml 中在权限部分之后添加以下内容。有关完整用法,请参阅示例应用程序。

<queries>
    <intent>
        <action android:name="android.speech.RecognitionService" />
    </intent>
</queries>

为 iOS 添加声音(可选)

Android 在语音监听开始或停止时会自动播放系统声音,但 iOS 不会。如果应用程序中的声音文件作为资产可用,此插件支持播放声音以指示 iOS 上的监听状态。要启用此插件使用的应用程序中的声音,请将声音文件添加到项目中,并在应用程序 pubspec.yaml 的 assets 部分中引用它们。声音文件的位置和文件名必须与下面显示的确切匹配,否则将找不到它们。该插件的示例应用程序展示了用法。注意:这些文件应该非常短,因为它们会延迟语音识别器的启动/停止,直到声音播放完成。

  assets:
  - assets/sounds/speech_to_text_listening.m4r
  - assets/sounds/speech_to_text_cancel.m4r
  - assets/sounds/speech_to_text_stop.m4r
  • speech_to_text_listening.m4r – 调用 listen 方法时播放。
  • speech_to_text_cancel.m4r – 调用 cancel 方法时播放。
  • speech_to_text_stop.m4r – 调用 stop 方法时播放。

技巧

切换识别语言

speech_to_text 插件默认使用设备的默认区域设置进行语音识别。但是,它也支持使用设备上安装的任何语言。要查找可用语言并选择特定语言,请使用这些属性。

SpeechToText 实例上有一个 locales 属性,该属性将设备上安装的区域设置列表作为 LocaleName 实例提供。然后,listen 方法接受一个可选的 localeId 命名参数,该参数将是 locales 中返回的任何值的 localeId 属性。调用示例如下:

   var locales = await speech.locales();

   // Some UI or other code to select a locale from the list
   // resulting in an index, selectedLocale

   var selectedLocale = locales[selectedLocale];
   speech.listen(
       onResult: resultListener,
       localeId: selectedLocale.localeId,
       );

故障排除

Android 上语音识别在短暂暂停后停止

Android 语音识别在说话人暂停时有一个非常短的超时。持续时间似乎因设备和 Android OS 版本而异。在我使用的设备中,没有一个暂停时间超过 5 秒。不幸的是,似乎没有办法改变这种行为。

Android 在语音识别开始/停止时发出哔哔声

这是 Android OS 的一项功能,没有受支持的方法可以禁用它。

连续语音识别

关于如何使用此插件实现连续语音识别的问题有很多。目前该插件专为短期间歇性使用而设计,例如在响应问题时或发出单个语音命令时。第 63 期是该讨论的当前所在地。目前还没有办法通过 Android 或 iOS 语音识别功能来实现此目标。

连续语音识别至少有两种不同的用例

  1. 语音助手式,识别特定短语会触发交互;
  2. 文本听写输入。

语音助手式交互可能最好通过与设备上现有的助手功能集成来处理,而不是构建单独的功能。对于标准的文本输入控件,键盘可用文本听写,尽管听写还有其他目前支持不佳的用途。

浏览器对语音识别的支持

Web 浏览器在语音识别支持方面有所不同。此问题包含一些详细信息。我看到的最佳列表是https://caniuse.cn/speech-recognitionhttps://mdn.org.cn/en-US/docs/Web/API/SpeechRecognition。特别是在第 239 期中,据报道 Brave 浏览器和 Firefox for Linux 不支持语音识别。

从录制的音频中进行语音识别

关于是否可以从录制的音频中识别语音的问题有很多。简短的回答是,这在 iOS 上可能可行,但在 Android 上似乎不可行。这里有一个关于此的开放式问题 #205。

编译 Android 时出现 SDK 版本错误

Manifest merger failed : uses-sdk:minSdkVersion 16 cannot be smaller than version 21 declared in library [:speech_to_text] 

speech_to_text 插件至少需要 Android SDK 21,因为 Android 中的某些语音功能是在该版本中引入的。要修复此错误,您需要更改 build.gradle 条目以反映此版本。这是截至撰写本文时该文件相关部分的外观。

   defaultConfig {
        applicationId "com.example.app"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

在 Android 上录制音频

目前无法在进行语音识别的同时在 Android 上录制音频。目前唯一的解决方案是在语音识别器运行时停止录制,然后再次开始。

编译 iOS 时出现错误的 Swift 版本

/Users/markvandergon/flutter/.pub-cache/hosted/pub.dartlang.org/speech_to_text-1.1.0/ios/Classes/SwiftSpeechToTextPlugin.swift:224:44: error: value of type 'SwiftSpeechToTextPlugin' has no member 'AVAudioSession'
                rememberedAudioCategory = self.AVAudioSession.Category
                                          ~~~~ ^~~~~~~~~~~~~~
    /Users/markvandergon/flutter/.pub-cache/hosted/pub.dartlang.org/speech_to_text-1.1.0/ios/Classes/SwiftSpeechToTextPlugin.swift:227:63: error: type 'Int' has no member 'notifyOthersOnDeactivation'
                try self.audioSession.setActive(true, withFlags: .notifyOthersOnDeactivation)

当 Swift 语言版本未正确设置时会发生这种情况。请参阅此线程以获取帮助 csdcorp/speech_to_text#45

编译 iOS 时不支持 Swift

`speech_to_text` does not specify a Swift version and none of the targets (`Runner`) integrating it have the `SWIFT_VERSION` attribute set.

这通常发生在只支持 Objective-C 的旧项目中。请参阅此线程以获取帮助 csdcorp/speech_to_text#88

在特定 Android 设备上不起作用

此问题的症状是 initialize 方法将始终失败。如果您使用 initialize 方法上的 debugLogging: true 标志打开调试日志记录,您将在 Android 日志中看到 'Speech recognition unavailable'。这里有一个关于此的冗长的问题讨论 csdcorp/speech_to_text#36。问题似乎是识别器并非总是在设备上自动启用。至少,以下两项关键内容有助于在这种情况下解决问题。

在 Android 模拟器上不起作用

上述关于在 Android 设备上使其正常工作的技巧对于模拟器也很有用。一些用户报告称在 Android 模拟器上看到另一个错误 – sdk gphone x86 (Pixel 3a API 30)。AUDIO_RECORD 权限已在 Manifest 中,并且在 Android 设置中手动设置了 Mic 权限。运行示例应用程序时,Initialize 工作正常,但 Start 失败,日志如下。

D/SpeechToTextPlugin(12555): put partial
D/SpeechToTextPlugin(12555): put languageTag
D/SpeechToTextPlugin(12555): Error 9 after start at 35 1000.0 / -100.0
D/SpeechToTextPlugin(12555): Cancel listening

解决方案

通过打开 Google、点击麦克风图标并授予其权限来解决。然后应用程序上的所有内容都能正常工作……

第一步

  1. 转到 Google Play
  2. 搜索“Google”
  3. 您应该找到此应用:https://play.google.com/store/apps/details?id=com.google.android.googlequicksearchbox 如果显示“已禁用”,请启用它

这是有帮助的 SO 帖子:https://stackoverflow.com/questions/28769320/how-to-check-wether-speech-recognition-is-available-or-not

第二步

确保应用程序具有所需的权限。此问题的症状是在开始监听会话时会收到永久错误通知“error_audio_error`。这是一个 Stack Overflow 帖子,解决了这个问题 https://stackoverflow.com/questions/46376193/android-speechrecognizer-audio-recording-error 这是重要的摘录。

您应该转到系统设置,应用,Google 应用,然后启用其麦克风权限。

用户报告的步骤

根据 issue #298,这是解决了他们问题的详细步骤。

  1. 安装 Google 应用
  2. 设置 > 语音 > 语言 – 选择语言
  3. 设置 > 语音 > 语言 > 离线语音识别 – 安装语言
  4. 设置 > 语言和区域 – 选择搜索语言和搜索区域
  5. 删除项目根路径下的 build 文件夹并重新运行

iOS 识别指南

Apple 有一个非常好的关于语音使用用户体验的指南,原文在此 https://developer.apple.com/documentation/speech/sfspeechrecognizer 我认为特别相关的是这一节。

为语音识别创建出色的用户体验

以下是在为应用添加语音识别支持时需要考虑的一些技巧。

准备好处理由语音识别限制引起的故障。由于语音识别是基于网络的服务,因此会强制执行限制,以便所有应用都能自由使用该服务。单个设备每天的识别次数可能有限,并且每个应用每天的请求次数也可能在全球范围内受到限制。如果识别请求很快失败(在开始后一到两秒内),请检查识别服务是否不可用。如果不可用,您可能希望要求用户稍后再试。

计划音频时长限制为一分钟。语音识别会给电池寿命和网络使用带来相对较高的负担。为尽量减少此负担,框架会停止持续时间超过一分钟的语音识别任务。此限制类似于与键盘相关的听写限制。在您的应用录制时提醒用户。例如,显示视觉指示器并在语音识别开始和结束时播放声音,以帮助用户了解他们正在被主动录制。您还可以显示正在识别的语音,以便用户了解您的应用正在做什么,并看到识别过程中可能出现的任何错误。

不要对私人或敏感信息进行语音识别。有些语音不适合识别。请勿发送密码、健康或财务数据以及其他敏感语音进行识别。

GitHub

查看 Github