HarmonyOS 文本转语音只能在主线程中使用吗?

textToSpeech.TextToSpeechEngine 文本转语音的操作。进行文件播放的时候。只能在主线程播放? 如果在Worker子线程中呢?

HarmonyOS
2024-10-21 12:30:03
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
FengTianYa

文字转语音的播放是可以在worker子线程中进行的,您可以将textToSpeech语音播报的逻辑放在worker子线程中实现。

可以参考以下demo:

index.ets

import { textToSpeech } from '@kit.CoreSpeechKit';  
import { BusinessError } from '@kit.BasicServicesKit';  
import { MessageEvents, worker } from '@kit.ArkTS';  
import { hilog } from '@kit.PerformanceAnalysisKit';  
  
const TAG: string = 'TtsDemo';  
let ttsEngine: textToSpeech.TextToSpeechEngine;  
  
function initWorker(message: string) {  
  let workerInstance: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/Worker.ets', {  
    name: 'TestWorker'  
  })  
  workerInstance.postMessage(message)  
  
  // 主线程接收worker线程信息  
  workerInstance.onmessage = (e: MessageEvents): void => {  
    // data:worker线程发送的信息  
    let data: string = e.data;  
    console.log("worker线程发送的信息:" + JSON.stringify(data));  
  
    // 销毁Worker对象  
    // workerInstance.terminate();  
  }  
}  
  
