当前位置: 首页 > news >正文

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

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

flutter_speech默认配置的最大录音时长是60秒,识别模式是短语音(recognitionMode=0)。对于语音搜索、语音指令这类场景完全够用。但如果你要做语音笔记、会议记录、实时字幕这类需要长时间识别的功能,就需要突破这个限制。

今天我们来探讨如何基于flutter_speech的架构实现持续语音识别。

一、maxAudioDuration 参数扩展

1.1 当前限制

constextraParam:Record<string,Object>={"recognitionMode":0,// 短语音模式"vadBegin":2000,"vadEnd":3000,"maxAudioDuration":60000// 60秒};

短语音模式下,VAD检测到静音就会自动停止。即使把maxAudioDuration设到很大,用户一停顿就结束了。

1.2 长语音模式

要实现持续识别,需要切换到长语音模式

constextraParam:Record<string,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 continuous=false})=>_channel.invokeMethod("speech.listen",{'continuous':continuous,});
// 原生端接收参数case"speech.listen":constargs=call.argsasRecord<string,Object>|null;constcontinuous=args?.['continuous']asboolean??false;this.startListening(result,continuous);break;

二、长时间语音识别的会话管理

2.1 单次长会话

最简单的方案——一个会话持续到用户手动停止:

startListening(recognitionMode=1, maxAudioDuration=300000) │ ├── onResult("你好", isLast=false) ├── onResult("你好今天", isLast=false) ├── (用户停顿5秒) ├── onResult("你好今天开会", isLast=false) ├── ...持续识别... │ └── 用户点击Stop → finish(sessionId) → onResult(最终结果, isLast=true)

优点:实现简单。
缺点:maxAudioDuration有上限,超长会话可能被系统中断。

2.2 分段续接方案

更健壮的方案——将长会话拆分为多个短会话,自动续接:

第1段:startListening → 识别 → onComplete → 保存结果 ↓ 自动 第2段:startListening → 识别 → onComplete → 拼接结果 ↓ 自动 第3段:startListening → 识别 → onComplete → 拼接结果 ↓ ... 直到用户手动停止

2.3 分段续接的实现

// 原生端实现(扩展方案)privatecontinuousMode:boolean=false;privateaccumulatedText:string='';privatesetupContinuousListener():void{if(!this.asrEngine)return;constchannel=this.channel;constplugin=this;this.asrEngine.setListener({onResult(sessionId,result){constfullText=plugin.accumulatedText+result.result;channel?.invokeMethod('speech.onSpeech',fullText);if(result.isLast){plugin.accumulatedText=fullText;if(plugin.continuousMode){// 自动开始下一段plugin.startNextSegment();}else{plugin.isListening=false;channel?.invokeMethod('speech.onRecognitionComplete',fullText);}}},onComplete(sessionId,eventMessage){if(plugin.continuousMode&&plugin.isListening){plugin.startNextSegment();}},onError(sessionId,errorCode,errorMessage){console.error(TAG,`onError in continuous mode:${errorCode}`);if(plugin.continuousMode&&errorCode===5){// 无语音输入,继续等待plugin.startNextSegment();}else{plugin.isListening=false;channel?.invokeMethod('speech.onError',errorCode);}},// ... onStart, onEvent});}privatestartNextSegment():void{try{constparams=this.buildStartParams(true);this.asrEngine?.startListening(params);}catch(e){console.error(TAG,`startNextSegment error:${JSON.stringify(e)}`);}}

2.4 分段间的无缝衔接

分段续接的最大挑战是衔接处的文本连贯性。两段之间可能会有重复或遗漏:

第1段结果:"今天的会议主要讨论" 第2段结果:"讨论三个议题" 拼接结果:"今天的会议主要讨论讨论三个议题" ← "讨论"重复了

解决方案:

  1. 简单拼接:直接拼接,接受少量重复(最简单)
  2. 重叠检测:检测两段结尾和开头的重叠部分,去重
  3. 标点分隔:在每段结尾加标点符号分隔
// 简单的重叠去重privatemergeSegments(prev:string,next:string):string{// 检查prev的结尾是否和next的开头重叠constmaxOverlap=Math.min(prev.length,next.length,10);for(leti=maxOverlap;i>0;i--){if(prev.endsWith(next.substring(0,i))){returnprev+next.substring(i);}}returnprev+next;}

三、自动重连与断点续识策略

3.1 网络中断的处理

在线识别模式下,网络中断会导致识别失败。对于长时间识别,需要自动重连:

