前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
引擎创建好了,监听器也设置好了,现在终于可以开始识别了。
startListening方法是用户按下"开始"按钮后真正触发的动作,也是整个语音识别流程中参数最多的一个环节。
Core
Speech
Kit的startListening需要传入一个StartParams对象,里面包含了音频参数、扩展参数、会话ID等配置。
这些参数直接影响识别的质量和行为——采样率设错了识别不出来,VAD参数设错了要么截断用户的话,要么等半天才返回结果。
我在调试这些参数的时候,反复试了十几种组合才找到比较平衡的配置。
今天把这些经验分享出来。
💡本文对应源码:
FlutterSpeechPlugin.ets的startListening方法,第180-222行。
一、StartParams参数结构详解
1.1
整体结构
interfaceStartParams{sessionId:string;//会话ID
audioInfo:AudioInfo;//音频参数
extraParams:Record<string,Object>;//扩展参数
}三个字段,每个都有讲究:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| sessionId | string | ✅ | 标识本次识别会话 |
| audioInfo | AudioInfo | ✅ | 音频采集参数 |
| extraParams | Record<string, Object> | ✅ | 识别行为控制参数 |
1.2
flutter_speech中的完整构建
privatestartListening(result:MethodResult):void{//...
...
constaudioParam:speechRecognizer.AudioInfo={audioType:'pcm',sampleRate:16000,soundChannel:1,sampleBit:16};constextraParam:Record<string,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
结构
interfaceAudioInfo{audioType:string;//音频格式
sampleRate:number;//采样率(Hz)
soundChannel:number;//声道数
sampleBit:number;//位深(bit)
}2.2
音频格式
audioType:'pcm'| 值 | 说明 | flutter_speech的选择 |
|---|---|---|
| ‘pcm’ | 原始PCM音频 | ✅ 使用这个 |
目前Core
Speech
Modulation,脉冲编码调制)是最基础的音频格式,没有压缩,数据量大但延迟低。
📌为什么只支持PCM:语音识别引擎需要原始音频数据来做特征提取。
如果传入压缩格式(如MP3、AAC),引擎还需要先解码,增加延迟。
PCM直接就能用,效率最高。
sampleRate
采样率
sampleRate:16000| 采样率 | 说明 | 适用场景 |
|---|---|---|
| 8000 Hz | 电话质量 | 低带宽场景 |
| 16000 Hz | 语音识别标准 | 推荐 |
| 44100 Hz | CD质量 | 音乐录制 |
| 48000 Hz | 专业音频 | 视频制作 |
16kHz是语音识别领域的事实标准。
大多数语音识别模型都是用16kHz的数据训练的,使用其他采样率可能导致识别准确率下降。
💡我的测试结果:我试过用8000Hz,识别率明显下降,尤其是声母相近的字容易混淆。
用44100Hz倒是没问题,但数据量大了将近3倍,浪费带宽和内存。
16000Hz是最佳平衡点。
soundChannel
声道数
soundChannel:1| 声道数 | 说明 | 适用场景 |
|---|---|---|
| 1 | 单声道 | 语音识别(推荐) |
| 2 | 双声道(立体声) | 音乐播放 |
语音识别只需要单声道。
人说话的声音是单一音源,双声道不会带来额外的信息量,只会增加数据量。
sampleBit
位深
sampleBit:16| 位深 | 动态范围 | 说明 |
|---|---|---|
8bit | 48 dB | 质量较低 |
| 16 dB | 标准质量(推荐) | |
24bit | 144 dB | 专业录音 |
32bit | 192 dB | 极高精度 |
16bit是语音识别的标准位深,能提供足够的动态范围来区分语音中的细微差异。
2.3=
MB
每秒32KB的数据量对于在线传输来说完全可以接受。
2.4
参数错误的后果
| 错误配置 | 后果 | 表现 |
|---|---|---|
| audioType不是’pcm’ | 引擎无法处理 | startListening抛异常 |
| sampleRate设为8000 | 识别率下降 | 识别结果不准确 |
| soundChannel设为2 | 可能不兼容 | 引擎可能报错 |
| sampleBit设为8 | 音质太差 | 识别率严重下降 |
三、extraParams
扩展参数:recognitionMode、VAD
配置3.1
结构
constextraParam:Record<string,Object>={"recognitionMode":0,"vadBegin":2000,"vadEnd":3000,"maxAudioDuration":60000};3.2recognitionMode
识别模式
"recognitionMode":0| 值 | 模式 | 说明 | 适用场景 |
|---|---|---|---|
| 0 | 短语音模式 | 识别短句,VAD自动停止 | 语音指令、搜索 |
| 1 | 长语音模式 | 持续识别,不自动停止 | 会议记录、听写 |
flutter_speech使用短语音模式(0),适合"说一句话就停"的交互场景。
两种模式的行为差异:
短语音模式(recognitionMode=0):
用户说话
(recognitionMode=1):
用户说话
手动调用finish停止
🎯选择建议:如果你的App是"按住说话"的交互模式,用短语音模式。
如果是"持续听写"的模式(比如语音笔记),用长语音模式。
3.3vadBegin
VAD开始超时
"vadBegin":2000//毫秒
含义:开始监听后,如果在vadBegin毫秒内没有检测到语音,就认为用户没有说话,自动停止识别。
startListening├──
onError(无语音输入)
| vadBegin值 | 效果 | 适用场景 |
|---|---|---|
| 1000ms | 等待1秒 | 快速响应场景 |
| 2000ms | 等待2秒 | 平衡(推荐) |
| 5000ms | 等待5秒 | 用户可能需要思考 |
| 10000ms | 等待10秒 | 极端宽松 |
💡调优经验:2秒是比较合理的值。
太短的话,用户还没来得及说话就超时了;太长的话,用户不说话时要等很久才能收到反馈。
3.4vadEnd
VAD结束超时
"vadEnd":3000//毫秒
含义:检测到语音后,如果连续vadEnd毫秒没有新的语音输入(静音),就认为用户说完了,自动停止识别。
用户说话├──
返回最终结果
| vadEnd值 | 效果 | 适用场景 |
|---|---|---|
| 1000ms | 1秒静音就停 | 快速指令 |
| 2000ms | 2秒静音就停 | 短句识别 |
| 3000ms | 3秒静音就停 | 平衡(推荐) |
| 5000ms | 5秒静音就停 | 长句/思考型 |
🤔vadEnd的取舍:设太短,用户说话中间停顿一下就被截断了(比如"我想…嗯…去北京"中间的停顿)。
设太长,用户说完后要等好几秒才能看到最终结果。
3秒是我测试下来的最佳平衡点。
3.5
vadBegin和vadEnd的配合
vadBegin=2000,vadEnd=3000
等待用户开口(vadBegin倒计时)
t=0.8s:
等待用户继续说(vadEnd倒计时)
t=6s:
返回最终结果
四、sessionId会话管理机制
4.1
sessionId的作用
privatesessionId:string='10000';sessionId是识别会话的唯一标识,用于:
- 区分不同的识别会话:虽然flutter_speech只有一个会话
- 在回调中关联请求:每个回调都会带上sessionId
- 停止/取消指定会话:
finish(sessionId)和cancel(sessionId)需要指定
4.2
flutter_speech的sessionId策略
flutter_speech使用了固定的sessionId('10000'),因为:
- 同一时间只有一个识别会话
- 不需要区分多个并发会话
- 简化了代码逻辑
//所有操作都用同一个sessionId
this.asrEngine.startListening({sessionId:this.sessionId,...});this.asrEngine.finish(this.sessionId);this.asrEngine.cancel(this.sessionId);4.3
如果需要动态sessionId
如果你的应用需要管理多个识别会话(flutter_speech不需要),可以用时间戳生成唯一ID:
//动态生成sessionId(flutter_speech未使用)
privategenerateSessionId():string{return`session_${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
不同场景的建议值
场景 建议值 说明 语音搜索 15000 (15秒)
搜索词通常很短 语音指令 10000 (10秒)
指令更短 通用场景 60000 (60秒)
flutter_speech的选择 语音笔记 300000 (5分钟)
长文本听写 会议记录 无限制 需要特殊处理 flutter_speech选择了60秒,对于大多数语音识别场景来说足够了。
5.4
超时后的行为
t=0s:startListening
onComplete
超时后引擎会自动停止,和用户主动调用
stop的效果类似。六、startListening
完整源码(带注释)
privatestartListening(result:MethodResult):void{//==========
前置检查
==========
if(!this.asrEngine){result.error('ERROR_ENGINE_NOT_INITIALIZED','Speechengine
first.'
,null);return;}try{//==========
防重入处理
==========
if(this.isListening){this.asrEngine.cancel(this.sessionId);this.isListening=false;}//==========
重置状态
==========
this.lastTranscription='';this.isListening=true;//==========
构建音频参数
==========
constaudioParam:speechRecognizer.AudioInfo={audioType:'pcm',sampleRate:16000,soundChannel:1,sampleBit:16};//==========
构建扩展参数
==========
constextraParam:Record<string,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,`startListeningerror:
${JSON.stringify(e)}`);this.isListening=false;result.error('ERROR_SPEECH_LISTEN',`Failedstart
listening:
${JSON.stringify(e)}`,null);}}6.2
流程图
startListening(result)├──
error('ERROR_ENGINE_NOT_INITIALIZED')
return
asrEngine.startListening(recognizerParams)
└──
result.success(true)
6.3
前置检查:引擎未初始化
if(!this.asrEngine){result.error('ERROR_ENGINE_NOT_INITIALIZED','Speechengine
first.'
,null);return;}如果用户没有先调用
activate就直接调用listen,引擎还没创建,需要返回明确的错误提示。6.4
防重入处理
if(this.isListening){this.asrEngine.cancel(this.sessionId);this.isListening=false;}如果上一次识别还没结束,用户又点了"开始",需要先取消上一次再开始新的。
不做这个处理的话,可能会出现两个识别会话冲突的问题。
🤦踩坑经历:我一开始没做防重入,测试时快速连续点击"开始"按钮,引擎就报错了。
加了这个逻辑后就稳定了。
6.5
状态重置
this.lastTranscription='';this.isListening=true;每次开始新的识别前,清空上次的识别结果,并标记为"正在监听"状态。
七、与Android
代码对比
Android:
privatevoidstartListening(MethodChannel.Resultresult){Intentintent=newIntent(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);}OpenHarmony:
privatestartListening(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 (StartParams)
语言 EXTRA_LANGUAGE 创建引擎时指定 部分结果 EXTRA_PARTIAL_RESULTS=true 默认支持 最大结果数 EXTRA_MAX_RESULTS 不支持 音频格式 系统自动 audioInfo手动指定 VAD控制 系统默认 vadBegin/vadEnd可配 最大时长 系统默认 maxAudioDuration可配 识别模式 LANGUAGE_MODEL_* recognitionMode 📌OpenHarmony的参数控制更精细: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语音识别启动的参数配置:
- AudioInfo:PCM格式、16kHz采样率、单声道、16bit位深——语音识别的标准配置
- recognitionMode:0=短语音(自动停止),1=长语音(手动停止)
- VAD参数:vadBegin控制等待开口时间,vadEnd控制静音停止时间
- maxAudioDuration:最大录音时长,防止无限录音
- sessionId:会话标识,flutter_speech使用固定值’10000’
- 防重入:重复调用时先cancel旧会话再开始新会话
下一篇我们讲语音识别的停止与取消——
stop和cancel两个方法的语义区别和实现细节。如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
/>
相关资源:
- Core
Speech
StartParams文档
- PCM音频格式详解
- VAD语音活动检测
- Android
RecognizerIntent文档
- flutter_speech
OpenHarmony源码
- 音频采样率与语音识别
- 开源鸿蒙跨平台社区
- Flutter-OHOS适配指南
/>
- vadBegin:根据用户从点击按钮到开始说话的平均时间来设置。


