Flutter三方库适配OpenHarmony【flutter_speech】— 持续语音识别与长录音

📅 发布时间:2026/7/11 17:32:10 👁️ 浏览次数:
Flutter三方库适配OpenHarmony【flutter_speech】— 持续语音识别与长录音
前言欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.netflutter_speech默认配置的最大录音时长是60秒识别模式是短语音recognitionMode0。对于语音搜索、语音指令这类场景完全够用。但如果你要做语音笔记、会议记录、实时字幕这类需要长时间识别的功能就需要突破这个限制。今天我们来探讨如何基于flutter_speech的架构实现持续语音识别。一、maxAudioDuration 参数扩展1.1 当前限制constextraParam:Recordstring,Object{recognitionMode:0,// 短语音模式vadBegin:2000,vadEnd:3000,maxAudioDuration:60000// 60秒};短语音模式下VAD检测到静音就会自动停止。即使把maxAudioDuration设到很大用户一停顿就结束了。1.2 长语音模式要实现持续识别需要切换到长语音模式constextraParam:Recordstring,Object{recognitionMode:1,// 长语音模式vadBegin:5000,// 5秒等待开口vadEnd:5000,// 5秒静音才停maxAudioDuration:300000// 5分钟};参数短语音模式长语音模式说明recognitionMode01长语音不会因静音自动停止vadBegin20005000给用户更多思考时间vadEnd30005000允许更长的停顿maxAudioDuration600003000005分钟1.3 通过Dart层传递模式参数当前flutter_speech的Dart API不支持传递识别模式。可以扩展// 扩展后的Dart API建议改进Futurelisten({bool continuousfalse})_channel.invokeMethod(speech.listen,{continuous:continuous,});// 原生端接收参数casespeech.listen:constargscall.argsasRecordstring,Object|null;constcontinuousargs?.[continuous]asboolean??false;this.startListening(result,continuous);break;二、长时间语音识别的会话管理2.1 单次长会话最简单的方案——一个会话持续到用户手动停止startListening(recognitionMode1, maxAudioDuration300000) │ ├── onResult(你好, isLastfalse) ├── onResult(你好今天, isLastfalse) ├── (用户停顿5秒) ├── onResult(你好今天开会, isLastfalse) ├── ...持续识别... │ └── 用户点击Stop → finish(sessionId) → onResult(最终结果, isLasttrue)优点实现简单。缺点maxAudioDuration有上限超长会话可能被系统中断。2.2 分段续接方案更健壮的方案——将长会话拆分为多个短会话自动续接第1段startListening → 识别 → onComplete → 保存结果 ↓ 自动 第2段startListening → 识别 → onComplete → 拼接结果 ↓ 自动 第3段startListening → 识别 → onComplete → 拼接结果 ↓ ... 直到用户手动停止2.3 分段续接的实现// 原生端实现扩展方案privatecontinuousMode:booleanfalse;privateaccumulatedText:string;privatesetupContinuousListener():void{if(!this.asrEngine)return;constchannelthis.channel;constpluginthis;this.asrEngine.setListener({onResult(sessionId,result){constfullTextplugin.accumulatedTextresult.result;channel?.invokeMethod(speech.onSpeech,fullText);if(result.isLast){plugin.accumulatedTextfullText;if(plugin.continuousMode){// 自动开始下一段plugin.startNextSegment();}else{plugin.isListeningfalse;channel?.invokeMethod(speech.onRecognitionComplete,fullText);}}},onComplete(sessionId,eventMessage){if(plugin.continuousModeplugin.isListening){plugin.startNextSegment();}},onError(sessionId,errorCode,errorMessage){console.error(TAG,onError in continuous mode:${errorCode});if(plugin.continuousModeerrorCode5){// 无语音输入继续等待plugin.startNextSegment();}else{plugin.isListeningfalse;channel?.invokeMethod(speech.onError,errorCode);}},// ... onStart, onEvent});}privatestartNextSegment():void{try{constparamsthis.buildStartParams(true);this.asrEngine?.startListening(params);}catch(e){console.error(TAG,startNextSegment error:${JSON.stringify(e)});}}2.4 分段间的无缝衔接分段续接的最大挑战是衔接处的文本连贯性。两段之间可能会有重复或遗漏第1段结果今天的会议主要讨论 第2段结果讨论三个议题 拼接结果今天的会议主要讨论讨论三个议题 ← 讨论重复了解决方案简单拼接直接拼接接受少量重复最简单重叠检测检测两段结尾和开头的重叠部分去重标点分隔在每段结尾加标点符号分隔// 简单的重叠去重privatemergeSegments(prev:string,next:string):string{// 检查prev的结尾是否和next的开头重叠constmaxOverlapMath.min(prev.length,next.length,10);for(letimaxOverlap;i0;i--){if(prev.endsWith(next.substring(0,i))){returnprevnext.substring(i);}}returnprevnext;}三、自动重连与断点续识策略3.1 网络中断的处理在线识别模式下网络中断会导致识别失败。对于长时间识别需要自动重连onError(sessionId,errorCode,errorMessage){if(plugin.continuousMode){if(errorCode1||errorCode2){// 网络错误延迟重试console.warn(TAG,network error, retrying in 3 seconds...);setTimeout((){if(plugin.continuousModeplugin.isListening){plugin.startNextSegment();}},3000);return;}}// 其他错误正常处理plugin.isListeningfalse;channel?.invokeMethod(speech.onError,errorCode);}3.2 重连策略策略实现适用场景立即重试错误后立即startListening临时网络抖动延迟重试等待3秒后重试网络短暂中断指数退避1s→2s→4s→8s…网络持续不稳定放弃超过N次重试后停止网络完全不可用3.3 断点续识网络恢复后已经说过的话不会重新识别。需要在UI上提示用户// Dart层提示voidonNetworkRecovery(){_showSnackBar(网络已恢复请继续说话);// 之前的识别结果已保存在accumulatedText中}四、识别结果拼接与文本累积4.1 文本累积策略// Dart层的文本累积classContinuousRecognitionState{finalListStringsegments[];// 每段的最终结果StringcurrentSegment;// 当前段的实时结果StringgetfullText{finalcompletedsegments.join();returncompletedcurrentSegment;}voidonPartialResult(Stringtext){currentSegmenttext;}voidonSegmentComplete(Stringtext){segments.add(text);currentSegment;}}4.2 UI显示// 显示累积的完整文本Widgetbuild(BuildContextcontext){returnSingleChildScrollView(child:Text(recognitionState.fullText,style:TextStyle(fontSize:16),),);}4.3 文本格式化长文本识别需要考虑格式化StringformatRecognitionText(Stringraw){// 1. 添加标点如果识别结果没有标点// 2. 分段落// 3. 首字母大写英文returnraw;}Core Speech Kit的中文识别通常会自带标点所以格式化的工作量不大。但分段续接时段与段之间的标点可能需要手动处理。五、电量与性能消耗的权衡5.1 长时间识别的资源消耗资源短语音(10秒)长语音(5分钟)长语音(1小时)CPU低中高内存~20MB~25MB~30MB网络流量~320KB~9.6MB~115MB电量忽略不计可感知显著5.2 优化建议降低CPU消耗使用长语音模式而不是反复创建短会话避免在onResult回调中做复杂计算降低网络消耗考虑离线识别模式准确率会降低弱网环境下降级到离线降低电量消耗在用户不说话时暂停识别但这会增加延迟提供省电模式选项5.3 用户提示长时间识别应该在UI上提示用户资源消耗// 显示录音时长和预估消耗Text(已录制${duration.inMinutes}:${(duration.inSeconds % 60).toString().padLeft(2, 0)}),Text(预估流量${(duration.inSeconds*32/1024).toStringAsFixed(1)}MB),5.4 后台识别的限制OpenHarmony对后台音频采集有限制。如果App进入后台麦克风可能被系统回收overridevoiddidChangeAppLifecycleState(AppLifecycleStatestate){if(stateAppLifecycleState.paused){// App进入后台if(_isListening){_speech.stop();// 保存当前结果_showNotification(语音识别已暂停);}}elseif(stateAppLifecycleState.resumed){// App回到前台if(_wasContinuousListening){_speech.listen();// 恢复识别}}}六、实现方案对比方案复杂度最大时长文本连贯性推荐场景单次长会话低5分钟好语音笔记分段续接中无限制中等会议记录分段重叠去重高无限制好实时字幕对于大多数场景单次长会话recognitionMode1 较大的maxAudioDuration就够了。只有需要超过5分钟的场景才需要分段续接。总结本文探讨了基于flutter_speech实现持续语音识别的方案长语音模式recognitionMode1不会因静音自动停止分段续接多个短会话自动衔接突破时长限制自动重连网络中断后延迟重试保证识别连续性文本累积分段结果拼接处理重叠和格式化资源权衡长时间识别需要关注CPU、内存、网络、电量消耗下一篇我们讲语音识别结果的后处理——标点符号、文本格式化、纠错等话题。如果这篇文章对你有帮助欢迎点赞、收藏、关注你的支持是我持续创作的动力相关资源Core Speech Kit长语音识别VAD语音活动检测指数退避算法Flutter App生命周期flutter_speech OpenHarmony源码开源鸿蒙跨平台社区