开发中使用——鸿蒙CoreSpeechKit让文字发声 原创

前端森之鸟
发布于 2025-8-30 22:53
浏览
0收藏

@TOC


开发中使用——鸿蒙CoreSpeechKit让文字发声

不再是一个安安静静的“APP”,做一个可以发出声音,甚至讲话、与人进行交互,是一个APP孜孜不倦的追求和最终理想。————一个APP的独白。

一、不得不说"小艺"

  • 渊源:华为手机自带语言助手"小艺",也是一个语言AI,手机系统自带,同样支持鸿蒙Next(你懂得)。

  • 特点:支持将一篇不超过10000字数的中英文文本(简体中文、繁体中文、数字、英文)合成为语音,并以选定音色进行播报。

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

  • 语言:支持的语种为中文、英文

  • 音色:支持的音色为聆小珊女声音色、英语(美国)劳拉女声音色、凌飞哲男声音色。

  • AI: 实打实的AI,这是基于手机系统提供的CoreSpeechKit基础语言服务,属于AI模块。
    有下图为证。

开发中使用——鸿蒙CoreSpeechKit让文字发声-鸿蒙开发者社区

二、具体实现(只需三步)

  • 实现原理,要初始化文本转语言的引擎(TextToSpeechEngine),然后给它配置参数、配置回调;然后就可以使用它进行操作了,让它干活了。

1.创建文本转语言引擎(TextToSpeechEngine)

调用createEngine接口,创建TextToSpeechEngine实例。并初始化引擎。使用callback异步回调。

  createEngine(){
    // 设置创建引擎参数
    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');
        // 接收创建引擎的实例
        this.ttsEngine = textToSpeechEngine;
      } else {
        console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);
      }
    });
  }

2.创建引擎需要的参数(SpeakParams、SpeakListener)

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

  initParam(){
    // 设置speak的回调信息
     this.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}`);
      }
    };

    // 设置回调
    this.ttsEngine?.setListener(this.speakListener);

  }

3.调用播报方法

让它干活,调用播报方法,开发者可以通过修改speakParams主动设置播报策略

  speak(text:string){

    // 设置播报相关参数
    this.extraParam= {"queueMode": 0, "speed": 1, "volume": 0.1, "pitch": 1, "languageContext": 'zh-CN',
      "audioType": "pcm", "soundChannel": 3, "playType": 1 };

    this.speakParams = {
      requestId: new Date().getTime().toString(),
      extraParams: this.extraParam
    };

    // 调用播报方法
    // 开发者可以通过修改speakParams主动设置播报策略
    this.ttsEngine?.speak(text, this.speakParams);
  }

4.停止播报

让它闭嘴,当需要停止合成及播报时,可调用stop接口。

  stop(){
    // 当需要查询文本转语音服务是否处于忙碌状态时
    // 才停止播报
    if(this.ttsEngine?.isBusy()){
      this.ttsEngine?.stop();
    }
  }

三、如何使用

1.得到语言引擎管理实例

我是将上述的方法,整理出了一个单例类TextToSpeechManager,方便在APP中全局使用。

private textToSpeechManger = TextToSpeechManager.getInstance();

2.初始化语言引擎和配置

在将要进入页面时,在方法aboutToAppear()中进行初始化。

aboutToAppear() {
  /// 初始化文本转语言引擎
  this.textToSpeechManger.createEngine();
  this.textToSpeechManger.initParam();
}

3.传入文本,生成语音

this.textToSpeechManger.speak('Hello kit');

四、使用技巧

我是用到了就写一下,后面用到了再进行补充。

  • 设置停顿,[p500],表示停顿500ms
this.textToSpeechManger.speak('Hello[p500] kit');

五、展示(Show your Time)

我不知道怎么上传视频,还是算了吧。就上传一张图片吧。

开发中使用——鸿蒙CoreSpeechKit让文字发声-鸿蒙开发者社区

六、参考文档

官方文档

七、代码奉上(仅供参考)

import { ItemModel } from '../model/ItemModel'
import { ItemStatus } from '../common/ItemStatus'
import { common } from '@kit.AbilityKit'
import { preferences } from '@kit.ArkData'

export class DataService {
  private static instance: DataService
  public  items: Array<ItemModel> = []
  private  myStore:string = 'myStore';
  private itemsKey: string = 'items_data'
  private static context: common.UIAbilityContext; // 应用上下文

  // 私有构造函数
  private constructor() {}

  // 初始化上下文(需在应用启动时调用)
  public static init(context: common.UIAbilityContext): void {
    DataService.context = context;
  }

  // 获取单例实例
  public static getInstance(): DataService {
    if (!DataService.instance) {
      if (!DataService.context) {
        throw new Error('Context not initialized! Call PreferencesUtil.init() first.');
      }
      DataService.instance = new DataService();
    }
    return DataService.instance;
  }


  async loadData() {
    try {
      let pref = await preferences.getPreferences(DataService.context, this.myStore);
      let itemsJson = await pref.get(this.itemsKey, '');
      let itemsData = JSON.parse(itemsJson.toString()) as Array<ItemModel>
      // console.info('expiringItems-itemsData',JSON.stringify(itemsData))

      this.items = itemsData.map(item => {
        let model = new ItemModel(item.id, item.name, new Date(item.expiryDate))
          model.addedDate = new Date(item.addedDate)
          model.status = item.status
        return model
        })

        // 更新所有物品的状态
        this.updateAllItemStatus()

    } catch (error) {
      console.error('加载数据失败:', error)
      this.items = []
    }
  }

  async saveData() {
    try {
      let itemsJson = JSON.stringify(this.items)

      let pref = await preferences.getPreferences(DataService.context, this.myStore);
      await pref.put(this.itemsKey, itemsJson)
      await pref.flush()

    } catch (error) {
      console.error('保存数据失败:', error)
    }
  }

  addItem(item: ItemModel) {
    this.items.push(item)
    this.saveData()
  }

  updateItem(item: ItemModel) {
    const index = this.items.findIndex(i => i.id === item.id)
    if (index !== -1) {
      this.items[index] = item
      this.saveData()
    }
  }

  deleteItem(id: string) {
    const index = this.items.findIndex(i => i.id === id)
    if (index !== -1) {
      this.items.splice(index, 1)
      this.saveData()
    }
  }

  getAllItems(): Array<ItemModel> {
    return this.items
  }

  getItemById(id: string): ItemModel | undefined {
    return this.items.find(item => item.id === id)
  }

  getItemsByStatus(status: ItemStatus): Array<ItemModel> {
    return this.items.filter(item => item.status === status)
  }

  updateAllItemStatus() {
    this.items.forEach((item) => {item.updateStatus()})
    // this.saveData()
  }
}

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2025-8-30 22:59:06修改
收藏
回复
举报
回复
    相关推荐