news 2026/7/5 2:00:02

《HarmonyOS技术精讲-Core Speech Kit(基础语音服务)》第2篇:语音识别核心功能——流式与非流式实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《HarmonyOS技术精讲-Core Speech Kit(基础语音服务)》第2篇:语音识别核心功能——流式与非流式实现

HarmonyOS技术精讲-Core Speech Kit(基础语音服务)第2篇:语音识别核心功能——流式与非流式实现

语音识别在HarmonyOS应用开发中,很多场景都绕不开。比如语音搜索、语音转写、语音控制等。官方提供的Core Speech Kit(基础语音服务)里,SpeechRecognizer这个API是核心入口。很多人第一次接触时,会发现官方示例能运行,但放到实际项目里,状态同步、生命周期、回调处理这些细节很容易出问题。

这个功能本身不复杂,但不同场景选错模式,体验会差很多。流式识别适合实时反馈,比如边说话边出文字;一次性识别适合离线快速处理一段固定的音频。选错方案,要么延迟太高,要么资源浪费。

它解决什么问题

SpeechRecognizer提供了两种语音识别模式:

  • 流式识别(Streaming):边输入边输出结果,适合实时语音转写、语音搜索。
  • 非流式识别(One-shot):输入一段完整的音频,一次性返回识别结果,适合语音指令、固定音频转写。
特性流式识别非流式识别
输入方式持续麦克风输入一次性音频文件
实时性高,边说话边出结果低,等待完整音频处理
延迟低,结果逐步返回较高,需完整处理后返回
适用场景实时字幕、语音搜索离线命令、一键转写
资源消耗持续占用麦克风和网络单次请求,占用少
离线支持支持(需加载离线模型)支持(需加载离线模型)

推荐场景

  • 流式:语音转字幕、语音搜索、语音助手实时交互
  • 非流式:语音指令(如“打开设置”)、会议录音转写

不推荐场景

  • 不需要实时反馈的,不应该用流式,浪费资源
  • 需要快速响应的,不应该用非流式,用户等待体验差

环境说明

DevEco Studio 版本:DevEco Studio 6.1.0 及以上 HarmonyOS SDK 版本:HarmonyOS 6.1.0(23) 及以上 目标设备:手机 / 平板

核心实现:流式语音识别

流式识别核心是startListeningstopListening。关键在于正确设置setListener的回调,处理onResultsonErroronEndOfSpeech等事件。

// StreamingRecognizer.etsimport{speechRecognizer}from'@kit.CoreSpeechKit';import{BusinessError}from'@kit.BasicServicesKit';import{audio}from'@kit.AudioKit';@Entry@Componentstruct StreamingRecognizerPage{@StaterecognizedText:string='';@StateisListening:boolean=false;privaterecognizer:speechRecognizer.SpeechRecognizer|null=null;privateaudioCapturer:audio.AudioCapturer|null=null;aboutToAppear():void{this.initRecognizer();}aboutToDisappear():void{this.stopListening();this.destroyRecognizer();}privateinitRecognizer():void{try{this.recognizer=speechRecognizer.createRecognizer();this.recognizer.setListener({onResults:(text:string)=>{// 流式结果逐步更新this.recognizedText=text;},onError:(error:BusinessError)=>{console.error('Recognizer error:',error.message);this.isListening=false;},onEndOfSpeech:()=>{// 用户停止说话,自动结束this.isListening=false;},onStartListening:()=>{console.log('开始监听');}});}catch(error){console.error('Create recognizer failed:',error.message);}}privatestartListening():void{if(!this.recognizer){return;}try{// 配置识别参数:语言、模式constconfig:speechRecognizer.SpeechRecognizerConfig={language:'zh-CN',// 简体中文recognitionMode:speechRecognizer.RecognitionMode.STREAMING,// 流式模式enablePunctuation:true,// 启用标点accuracy:speechRecognizer.RecognitionAccuracy.NORMAL};this.recognizer.startListening(config);this.isListening=true;this.recognizedText='';}catch(error){console.error('Start listening failed:',error.message);}}privatestopListening():void{if(this.recognizer&&this.isListening){try{this.recognizer.stopListening();}catch(error){console.error('Stop listening failed:',error.message);}this.isListening=false;}}privatedestroyRecognizer():void{if(this.recognizer){try{this.recognizer.destroy();}catch(error){console.error('Destroy recognizer failed:',error.message);}this.recognizer=null;}}build(){Column({space:20}){Text('流式语音识别').fontSize(24).fontWeight(FontWeight.Bold)Text(this.recognizedText||'等待语音输入...').width('90%').height(200).backgroundColor('#F5F5F5').borderRadius(12).padding(16).fontSize(18).textAlign(TextAlign.Start)Button(this.isListening?'停止录音':'开始录音').width(200).height(50).onClick(()=>{if(this.isListening){this.stopListening();}else{this.startListening();}})}.width('100%').height('100%').padding(20)}}

注意事项

  • setListener必须在startListening之前调用,否则会错过回调
  • 页面销毁时一定要调用destroy(),否则会泄漏资源
  • 流式模式下,onResults会频繁回调,每次更新新的识别结果

核心实现:非流式(一次性)识别

非流式识别适合传入一个完整的音频文件(支持PCM、WAV等格式)。核心是recognize方法,传入音频数据,通过回调返回结果。

// OneShotRecognizer.etsimport{speechRecognizer}from'@kit.CoreSpeechKit';import{BusinessError}from'@kit.BasicServicesKit';import{fileIo}from'@kit.CoreFileKit';import{common}from'@kit.AbilityKit';@Entry@Componentstruct OneShotRecognizerPage{@StateresultText:string='';@StateisProcessing:boolean=false;privaterecognizer:speechRecognizer.SpeechRecognizer|null=null;aboutToAppear():void{this.initRecognizer();}aboutToDisappear():void{this.destroyRecognizer();}privateinitRecognizer():void{try{this.recognizer=speechRecognizer.createRecognizer();this.recognizer.setListener({// 非流式一次性识别结果onResults:(text:string)=>{this.resultText=text;this.isProcessing=false;},onError:(error:BusinessError)=>{console.error('Recognizer error:',error.message);this.isProcessing=false;}});}catch(error){console.error('Create recognizer failed:',error.message);}}privateasyncstartRecognition():Promise<void>{if(!this.recognizer||this.isProcessing){return;}this.isProcessing=true;this.resultText='';try{// 读取音频文件constcontext=getContext(this)ascommon.UIAbilityContext;constfileUri:string=context.cacheDir+'/test_audio.pcm';constfile:fileIo.File=awaitfileIo.open(fileUri,fileIo.OpenMode.READ_ONLY);constbuffer:ArrayBuffer=newArrayBuffer(fileIo.statSync(file.fd).size);awaitfileIo.read(file.fd,buffer);fileIo.close(file);// 配置并执行识别constconfig:speechRecognizer.SpeechRecognizerConfig={language:'zh-CN',recognitionMode:speechRecognizer.RecognitionMode.ONE_SHOT,// 一次性模式enablePunctuation:true};// 第二个参数是音频格式this.recognizer.recognize(buffer,config,speechRecognizer.AudioFormat.PCM_16BIT);}catch(error){console.error('Recognition failed:',error.message);this.isProcessing=false;}}privatedestroyRecognizer():void{if(this.recognizer){try{this.recognizer.destroy();}catch(error){console.error('Destroy recognizer failed:',error.message);}this.recognizer=null;}}build(){Column({space:20}){Text('一次性语音识别').fontSize(24).fontWeight(FontWeight.Bold)Text(this.resultText||'请选择音频文件进行识别').width('90%').height(200).backgroundColor('#F5F5F5').borderRadius(12).padding(16).fontSize(18).textAlign(TextAlign.Start)Button(this.isProcessing?'识别中...':'开始识别(从缓存读取音频)').width(200).height(50).enabled(!this.isProcessing).onClick(()=>{this.startRecognition();})}.width('100%').height('100%').padding(20)}}

注意事项

  • 一次性识别的音频格式必须与recognize方法传入的参数一致,否则识别失败
  • 音频文件路径需要是应用沙箱内的路径,不能是外部路径
  • 一次性识别不支持部分结果,只有等处理完成后才回调onResults

配置热词与自定义词库

热词功能可以提升特定词汇的识别准确率。比如医疗、法律等专业领域的术语。

// 配置热词privateaddCustomWords(words:string[]):void{if(!this.recognizer){return;}try{// 自定义词汇接口this.recognizer.addWords(words);console.log('Custom words added:',words.join(', '));}catch(error){console.error('Add custom words failed:',error.message);}}// 使用示例privateaddHotWords():void{consthotWords:string[]=['鸿蒙','ArkTS','HarmonyOS','语音识别'];this.addCustomWords(hotWords);}

注意事项

  • 热词最好在startListeningrecognize之前添加
  • 热词列表长度有限制(约50-100个),具体看设备能力
  • 热词只对当前识别器实例有效,重新创建后需要重新添加

常见问题与踩坑记录

问题1:页面返回后识别器状态丢失

现象:页面跳转到其他页面再返回后,调用startListening报错,或者回调不触发。

原因SpeechRecognizer实例在页面销毁时没有被正确释放,重新进入页面时创建了新实例,但旧的实例可能还持有音频资源。

解决方案

  • 严格遵循生命周期:aboutToAppear中创建,aboutToDisappear中销毁
  • 不要在页面间共享同一个识别器实例,每个页面独立创建
  • 如果使用单页面架构,确保在onPageHide时停止识别,onPageShow时恢复

问题2:多次快速开始识别导致崩溃

现象:用户连续快速点击“开始录音”按钮,应用闪退或卡死。

原因SpeechRecognizer内部状态机不支持在startListening后未stopListening前再次调用startListening。连续快速点击会破坏状态。

解决方案

// 使用防抖或状态锁定privatestartListeningSafe():void{if(this.isListening||this.isProcessing){console.warn('Recognizer is busy, ignore this request');return;}// 确保先停止再启动if(this.recognizer){try{this.recognizer.stopListening();}catch(e){// 忽略未启动时的错误}}this.startListening();}

最佳实践

  1. 不要在回调里直接做耗时操作onResults回调是在异步线程中执行的,直接在里面更新UI没有问题,但如果做文件IO、网络请求,会阻塞后续回调处理。建议使用postMessagerunOnMainThread回到主线程处理。

  2. 初始化识别器时检查权限。语音识别需要ohos.permission.MICROPHONE权限。如果没有提前授权,startListening会静默失败。建议在页面加载时先请求权限。

  3. 合理选择识别模式。流式识别内部会持续占用麦克风和编解码资源,如果页面不可见(比如被压入后台),应该主动调用stopListening释放资源。一次性识别则不需要,直接完成后自动释放。

FAQ

Q:为什么流式识别在真机上一直返回空字符串,模拟器正常?
A:模拟器通常使用虚拟麦克风输入,真机需要确认是否授权ohos.permission.MICROPHONE权限,以及是否有其他应用占用了麦克风。

Q:页面返回后,重新进入识别页面,识别结果不更新怎么办?
A:检查aboutToAppear中是否重新创建了识别器实例。如果创建了但旧实例没有正确销毁,会导致识别器状态冲突。建议在aboutToDisappear中调用destroy()

Q:离线模式下,首次使用需要下载模型,下载失败如何处理?
A:离线识别需要下载语音模型包(约100-200MB),下载过程是异步的。建议在aboutToAppear中调用speechRecognizer.downloadModel,监听下载进度,如果失败可以提示用户检查网络或切换在线模式。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 1:55:13

【监控与可观测性】05-OpenTelemetry入门:统一链路追踪落地方案

OpenTelemetry 入门&#xff1a;统一链路追踪落地方案 专栏&#xff1a; 监控 & 可观测性 难度&#xff1a; 进阶 标签&#xff1a; OpenTelemetry 链路追踪 可观测性 Jaeger 分布式追踪 前言 微服务架构下&#xff0c;一个请求经过十几个服务&#xff0c;出问题时不知道…

作者头像 李华
网站建设 2026/7/5 1:52:44

2026年必看:如何找到真正靠谱的苦荞粉供应商?

随着健康意识的提升&#xff0c;越来越多的人开始关注食品的营养价值与来源。在这样的背景下&#xff0c;以苦荞粉为代表的天然健康食品正逐渐受到市场的追捧。然而&#xff0c;在众多品牌和产品中挑选出品质上乘且值得信赖的苦荞粉并非易事。本文将从几个关键维度出发&#xf…

作者头像 李华
网站建设 2026/7/5 1:51:09

Java计算机毕设之在线随机组卷考试管理平台的设计与实现 基于 SpringBoot 的考试成绩分析统计系统(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/7/5 1:50:54

短剧拟真配音实操:从音色克隆到情绪融合的全流程教程

短剧出海配音要做到真人演员级别的拟真感&#xff0c;需要经历音色克隆、情绪配置、特殊音色处理三个核心步骤。本文提供完整操作流程。一、前置&#xff1a;什么材料需要准备原片视频&#xff08;或已分离的演员人声音频&#xff09;字幕文件&#xff08;SRT格式&#xff0c;如…

作者头像 李华
网站建设 2026/7/5 1:40:34

商用容积式电热水炉厂家

一、“杰格优“容积式电热水炉介绍容积式电热水炉又称电热水锅炉&#xff0c;商用容积式电热水器&#xff0c;内置大容量承压式储热水箱&#xff0c;压力0.6mpa、1.0mpa&#xff0c;加厚不锈钢内胆存储热水&#xff0c;可集中热水供应出水稳定&#xff0c;输出大量热水适用于集…

作者头像 李华