【鴻蒙開發】第二十四章 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.pswp.cn/web/69232.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/69232.shtml
英文地址,請注明出處:http://en.pswp.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* …