onError(sessionId,errorCode,errorMessage){if(plugin.continuousMode){if(errorCode===1||errorCode===2){// 网络错误,延迟重试console.warn(TAG,'network error, retrying in 3 seconds...');setTimeout(()=>{if(plugin.continuousMode&&plugin.isListening){plugin.startNextSegment();}},3000);return;}}// 其他错误正常处理plugin.isListening=false;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{finalList<String>segments=[];// 每段的最终结果StringcurrentSegment='';// 当前段的实时结果StringgetfullText{finalcompleted=segments.join('');returncompleted+currentSegment;}voidonPartialResult(Stringtext){currentSegment=text;}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(state==AppLifecycleState.paused){// App进入后台if(_isListening){_speech.stop();// 保存当前结果_showNotification('语音识别已暂停');}}elseif(state==AppLifecycleState.resumed){// App回到前台if(_wasContinuousListening){_speech.listen();// 恢复识别}}}

六、实现方案对比

方案复杂度最大时长文本连贯性推荐场景
单次长会话5分钟语音笔记
分段续接无限制中等会议记录
分段+重叠去重无限制实时字幕

对于大多数场景,单次长会话(recognitionMode=1 + 较大的maxAudioDuration)就够了。只有需要超过5分钟的场景才需要分段续接。

总结

本文探讨了基于flutter_speech实现持续语音识别的方案:

  1. 长语音模式:recognitionMode=1,不会因静音自动停止
  2. 分段续接:多个短会话自动衔接,突破时长限制
  3. 自动重连:网络中断后延迟重试,保证识别连续性
  4. 文本累积:分段结果拼接,处理重叠和格式化
  5. 资源权衡:长时间识别需要关注CPU、内存、网络、电量消耗

下一篇我们讲语音识别结果的后处理——标点符号、文本格式化、纠错等话题。

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!


相关资源:

  • Core Speech Kit长语音识别
  • VAD语音活动检测
  • 指数退避算法
  • Flutter App生命周期
  • flutter_speech OpenHarmony源码
  • 开源鸿蒙跨平台社区

http://www.jsqmd.com/news/399216/

相关文章:

  • 2026版Eclipse IDE深度解析:从Java开发到全栈容器化部署的终极指南
  • DeepSeek总结的PostgreSQL 19新功能:第一部分
  • 卫星通信系统工程设计与应用【1.9】
  • 原创论文:基于LSTM的共享单车需求预测研究
  • 《时间简史》深度读书笔记(系统整合扩展版)
  • vue+springboot校园综合服务系统的设计与实现
  • vue+springboot校园资料分享推荐系统 学习资源共享系统
  • 基于Matlab的六自由度并联摇摆台反解控制算法探索:Stewart平台与GUI的魅力结合
  • python继承list类
  • amp;#128640; Manim CE v0.20.0 发布:动画构建更丝滑,随机性终于“可控”了!
  • 镜像宣城示范工程:三维空间计算重塑城市运行逻辑——基于三角测量厘米级定位与无感连续表达的空间级感知革命
  • 在“成为超人”与“仍是自己”之间:神经增强时代的哲学追问与文明责任——基于赵中华《神经增强的超人类主义叙事与批判性反思》的延伸思考
  • python dict setdefault方法
  • 无人驾驶-2024-09-智能驾驶与机器视觉08:视觉建图与定位
  • 食品X光机技术盘点:从看见到看懂核心升级
  • 企业集成平台iPaaS市场格局与主流产品选型测评
  • API管理系统:企业数字化转型的桥梁与核心价值解析
  • 《信号与系统》欧拉公式、泰勒级数、拉普拉斯变换、傅里叶变换、小波变换,他们出现的时间顺序以及他们之间的关系
  • MySQL锁机制:行锁与表锁及锁升级原理
  • 第3章 Windows运行机理-3.1 内核分析(6)
  • [Kaleidscope of Physics] 有心力和有心运动
  • 第3章 Windows运行机理-3.1 内核分析(7)
  • 2026年永真片市场概览:哪些品牌口碑与销量俱佳?永真片/生脉饮/养胃颗粒/抗衰老片/人参方,永真片品牌推荐排行榜单 - 品牌推荐师
  • 深入浅出Java线程池(二)
  • 本科生收藏!千笔,备受推崇的AI论文平台
  • vue+springboot校园学生健康监测数据管理系统的设计与实现
  • vue+springboot校园活动报名系统 场地预约系统
  • vue+springboot微信小程序 网上订餐配送系统
  • vue+springboot新农村信息平台建设——土地资源管理子系统
  • 浮点数在内存中的存储结构