@Entry  
@Component  
struct Index {  
  @State createCount: number = 0;  
  @State originalText: string = "\n\t\t古人学问无遗力,少壮工夫老始成;\n\t\t" +  
    "纸上得来终觉浅,绝知此事要躬行。\n\t\t";  
  
  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;  
              hilog.info(0x0000, TAG, "original text: " + this.originalText);  
            })  
  
          Button() {  
            Text("CreateEngine")  
              .fontColor(Color.White)  
              .fontSize(20)  
          }  
          .type(ButtonType.Capsule)  
          .backgroundColor("#0x317AE7")  
          .width("80%")  
          .height(50)  
          .margin(10)  
          .onClick(() => {  
            this.createCount++;  
            console.log(`createByCallback: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++;  
            console.log(`speak:createCount:${this.createCount}`)  
  
            //播放逻辑放在worker子线程  
            initWorker(this.originalText);  
          })  
  
          Button() {  
            Text("stop")  
              .fontColor(Color.White)  
              .fontSize(20)  
          }  
          .type(ButtonType.Capsule)  
          .backgroundColor("#0x317AE7")  
          .width("80%")  
          .height(50)  
          .margin(10)  
          .onClick(() => {  
            //停止播报  
            hilog.info(0x0000, TAG, "isSpeaking click:-->");  
            ttsEngine.stop();  
          })  
  
        }  
        .layoutWeight(1)  
      }  
      .width('100%')  
      .height('100%')  
  
    }  
  }  
  //创建引擎,通过callback形式返回  
  //当引擎不存在、引擎资源不存在、初始化超时,返回错误码1002300005,引擎创建失败  
  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  
    };  
    try {  
      //调用createEngine方法  
      textToSpeech.createEngine(initParamsInfo, (err: BusinessError, textToSpeechEngine: textToSpeech.TextToSpeechEngine) => {  
        if (!err) {  
          console.log('createEngine is success');  
          //接收创建引擎的实例  
          ttsEngine = textToSpeechEngine;  
        } else {  
          //创建引擎失败时返回错误码1002300005,可能原因:引擎不存在、资源不存在、创建引擎超时  
          console.error("errCode is " + JSON.stringify(err.code));  
          console.error("errMessage is " + JSON.stringify(err.message));  
        }  
      });  
    } catch (error) {  
      let message = (error as BusinessError).message;  
      let code = (error as BusinessError).code;  
      console.error(`createEngine failed, error code: ${code}, message: ${message}.`)  
    }  
  }

Worker.ets

import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';  
import { textToSpeech } from '@kit.CoreSpeechKit';  
import { UuidBasic } from '../pages/UuidBasic';  
import { BusinessError } from '@kit.BasicServicesKit';  
  
const workerPort: ThreadWorkerGlobalScope = worker.workerPort;  
let ttsEngine: textToSpeech.TextToSpeechEngine;  
  
// worker线程接收主线程消息  
workerPort.onmessage = (e: MessageEvents): void => {  
  // data:主线程发送的消息  
  let data: string = e.data;  
  console.log("主线程发送的消息:" + data);  
  
  //设置播报相关参数  
  let extraParam: Record<string, Object> = {  
    "queueMode": 0,  
    "speed": 1,  
    "volume": 2,  
    "pitch": 1,  
    "languageContext": 'zh-CN',  
    "audioType": "pcm",  
    "soundChannel": 1,  
    "playType": 1  
  }  
  let speakParams: textToSpeech.SpeakParams = {  
    requestId: UuidBasic.createUUID(),  
    extraParams: extraParam  
  }  
  
  createByCallback(() => {  
    //设置回调  
    setListener();  
    //调用speak播报方法  
    ttsEngine.speak(data, speakParams);  
  
    // worker线程向主线程发送信息  
    workerPort.postMessage({ message: "我是worker线程发送到宿主线程的消息" })  
  })  
}  
  
  
//发生error的回调  
workerPort.onerror = (e: ErrorEvent) => {  
  console.log("worker线程接收宿主线程消息错误:" + e.error)  
}  
  
//创建引擎,通过callback形式返回  
//当引擎不存在、引擎资源不存在、初始化超时,返回错误码1002300005,引擎创建失败  
function createByCallback(successCallback: Function) {  
  //设置创建引擎参数  
  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  
  };  
  try {  
    textToSpeech.createEngine(initParamsInfo, (err: BusinessError, textToSpeechEngine: textToSpeech.TextToSpeechEngine) => {  
      if (!err) {  
        console.log('createEngine is success');  
        ttsEngine = textToSpeechEngine;  
        successCallback();  
      } else {  
        console.error("errCode is " + JSON.stringify(err.code));  
        console.error("errMessage is " + JSON.stringify(err.message));  
      }  
    });  
  } catch (error) {  
    let message = (error as BusinessError).message;  
    let code = (error as BusinessError).code;  
    console.error(`createEngine failed, error code: ${code}, message: ${message}.`)  
  }  
}  
function setListener() {  
  let speakListener: textToSpeech.SpeakListener = {  
    onStart(utteranceId: string, response: textToSpeech.StartResponse) {  
      console.log('speakListener onStart: ' + ' utteranceId: ' + utteranceId + ' response: ' + JSON.stringify(response));  
    },  
    onComplete(utteranceId: string, response: textToSpeech.CompleteResponse) {  
      console.log('speakListener onComplete: ' + ' utteranceId: ' + utteranceId + ' response: ' + JSON.stringify(response));  
    },  
    onStop(utteranceId: string, response: textToSpeech.StopResponse) {  
      console.log('speakListener onStop: ' + ' utteranceId: ' + utteranceId + ' response: ' + JSON.stringify(response));  
    },  
    onData(utteranceId: string, audio: ArrayBuffer, response: textToSpeech.SynthesisResponse) {  
      console.log('speakListener onData: ' + ' utteranceId: ' + utteranceId + ' sequence: ' + JSON.stringify(response) + ' audio: ' + audio);  
    },  
    onError(utteranceId: string, errorCode: number, errorMessage: string) {  
      console.error('speakListener onError: ' + ' utteranceId: ' + utteranceId + ' errorCode: ' + errorCode + ' errorMessage: ' + errorMessage);  
    }  
  };  
  ttsEngine.setListener(speakListener);  
}

UuidBasic.ts

const baseChar: string[] = (() => {  
  const array: string[] = [];  
  // 将数字0-9加入到 baseChar 中  
  for (let i = 0; i < 10; i++) {  
    array.push(i.toString(10));  
  }  
  // 将小写字母a-z加入到 baseChar 中  
  for (let i = 0; i < 26; i++) {  
    array.push(String.fromCharCode('a'.charCodeAt(0) + i));  
  }  
  // 将大写字母A-Z加入到 baseChar 中  
  for (let i = 0; i < 26; i++) {  
    array.push(String.fromCharCode('A'.charCodeAt(0) + i));  
  }  
  return array;  
})();  
// UUID参数集合, 使用 count 能保证在本地环境中UUID绝对不会重复.  
const UuidObject: Record<string, { prefix: string; count: number }> = {};  
export class UuidBasic{  
  // 进制转换/数字转字符  
  private static scaleTransition(value: number, base = baseChar): string {  
    if (value < 0) {  
      throw new Error('scaleTransition Error, value < 0');  
    }  
    if (value === 0) {  
      return base[0];  
    }  
    const radix = base.length;  
    let result = '';  
    let resValue = value;  
    while (resValue > 0) {  
      result = base[resValue % radix] + result;  
      resValue = Math.floor(resValue / radix);  
    }  
    return result;  
  }  
  // 生成随机的UUID  
  public static createUUID(prefix = '') {  
    let uuidObject = UuidObject[prefix];  
    if (uuidObject === undefined) {  
      const str = `${this.scaleTransition(Date.now())}-${this.scaleTransition(  
        Math.floor(Math.random() * 10000000000),  
      )}-`;  
      if (prefix) {  
        uuidObject = {  
          prefix: `${prefix}-${str}`,  
          count: 0,  
        };  
      } else {  
        uuidObject = {  
          prefix: str,  
          count: 0,  
        };  
      }  
      UuidObject[prefix] = uuidObject;  
    }  
    return uuidObject.prefix + this.scaleTransition(uuidObject.count++);  
  }  
}
分享
微博
QQ
微信
回复
2024-10-21 15:05:49
相关问题
文本转语音的方法有哪些?
358浏览 • 1回复 待解决
Worker的宿主线程必须是主线程
422浏览 • 1回复 待解决
HarmonyOS主线程线程切换问题
626浏览 • 1回复 待解决
HarmonyOS 主线程刷新UI
267浏览 • 1回复 待解决