274 lines
7.1 KiB
Markdown
274 lines
7.1 KiB
Markdown
# YX ASR 语音识别插件 - 使用说明
|
||
|
||
## 📋 项目概述
|
||
|
||
YX ASR 是一个基于 sherpa_onnx 的 Flutter 语音识别插件,提供完全离线的实时语音转文字功能。所有代码注释均为中文,便于团队理解和维护。
|
||
|
||
## 🎯 核心特性
|
||
|
||
- ✅ **完全离线** - 基于 sherpa_onnx,无需网络连接
|
||
- ✅ **实时识别** - 边说边转换,支持部分结果显示
|
||
- ✅ **多语言支持** - 支持中文、英文等多种语言模型
|
||
- ✅ **跨平台** - 支持 iOS 和 Android
|
||
- ✅ **隐私保护** - 语音数据不会离开设备
|
||
- ✅ **中文注释** - 所有代码注释均为中文
|
||
|
||
## 📁 项目结构
|
||
|
||
```
|
||
yx_asr/
|
||
├── lib/
|
||
│ ├── src/
|
||
│ │ ├── models/
|
||
│ │ │ ├── speech_recognition_result.dart # 语音识别结果模型
|
||
│ │ │ └── speech_recognition_error.dart # 语音识别错误模型
|
||
│ │ ├── widgets/
|
||
│ │ │ └── recording_button.dart # 可自定义的录音按钮组件
|
||
│ │ └── yx_asr_complete.dart # 主要实现类
|
||
│ └── yx_asr.dart # 库入口文件
|
||
├── assets/models/ # 模型文件目录
|
||
├── example/ # 示例应用
|
||
├── 使用说明.md # 本文档
|
||
├── SHERPA_ONNX_USAGE.md # 详细技术文档
|
||
└── README.md # 项目说明
|
||
```
|
||
|
||
## 🚀 快速开始
|
||
|
||
### 1. 添加依赖
|
||
|
||
在 `pubspec.yaml` 中添加:
|
||
|
||
```yaml
|
||
dependencies:
|
||
yx_asr: ^1.0.0
|
||
```
|
||
|
||
### 2. 准备模型文件
|
||
|
||
下载 sherpa_onnx 模型文件并放置到 `assets/models/` 目录:
|
||
|
||
```
|
||
assets/models/
|
||
├── encoder-epoch-99-avg-1.int8.onnx # 编码器模型
|
||
├── decoder-epoch-99-avg-1.int8.onnx # 解码器模型
|
||
├── joiner-epoch-99-avg-1.int8.onnx # 连接器模型
|
||
└── tokens.txt # 词汇表文件
|
||
```
|
||
|
||
### 3. 基本使用
|
||
|
||
```dart
|
||
import 'package:yx_asr/yx_asr.dart';
|
||
|
||
class SpeechPage extends StatefulWidget {
|
||
@override
|
||
_SpeechPageState createState() => _SpeechPageState();
|
||
}
|
||
|
||
class _SpeechPageState extends State<SpeechPage> {
|
||
final YxAsr _asr = YxAsr();
|
||
String _result = '';
|
||
bool _isListening = false;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_initializeASR();
|
||
}
|
||
|
||
// 初始化语音识别
|
||
Future<void> _initializeASR() async {
|
||
final success = await _asr.initializeWithModel('assets/models');
|
||
|
||
if (success) {
|
||
// 监听识别结果
|
||
_asr.onResult.listen((result) {
|
||
setState(() {
|
||
_result = result.recognizedWords;
|
||
});
|
||
});
|
||
|
||
// 监听错误
|
||
_asr.onError.listen((error) {
|
||
print('错误: ${error.errorMsg}');
|
||
});
|
||
|
||
// 监听状态变化
|
||
_asr.onListeningStatusChanged.listen((isListening) {
|
||
setState(() {
|
||
_isListening = isListening;
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
// 切换录音状态
|
||
Future<void> _toggleRecording() async {
|
||
if (_isListening) {
|
||
await _asr.stopListening();
|
||
} else {
|
||
await _asr.startListening(partialResults: true);
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: AppBar(title: Text('语音识别')),
|
||
body: Column(
|
||
children: [
|
||
Text('识别结果: $_result'),
|
||
ElevatedButton(
|
||
onPressed: _toggleRecording,
|
||
child: Text(_isListening ? '停止录音' : '开始录音'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 使用录音按钮组件
|
||
|
||
```dart
|
||
RecordingButton(
|
||
onResult: (result) {
|
||
print('识别结果: ${result.recognizedWords}');
|
||
},
|
||
onError: (error) {
|
||
print('错误: ${error.errorMsg}');
|
||
},
|
||
localeId: 'zh-CN',
|
||
size: 80.0,
|
||
recordingColor: Colors.red,
|
||
idleColor: Colors.blue,
|
||
tooltip: '点击开始录音',
|
||
)
|
||
```
|
||
|
||
## 🔧 API 说明
|
||
|
||
### YxAsr 主类
|
||
|
||
#### 初始化方法
|
||
- `initializeWithModel(String modelPath)` - 使用指定模型初始化识别器
|
||
- `initialize([String modelPath])` - 便捷初始化方法
|
||
|
||
#### 控制方法
|
||
- `startListening({bool partialResults})` - 开始语音识别
|
||
- `stopListening()` - 停止语音识别
|
||
- `cancel()` - 取消语音识别
|
||
|
||
#### 状态查询
|
||
- `isListening` - 是否正在监听
|
||
- `isAvailable()` - 检查语音识别是否可用
|
||
- `hasPermission()` - 检查是否有麦克风权限
|
||
- `requestPermission()` - 请求麦克风权限
|
||
|
||
#### 事件流
|
||
- `onResult` - 识别结果流
|
||
- `onError` - 错误信息流
|
||
- `onListeningStatusChanged` - 监听状态变化流
|
||
|
||
### 数据模型
|
||
|
||
#### SpeechRecognitionResult(语音识别结果)
|
||
- `recognizedWords` - 识别出的文字内容
|
||
- `finalResult` - 是否为最终结果
|
||
- `confidence` - 识别置信度(0.0 到 1.0)
|
||
- `alternatives` - 备选识别结果
|
||
|
||
#### SpeechRecognitionError(语音识别错误)
|
||
- `errorType` - 错误类型
|
||
- `errorMsg` - 人类可读的错误消息
|
||
- `errorCode` - 平台特定的错误代码
|
||
|
||
## 🎨 自定义录音按钮
|
||
|
||
RecordingButton 组件支持丰富的自定义选项:
|
||
|
||
```dart
|
||
RecordingButton(
|
||
// 回调函数
|
||
onResult: (result) => handleResult(result),
|
||
onError: (error) => handleError(error),
|
||
onListeningStatusChanged: (isListening) => updateUI(isListening),
|
||
|
||
// 语言设置
|
||
localeId: 'zh-CN',
|
||
partialResults: true,
|
||
|
||
// 外观自定义
|
||
size: 100.0,
|
||
idleColor: Colors.blue,
|
||
recordingColor: Colors.red,
|
||
disabledColor: Colors.grey,
|
||
idleIcon: Icons.mic,
|
||
recordingIcon: Icons.stop,
|
||
iconSize: 40.0,
|
||
|
||
// 其他设置
|
||
enabled: true,
|
||
tooltip: '点击开始录音',
|
||
)
|
||
```
|
||
|
||
## 🔍 错误处理
|
||
|
||
```dart
|
||
_asr.onError.listen((error) {
|
||
switch (error.errorType) {
|
||
case SpeechRecognitionErrorType.permissionDenied:
|
||
// 处理权限被拒绝
|
||
showPermissionDialog();
|
||
break;
|
||
case SpeechRecognitionErrorType.service:
|
||
// 处理服务错误
|
||
showServiceError(error.errorMsg);
|
||
break;
|
||
case SpeechRecognitionErrorType.audio:
|
||
// 处理音频错误
|
||
showAudioError(error.errorMsg);
|
||
break;
|
||
default:
|
||
// 处理其他错误
|
||
showGenericError(error.errorMsg);
|
||
}
|
||
});
|
||
```
|
||
|
||
## 📱 平台配置
|
||
|
||
### Android
|
||
在 `android/app/src/main/AndroidManifest.xml` 中添加:
|
||
```xml
|
||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||
```
|
||
|
||
### iOS
|
||
在 `ios/Runner/Info.plist` 中添加:
|
||
```xml
|
||
<key>NSMicrophoneUsageDescription</key>
|
||
<string>此应用需要麦克风权限进行语音识别</string>
|
||
```
|
||
|
||
## 🎯 最佳实践
|
||
|
||
1. **模型选择** - 根据应用需求选择合适的模型大小
|
||
2. **权限处理** - 在使用前检查和请求权限
|
||
3. **错误处理** - 实现完善的错误处理机制
|
||
4. **资源管理** - 在适当时机调用 `dispose()` 释放资源
|
||
5. **用户体验** - 提供清晰的视觉反馈和状态提示
|
||
|
||
## 📚 更多资源
|
||
|
||
- 详细技术文档:`SHERPA_ONNX_USAGE.md`
|
||
- 示例应用:`example/` 目录
|
||
- 模型下载:[sherpa-onnx releases](https://github.com/k2-fsa/sherpa-onnx/releases/)
|
||
|
||
## 🤝 技术支持
|
||
|
||
如有问题,请查看示例代码或提交 Issue。
|