【鸿蒙开发】第二十四章 AI - Core Speech Kit(基础语音服务)

目录

1 简介

1.1 场景介绍

1.2 约束与限制

2 文本转语音

2.1 场景介绍

2.2 约束与限制

2.3 开发步骤

2.4 设置播报策略

2.4.1 设置单词播报方式 

2.4.2 设置数字播报策略

2.4.3 插入静音停顿

2.4.4 指定汉字发音

2.5 开发实例

3 语音识别

3.1 场景介绍

3.2 约束与限制

3.3 开发步骤

3.4 开发实例


1 简介

Core Speech Kit(基础语音服务)集成了语音类基础AI能力,包括文本转语音(TextToSpeech)语音识别(SpeechRecognizer)能力,便于用户与设备进行互动,实现将实时输入的语音与文本之间相互转换。

1.1 场景介绍

  • 文本转语音:将一段不超过10000字符的文本合成为语音并进行播报。
  • 语音识别:将一段音频信息(短语音模式不超过60s,长语音模式不超过8h)转换为文本,可以将pcm音频文件或者实时语音转换为文字。

1.2 约束与限制

AI能力

约束

文本转语音

  • 支持的语种类型:中文。(简体中文、繁体中文、中文语境下的英文)
  • 支持的音色类型:聆小珊女声音色。
  • 文本长度:不超过10000字符。

语音识别

  • 支持的语种类型:中文普通话。
  • 支持的模型类型:离线。
  • 语音时长:短语音模式不超过60s,长语音模式不超过8h。

2 文本转语音

Core Speech Kit支持将一篇不超过10000字符的中文文本(简体中文、繁体中文、数字、中文语境下的英文)合成为语音,并以聆小珊女声音色中文播报。

开发者可对播报的策略进行设置,包括单词播报数字播报静音停顿汉字发音策略

2.1 场景介绍

手机/平板等设备在无网状态下,系统应用无障碍(屏幕朗读)接入文本转语音能力,为视障人士不方便阅读场景提供播报能力。

2.2 约束与限制

该能力当前不支持模拟器

2.3 开发步骤

1.   在使用文本转语音时,将实现文本转语音相关的类添加至工程。

import { textToSpeech } from '@kit.CoreSpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';

 2.   调用createEngine接口,创建textToSpeechEngine实例。createEngine接口提供了两种调用形式,当前以其中一种作为示例,其他方式可参考API参考。

