前言欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net引擎创建好了监听器也设置好了现在终于可以开始识别了。startListening方法是用户按下开始按钮后真正触发的动作也是整个语音识别流程中参数最多的一个环节。Core Speech Kit的startListening需要传入一个StartParams对象里面包含了音频参数、扩展参数、会话ID等配置。这些参数直接影响识别的质量和行为——采样率设错了识别不出来VAD参数设错了要么截断用户的话要么等半天才返回结果。我在调试这些参数的时候反复试了十几种组合才找到比较平衡的配置。今天把这些经验分享出来。本文对应源码FlutterSpeechPlugin.ets的startListening方法第180-222行。一、StartParams 参数结构详解1.1 整体结构interfaceStartParams{sessionId:string;// 会话IDaudioInfo:AudioInfo;// 音频参数extraParams:Recordstring,Object;// 扩展参数}三个字段每个都有讲究字段类型必填说明sessionIdstring✅标识本次识别会话audioInfoAudioInfo✅音频采集参数extraParamsRecordstring, Object✅识别行为控制参数1.2 flutter_speech中的完整构建privatestartListening(result:MethodResult):void{// ... 前置检查 ...constaudioParam:speechRecognizer.AudioInfo{audioType:pcm,sampleRate:16000,soundChannel:1,sampleBit:16};constextraParam:Recordstring,Object{recognitionMode:0,vadBegin:2000,vadEnd:3000,maxAudioDuration:60000};constrecognizerParams:speechRecognizer.StartParams{sessionId:this.sessionId,audioInfo:audioParam,extraParams:extraParam};this.asrEngine.startListening(recognizerParams);result.success(true);}二、AudioInfo 音频参数PCM 格式、采样率、声道、位深2.1 AudioInfo 结构interfaceAudioInfo{audioType:string;// 音频格式sampleRate:number;// 采样率HzsoundChannel:number;// 声道数sampleBit:number;// 位深bit}2.2 各参数详解audioType - 音频格式audioType:pcm值说明flutter_speech的选择‘pcm’原始PCM音频✅ 使用这个目前Core Speech Kit只支持PCM格式。PCMPulse Code Modulation脉冲编码调制是最基础的音频格式没有压缩数据量大但延迟低。为什么只支持PCM语音识别引擎需要原始音频数据来做特征提取。如果传入压缩格式如MP3、AAC引擎还需要先解码增加延迟。PCM直接就能用效率最高。sampleRate - 采样率sampleRate:16000采样率说明适用场景8000 Hz电话质量低带宽场景16000 Hz语音识别标准推荐44100 HzCD质量音乐录制48000 Hz专业音频视频制作16kHz是语音识别领域的事实标准。大多数语音识别模型都是用16kHz的数据训练的使用其他采样率可能导致识别准确率下降。我的测试结果我试过用8000Hz识别率明显下降尤其是声母相近的字容易混淆。用44100Hz倒是没问题但数据量大了将近3倍浪费带宽和内存。16000Hz是最佳平衡点。soundChannel - 声道数soundChannel:1声道数说明适用场景1单声道语音识别推荐2双声道立体声音乐播放语音识别只需要单声道。人说话的声音是单一音源双声道不会带来额外的信息量只会增加数据量。sampleBit - 位深sampleBit:16位深动态范围说明8 bit48 dB质量较低16 bit96 dB标准质量推荐24 bit144 dB专业录音32 bit192 dB极高精度16bit是语音识别的标准位深能提供足够的动态范围来区分语音中的细微差异。2.3 音频参数的数据量计算数据量 采样率 × 位深 × 声道数 × 时长 flutter_speech的配置 16000 Hz × 16 bit × 1 channel 256,000 bits/s 32 KB/s 录制60秒的数据量 32 KB/s × 60s 1,920 KB ≈ 1.9 MB每秒32KB的数据量对于在线传输来说完全可以接受。2.4 参数错误的后果错误配置后果表现audioType不是’pcm’引擎无法处理startListening抛异常sampleRate设为8000识别率下降识别结果不准确soundChannel设为2可能不兼容引擎可能报错sampleBit设为8音质太差识别率严重下降三、extraParams 扩展参数recognitionMode、VAD 配置3.1 extraParams 结构constextraParam:Recordstring,Object{recognitionMode:0,vadBegin:2000,vadEnd:3000,maxAudioDuration:60000};3.2 recognitionMode - 识别模式recognitionMode:0值模式说明适用场景0短语音模式识别短句VAD自动停止语音指令、搜索1长语音模式持续识别不自动停止会议记录、听写flutter_speech使用短语音模式0适合说一句话就停的交互场景。两种模式的行为差异短语音模式 (recognitionMode0) 用户说话 → 停顿 → VAD检测到静音 → 自动停止 → 返回结果 长语音模式 (recognitionMode1) 用户说话 → 停顿 → 继续等待 → 用户继续说 → ... → 手动调用finish停止选择建议如果你的App是按住说话的交互模式用短语音模式。如果是持续听写的模式比如语音笔记用长语音模式。3.3 vadBegin - VAD开始超时vadBegin:2000// 毫秒含义开始监听后如果在vadBegin毫秒内没有检测到语音就认为用户没有说话自动停止识别。startListening │ ├── 0ms: 开始等待语音 │ ├── 500ms: 还没检测到语音... │ ├── 1000ms: 还没检测到语音... │ ├── 1500ms: 还没检测到语音... │ └── 2000ms: vadBegin超时→ onError(无语音输入)vadBegin值效果适用场景1000ms等待1秒快速响应场景2000ms等待2秒平衡推荐5000ms等待5秒用户可能需要思考10000ms等待10秒极端宽松调优经验2秒是比较合理的值。太短的话用户还没来得及说话就超时了太长的话用户不说话时要等很久才能收到反馈。3.4 vadEnd - VAD结束超时vadEnd:3000// 毫秒含义检测到语音后如果连续vadEnd毫秒没有新的语音输入静音就认为用户说完了自动停止识别。用户说话 │ ├── 今天天气 ← 正在说 │ ├── 停顿 0.5秒 ← 还在vadEnd范围内继续等待 │ ├── 怎么样 ← 继续说 │ ├── 停顿 1秒 ← 还在范围内 │ ├── 停顿 2秒 ← 还在范围内 │ └── 停顿 3秒 ← vadEnd超时→ 认为说完了 → 返回最终结果vadEnd值效果适用场景1000ms1秒静音就停快速指令2000ms2秒静音就停短句识别3000ms3秒静音就停平衡推荐5000ms5秒静音就停长句/思考型vadEnd的取舍设太短用户说话中间停顿一下就被截断了比如我想…嗯…去北京中间的停顿。设太长用户说完后要等好几秒才能看到最终结果。3秒是我测试下来的最佳平衡点。3.5 vadBegin和vadEnd的配合vadBegin2000, vadEnd3000 的完整时序 t0s: startListening → 开始等待语音 t0-2s: 等待用户开口vadBegin倒计时 t0.8s: 用户开始说话 → vadBegin停止计时 t0.8-3s: 用户持续说话 t3s: 用户停止说话 → vadEnd开始计时 t3-6s: 等待用户继续说vadEnd倒计时 t6s: vadEnd超时 → 返回最终结果四、sessionId 会话管理机制4.1 sessionId的作用privatesessionId:string10000;sessionId是识别会话的唯一标识用于区分不同的识别会话虽然flutter_speech只有一个会话在回调中关联请求每个回调都会带上sessionId停止/取消指定会话finish(sessionId)和cancel(sessionId)需要指定4.2 flutter_speech的sessionId策略flutter_speech使用了固定的sessionId10000因为同一时间只有一个识别会话不需要区分多个并发会话简化了代码逻辑// 所有操作都用同一个sessionIdthis.asrEngine.startListening({sessionId:this.sessionId,...});this.asrEngine.finish(this.sessionId);this.asrEngine.cancel(this.sessionId);4.3 如果需要动态sessionId如果你的应用需要管理多个识别会话flutter_speech不需要可以用时间戳生成唯一ID// 动态生成sessionIdflutter_speech未使用privategenerateSessionId():string{returnsession_${Date.now()}_${Math.random().toString(36).substr(2,9)};}4.4 sessionId与回调的关联每个监听器回调都会带上sessionId参数onStart(sessionId:string,eventMessage:string):void{...}onResult(sessionId:string,result:SpeechRecognitionResult):void{...}onError(sessionId:string,errorCode:number,errorMessage:string):void{...}flutter_speech在回调中只用sessionId做日志记录不做逻辑判断onStart(sessionId:string,eventMessage:string):void{console.info(TAG,onStart: sessionId${sessionId});// 仅日志channel?.invokeMethod(speech.onRecognitionStarted,null);},五、maxAudioDuration 最大录音时长控制5.1 参数含义maxAudioDuration:60000// 毫秒 60秒这个参数限制了单次识别的最大录音时长。超过这个时长引擎会自动停止识别并返回结果。5.2 为什么需要限制防止资源浪费无限制的录音会持续占用麦克风和网络用户体验超长的识别通常意味着用户忘记停止了系统限制Core Speech Kit可能有内部的时长限制5.3 不同场景的建议值场景建议值说明语音搜索15000 (15秒)搜索词通常很短语音指令10000 (10秒)指令更短通用场景60000 (60秒)flutter_speech的选择语音笔记300000 (5分钟)长文本听写会议记录无限制需要特殊处理flutter_speech选择了60秒对于大多数语音识别场景来说足够了。5.4 超时后的行为t0s: startListening t0-60s: 正常识别 t60s: maxAudioDuration超时 → onResult(最终结果, isLasttrue) → onComplete超时后引擎会自动停止和用户主动调用stop的效果类似。六、startListening 方法的完整实现6.1 完整源码带注释privatestartListening(result:MethodResult):void{// 前置检查 if(!this.asrEngine){result.error(ERROR_ENGINE_NOT_INITIALIZED,Speech engine not initialized. Call activate first.,null);return;}try{// 防重入处理 if(this.isListening){this.asrEngine.cancel(this.sessionId);this.isListeningfalse;}// 重置状态 this.lastTranscription;this.isListeningtrue;// 构建音频参数 constaudioParam:speechRecognizer.AudioInfo{audioType:pcm,sampleRate:16000,soundChannel:1,sampleBit:16};// 构建扩展参数 constextraParam:Recordstring,Object{recognitionMode:0,vadBegin:2000,vadEnd:3000,maxAudioDuration:60000};// 构建启动参数 constrecognizerParams:speechRecognizer.StartParams{sessionId:this.sessionId,audioInfo:audioParam,extraParams:extraParam};// 开始识别 this.asrEngine.startListening(recognizerParams);result.success(true);}catch(e){console.error(TAG,startListening error:${JSON.stringify(e)});this.isListeningfalse;result.error(ERROR_SPEECH_LISTEN,Failed to start listening:${JSON.stringify(e)},null);}}6.2 流程图startListening(result) │ ├── asrEngine null? │ └── 是 → error(ERROR_ENGINE_NOT_INITIALIZED) → return │ ├── isListening true? (防重入) │ └── 是 → cancel当前会话 → isListening false │ ├── 重置状态 │ ├── lastTranscription │ └── isListening true │ ├── 构建参数 │ ├── audioParam: PCM/16kHz/单声道/16bit │ ├── extraParam: 短语音/VAD 2s3s/最长60s │ └── recognizerParams: sessionId audio extra │ ├── asrEngine.startListening(recognizerParams) │ └── 失败 → catch → isListening false → error │ └── result.success(true)6.3 前置检查引擎未初始化if(!this.asrEngine){result.error(ERROR_ENGINE_NOT_INITIALIZED,Speech engine not initialized. Call activate first.,null);return;}如果用户没有先调用activate就直接调用listen引擎还没创建需要返回明确的错误提示。6.4 防重入处理if(this.isListening){this.asrEngine.cancel(this.sessionId);this.isListeningfalse;}如果上一次识别还没结束用户又点了开始需要先取消上一次再开始新的。不做这个处理的话可能会出现两个识别会话冲突的问题。踩坑经历我一开始没做防重入测试时快速连续点击开始按钮引擎就报错了。加了这个逻辑后就稳定了。6.5 状态重置this.lastTranscription;this.isListeningtrue;每次开始新的识别前清空上次的识别结果并标记为正在监听状态。七、与Android startListening的对比7.1 代码对比AndroidprivatevoidstartListening(MethodChannel.Resultresult){IntentintentnewIntent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS,true);intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS,3);intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE,locale);speechRecognizer.startListening(intent);result.success(true);}OpenHarmonyprivatestartListening(result:MethodResult):void{constrecognizerParams:speechRecognizer.StartParams{sessionId:this.sessionId,audioInfo:{audioType:pcm,sampleRate:16000,soundChannel:1,sampleBit:16},extraParams:{recognitionMode:0,vadBegin:2000,vadEnd:3000,maxAudioDuration:60000}};this.asrEngine.startListening(recognizerParams);result.success(true);}7.2 参数对比功能Android (Intent)OpenHarmony (StartParams)语言EXTRA_LANGUAGE创建引擎时指定部分结果EXTRA_PARTIAL_RESULTStrue默认支持最大结果数EXTRA_MAX_RESULTS不支持音频格式系统自动audioInfo手动指定VAD控制系统默认vadBegin/vadEnd可配最大时长系统默认maxAudioDuration可配识别模式LANGUAGE_MODEL_*recognitionModeOpenHarmony的参数控制更精细Android的很多参数是系统默认的开发者无法控制。OpenHarmony允许开发者精确配置音频参数和VAD参数灵活性更高。八、参数调优建议8.1 不同场景的推荐配置语音搜索场景constextraParam{recognitionMode:0,// 短语音vadBegin:1500,// 1.5秒等待开口vadEnd:2000,// 2秒静音就停maxAudioDuration:15000// 最长15秒};语音指令场景constextraParam{recognitionMode:0,// 短语音vadBegin:2000,// 2秒等待vadEnd:1500,// 1.5秒静音就停指令通常很短maxAudioDuration:10000// 最长10秒};听写/笔记场景constextraParam{recognitionMode:1,// 长语音vadBegin:5000,// 5秒等待用户可能在思考vadEnd:5000,// 5秒静音才停允许长停顿maxAudioDuration:300000// 最长5分钟};8.2 调优原则vadBegin根据用户从点击按钮到开始说话的平均时间来设置。通常1.5-3秒vadEnd根据用户说话中停顿的平均时长来设置。短句1.5-2秒长句3-5秒maxAudioDuration根据业务场景的最大合理时长来设置。宁可设大一点recognitionMode一句话的交互用0持续听写用1总结本文详细讲解了flutter_speech语音识别启动的参数配置AudioInfoPCM格式、16kHz采样率、单声道、16bit位深——语音识别的标准配置recognitionMode0短语音自动停止1长语音手动停止VAD参数vadBegin控制等待开口时间vadEnd控制静音停止时间maxAudioDuration最大录音时长防止无限录音sessionId会话标识flutter_speech使用固定值’10000’防重入重复调用时先cancel旧会话再开始新会话下一篇我们讲语音识别的停止与取消——stop和cancel两个方法的语义区别和实现细节。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源Core Speech Kit StartParams文档PCM音频格式详解VAD语音活动检测Android RecognizerIntent文档flutter_speech OpenHarmony源码音频采样率与语音识别开源鸿蒙跨平台社区Flutter-OHOS适配指南