学习Arkts的CoreSpeechKit-语音识别
基于官方文档进行学习理解和调用有需要的可以参考看看1. createEngine() 先创建语音识别引擎实例--必须2. setListener() 设置监听结果回调开始/结果/完成/错误--必须3. startListening() 开始识别开始麦克风录音识别引擎自动打开麦克风录音并识别如果是文件识别还要配合writeAudio使用 --可选4. writeAudio() 向引擎写入音频数据麦克风录音 或 本地文件--可选5. finish()/cancel() 结束或取消当前识别会话 --可选6. shutdown() 释放引擎资源必须调用否则内存泄漏--必须LogUtil.log使用了外部库如果复制示例代码报错可以换成项目中已有日志工具或者下载ohpm i abner/log一、先在module.json5中配置{ name: ohos.permission.MICROPHONE, reason: $string:getAudio, usedScene: { abilities: [EntryAbility] } },二、创建引擎必须官方文档中有两个示例可以随便选一个用主要是speechRecognizer.createEngine()这个方法参数配置啥的可以看自己需求引擎创建的时候配置的recognizerModeshort是短录音60秒自动结束long是长语音模式不超过8hButton() { Text(CreateEngineByCallback) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { this.createByCallback(); })private createByCallback() { let extraParam: Recordstring, Object {locate: CN, recognizerMode: short}; let initParamsInfo: speechRecognizer.CreateEngineParams { language: zh-CN, online: 1, extraParams: extraParam }; // createEngine 是异步的通过 callback 返回引擎实例 speechRecognizer.createEngine(initParamsInfo, (err: BusinessError, speechRecognitionEngine: speechRecognizer.SpeechRecognitionEngine) { if (!err) { LogUtil.log( Succeeded in creating engine.,--引擎创建成功); // 保存引擎实例到模块级变量后续所有操作都通过这个实例 asrEngine speechRecognitionEngine; } else { // 常见错误码 // 1002200001 → 创建失败语种不支持/模式不支持/初始化超时/资源不存在 // 1002200006 → 引擎忙碌中上一次引擎没释放就重复创建或多个应用同时调用 // 1002200008 → 引擎已被销毁调用 shutdown 后又尝试操作 LogUtil.log( 失败失败失败.,--引擎创建失败); } }); }三、设置监听在这里的onResult中获取语音转文字的结果必须//引擎创建好后设置一次就好是绑定在引擎上的会自动监听释放引擎之后再次创建引擎时需要重新设置 Button() { Text(setListener) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { this.setListener(); })private setListener() { let setListener: speechRecognizer.RecognitionListener { // 引擎开始识别的回调 onStart(sessionId: string, eventMessage: string) { LogUtil.log(onStart start,--开始识别了) }, // 引擎内部事件回调如VAD检测到用户开始说话或停止说话 onEvent(sessionId: string, eventCode: number, eventMessage: string) { LogUtil.log(onEvent start,--回调) hilog.info(0x0000, TAG, onEvent, sessionId: ${sessionId} eventCode: ${eventCode} eventMessage: ${eventMessage}); }, // 识别结果回调核心回调 // result.isFinal false → 中间结果用户还在说实时返回的部分识别结果 // result.isFinal true → 最终结果用户说完了这是最终的完整识别结果 onResult(sessionId: string, result: speechRecognizer.SpeechRecognitionResult) { LogUtil.log(onResult start:${JSON.stringify(result)},--识别结果回调) }, // 识别完成回调整个会话正常结束 onComplete(sessionId: string, eventMessage: string) { LogUtil.log(onComplete start:${eventMessage},--识别完成回调) }, // 错误回调 onError(sessionId: string, errorCode: number, errorMessage: string) { LogUtil.log(onError start:${errorCode},--错误回调) }, } // 将监听器注册到引擎上 asrEngine.setListener(setListener); };/** * 设置识别结果监听器 * 这是获取识别结果的核心引擎所有的事件通知都通过这个监听器返回 * * 五个回调说明 * onStart → 引擎开始识别通知你我准备好了 * onEvent → 引擎内部事件如检测到开始说话/停止说话 * onResult → 识别结果最重要包含识别出的文字 * result.isFinal: true最终结果识别完毕 false中间结果还在说 * onComplete → 本次识别会话结束 * onError → 出错了通过 errorCode 判断原因 * 1002200002 → startListening 重复调用上一个会话没结束就又开始新的 */四、开始识别 1麦克风实时录音方式进行识别Button() { Text(startRecording) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { this.startRecording(); })// 麦克风语音转文本 private startRecording() { try { this.startListeningForRecording(); } catch (err) { this.generatedText Message: ${err.message}.; } }//开始识别 private startListeningForRecording() { let audioParam: speechRecognizer.AudioInfo { audioType: pcm, sampleRate: 16000, soundChannel: 1, sampleBit: 16 }; // audioInfo参数配置请参考AudioInfo let extraParam: Recordstring, Object { recognitionMode: 0, vadBegin: 2000, vadEnd: 3000, maxAudioDuration: 20000 }; let recognizerParams: speechRecognizer.StartParams { sessionId: this.sessionId, audioInfo: audioParam, extraParams: extraParam }; LogUtil.log(startListening start,--); try { asrEngine?.startListening(recognizerParams); } catch (err) { console.error(error code: ${err.code}, message: ${err.message}.); } }五、开始识别 2本地文件中获取音频信息进行识别writeAudio 方法要求传入 PCM 格式的音频数据。如果要从本地选择音频文件进行识别需要自行编写方法拉起文件选择器将选中的音频文件解码为 PCM 格式后保存到应用沙箱中再调用 writeAudio 方法读取识别。当前开发未用上该方法所以不详细写从文件中读取音频的话需要一个音频采集工具FileCapturer这个FileCapturer文件可以在官方文档的指南-CoreSpeechKit-语音识别-开发实例里面找到直接复制// 流程startListeningForWriteAudio() → 从filesDir读取pcm文件 → 分块writeAudio()写入引擎 // 与录音方式的区别音频来源不同文件 vs 麦克风其余流程一致 // 注意文件必须是 pcm 格式参数需匹配16kHz采样率、单声道、16bit Button() { Text(writeAudio) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { this.audioToText(); })State mFileCapturer: FileCapturer new FileCapturer(); // 写音频流 private async audioToText() { try { this.setListener(); // Set the parameters related to the start of identification. let audioParam: speechRecognizer.AudioInfo { audioType: pcm, sampleRate: 16000, soundChannel: 1, sampleBit: 16 }; let recognizerParams: speechRecognizer.StartParams { sessionId: this.sessionId, audioInfo: audioParam }; // Invoke the start recognition method. asrEngine?.startListening(recognizerParams); // Get Audio from File let data: ArrayBuffer | undefined undefined; let ctx this.getUIContext().getHostContext() as Context; let filenames: string[] fileIo.listFileSync(ctx.resourceDir); if (filenames.length 0) { console.error(length is null); return; } let filePath: string ctx.resourceDir / filenames[0]; (this.mFileCapturer as FileCapturer).setFilePath(filePath); this.mFileCapturer.init((dataBuffer: ArrayBuffer) { data dataBuffer; let uint8Array: Uint8Array new Uint8Array(data); asrEngine?.writeAudio(this.sessionId2, uint8Array); }); await this.mFileCapturer.start(); asrEngine?.finish(this.sessionId); this.mFileCapturer.release(); } catch (err) { this.generatedText Message: ${err.message}.; } }六、结束识别提前结束识别与 cancel 的区别finish 会返回已有的识别结果cancel 直接丢弃asrEngine.finish(this.sessionId)Button() { Text(finish) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { asrEngine.finish(this.sessionId); })七、取消识别与 finish 的区别cancel 不触发 onComplete 回调直接中断asrEngine.cancel(this.sessionId);Button() { Text(cancel) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { asrEngine.cancel(this.sessionId); })八、释放引擎必须不释放的话会造成内存泄漏、麦克风被占用asrEngine.shutdown();Button() { Text(shutdown) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AA7) .width(80%) .height(50) .margin(10) .onClick(() { asrEngine.shutdown(); })九、变量声明asrEngine要在全局定义let asrEngine: speechRecognizer.SpeechRecognitionEngine; Entry Component struct Index { State createCount: number 0; // 记录创建引擎的次数用于调试 State result: boolean false; State voiceInfo: string ; // 语音识别的文字结果十、类封装基于上述方法封装了一个类可以多处调用目前只支持实时录音转文字的import { speechRecognizer } from kit.CoreSpeechKit; import { BusinessError } from kit.BasicServicesKit; import { LogUtil } from ./Log; export class CoreSpeech { private asrEngine: speechRecognizer.SpeechRecognitionEngine | undefined; private sessionId: string; /** 识别结果回调调用方设置接收识别到的文字 */ onResult?: (result: speechRecognizer.SpeechRecognitionResult) void; /** 错误回调 */ onError?: (errorCode: number, errorMessage: string) void; /** 开始识别回调 */ onStart?: () void; /** 识别完成回调 */ onComplete?: () void; /** 引擎事件回调VAD等 */ onEvent?: (eventCode: number, eventMessage: string) void; constructor(sessionId?: string) { this.sessionId sessionId || String(Date.now()); } /** * 步骤1创建语音识别引擎异步 * 创建成功后自动设置监听器 */ createEngine(): Promisevoid { return new Promise((resolve, reject) { //这里配置参数信息 可以根据需求自己修改 let extraParam: Recordstring, Object { locate: CN, recognizerMode: short }; let initParamsInfo: speechRecognizer.CreateEngineParams { language: zh-CN, online: 1, extraParams: extraParam }; speechRecognizer.createEngine(initParamsInfo, (err: BusinessError, engine: speechRecognizer.SpeechRecognitionEngine) { if (err) { // 常见错误码 // 1002200001 → 创建失败语种不支持/模式不支持/初始化超时/资源不存在 // 1002200006 → 引擎忙碌中上一次引擎没释放就重复创建 LogUtil.log(JSON.stringify(err), --引擎创建失败); reject(err); } else { this.asrEngine engine; this.setListener(); LogUtil.log(success, --引擎创建成功); resolve(); } }); }); } /** * 步骤2设置监听器createEngine 成功后自动调用一般不需要手动调用 */ private setListener() { let listener: speechRecognizer.RecognitionListener { onStart: (sessionId: string, eventMessage: string) { LogUtil.log(onStart, --开始识别); this.onStart?.(); }, onEvent: (sessionId: string, eventCode: number, eventMessage: string) { LogUtil.log(onEvent: ${eventCode}, --引擎事件); this.onEvent?.(eventCode, eventMessage); }, onResult: (sessionId: string, result: speechRecognizer.SpeechRecognitionResult) { LogUtil.log(onResult: ${JSON.stringify(result)}, --识别结果); this.onResult?.(result); }, onComplete: (sessionId: string, eventMessage: string) { LogUtil.log(onComplete, --识别完成); this.onComplete?.(); }, onError: (sessionId: string, errorCode: number, errorMessage: string) { LogUtil.log(onError: ${errorCode} ${errorMessage}, --错误回调); this.onError?.(errorCode, errorMessage); }, }; this.asrEngine?.setListener(listener); } /** * 步骤3开始麦克风录音识别 * 引擎内部会自动打开麦克风采集音频并识别 */ startRecording() { let audioParam: speechRecognizer.AudioInfo { audioType: pcm, sampleRate: 16000, soundChannel: 1, sampleBit: 16 }; let recognizerParams: speechRecognizer.StartParams { sessionId: this.sessionId, audioInfo: audioParam, extraParams: { recognitionMode: 0, vadBegin: 2000, // 用户开始说话的超时时间ms vadEnd: 3000, // 用户停止说话的超时时间ms maxAudioDuration: 20000 // 单次识别最大音频时长ms } }; try { this.asrEngine?.startListening(recognizerParams); } catch (err) { let error err as BusinessError; LogUtil.log(startRecording error: ${error.code} ${error.message}, --录音启动失败); this.onError?.(error.code, error.message); } } /** * 步骤4结束当前识别会话 * 调用后引擎会返回最终识别结果通过 onResult 回调isFinaltrue */ finish() { try { this.asrEngine?.finish(this.sessionId); } catch (err) { let error err as BusinessError; LogUtil.log(finish error: ${error.code}, --结束识别失败); } } cancel(){ try { this.asrEngine?.cancel(this.sessionId); } catch (err) { let error err as BusinessError; LogUtil.log(cancel error: ${error.code}, --结束取消失败); } } /** * 步骤5释放引擎资源必须在页面销毁时调用 * 不调用会导致内存泄漏和麦克风占用 */ shutdown() { try { this.asrEngine?.shutdown(); this.asrEngine undefined; LogUtil.log(shutdown, --引擎已释放); } catch (err) { let error err as BusinessError; LogUtil.log(shutdown error: ${error.code}, --引擎释放失败); } } }十一、使用案例调用类的使用案例import { CoreSpeech } from utils; Entry Component struct Index { State createCount: number 0; // 记录创建引擎的次数用于调试 State result: boolean false; State voiceInfo: string ; // 语音识别的文字结果 State speech:CoreSpeechnew CoreSpeech() build() { Column() { Scroll() { Column() { Button() { Text(创建引擎) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { this.speech.createEngine() }) Button() { Text(开始识别) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { this.speech.startRecording() this.speech.onResult(result){ this.voiceInforesult.result } }) Column(){ Text(this.voiceInfo) .fontColor(Color.White) } .width(100%) .height(100) .backgroundColor(Color.Black) Button() { Text(完成) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { this.speech.finish() }) Button() { Text(取消) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AE7) .width(80%) .height(50) .margin(10) .onClick(() { this.speech.cancel() }) Button() { Text(释放引擎) .fontColor(Color.White) .fontSize(20) } .type(ButtonType.Capsule) .backgroundColor(#0x317AA7) .width(80%) .height(50) .margin(10) .onClick(() { this.speech.shutdown() }) } .layoutWeight(1) } .width(100%) .height(100%) } } }仅供学习参考有问题的地方欢迎指出修正~