let ttsEngine: textToSpeech.TextToSpeechEngine;// 设置创建引擎参数
let extraParam: Record<string, Object> = {"style": 'interaction-broadcast', "locate": 'CN', "name": 'EngineName'};
let initParamsInfo: textToSpeech.CreateEngineParams = {language: 'zh-CN',person: 0,online: 1,extraParams: extraParam
};// 调用createEngine方法
textToSpeech.createEngine(initParamsInfo, (err: BusinessError, textToSpeechEngine: textToSpeech.TextToSpeechEngine) => {if (!err) {console.info('Succeeded in creating engine');// 接收创建引擎的实例ttsEngine = textToSpeechEngine;} else {console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);}
});

3. 得到TextToSpeechEngine实例对象后,实例化SpeakParams对象、SpeakListener对象,并传入待合成及播报的文本originalText,调用speak接口进行播报。

// 设置speak的回调信息
let speakListener: textToSpeech.SpeakListener = {// 开始播报回调onStart(requestId: string, response: textToSpeech.StartResponse) {console.info(`onStart, requestId: ${requestId} response: ${JSON.stringify(response)}`);},// 合成完成及播报完成回调onComplete(requestId: string, response: textToSpeech.CompleteResponse) {console.info(`onComplete, requestId: ${requestId} response: ${JSON.stringify(response)}`);},// 停止播报回调onStop(requestId: string, response: textToSpeech.StopResponse) {console.info(`onStop, requestId: ${requestId} response: ${JSON.stringify(response)}`);},// 返回音频流onData(requestId: string, audio: ArrayBuffer, response: textToSpeech.SynthesisResponse) {console.info(`onData, requestId: ${requestId} sequence: ${JSON.stringify(response)} audio: ${JSON.stringify(audio)}`);},// 错误回调onError(requestId: string, errorCode: number, errorMessage: string) {console.error(`onError, requestId: ${requestId} errorCode: ${errorCode} errorMessage: ${errorMessage}`);}
};
// 设置回调
ttsEngine.setListener(speakListener);
let originalText: string = 'Hello HarmonyOS';
// 设置播报相关参数
let extraParam: Record<string, Object> = {"queueMode": 0, "speed": 1, "volume": 2, "pitch": 1, "languageContext": 'zh-CN',  
"audioType": "pcm", "soundChannel": 3, "playType": 1 };
let speakParams: textToSpeech.SpeakParams = {requestId: '123456', // requestId在同一实例内仅能用一次,请勿重复设置extraParams: extraParam
};
// 调用播报方法
// 开发者可以通过修改speakParams主动设置播报策略
ttsEngine.speak(originalText, speakParams);

4. (可选)当需要停止合成及播报时,可调用stop接口。

ttsEngine.stop();

5. (可选)当需要查询文本转语音服务是否处于忙碌状态时,可调用isBusy接口。

ttsEngine.isBusy();

6.(可选)当需要查询支持的语种音色信息时,可调用listVoices接口。

listVoices接口提供了两种调用形式,当前以其中一种作为示例,其他方式可参考API参考。

// 在组件中声明并初始化字符串voiceInfo
@State voiceInfo: string = "";// 设置查询相关参数
let voicesQuery: textToSpeech.VoiceQuery = {requestId: '12345678', // requestId在同一实例内仅能用一次,请勿重复设置online: 1
};
// 调用listVoices方法,以callback返回
ttsEngine.listVoices(voicesQuery, (err: BusinessError, voiceInfo: textToSpeech.VoiceInfo[]) => {if (!err) {// 接收目前支持的语种音色等信息this.voiceInfo = JSON.stringify(voiceInfo);console.info(`Succeeded in listing voices, voiceInfo is ${this.voiceInfo}`);} else {console.error(`Failed to list voices. Code: ${err.code}, message: ${err.message}`);}
});

2.4 设置播报策略

由于不同场景下,模型自动判断所选择的播报策略可能与实际需求不同,此章节提供对于播报策略进行主动设置的方法。

说明

以下取值说明均为有效取值,若所使用的数值在有效取值之外则播报结果可能与预期不符,并产生错误的播报结果。

2.4.1 设置单词播报方式 

文本格式:[hN] (N=0/1/2)

N取值说明:

取值

说明

0

智能判断单词播放方式。默认值为0。

1

逐个字母进行播报。

2

以单词方式进行播报。

文本示例:

"hello[h1] world"

hello使用单词发音,world及后续单词将会逐个字母进行发音。

2.4.2 设置数字播报策略

格式:[nN] (N=0/1/2)

N取值说明:

取值

说明

0

智能判断数字处理策略。默认值为0。

1

作为号码逐个数字播报。

2

作为数值播报。超过18位数字不支持,自动按逐个数字进行播报。

文本示例

"[n2]123[n1]456[n0]"

其中,123将会按照数值播报,456则会按照号码播报,而后的文本中的数字,均会自动判断

2.4.3 插入静音停顿

格式:[pN]

描述:N为无符号整数,单位为ms

文本示例:

"你好[p500]小艺"

该句播报时,将会在“你好”后插入500ms的静音停顿。

2.4.4 指定汉字发音

汉字声调用后接一位数字1~5分别表示阴平、阳平、上声、去声和轻声5个声调。

格式:[=MN]

描述:M表示拼音,N表示声调。

N取值说明:

取值

说明

1

阴平

2

阳平

3

上声

4

去声

5

轻声

文本示例:

"着[=zhuo2]手"

2.5 开发实例

点击按钮,播报一段文本。

import { textToSpeech } from '@kit.CoreSpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';let ttsEngine: textToSpeech.TextToSpeechEngine;
@Entry
@Component
struct Index {@State createCount: number = 0;@State result: boolean = false;@State voiceInfo: string = "";@State text: string = "";@State textContent: string = "";@State utteranceId: string = "123456";@State originalText: string = "\n\t\t古人学问无遗力,少壮工夫老始成;\n\t\t" +"纸上得来终觉浅,绝知此事要躬行。\n\t\t";@State illegalText: string = "";build() {Column() {Scroll() {Column() {TextArea({ placeholder: 'Please enter tts original text', text: `${this.originalText}` }).margin(20).focusable(false).border({ width: 5, color: 0x317AE7, radius: 10, style: BorderStyle.Dotted }).onChange((value: string) => {this.originalText = value;console.info(`original text: ${this.originalText}`);})Button() {Text("CreateEngineByCallback").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {this.createCount++;console.info(`CreateTtsEngine:createCount:${this.createCount}`);this.createByCallback();})Button() {Text("speak").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {this.createCount++;this.speak();})Button() {Text("listVoicesCallback").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {this.listVoicesCallback();})Button() {Text("stop").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {// 停止播报console.info("Stop button clicked.");ttsEngine.stop();})Button() {Text("isBusy").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {// 查询播报状态let isBusy = ttsEngine.isBusy();console.info(`isBusy: ${isBusy}`);})Button() {Text("shutdown").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AA7").width("80%").height(50).margin(10).onClick(() => {// 释放引擎ttsEngine.shutdown();})}.layoutWeight(1)}.width('100%').height('100%')}}// 创建引擎,通过callback形式返回private createByCallback() {// 设置创建引擎参数let extraParam: Record<string, Object> = {"style": 'interaction-broadcast', "locate": 'CN', "name": 'EngineName'};let initParamsInfo: textToSpeech.CreateEngineParams = {language: 'zh-CN',person: 0,online: 1,extraParams: extraParam};// 调用createEngine方法textToSpeech.createEngine(initParamsInfo, (err: BusinessError, textToSpeechEngine: textToSpeech.TextToSpeechEngine) => {if (!err) {console.info('Succeeded in creating engine.');// 接收创建引擎的实例ttsEngine = textToSpeechEngine;} else {console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);}});};// 调用speak播报方法private speak() {let speakListener: textToSpeech.SpeakListener = {// 开始播报回调onStart(requestId: string, response: textToSpeech.StartResponse) {console.info(`onStart, requestId: ${requestId} response: ${JSON.stringify(response)}`);},// 完成播报回调onComplete(requestId: string, response: textToSpeech.CompleteResponse) {console.info(`onComplete, requestId: ${requestId} response: ${JSON.stringify(response)}`);}, // 停止播报完成回调,调用stop方法并完成时会触发此回调onStop(requestId: string, response: textToSpeech.StopResponse) {console.info(`onStop, requestId: ${requestId} response: ${JSON.stringify(response)}`);},// 返回音频流onData(requestId: string, audio: ArrayBuffer, response: textToSpeech.SynthesisResponse) {console.info(`onData, requestId: ${requestId} sequence: ${JSON.stringify(response)} audio: ${JSON.stringify(audio)}`);},// 错误回调,播报过程发生错误时触发此回调onError(requestId: string, errorCode: number, errorMessage: string) {console.error(`onError, requestId: ${requestId} errorCode: ${errorCode} errorMessage: ${errorMessage}`);}};// 设置回调ttsEngine.setListener(speakListener);// 设置播报相关参数let extraParam: Record<string, Object> = {"queueMode": 0, "speed": 1, "volume": 2, "pitch": 1, "languageContext": 'zh-CN', "audioType": "pcm", "soundChannel": 3, "playType":1}let speakParams: textToSpeech.SpeakParams = {requestId: '123456-a', // requestId在同一实例内仅能用一次,请勿重复设置extraParams: extraParam};// 调用speak播报方法ttsEngine.speak(this.originalText, speakParams);};// 查询语种音色信息,以callback形式返回private listVoicesCallback() {// 设置查询相关参数let voicesQuery: textToSpeech.VoiceQuery = {requestId: '123456-b', // requestId在同一实例内仅能用一次,请勿重复设置online: 1};// 调用listVoices方法,以callback返回语种音色查询结果ttsEngine.listVoices(voicesQuery, (err: BusinessError, voiceInfo: textToSpeech.VoiceInfo[]) => {if (!err) {// 接收目前支持的语种音色等信息this.voiceInfo = JSON.stringify(voiceInfo);console.info(`Succeeded in listing voices, voiceInfo is ${voiceInfo}`);} else {console.error(`Failed to list voices. Code: ${err.code}, message: ${err.message}`);}});};
}

3 语音识别

将一段中文音频信息(中文、中文语境下的英文;短语音模式不超过60s,长语音模式不超过8h)转换为文本,音频信息可以为pcm音频文件或者实时语音

3.1 场景介绍

手机/平板等设备在无网状态下,为听障人士或不方便收听音频场景提供音频转文本能力。

3.2 约束与限制

该能力当前不支持模拟器

3.3 开发步骤

1. 在使用语音识别时,将实现语音识别相关的类添加至工程。

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

2. 调用createEngine方法,对引擎进行初始化,并创建SpeechRecognitionEngine实例。

createEngine方法提供了两种调用形式,当前以其中一种作为示例,其他方式可参考API参考。

let asrEngine: speechRecognizer.SpeechRecognitionEngine;
let sessionId: string = '123456';
// 创建引擎,通过callback形式返回
// 设置创建引擎参数
let extraParam: Record<string, Object> = {"locate": "CN", "recognizerMode": "short"};
let initParamsInfo: speechRecognizer.CreateEngineParams = {language: 'zh-CN',online: 1,extraParams: extraParam
};
// 调用createEngine方法
speechRecognizer.createEngine(initParamsInfo, (err: BusinessError, speechRecognitionEngine: speechRecognizer.SpeechRecognitionEngine) => {if (!err) {console.info('Succeeded in creating engine.');// 接收创建引擎的实例asrEngine = speechRecognitionEngine;} else {console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);}
});

3. 得到SpeechRecognitionEngine实例对象后,实例化RecognitionListener对象,调用setListener方法设置回调,用来接收语音识别相关的回调信息。

// 创建回调对象
let setListener: speechRecognizer.RecognitionListener = {// 开始识别成功回调onStart(sessionId: string, eventMessage: string) {console.info(`onStart, sessionId: ${sessionId} eventMessage: ${eventMessage}`);},// 事件回调onEvent(sessionId: string, eventCode: number, eventMessage: string) {console.info(`onEvent, sessionId: ${sessionId} eventCode: ${eventCode} eventMessage: ${eventMessage}`);},// 识别结果回调,包括中间结果和最终结果onResult(sessionId: string, result: speechRecognizer.SpeechRecognitionResult) {console.info(`onResult, sessionId: ${sessionId} sessionId: ${JSON.stringify(result)}`);},// 识别完成回调onComplete(sessionId: string, eventMessage: string) {console.info(`onComplete, sessionId: ${sessionId} eventMessage: ${eventMessage}`);},// 错误回调,错误码通过本方法返回// 如:返回错误码1002200006,识别引擎正忙,引擎正在识别中// 更多错误码请参考错误码参考onError(sessionId: string, errorCode: number, errorMessage: string) {console.error(`onError, sessionId: ${sessionId} errorCode: ${errorCode} errorMessage: ${errorMessage}`);}
}
// 设置回调
asrEngine.setListener(setListener);

 4. 分别为音频文件转文字和麦克风转文字功能设置开始识别的相关参数,调用startListening方法,开始合成。

// 开始识别
private startListeningForWriteAudio() {// 设置开始识别的相关参数let recognizerParams: speechRecognizer.StartParams = {sessionId: this.sessionId,audioInfo: { audioType: 'pcm', sampleRate: 16000, soundChannel: 1, sampleBit: 16 } //audioInfo参数配置请参考AudioInfo}// 调用开始识别方法asrEngine.startListening(recognizerParams);
};private startListeningForRecording() {let audioParam: speechRecognizer.AudioInfo = { audioType: 'pcm', sampleRate: 16000, soundChannel: 1, sampleBit: 16 }let extraParam: Record<string, Object> = {"recognitionMode": 0,"vadBegin": 2000,"vadEnd": 3000,"maxAudioDuration": 20000}let recognizerParams: speechRecognizer.StartParams = {sessionId: this.sessionId,audioInfo: audioParam,extraParams: extraParam}console.info('startListening start');asrEngine.startListening(recognizerParams);
};

5. 传入音频流,调用writeAudio方法,开始写入音频流。读取音频文件时,开发者需预先准备一个pcm格式音频文件。

let uint8Array: Uint8Array = new Uint8Array();
// 可以通过如下方式获取音频流:1、通过录音获取音频流;2、从音频文件中读取音频流
// 2、从音频文件中读取音频流:demo参考
// 写入音频流,音频流长度仅支持640或1280
asrEngine.writeAudio(sessionId, uint8Array);

6. (可选)当需要查询语音识别服务支持的语种信息,可调用listLanguages方法。

listLanguages方法提供了两种调用形式,当前以其中一种作为示例,其他方式可参考API参考。

// 设置查询相关的参数
let languageQuery: speechRecognizer.LanguageQuery = {sessionId: sessionId
};
// 调用listLanguages方法
asrEngine.listLanguages(languageQuery).then((res: Array<string>) => {console.info(`Succeeded in listing languages, result: ${JSON.stringify(res)}.`);
}).catch((err: BusinessError) => {console.error(`Failed to list languages. Code: ${err.code}, message: ${err.message}.`);
});

7. (可选)当需要结束识别时,可调用finish方法。

// 结束识别
asrEngine.finish(sessionId);

8.(可选)当需要取消识别时,可调用cancel方法。

// 取消识别
asrEngine.cancel(sessionId);

9. (可选)当需要释放语音识别引擎资源时,可调用shutdown方法。

// 释放识别引擎资源
asrEngine.shutdown();

10. 需要在module.json5配置文件中添加ohos.permission.MICROPHONE权限,确保麦克风使用正常。详细步骤可查看声明权限章节。

//...
"requestPermissions": [{"name" : "ohos.permission.MICROPHONE","reason": "$string:reason","usedScene": {"abilities": ["EntryAbility"],"when":"inuse"}}
],
//...

3.4 开发实例

点击按钮,将一段音频信息转换为文本。index.ets文件如下:

import { speechRecognizer } from '@kit.CoreSpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { fileIo } from '@kit.CoreFileKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import AudioCapturer from './AudioCapturer';const TAG = 'CoreSpeechKitDemo';let asrEngine: speechRecognizer.SpeechRecognitionEngine;@Entry
@Component
struct Index {@State createCount: number = 0;@State result: boolean = false;@State voiceInfo: string = "";@State sessionId: string = "123456";private mAudioCapturer = new AudioCapturer();build() {Column() {Scroll() {Column() {Button() {Text("CreateEngineByCallback").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {this.createCount++;hilog.info(0x0000, TAG, `CreateAsrEngine:createCount:${this.createCount}`);this.createByCallback();})Button() {Text("setListener").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {this.setListener();})Button() {Text("startRecording").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {this.startRecording();})Button() {Text("writeAudio").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {this.writeAudio();})Button() {Text("queryLanguagesCallback").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {this.queryLanguagesCallback();})Button() {Text("finish").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {// 结束识别hilog.info(0x0000, TAG, "finish click:-->");asrEngine.finish(this.sessionId);})Button() {Text("cancel").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AE7").width("80%").height(50).margin(10).onClick(() => {// 取消识别hilog.info(0x0000, TAG, "cancel click:-->");asrEngine.cancel(this.sessionId);})Button() {Text("shutdown").fontColor(Color.White).fontSize(20)}.type(ButtonType.Capsule).backgroundColor("#0x317AA7").width("80%").height(50).margin(10).onClick(() => {// 释放引擎asrEngine.shutdown();})}.layoutWeight(1)}.width('100%').height('100%')}}// 创建引擎,通过callback形式返回private createByCallback() {// 设置创建引擎参数let extraParam: Record<string, Object> = {"locate": "CN", "recognizerMode": "short"};let initParamsInfo: speechRecognizer.CreateEngineParams = {language: 'zh-CN',online: 1,extraParams: extraParam};// 调用createEngine方法speechRecognizer.createEngine(initParamsInfo, (err: BusinessError, speechRecognitionEngine:speechRecognizer.SpeechRecognitionEngine) => {if (!err) {hilog.info(0x0000, TAG, 'Succeeded in creating engine.');// 接收创建引擎的实例asrEngine = speechRecognitionEngine;} else {// 无法创建引擎时返回错误码1002200001,原因:语种不支持、模式不支持、初始化超时、资源不存在等导致创建引擎失败// 无法创建引擎时返回错误码1002200006,原因:引擎正在忙碌中,一般多个应用同时调用语音识别引擎时触发// 无法创建引擎时返回错误码1002200008,原因:引擎已被销毁hilog.error(0x0000, TAG, `Failed to create engine. Code: ${err.code}, message: ${err.message}.`);}});}// 查询语种信息,以callback形式返回private queryLanguagesCallback() {// 设置查询相关参数let languageQuery: speechRecognizer.LanguageQuery = {sessionId: '123456'};// 调用listLanguages方法asrEngine.listLanguages(languageQuery, (err: BusinessError, languages: Array<string>) => {if (!err) {// 接收目前支持的语种信息hilog.info(0x0000, TAG, `Succeeded in listing languages, result: ${JSON.stringify(languages)}`);} else {hilog.error(0x0000, TAG, `Failed to create engine. Code: ${err.code}, message: ${err.message}.`);}});};// 开始识别private startListeningForWriteAudio() {// 设置开始识别的相关参数let recognizerParams: speechRecognizer.StartParams = {sessionId: this.sessionId,audioInfo: { audioType: 'pcm', sampleRate: 16000, soundChannel: 1, sampleBit: 16 } //audioInfo参数配置请参考AudioInfo}// 调用开始识别方法asrEngine.startListening(recognizerParams);};private startListeningForRecording() {let audioParam: speechRecognizer.AudioInfo = { audioType: 'pcm', sampleRate: 16000, soundChannel: 1, sampleBit: 16 }let extraParam: Record<string, Object> = {"recognitionMode": 0,"vadBegin": 2000,"vadEnd": 3000,"maxAudioDuration": 20000}let recognizerParams: speechRecognizer.StartParams = {sessionId: this.sessionId,audioInfo: audioParam,extraParams: extraParam}hilog.info(0x0000, TAG, 'startListening start');asrEngine.startListening(recognizerParams);};// 写音频流private async writeAudio() {this.startListeningForWriteAudio();hilog.error(0x0000, TAG, `Failed to read from file. Code`);let ctx = getContext(this);let filenames: string[] = fileIo.listFileSync(ctx.filesDir);if (filenames.length <= 0) {hilog.error(0x0000, TAG, `Failed to read from file. Code`);return;}hilog.error(0x0000, TAG, `Failed to read from file. Code`);let filePath: string = `${ctx.filesDir}/${filenames[0]}`;let file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE);try {let buf: ArrayBuffer = new ArrayBuffer(1280);let offset: number = 0;while (1280 == fileIo.readSync(file.fd, buf, {offset: offset})) {let uint8Array: Uint8Array = new Uint8Array(buf);asrEngine.writeAudio("123456", uint8Array);await this.countDownLatch(1);offset = offset + 1280;}} catch (err) {hilog.error(0x0000, TAG, `Failed to read from file. Code: ${err.code}, message: ${err.message}.`);} finally {if (null != file) {fileIo.closeSync(file);}}}// 麦克风语音转文本private async startRecording() {this.startListeningForRecording();// 录音获取音频let data: ArrayBuffer;hilog.info(0x0000, TAG, 'create capture success');this.mAudioCapturer.init((dataBuffer: ArrayBuffer) => {hilog.info(0x0000, TAG, 'start write');hilog.info(0x0000, TAG, 'ArrayBuffer ' + JSON.stringify(dataBuffer));data = dataBufferlet uint8Array: Uint8Array = new Uint8Array(data);hilog.info(0x0000, TAG, 'ArrayBuffer uint8Array ' + JSON.stringify(uint8Array));// 写入音频流asrEngine.writeAudio("1234567", uint8Array);});};// 计时public async countDownLatch(count: number) {while (count > 0) {await this.sleep(40);count--;}}// 睡眠private sleep(ms: number):Promise<void> {return new Promise(resolve => setTimeout(resolve, ms));}// 设置回调private setListener() {// 创建回调对象let setListener: speechRecognizer.RecognitionListener = {// 开始识别成功回调onStart(sessionId: string, eventMessage: string) {hilog.info(0x0000, TAG, `onStart, sessionId: ${sessionId} eventMessage: ${eventMessage}`);},// 事件回调onEvent(sessionId: string, eventCode: number, eventMessage: string) {hilog.info(0x0000, TAG, `onEvent, sessionId: ${sessionId} eventCode: ${eventCode} eventMessage: ${eventMessage}`);},// 识别结果回调,包括中间结果和最终结果onResult(sessionId: string, result: speechRecognizer.SpeechRecognitionResult) {hilog.info(0x0000, TAG, `onResult, sessionId: ${sessionId} sessionId: ${JSON.stringify(result)}`);},// 识别完成回调onComplete(sessionId: string, eventMessage: string) {hilog.info(0x0000, TAG, `onComplete, sessionId: ${sessionId} eventMessage: ${eventMessage}`);},// 错误回调,错误码通过本方法返回// 返回错误码1002200002,开始识别失败,重复启动startListening方法时触发// 更多错误码请参考错误码参考onError(sessionId: string, errorCode: number, errorMessage: string) {hilog.error(0x0000, TAG, `onError, sessionId: ${sessionId} errorCode: ${errorCode} errorMessage: ${errorMessage}`);},}// 设置回调asrEngine.setListener(setListener);};
}

添加AudioCapturer.ts文件用于获取麦克风音频流。

'use strict';
/** Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.*/import {audio} from '@kit.AudioKit';
import { hilog } from '@kit.PerformanceAnalysisKit';const TAG = 'AudioCapturer';/*** Audio collector tool*/
export default class AudioCapturer {/*** Collector object*/private mAudioCapturer = null;/*** Audio Data Callback Method*/private mDataCallBack: (data: ArrayBuffer) => void = null;/*** Indicates whether recording data can be obtained.*/private mCanWrite: boolean = true;/*** Audio stream information*/private audioStreamInfo = {samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_16000,channels: audio.AudioChannel.CHANNEL_1,sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW}/*** Audio collector information*/private audioCapturerInfo = {source: audio.SourceType.SOURCE_TYPE_MIC,capturerFlags: 0}/*** Audio Collector Option Information*/private audioCapturerOptions = {streamInfo: this.audioStreamInfo,capturerInfo: this.audioCapturerInfo}/***  Initialize* @param audioListener*/public async init(dataCallBack: (data: ArrayBuffer) => void) {if (null != this.mAudioCapturer) {hilog.error(0x0000, TAG, 'AudioCapturerUtil already init');return;}this.mDataCallBack = dataCallBack;this.mAudioCapturer = await audio.createAudioCapturer(this.audioCapturerOptions).catch(error => {hilog.error(0x0000, TAG, `AudioCapturerUtil init createAudioCapturer failed, code is ${error.code}, message is ${error.message}`);});}/*** start recording*/public async start() {hilog.error(0x0000, TAG, `AudioCapturerUtil start`);let stateGroup = [audio.AudioState.STATE_PREPARED, audio.AudioState.STATE_PAUSED, audio.AudioState.STATE_STOPPED];if (stateGroup.indexOf(this.mAudioCapturer.state) === -1) {hilog.error(0x0000, TAG, `AudioCapturerUtil start failed`);return;}this.mCanWrite = true;await this.mAudioCapturer.start();while (this.mCanWrite) {let bufferSize = await this.mAudioCapturer.getBufferSize();let buffer = await this.mAudioCapturer.read(bufferSize, true);this.mDataCallBack(buffer)}}/*** stop recording*/public async stop() {if (this.mAudioCapturer.state !== audio.AudioState.STATE_RUNNING && this.mAudioCapturer.state !== audio.AudioState.STATE_PAUSED) {hilog.error(0x0000, TAG, `AudioCapturerUtil stop Capturer is not running or paused`);return;}this.mCanWrite = false;await this.mAudioCapturer.stop();if (this.mAudioCapturer.state === audio.AudioState.STATE_STOPPED) {hilog.info(0x0000, TAG, `AudioCapturerUtil Capturer stopped`);} else {hilog.error(0x0000, TAG, `Capturer stop failed`);}}/*** release*/public async release() {if (this.mAudioCapturer.state === audio.AudioState.STATE_RELEASED || this.mAudioCapturer.state === audio.AudioState.STATE_NEW) {hilog.error(0x0000, TAG, `Capturer already released`);return;}await this.mAudioCapturer.release();this.mAudioCapturer = null;if (this.mAudioCapturer.state == audio.AudioState.STATE_RELEASED) {hilog.info(0x0000, TAG, `Capturer released`);} else {hilog.error(0x0000, TAG, `Capturer release failed`);}}
}

EntryAbility.ets文件中添加麦克风权限。

import { abilityAccessCtrl, AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');}onDestroy(): void {hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');}onWindowStageCreate(windowStage: window.WindowStage): void {// Main window is created, set main page for this abilityhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');let atManager = abilityAccessCtrl.createAtManager();atManager.requestPermissionsFromUser(this.context, ['ohos.permission.MICROPHONE']).then((data) => {hilog.info(0x0000, 'testTag', 'data:' + JSON.stringify(data));hilog.info(0x0000, 'testTag', 'data permissions:' + data.permissions);hilog.info(0x0000, 'testTag', 'data authResults:' + data.authResults);}).catch((err: BusinessError) => {hilog.error(0x0000, 'testTag', 'errCode: ' + err.code + 'errMessage: ' + err.message);});windowStage.loadContent('pages/Index', (err, data) => {if (err.code) {hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');});}onWindowStageDestroy(): void {// Main window is destroyed, release UI related resourceshilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');}onForeground(): void {// Ability has brought to foregroundhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');}onBackground(): void {// Ability has back to backgroundhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/69232.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据分析:企业数字化转型的金钥匙

引言&#xff1a;数字化浪潮下的数据金矿 在数字化浪潮席卷全球的背景下&#xff0c;有研究表明&#xff0c;只有不到30%的企业能够充分利用手中掌握的数据&#xff0c;这是否让人深思&#xff1f;数据已然成为企业最为宝贵的资产之一。然而&#xff0c;企业是否真正准备好从数…

Starrocks 对比 Clickhouse

极速查询的单表查询 StarRocks 在极速查询方面上做了很多&#xff0c;下面着重介绍四点&#xff1a; 1&#xff09;向量化执行&#xff1a;StarRocks 实现了从存储层到查询层的全面向量化执行&#xff0c;这是 StarRocks 速度优势的基础。向量化执行充分发挥了 CPU 的处理能力…

Vue 入门到实战 八

第8章 组合API与响应性 目录 8.1 响应性 8.1.1 什么是响应性 8.1.2 响应性原理 8.2 为什么使用组合API 8.3 setup组件选项 8.3.1 setup函数的参数 8.3.2 setup函数的返回值 8.3.3 使用ref创建响应式引用 8.3.4 setup内部调用生命周期钩子函数 8.4 提供/注入 8.4.1 …

Java使用aspose实现pdf转word

Java使用aspose实现pdf转word 一、下载aspose-pdf-21.6.jar包【下载地址】&#xff0c;存放目录结构如图&#xff1b;配置pom.xml。 <!--pdf to word--> <dependency><groupId>com.aspose</groupId><artifactId>aspose-pdf</artifactId>…

使用Node.js搭配express框架快速构建后端业务接口模块Demo

使用Node.js搭配express框架快速构建后端业务接口模块Demo&#xff01;实际开发中&#xff0c;有很多项目&#xff0c;其实都是可以使用node.js来完成对接mysql数据库的&#xff0c;express确实使用起来非常简单&#xff0c;入手快&#xff0c;效率非常高。下面是一个简单的案例…

Python----Python高级(并发编程:协程Coroutines,事件循环,Task对象,协程间通信,协程同步,将协程分布到线程池/进程池中)

一、协程 1.1、协程 协程&#xff0c;Coroutines&#xff0c;也叫作纤程(Fiber) 协程&#xff0c;全称是“协同程序”&#xff0c;用来实现任务协作。是一种在线程中&#xff0c;比线程更加轻量级的存在&#xff0c;由程序员自己写程序来管理。 当出现IO阻塞时&#xff0c;…

Unity 加载OSGB(webgl直接加载,无需转换格式!)

Unity webgl加载倾斜摄影数据 前言效果图后续不足 前言 Unity加载倾斜摄影数据&#xff0c;有很多的插件方便好用&#xff0c;但是发布到网页端均失败&#xff0c;因为webgl 的限制&#xff0c;IO读取失效。 前不久发现一个开源项目: UnityOSGB-main 通过两种方式在 Unity 中…

【Block总结】PSA,金字塔挤压注意力,解决传统注意力机制在捕获多尺度特征时的局限性

论文信息 标题: EPSANet: An Efficient Pyramid Squeeze Attention Block on Convolutional Neural Network论文链接: arXivGitHub链接: https://github.com/murufeng/EPSANet 创新点 EPSANet提出了一种新颖的金字塔挤压注意力&#xff08;PSA&#xff09;模块&#xff0c;旨…

【重新认识C语言----结构体篇】

目录 -----------------------------------------begin------------------------------------- 引言 1. 结构体的基本概念 1.1 为什么需要结构体&#xff1f; 1.2 结构体的定义 2. 结构体变量的声明与初始化 2.1 声明结构体变量 2.2 初始化结构体变量 3. 结构体成员的访…

如何在Vscode中接入Deepseek

一、获取Deepseek APIKEY 首先&#xff0c;登录Deepseek官网的开放平台&#xff1a;DeepSeek 选择API开放平台&#xff0c;然后登录Deepseek后台。 点击左侧菜单栏“API keys”&#xff0c;并创建API key。 需要注意的是&#xff0c;生成API key复制保存到本地&#xff0c;丢失…

电脑开机提示按f1原因分析及终极解决方法来了

经常有网友问到一个问题&#xff0c;我电脑开机后提示按f1怎么解决&#xff1f;不管理是台式电脑&#xff0c;还是笔记本&#xff0c;都有可能会遇到开机需要按F1&#xff0c;才能进入系统的问题&#xff0c;引起这个问题的原因比较多&#xff0c;今天小编在这里给大家列举了比…

AI协助探索AI新构型自动化创新的技术实现

一、AI自进化架构的核心范式 1. 元代码生成与模块化重构 - 代码级自编程&#xff1a;基于神经架构搜索的强化学习框架&#xff0c;AI可通过生成元代码模板&#xff08;框架的抽象层定义、神经元结点-网络拓扑态的编码抽象定义&#xff09;自动组合功能模块。例如&#xff0…

RAID独立硬盘冗余阵列

目录 一、RAID基本功能 二、RAID常见级别 三、实现方式 1、软件磁盘阵列 2、硬件磁盘阵列 四、热备盘 RAID&#xff08;Redundant Array of Independent Disks&#xff09;是一种通过将多个硬盘组合成一个逻辑单元来提升存储性能、冗余性或两者兼具的技术。 一、RAID基本…

【高级篇 / IPv6】(7.2) ❀ 04. 在60E上配置ADSL拨号宽带上网(IPv4) ❀ FortiGate 防火墙

【简介】除了单位用户以外&#xff0c;大部分个人用户目前使用的仍然是30E、50E、60E系列防火墙&#xff0c;固件无法达到目前最高版本7.6&#xff0c;这里以最常用的60E为例&#xff0c;演示固件版本7.2下实现ADSL拨号宽带的IPv6上网。由于内容比较多&#xff0c;文章分上、下…

Qt之设置QToolBar上的按钮样式

通常给QAction设置icon后,菜单栏的菜单项和工具栏(QToolBar)上对应的按钮会同时显示该icon。工具栏还可以使用setToolButtonStyle函数设置按钮样式,其参数为枚举值: enum ToolButtonStyle {ToolButtonIconOnly,ToolButtonTextOnly,ToolButtonTextBesideIcon,ToolButtonTe…

【从零开始系列】DeepSeek-R1:(本地部署使用)思维链推理大模型,开源的神!——Windows/Linux本地环境测试 + vLLM远程部署服务

目录 一、环境配置 1.硬件设备评估 2.基础环境安装 3.模型参数下载 (1) huggingface镜像源下载 (2) modelscope魔搭社区下载 &#xff08;推荐&#xff09; 二、基础使用&#xff08;Linux、Window兼容&#xff09; 1.Transformers库自编代码 三、进阶使用&#xff08;仅Lin…

DeepSeek 开源模型全解析(2024.1.1–2025.2.6)

目录 一、通用大语言模型&#xff1a;DeepSeek-V3 系列 137 二、推理优化模型&#xff1a;DeepSeek-R1 系列 811 三、多模态模型&#xff1a;Janus 系列 10 四、生态整合与部署建议 五、总结与展望 以下为 DeepSeek 在 2024 年 1 月至 2025 年 2 月期间发布的开源模型及其…

Mac: docker安装以后报错Command not found: docker

文章目录 前言解决办法&#xff08;新的&#xff09;解决步骤&#xff08;原来的&#xff09;不推荐总结 前言 ​本操作参考 http://blog.csdn.net/enhenglhm/article/details/137955756 原作者&#xff0c;更详细请&#xff0c;查看详细内容请关注原作者。 一般&#xff0c;…

《手札·开源篇》数字化转型助力永磁电机企业降本增效:快速设计软件如何让研发效率提升40%?

数字化转型助力永磁电机企业降本增效&#xff1a;快速设计软件如何让研发效率提升40%&#xff1f; 一、痛点&#xff1a;传统研发模式正在吃掉企业的利润 永磁电机行业面临两大挑战&#xff1a; 研发周期长&#xff1a;一款新电机从设计到量产需6-12个月&#xff0c;电磁计算…

0207作业

思维导图 服务器 enum Type{TYPE_REGIST,TYPE_LOGIN };typedef struct Pack{int size;enum Type type;char buf[2048];}pack_t;typedef struct list{union Data{struct List* tail;char str[64];}data;struct List* next;struct List* prev; }List;List* create_node(){List* …