speech_to_text
一个库,可公开设备特定的语音识别功能。
此插件包含一组类,可轻松在 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 方法在每个应用程序会话中只需要调用一次。之后可以使用 listen、start、stop 和 cancel 与插件进行交互。后续调用 initialize 会被忽略,这很安全,但意味着 onStatus 和 onError 回调在首次调用 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 语音识别功能来实现此目标。
连续语音识别至少有两种不同的用例
- 语音助手式,识别特定短语会触发交互;
- 文本听写输入。
语音助手式交互可能最好通过与设备上现有的助手功能集成来处理,而不是构建单独的功能。对于标准的文本输入控件,键盘可用文本听写,尽管听写还有其他目前支持不佳的用途。
浏览器对语音识别的支持
Web 浏览器在语音识别支持方面有所不同。此问题包含一些详细信息。我看到的最佳列表是https://caniuse.cn/speech-recognition 和https://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、点击麦克风图标并授予其权限来解决。然后应用程序上的所有内容都能正常工作……
第一步
- 转到 Google Play
- 搜索“Google”
- 您应该找到此应用: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,这是解决了他们问题的详细步骤。
- 安装 Google 应用
- 设置 > 语音 > 语言 – 选择语言
- 设置 > 语音 > 语言 > 离线语音识别 – 安装语言
- 设置 > 语言和区域 – 选择搜索语言和搜索区域
- 删除项目根路径下的 build 文件夹并重新运行
iOS 识别指南
Apple 有一个非常好的关于语音使用用户体验的指南,原文在此 https://developer.apple.com/documentation/speech/sfspeechrecognizer 我认为特别相关的是这一节。
为语音识别创建出色的用户体验
以下是在为应用添加语音识别支持时需要考虑的一些技巧。
准备好处理由语音识别限制引起的故障。由于语音识别是基于网络的服务,因此会强制执行限制,以便所有应用都能自由使用该服务。单个设备每天的识别次数可能有限,并且每个应用每天的请求次数也可能在全球范围内受到限制。如果识别请求很快失败(在开始后一到两秒内),请检查识别服务是否不可用。如果不可用,您可能希望要求用户稍后再试。
计划音频时长限制为一分钟。语音识别会给电池寿命和网络使用带来相对较高的负担。为尽量减少此负担,框架会停止持续时间超过一分钟的语音识别任务。此限制类似于与键盘相关的听写限制。在您的应用录制时提醒用户。例如,显示视觉指示器并在语音识别开始和结束时播放声音,以帮助用户了解他们正在被主动录制。您还可以显示正在识别的语音,以便用户了解您的应用正在做什么,并看到识别过程中可能出现的任何错误。
不要对私人或敏感信息进行语音识别。有些语音不适合识别。请勿发送密码、健康或财务数据以及其他敏感语音进行识别。