17 changed files with 829 additions and 91 deletions
@ -0,0 +1,123 @@ |
|||||
|
{ |
||||
|
"originHash" : "c63c63846d9c539229e96de38d6af51417e28c0ee9a0bc48bd0f0f19d923c329", |
||||
|
"pins" : [ |
||||
|
{ |
||||
|
"identity" : "abseil-cpp-binary", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/abseil-cpp-binary.git", |
||||
|
"state" : { |
||||
|
"revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", |
||||
|
"version" : "1.2024072200.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "app-check", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/app-check.git", |
||||
|
"state" : { |
||||
|
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5", |
||||
|
"version" : "11.2.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "firebase-ios-sdk", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/firebase/firebase-ios-sdk", |
||||
|
"state" : { |
||||
|
"revision" : "2e4a4daac9da51f79c131e76cb47e785dd7708c4", |
||||
|
"version" : "11.10.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "googleappmeasurement", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/GoogleAppMeasurement.git", |
||||
|
"state" : { |
||||
|
"revision" : "defe1970a9099d47765592ebd7ac5f5e9fdb57c6", |
||||
|
"version" : "11.10.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "googledatatransport", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/GoogleDataTransport.git", |
||||
|
"state" : { |
||||
|
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9", |
||||
|
"version" : "10.1.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "googleutilities", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/GoogleUtilities.git", |
||||
|
"state" : { |
||||
|
"revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb", |
||||
|
"version" : "8.0.2" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "grpc-binary", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/grpc-binary.git", |
||||
|
"state" : { |
||||
|
"revision" : "cc0001a0cf963aa40501d9c2b181e7fc9fd8ec71", |
||||
|
"version" : "1.69.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "gtm-session-fetcher", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/gtm-session-fetcher.git", |
||||
|
"state" : { |
||||
|
"revision" : "4d70340d55d7d07cc2fdf8e8125c4c126c1d5f35", |
||||
|
"version" : "4.4.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "interop-ios-for-google-sdks", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/interop-ios-for-google-sdks.git", |
||||
|
"state" : { |
||||
|
"revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", |
||||
|
"version" : "101.0.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "leveldb", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/firebase/leveldb.git", |
||||
|
"state" : { |
||||
|
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", |
||||
|
"version" : "1.22.5" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "nanopb", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/firebase/nanopb.git", |
||||
|
"state" : { |
||||
|
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", |
||||
|
"version" : "2.30910.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "promises", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/promises.git", |
||||
|
"state" : { |
||||
|
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", |
||||
|
"version" : "2.4.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "swift-protobuf", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/apple/swift-protobuf.git", |
||||
|
"state" : { |
||||
|
"revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", |
||||
|
"version" : "1.29.0" |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
"version" : 3 |
||||
|
} |
||||
@ -0,0 +1,123 @@ |
|||||
|
{ |
||||
|
"originHash" : "c63c63846d9c539229e96de38d6af51417e28c0ee9a0bc48bd0f0f19d923c329", |
||||
|
"pins" : [ |
||||
|
{ |
||||
|
"identity" : "abseil-cpp-binary", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/abseil-cpp-binary.git", |
||||
|
"state" : { |
||||
|
"revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", |
||||
|
"version" : "1.2024072200.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "app-check", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/app-check.git", |
||||
|
"state" : { |
||||
|
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5", |
||||
|
"version" : "11.2.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "firebase-ios-sdk", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/firebase/firebase-ios-sdk", |
||||
|
"state" : { |
||||
|
"revision" : "2e4a4daac9da51f79c131e76cb47e785dd7708c4", |
||||
|
"version" : "11.10.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "googleappmeasurement", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/GoogleAppMeasurement.git", |
||||
|
"state" : { |
||||
|
"revision" : "defe1970a9099d47765592ebd7ac5f5e9fdb57c6", |
||||
|
"version" : "11.10.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "googledatatransport", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/GoogleDataTransport.git", |
||||
|
"state" : { |
||||
|
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9", |
||||
|
"version" : "10.1.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "googleutilities", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/GoogleUtilities.git", |
||||
|
"state" : { |
||||
|
"revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb", |
||||
|
"version" : "8.0.2" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "grpc-binary", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/grpc-binary.git", |
||||
|
"state" : { |
||||
|
"revision" : "cc0001a0cf963aa40501d9c2b181e7fc9fd8ec71", |
||||
|
"version" : "1.69.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "gtm-session-fetcher", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/gtm-session-fetcher.git", |
||||
|
"state" : { |
||||
|
"revision" : "4d70340d55d7d07cc2fdf8e8125c4c126c1d5f35", |
||||
|
"version" : "4.4.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "interop-ios-for-google-sdks", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/interop-ios-for-google-sdks.git", |
||||
|
"state" : { |
||||
|
"revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", |
||||
|
"version" : "101.0.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "leveldb", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/firebase/leveldb.git", |
||||
|
"state" : { |
||||
|
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", |
||||
|
"version" : "1.22.5" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "nanopb", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/firebase/nanopb.git", |
||||
|
"state" : { |
||||
|
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", |
||||
|
"version" : "2.30910.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "promises", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/google/promises.git", |
||||
|
"state" : { |
||||
|
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", |
||||
|
"version" : "2.4.0" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"identity" : "swift-protobuf", |
||||
|
"kind" : "remoteSourceControl", |
||||
|
"location" : "https://github.com/apple/swift-protobuf.git", |
||||
|
"state" : { |
||||
|
"revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", |
||||
|
"version" : "1.29.0" |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
"version" : 3 |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
|
<plist version="1.0"> |
||||
|
<dict> |
||||
|
<key>CLIENT_ID</key> |
||||
|
<string>907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278.apps.googleusercontent.com</string> |
||||
|
<key>REVERSED_CLIENT_ID</key> |
||||
|
<string>com.googleusercontent.apps.907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278</string> |
||||
|
<key>ANDROID_CLIENT_ID</key> |
||||
|
<string>907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com</string> |
||||
|
<key>API_KEY</key> |
||||
|
<string>AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808</string> |
||||
|
<key>GCM_SENDER_ID</key> |
||||
|
<string>907754492821</string> |
||||
|
<key>PLIST_VERSION</key> |
||||
|
<string>1</string> |
||||
|
<key>BUNDLE_ID</key> |
||||
|
<string>com.yunqiinnovation.deepsound</string> |
||||
|
<key>PROJECT_ID</key> |
||||
|
<string>deepsound-e0e3d</string> |
||||
|
<key>STORAGE_BUCKET</key> |
||||
|
<string>deepsound-e0e3d.firebasestorage.app</string> |
||||
|
<key>IS_ADS_ENABLED</key> |
||||
|
<false></false> |
||||
|
<key>IS_ANALYTICS_ENABLED</key> |
||||
|
<false></false> |
||||
|
<key>IS_APPINVITE_ENABLED</key> |
||||
|
<true></true> |
||||
|
<key>IS_GCM_ENABLED</key> |
||||
|
<true></true> |
||||
|
<key>IS_SIGNIN_ENABLED</key> |
||||
|
<true></true> |
||||
|
<key>GOOGLE_APP_ID</key> |
||||
|
<string>1:907754492821:ios:246794fc44b23174145c9c</string> |
||||
|
</dict> |
||||
|
</plist> |
||||
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
|
<plist version="1.0"> |
||||
|
<dict> |
||||
|
<key>keychain-access-groups</key> |
||||
|
<array> |
||||
|
<string>$(AppIdentifierPrefix)com.yunqiinnovation.deepsound</string> |
||||
|
</array> |
||||
|
</dict> |
||||
|
</plist> |
||||
@ -0,0 +1,270 @@ |
|||||
|
import 'dart:async'; |
||||
|
import 'dart:convert'; |
||||
|
import 'dart:typed_data'; |
||||
|
import 'package:crypto/crypto.dart'; |
||||
|
import 'package:record/record.dart'; |
||||
|
import 'package:web_socket_channel/web_socket_channel.dart'; |
||||
|
import '../../../core/utils/logger.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
import '../asr_service.dart'; |
||||
|
|
||||
|
/// 讯飞 语音识别服务 |
||||
|
/// |
||||
|
class XunfeiAsrService extends GetxService implements AsrService { |
||||
|
final String baseUrl = 'wss://rtasr.xfyun.cn/v1/ws'; |
||||
|
final int _chunkSize = 1280; // 每次发送的数据大小 |
||||
|
|
||||
|
final String appId = '43d87d22'; |
||||
|
final String apiKey = '83f063755e8d342670002f50a4471d42'; |
||||
|
|
||||
|
AudioRecorder? recorder; |
||||
|
Timer? _timer; |
||||
|
|
||||
|
WebSocketChannel? _channel; |
||||
|
bool isConnected = false; //是否链接 |
||||
|
|
||||
|
bool _isInitialized = false; |
||||
|
|
||||
|
final List<String> _defaultSupportedLanguages = ['zh-CN']; |
||||
|
String _languageStr = 'cn'; |
||||
|
|
||||
|
// 连续识别相关 |
||||
|
bool _isContinuousRecognitionActive = false; |
||||
|
StreamController<RecognitionEvent>? _eventStreamController; |
||||
|
|
||||
|
@override |
||||
|
List<String> get supportedLanguages => _defaultSupportedLanguages; |
||||
|
|
||||
|
Uint8List _buff = Uint8List(0); //音频缓存区 |
||||
|
BytesBuilder _originaBytes = BytesBuilder(); //原始音频 |
||||
|
bool isRecording = false; |
||||
|
|
||||
|
@override |
||||
|
Future<bool> initialize({ |
||||
|
List<String>? supportedLanguages, |
||||
|
}) async { |
||||
|
try { |
||||
|
final List<String> languages = |
||||
|
supportedLanguages ?? _defaultSupportedLanguages; |
||||
|
|
||||
|
String languagetoStr = languages.first.split('-')[1].toLowerCase(); |
||||
|
_languageStr = languagetoStr; |
||||
|
|
||||
|
_isInitialized = true; |
||||
|
Logger.info('Azure 语音识别服务初始化成功'); |
||||
|
return true; |
||||
|
} catch (e) { |
||||
|
Logger.error('Azure 语音识别服务初始化失败: $e'); |
||||
|
_isInitialized = false; |
||||
|
rethrow; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Future<RecognitionEvent> recognizeOnce() async { |
||||
|
if (!_isInitialized) { |
||||
|
await initialize(); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
throw Exception('无效的识别结果格式'); |
||||
|
} catch (e) { |
||||
|
Logger.error('语音识别失败: $e'); |
||||
|
return RecognitionEvent.error(e.toString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Future<Stream<RecognitionEvent>> startContinuousRecognition() async { |
||||
|
if (!_isInitialized) { |
||||
|
await initialize(); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
_eventStreamController = StreamController<RecognitionEvent>.broadcast(); |
||||
|
|
||||
|
await recorder?.dispose(); |
||||
|
recorder = AudioRecorder(); |
||||
|
Stream<Uint8List>? stream = await recorder?.startStream( |
||||
|
const RecordConfig( |
||||
|
sampleRate: 16000, |
||||
|
encoder: AudioEncoder.pcm16bits, |
||||
|
numChannels: 1, |
||||
|
), |
||||
|
); |
||||
|
isRecording = true; |
||||
|
stream?.listen((data) { |
||||
|
if (isRecording) { |
||||
|
_buff = _appendToBuffer(data); |
||||
|
_originaBytes.add(data); |
||||
|
} |
||||
|
}); |
||||
|
await _tryConnecting(); |
||||
|
_timer = Timer.periodic(const Duration(milliseconds: 40), (timer) async { |
||||
|
//每40毫秒读取一次数据 |
||||
|
var frame = _getAudioData(); |
||||
|
if (frame.isNotEmpty) { |
||||
|
_channel?.sink.add(frame); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
_isContinuousRecognitionActive = true; |
||||
|
return _eventStreamController!.stream; |
||||
|
} catch (e) { |
||||
|
Logger.error('开始连续语音识别失败: $e'); |
||||
|
rethrow; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//写入音频数据到缓存区中 |
||||
|
Uint8List _appendToBuffer(Uint8List newData) { |
||||
|
var newBuffer = Uint8List(_buff.length + newData.length); |
||||
|
newBuffer.setAll(0, _buff); |
||||
|
newBuffer.setAll(_buff.length, newData); |
||||
|
return newBuffer; |
||||
|
} |
||||
|
|
||||
|
//读取缓存区中一帧数据 |
||||
|
Uint8List _getAudioData() { |
||||
|
if (_buff.length >= _chunkSize) { |
||||
|
// 从缓冲区中读取1280字节的数据 |
||||
|
var data = _buff.sublist(0, _chunkSize); |
||||
|
// 移除已读取的数据 |
||||
|
_buff = _buff.sublist(_chunkSize); |
||||
|
return data; |
||||
|
} else { |
||||
|
return _buff; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//尝试连接 |
||||
|
Future<void> _tryConnecting() async { |
||||
|
try { |
||||
|
final url = _geturl(); |
||||
|
_channel = WebSocketChannel.connect(Uri.parse(url)); |
||||
|
_channel?.stream.timeout(const Duration(seconds: 10)); //设置超时时间 |
||||
|
await _channel?.ready; |
||||
|
isConnected = true; |
||||
|
_channel?.stream.listen( |
||||
|
(message) { |
||||
|
_handle(message); |
||||
|
}, |
||||
|
onError: (error) { |
||||
|
isConnected = false; |
||||
|
Logger.error('连接失败: $error'); |
||||
|
}, |
||||
|
onDone: () { |
||||
|
isConnected = false; |
||||
|
Logger.error('WebSocket 连接已关闭!'); |
||||
|
}, |
||||
|
cancelOnError: true, |
||||
|
); |
||||
|
} catch (e) { |
||||
|
Logger.error("初始化异常----------------$e"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//处理数据 |
||||
|
void _handle(data) { |
||||
|
try { |
||||
|
final result = jsonDecode(data); |
||||
|
if (result['code'] == "0" && result['data'] != "") { |
||||
|
final data = jsonDecode(result['data']); |
||||
|
String msg = ""; |
||||
|
String role = ''; |
||||
|
|
||||
|
List rtList = data['cn']['st']['rt']; |
||||
|
for (var rt in rtList) { |
||||
|
for (var ws in rt['ws']) { |
||||
|
for (var ws in ws['cw']) { |
||||
|
msg += ws['w']; |
||||
|
if (ws['rl'] != "0") { |
||||
|
role = ws['rl']; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
Logger.info( |
||||
|
"识别到的内容 type:${data['cn']['st']['type']} msg:$msg rl:$role"); |
||||
|
if (msg.isNotEmpty) { |
||||
|
if (data['cn']['st']['type'] == "0") { |
||||
|
_eventStreamController?.add(RecognitionEvent( |
||||
|
type: RecognitionEventType.finalResult, |
||||
|
text: msg, |
||||
|
role: role, |
||||
|
)); |
||||
|
} else { |
||||
|
_eventStreamController?.add(RecognitionEvent( |
||||
|
type: RecognitionEventType.intermediateResult, |
||||
|
text: msg, |
||||
|
role: role, |
||||
|
)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} catch (e) { |
||||
|
Logger.error("接受消息异常 $e"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
///获取链接地址 |
||||
|
String _geturl() { |
||||
|
// 生成时间戳 |
||||
|
String ts = (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(); |
||||
|
// 计算 baseString |
||||
|
String baseString = md5.convert(utf8.encode(appId + ts)).toString(); |
||||
|
// 计算 signa |
||||
|
var hmacSha1 = Hmac(sha1, utf8.encode(apiKey)); |
||||
|
var signa = hmacSha1.convert(utf8.encode(baseString)); |
||||
|
String signaBase64 = base64Encode(signa.bytes); |
||||
|
String url = ""; |
||||
|
// 构建 WebSocket URL |
||||
|
url = |
||||
|
'$baseUrl?appid=$appId&ts=$ts&lang=$_languageStr&roleType=2&signa=${Uri.encodeComponent(signaBase64)}'; |
||||
|
return url; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Future<bool> stopContinuousRecognition() async { |
||||
|
try { |
||||
|
await recorder?.stop(); |
||||
|
isRecording = false; |
||||
|
_timer?.cancel(); |
||||
|
_buff = Uint8List(0); |
||||
|
_originaBytes = BytesBuilder(); |
||||
|
if (isConnected) { |
||||
|
_channel?.sink.add(utf8.encode('{"end": true}')); //结束 |
||||
|
} |
||||
|
|
||||
|
_isContinuousRecognitionActive = false; |
||||
|
return true; |
||||
|
} catch (e) { |
||||
|
Logger.error('停止连续语音识别失败: $e'); |
||||
|
rethrow; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool isContinuousRecognitionActive() { |
||||
|
return _isContinuousRecognitionActive; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Future<void> dispose() async { |
||||
|
try { |
||||
|
await stopContinuousRecognition(); |
||||
|
recorder?.dispose(); |
||||
|
_channel?.sink.close(); |
||||
|
|
||||
|
_eventStreamController?.close(); |
||||
|
_eventStreamController = null; |
||||
|
|
||||
|
_isInitialized = false; |
||||
|
|
||||
|
Logger.info('Azure 语音识别资源已释放'); |
||||
|
} catch (e) { |
||||
|
Logger.error('释放语音识别资源失败: $e'); |
||||
|
_isInitialized = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue