news 2026/4/16 16:45:29

鸿蒙学习实战之路-语音识别-离线转文本实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙学习实战之路-语音识别-离线转文本实现

鸿蒙学习实战之路-语音识别-离线转文本实现

最近好多朋友问我:“西兰花啊,我想做个鸿蒙应用,需要离线语音转文字功能,这玩意儿难不难啊?” 害,这问题可问对人了!作为一个正在把npm install炒成ohpm install的前端厨子_,我刚好用鸿蒙的 SpeechKit 实现过类似功能~

今天这篇,我就手把手带你实现离线语音识别转文本,全程不超过 10 分钟(不含下载依赖时间)~

功能概述

简单来说,这玩意儿就是把中文音频(支持中文普通话及中文语境下的英文)转换成文字,支持 PCM 音频文件或实时语音输入。短语音模式不超过 60 秒,长语音模式不超过 8 小时,特别适合手机/平板等设备在无网状态下使用。

场景介绍

适用于听障人士辅助、会议记录、语音笔记等场景,尤其是在地铁、山区等无网环境下,依然能正常工作,相当实用!

约束与限制

约束项具体说明
支持语种中文普通话
模型类型离线
语音时长短语音 ≤60 秒,长语音 ≤8 小时
音频格式PCM 格式,16000Hz 采样率,单声道,16 位采样位深

🥦西兰花警告
我有个朋友第一次做的时候,随便用了个 MP3 格式的音频,结果识别率为 0!血泪教训啊朋友们,必须严格按照要求的音频格式来~


开发步骤

1. 导入依赖

首先咱们得把需要的工具库导进来,就像炒菜前先备菜一样~

import{speechRecognizer}from"@kit.CoreSpeechKit";import{BusinessError}from"@kit.BasicServicesKit";

2. 初始化引擎

接下来要创建语音识别引擎实例,这就像是给咱们的语音识别系统装个发动机~

letasrEngine:speechRecognizer.SpeechRecognitionEngine;letsessionId:string="123456";// 自定义会话ID,随便起个就行// 配置引擎参数constinitParams:speechRecognizer.CreateEngineParams={language:"zh-CN",// 支持的语种,咱们就用中文online:1,// 0:离线模式, 1:在线模式(预留),这里虽然写1,但实际是离线extraParams:{locate:"CN",recognizerMode:"short",// short/long,根据需求选},};// 创建引擎speechRecognizer.createEngine(initParams,(err:BusinessError,engine:speechRecognizer.SpeechRecognitionEngine)=>{if(!err){console.info("引擎初始化成功,准备开工!");asrEngine=engine;// 设置回调监听setRecognitionListener();}else{// 错误码说明:// 1002200001: 语种不支持/模式不支持/初始化超时/资源不存在// 1002200006: 引擎忙碌中(多应用同时调用)// 1002200008: 引擎已被销毁console.error(`引擎初始化失败:${err.code}-${err.message}`);}});

3. 设置识别回调

现在咱们得给引擎装个"耳朵",让它能把听到的内容反馈给咱们~

functionsetRecognitionListener(){constlistener:speechRecognizer.RecognitionListener={// 开始识别成功回调onStart(sessionId:string,eventMessage:string){console.info(`开始识别: sessionId=${sessionId}, message=${eventMessage}`);},// 事件回调onEvent(sessionId:string,eventCode:number,eventMessage:string){console.info(`事件通知: sessionId=${sessionId}, code=${eventCode}, message=${eventMessage}`);},// 识别结果回调(包含中间结果和最终结果)onResult(sessionId:string,result:speechRecognizer.SpeechRecognitionResult){console.info(`识别结果:${JSON.stringify(result)}`);// 结果格式: {result: "识别文本", isFinal: true/false}},// 识别完成回调onComplete(sessionId:string,eventMessage:string){console.info(`识别完成:${eventMessage}`);},// 错误回调onError(sessionId:string,errorCode:number,errorMessage:string){console.error(`识别错误:${errorCode}-${errorMessage}`);// 1002200002: 重复调用startListening},};asrEngine.setListener(listener);}

4. 配置识别参数并启动

接下来咱们要给引擎设置一些具体参数,比如音频格式、采样率之类的,就像给汽车调校参数一样~

// 音频配置constaudioConfig:speechRecognizer.AudioInfo={audioType:"pcm",// 音频类型,必须是PCMsampleRate:16000,// 采样率(Hz),必须是16000soundChannel:1,// 声道数(1:单声道)sampleBit:16,// 采样位深,必须是16};// 识别参数constrecognizerParams:speechRecognizer.StartParams={sessionId:sessionId,audioInfo:audioConfig,extraParams:{recognitionMode:0,// 0:连续识别, 1:一句话识别vadBegin:2000,// 开始静音检测时长(ms)vadEnd:3000,// 结束静音检测时长(ms)maxAudioDuration:20000,// 最大音频时长(ms)},};// 启动识别asrEngine.startListening(recognizerParams);

🥦西兰花小贴士
这里的音频参数一定要严格按照约束与限制里的要求设置,不然识别结果会惨不忍睹哦~

5. 写入音频流

现在咱们需要把音频数据喂给引擎,就像给发动机加油一样~

// 从文件读取或麦克风获取PCM音频流functionwriteAudioStream(audioData:Uint8Array){// 音频流长度需为640或1280字节,这点很重要!asrEngine.writeAudio(sessionId,audioData);}

6. 资源释放

最后,咱们用完引擎后一定要记得释放资源,就像用完厨房要打扫卫生一样~

// 结束识别asrEngine.finish(sessionId);// 取消识别asrEngine.cancel(sessionId);// 释放引擎资源asrEngine.shutdown();

7. 权限配置

别忘记在module.json5中添加麦克风权限,不然应用会直接崩溃哦~

{"module":{"requestPermissions":[{"name":"ohos.permission.MICROPHONE","reason":"需要麦克风权限进行语音识别","usedScene":{"abilities":["EntryAbility"],"when":"inuse"}}]}}

开发实例

说了这么多,咱们直接上完整的示例代码吧!我给大家准备了一个简单的页面,包含录音识别和文件识别两种功能~

完整页面实现(Index.ets)

import{speechRecognizer}from'@kit.CoreSpeechKit';import{BusinessError}from'@kit.BasicServicesKit';import{fileIo}from'@kit.CoreFileKit';import{PromptAction}from'@kit.ArkUI';constTAG='SpeechRecognitionDemo';letasrEngine:speechRecognizer.SpeechRecognitionEngine;@Entry @Component struct SpeechRecognitionPage{@State resultText:string="识别结果将显示在这里~";@State isRecording:boolean=false;privatesessionId:string=Date.now().toString();// 用时间戳当会话ID,避免重复privatefileCapturer:FileCapturer=newFileCapturer();build(){Column(){Text("语音识别演示").fontSize(24).fontWeight(FontWeight.Bold).margin(20).fontColor('#333');Text(this.resultText).width('90%').height(200).borderWidth(1).borderRadius(5).padding(10).margin(10).textAlign(TextAlign.Start).backgroundColor('#f5f5f5');Row(){Button(this.isRecording?"停止识别":"开始录音识别").onClick(()=>this.toggleRecording()).type(ButtonType.Capsule).backgroundColor(this.isRecording?'#ff4d4f':'#007dff').width(150).height(40).margin(10);Button("文件识别").onClick(()=>this.startFileRecognition()).type(ButtonType.Capsule).backgroundColor('#007dff').width(150).height(40).margin(10);}}.width('100%').height('100%').justifyContent(FlexAlign.Center);}// 初始化引擎(用Promise封装,更方便使用)privateinitEngine(){returnnewPromise<void>((resolve,reject)=>{if(asrEngine){resolve();return;}constinitParams:speechRecognizer.CreateEngineParams={language:'zh-CN',online:1,extraParams:{"locate":"CN","recognizerMode":"short"}};speechRecognizer.createEngine(initParams,(err:BusinessError,engine:speechRecognizer.SpeechRecognitionEngine)=>{if(!err){asrEngine=engine;this.setListener();resolve();}else{this.resultText=`引擎初始化失败:${err.message}`;reject(err);}});});}// 设置识别回调privatesetListener(){constlistener:speechRecognizer.RecognitionListener={onResult:(sessionId:string,result:speechRecognizer.SpeechRecognitionResult)=>{this.resultText=result.result;},onError:(sessionId:string,errorCode:number,errorMessage:string)=>{this.resultText=`识别错误:${errorCode}-${errorMessage}`;this.isRecording=false;},onComplete:()=>{this.isRecording=false;PromptAction.showToast({message:"识别完成"});}};asrEngine.setListener(listener);}// 开始/停止录音识别privateasynctoggleRecording(){if(this.isRecording){// 停止识别asrEngine.finish(this.sessionId);this.isRecording=false;}else{// 开始识别try{awaitthis.initEngine();constaudioParam:speechRecognizer.AudioInfo={audioType:'pcm',sampleRate:16000,soundChannel:1,sampleBit:16};asrEngine.startListening({sessionId:this.sessionId,audioInfo:audioParam});this.isRecording=true;this.resultText="正在识别...";}catch(err){this.resultText=`启动识别失败:${(errasBusinessError).message}`;}}}// 文件识别privateasyncstartFileRecognition(){try{awaitthis.initEngine();this.resultText="正在读取文件并识别...";// 配置音频参数constaudioParam:speechRecognizer.AudioInfo={audioType:'pcm',sampleRate:16000,soundChannel:1,sampleBit:16};// 启动识别asrEngine.startListening({sessionId:this.sessionId,audioInfo:audioParam});// 读取并写入音频文件constcontext=this.getUIContext().getHostContext()asContext;constfilePath=context.resourceDir+"/test.pcm";// 资源目录下的PCM文件awaitthis.fileCapturer.startRead(filePath,(data:Uint8Array)=>{asrEngine.writeAudio(this.sessionId,data);});}catch(err){this.resultText=`文件识别失败:${(errasBusinessError).message}`;}}}

文件读取工具类(FileCapturer.ets)

为了方便大家使用,我还写了个文件读取工具类,专门用来读取 PCM 音频文件~

import{fileIo}from"@kit.CoreFileKit";constSEND_SIZE:number=1280;// 每次发送的音频数据大小,必须是640或1280exportdefaultclassFileCapturer{privatemIsReading:boolean=false;privatemFile:fileIo.File|null=null;// 读取PCM文件并通过回调返回数据asyncstartRead(filePath:string,callback:(data:Uint8Array)=>void):Promise<void>{returnnewPromise((resolve,reject)=>{try{this.mIsReading=true;this.mFile=fileIo.openSync(filePath,fileIo.OpenMode.READ_ONLY);constreadLoop=async()=>{if(!this.mIsReading)returnresolve();constbuffer=newArrayBuffer(SEND_SIZE);constbytesRead=fileIo.readSync(this.mFile!.fd,buffer,{offset:0,});if(bytesRead>0){callback(newUint8Array(buffer.slice(0,bytesRead)));awaitnewPromise((resolve)=>setTimeout(resolve,40));// 模拟实时音频流readLoop();}else{fileIo.closeSync(this.mFile!);resolve();}};readLoop();}catch(e){reject(e);}});}// 停止读取stop(){this.mIsReading=false;if(this.mFile){fileIo.closeSync(this.mFile);}}}

🥦西兰花警告
示例中的test.pcm文件需要你自己准备哦,记得放在资源目录下,并且格式要符合要求!


错误码参考

为了避免大家遇到问题时手忙脚乱,我整理了一些常见的错误码和解决方案~

错误码说明解决方案
1002200001引擎初始化失败检查语种设置/确保模型文件存在
1002200002重复调用 startListening确保前一次识别已完成
1002200006引擎忙碌中等待其他应用释放引擎资源
1002200008引擎已被销毁重新初始化引擎

下一步行动

学会了离线语音识别,是不是感觉自己又掌握了一项新技能?接下来你可以尝试:

  1. 把这个功能集成到你的鸿蒙应用中
  2. 结合文本翻译功能,实现多语言语音翻译
  3. 优化识别结果的显示效果,比如添加实时字幕

推荐资源

📚 推荐资料:

  • Core Speech Kit 概述
  • API 参考文档
  • 错误码完整列表

我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦

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

Java毕设选题推荐:基于springboot的户外救援系统基于SpringBoot的救援指挥系统【附源码、mysql、文档、调试+代码讲解+全bao等】

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

作者头像 李华
网站建设 2026/4/16 14:06:44

8个AI论文平台推荐!继续教育学生轻松搞定毕业论文格式规范!

8个AI论文平台推荐&#xff01;继续教育学生轻松搞定毕业论文格式规范&#xff01; AI 工具助力论文写作&#xff0c;让学术之路更轻松 在当前的学术环境中&#xff0c;越来越多的学生和科研工作者开始借助 AI 工具来提升论文写作效率。尤其是在继续教育领域&#xff0c;面对…

作者头像 李华
网站建设 2026/4/16 13:05:22

Pandas 相关性分析

Pandas 相关性分析 引言 在数据分析领域,Pandas 是一个不可或缺的工具。它提供了丰富的数据处理功能,其中相关性分析是数据分析的重要部分。本文将详细介绍 Pandas 相关性分析的基本概念、方法以及实际应用。 相关性分析概述 1. 什么是相关性分析? 相关性分析是研究两个…

作者头像 李华
网站建设 2026/4/16 16:24:04

超高创新!强化学习+深度学习单变量时间序列预测MATLAB代码

基于强化学习自动选择最优深度学习模型的时间序列预测。代码通过强化学习智能体在LSTM、BiLSTM、GRU三种模型中选择最佳模型&#xff0c;然后进行单变量时间序列预测和性能评估。 算法步骤 数据预处理阶段 • 导入数据 • 构建时间序列样本&#xff08;滑动窗口方法&#xff09…

作者头像 李华
网站建设 2026/4/16 16:13:17

AI Agent:智能体的觉醒与自主智能的新纪元

目录 一、 AI Agent的灵魂&#xff1a;感知、推理、行动的永恒循环 二、 Agent的核心驱动力&#xff1a;大型语言模型与工具集成 三、 Agent的形态与应用疆域&#xff1a;从虚拟助手到物理世界的智能执行者 四、 Agent的实现基石&#xff1a;框架、模型与内存机制 五、 AI…

作者头像 李华
网站建设 2026/4/16 14:01:42

Redis分布式锁全攻略:原理、陷阱与Redisson最佳实践

在分布式系统中,分布式锁是保障数据一致性与任务互斥执行的核心手段。Redis因其高性能与广泛应用,成为最常见的分布式锁实现方案。 然而,自研Redis分布式锁暗藏诸多陷阱,如果处理不当,容易出现死锁、误删、超时等严重问题。 本文将全面解析 Redis 分布式锁的 基本原理、常…

作者头像 李华