重生之我在大学自学鸿蒙开发第七天-《AI语音朗读》

目录

一、前言

二、实践

2.1、原理分析

2.2、创建文本朗读引擎

2.3、监听播报状态

核心作用

各部分说明

变量定义

[初始化方法 initListener()](#初始化方法 initListener())

2.4、创建文本转语音(TTS)引擎实例

异常捕获结构

创建引擎的核心调用

创建成功后的处理

2.5、初始化监听器和引擎

2.6、使用引擎

三、测试

语音功能的触发与控制流程

1.初始化与事件监听

[2.播放 / 停止语音的交互逻辑](#2.播放 / 停止语音的交互逻辑)

[3. 语音播放的状态同步](#3. 语音播放的状态同步)

四、代码


一、前言

刚才看了下官网说的是只能真机测试,虚拟机不可以实现此功能,所以本章节可能无法进行测试,这里就简单帮大家分析下代码,尽量理解里面的逻辑,我估计这种小功能可能会有完整的组件可以应用,当然只是我个人理解。大家主要还是去学习前6章,本章其实可以跳过,我这里是根据官方文档一点一点进行学习的,就不跳过了。

二、实践

本章节内容参考官方链接

通过结构化数据构建页面-HarmonyOS应用开发快速入门-Codelabs-华为开发者联盟

2.1、原理分析

官方解释:

  • 首先将实现文本转语音相关的模块导入工程。
  • 设置创建TextToSpeechEngine引擎的参数,TextToSpeechEngine下文简称引擎。
  • 调用createEngine()接口,创建TextToSpeechEngine实例。
  • 得到TextToSpeechEngine实例对象后,实例化SpeakParams对象、SpeakListener对象。
  • 调用speak()接口进行播报。

2.2、创建文本朗读引擎

要先创建一个Speaker用于完成朗读引擎的工作

创建实例完成初始化引擎

这里的参数详细解释如下,大家可以根据参数进行调整

  • language: 'zh-CN' 指定语音合成使用的语言,zh-CN 表示中文(中国大陆)。

  • person: 0 选择语音合成的发音人(音色),数值 0 通常对应默认的标准音色,不同引擎可能会有不同的数值对应不同的人声(如男声、女声、儿童声等)。

  • online: 1 设置是否使用在线合成模式,1 表示启用在线模式(通常能提供更丰富的音色和更好的合成效果),0 则表示使用离线模式。

  • extraParams(额外参数,以 JSON 对象形式存在)

    • style: 'interaction-broadcast':指定语音的风格,interaction-broadcast 通常表示交互播报风格,适合对话式场景。
    • locate: 'CN':指定地区,CN 表示中国地区,可能影响语音的本地化处理。
    • name: 'EngineName':指定 TTS 引擎的名称,可能用于区分不同的引擎实例或版本。

2.3、监听播报状态

先定义一个语音合成(TTS)的事件监听器 speakListener 及其初始化方法 initListener(),用于处理语音合成过程中的各种事件回调

核心作用

speakListener 是一个监听器对象,用于监听文本转语音(TTS)过程中的关键事件(开始、完成、停止、数据返回、错误等),并在对应事件发生时执行相应逻辑。

各部分说明

变量定义
复制代码
speakListener?: textToSpeech.SpeakListener;
  • 声明了一个可选的 speakListener 变量,类型为 textToSpeech.SpeakListener(TTS 引擎定义的监听器接口)。
  • ? 表示该变量可以为 undefined(非必需)。
初始化方法 initListener()

该方法为 speakListener 赋值,定义了各类事件的回调函数:

  • onStart 回调

    复制代码
    onStart(requestId: string, response: textToSpeech.StartResponse) {}
    • 触发时机:语音合成开始时调用。
    • 参数:
      • requestId:当前合成请求的唯一标识(用于区分多个请求)。
      • response:开始合成的响应信息(可能包含合成任务的元数据)。
  • onComplete 回调

    复制代码
    onComplete(requestId: string, response: textToSpeech.CompleteResponse) {
      if (response.type === 1) {
        emitter.emit("eventId");
      }
    }
    • 触发时机:语音合成正常完成时调用。
    • 逻辑:当 response.type1 时,通过 emitter 触发名为 eventId 的事件(可能用于通知上层逻辑合成已完成)。
  • onStop 回调

    复制代码
    onStop(requestId: string, response: textToSpeech.StopResponse) {
      if (response.type === 1) {
        emitter.emit("eventId");
      }
    }
    • 触发时机:语音合成被主动停止时调用(如调用了停止接口)。
    • 逻辑:与 onComplete 类似,当 response.type1 时,触发 eventId 事件。
  • onData 回调

    复制代码
    onData(requestId: string, audio: ArrayBuffer, response: textToSpeech.SynthesisResponse) {}
    • 触发时机:合成过程中返回音频数据时调用(通常是流式返回,多次触发)。
    • 参数:
      • audio:合成的音频数据(二进制缓冲区,可用于播放)。
      • response:音频数据的相关信息(如格式、进度等)。
  • onError 回调

    复制代码
    onError(requestId: string, errorCode: number, errorMessage: string) {}
    • 触发时机:合成过程中发生错误时调用(如网络异常、参数错误等)。
    • 参数:
      • errorCode:错误码(用于定位具体错误类型)。
      • errorMessage:错误描述信息(可读性文本)。

2.4、创建文本转语音(TTS)引擎实例

定义一个 createEngine() 方法,用于创建文本转语音(TTS)引擎实例,并进行初始化配置

异常捕获结构

复制代码
try {
  // 核心逻辑:创建引擎
} catch (error) {
  // 捕获并处理创建过程中的异常
  let message = (error as BusinessError).message;
  let code = (error as BusinessError).code;
}
  • 使用 try-catch 包裹核心逻辑,防止创建引擎时的同步错误导致程序崩溃
  • 捕获到错误后,提取错误信息(message)和错误码(code),便于问题定位(目前仅提取未做进一步处理,实际应用中可能会添加日志记录或用户提示)

创建引擎的核心调用

复制代码
textToSpeech.createEngine(
  this.initParamsInfo,  // 参数1:初始化配置
  // 参数2:回调函数(处理创建结果)
  (err: BusinessError, textToSpeechEngine: textToSpeech.TextToSpeechEngine) => {
    if (!err) {
      // 成功逻辑
      this.ttsEngine = textToSpeechEngine;
      this.ttsEngine.setListener(this.speakListener);
    } else {
      // 失败逻辑(目前为空)
    }
  }
);
  • 调用 textToSpeech.createEngine 接口创建引擎,接收两个参数:
    • 第一个参数this.initParamsInfo即之前定义的引擎初始化参数(语言、音色、在线模式等配置)
    • 第二个参数 :回调函数用于处理创建结果(异步返回),包含两个参数:
      • err:错误信息(如果创建失败,会包含错误详情;成功则为 nullundefined
      • textToSpeechEngine:创建成功后的引擎实例对象

创建成功后的处理

复制代码
if (!err) {
  this.ttsEngine = textToSpeechEngine;  // 保存引擎实例到当前对象
  this.ttsEngine.setListener(this.speakListener);  // 绑定事件监听器
}
  • err 不存在时,表示引擎创建成功
  • 将创建好的引擎实例 textToSpeechEngine 保存到当前对象的 this.ttsEngine 属性中,方便后续调用(如执行语音合成)
  • 通过 setListener 方法为引擎绑定之前初始化的 speakListener,使引擎在合成过程中能触发对应的事件回调(开始、完成、错误等)

2.5、初始化监听器和引擎

复制代码
  constructor() {
    this.initListener()
    this.createEngine()
  }

最后别忘了导出,导出后才可以被其他模块访问

2.6、使用引擎

最后编写停止销毁引擎函数即可

三、测试

这里单独创建一个组件进行测试,这里使用quickstart组件

复制代码
import { emitter } from '@kit.BasicServicesKit';
import { Speaker } from 'utils';

@Component
export struct Header {
  @State message: string = '快速入门';
  @State isPlaying: boolean = false;
  speaker: Speaker = new Speaker();
  content: string = '余承东邀请你开启鸿蒙体验之旅 欢迎来到HarmonyOS世界';

  aboutToAppear(): void {
    emitter.on('eventId', () => {
      this.isPlaying = false;
    });
  }

  build() {
    Row() {
      Text(this.message)
        .fontFamily('HarmonyHeiTi-Bold')
        .fontSize(24)
        .textAlign(TextAlign.Start)
        .lineHeight(33)
        .fontWeight(700)

      Image(this.isPlaying ? $r('app.media.ic_AI_read_on') : $r('app.media.ic_AI_read_normal'))
        .width(40)
        .height(40)
        .onClick(() => {
          this.isPlaying = !this.isPlaying;
          if (this.isPlaying === true) {
            this.speaker.startSpeak(this.content)
          } else {
            this.speaker.stopSpeak()
          }
        })
    }
    .padding({
      left: 16,
      right: 16
    })
    .justifyContent(FlexAlign.SpaceBetween)
    .height(56)
    .margin({
      bottom: 11
    })
    .width('100%')
  }
}

语音功能的触发与控制流程

1.初始化与事件监听

复制代码
aboutToAppear(): void {
  emitter.on('eventId', () => {
    this.isPlaying = false; // 收到事件后,将播放状态设为"未播放"
  });
}
  • aboutToAppear 是组件生命周期方法(组件出现前调用)。
  • 这里通过 emitter.on 监听名为 eventId 的事件,当事件触发时(通常是语音播放完成或被停止时),会将 isPlaying 设为 false,同步更新 UI 状态(如切换按钮图标)。

2.播放 / 停止语音的交互逻辑

复制代码
Image(this.isPlaying ? $r('app.media.ic_AI_read_on') : $r('app.media.ic_AI_read_normal'))
  .width(40)
  .height(40)
  .onClick(() => {
    this.isPlaying = !this.isPlaying; // 切换播放状态
    if (this.isPlaying === true) {
      this.speaker.startSpeak(this.content); // 开始播放语音
    } else {
      this.speaker.stopSpeak(); // 停止播放语音
    }
  })
  • 按钮图标 :根据 isPlaying 状态显示不同图标(播放中显示 "正在播放" 图标,停止时显示 "默认" 图标)。
  • 点击事件
    • 点击时先切换 isPlaying 状态(取反)。
    • 若切换后为 true(开始播放):调用 speaker.startSpeak(this.content),传入需要朗读的文本 content,由 Speaker 类处理文本转语音并播放。
    • 若切换后为 false(停止播放):调用 speaker.stopSpeak(),由 Speaker 类终止当前语音播放。

3. 语音播放的状态同步

当语音播放完成(正常结束)或被停止(主动调用停止)时:

  • Speaker 内部会通过之前定义的 speakListener 监听器捕获 onCompleteonStop 事件(参考前面的 speakListener 代码)。
  • 监听器中会触发 emitter.emit("eventId"),发送 eventId 事件。
  • 本组件通过 emitter.on('eventId', ...) 收到事件后,将 isPlaying 设为 false,自动更新按钮图标为 "未播放" 状态,完成 UI 与实际播放状态的同步。

这里在主页面中引用下就行了,因为我这里没有真机就用@Preview简单进行下预览

四、代码

核心文件:

Speaker

复制代码
import { textToSpeech } from '@kit.CoreSpeechKit';
import { BusinessError, emitter } from '@kit.BasicServicesKit';

export class Speaker {
  ttsEngine?: textToSpeech.TextToSpeechEngine;
  extraParam: Record<string, Object> = {
    "queueMode": 0,
    "speed": 1,
    "volume": 2,
    "pitch": 1,
    "languageContext": 'zh-CN',
    "audioType": "pcm",
    "soundChannel": 3,
    "playType": 1
  };
  //初始化引擎
  initParamsInfo: textToSpeech.CreateEngineParams = {
    language: 'zh-CN',
    person: 0,
    online: 1,
    extraParams: { "style": 'interaction-broadcast', "locate": 'CN', "name": 'EngineName' }
  };
  speakListener?: textToSpeech.SpeakListener;

  constructor() {
    this.initListener()
    this.createEngine()
  }

  initListener() {
    this.speakListener = {
      onStart(requestId: string, response: textToSpeech.StartResponse) {
      },

      onComplete(requestId: string, response: textToSpeech.CompleteResponse) {
        if (response.type === 1) {
          emitter.emit("eventId");
        }
      },

      onStop(requestId: string, response: textToSpeech.StopResponse) {
        if (response.type === 1) {
          emitter.emit("eventId");
        }
      },

      onData(requestId: string, audio: ArrayBuffer, response: textToSpeech.SynthesisResponse) {
      },

      onError(requestId: string, errorCode: number, errorMessage: string) {
      }
    };
  }

  createEngine() {
    try {
      textToSpeech.createEngine(this.initParamsInfo,
        (err: BusinessError, textToSpeechEngine: textToSpeech.TextToSpeechEngine) => {
          if (!err) {
            this.ttsEngine = textToSpeechEngine;
            this.ttsEngine.setListener(this.speakListener);
          } else {
          }
        });
    } catch (error) {
      let message = (error as BusinessError).message;
      let code = (error as BusinessError).code;
    }
  }

  startSpeak(content: string) {
    let speakParams: textToSpeech.SpeakParams = {
      requestId: Date.now().toString(),
      extraParams: this.extraParam
    };
    this.ttsEngine?.speak(content, speakParams);
  }

  stopSpeak() {
    this.ttsEngine?.stop();
  }

  shutdownEngine() {
    this.ttsEngine?.shutdown();
  }
}
相关推荐
Zz_waiting.3 小时前
服务注册 / 服务发现 - Eureka
spring cloud·云原生·eureka·服务发现
追梦者1233 小时前
k8s项目实战篇 kubesphere安装
云原生·容器·kubernetes
fakerth4 小时前
【OpenHarmony】存储管理服务模块架构
架构·操作系统·openharmony
失散134 小时前
分布式专题——46 ElasticSearch高级查询语法Query DSL实战
java·分布式·elasticsearch·架构
瑶总迷弟4 小时前
使用 Docker 和 docker-compose 快速部署 openGauss
linux·数据库·云原生·eureka
-L74 小时前
【Kubernetes】常见面试题汇总(二十五)
云原生·容器·kubernetes
没有bug.的程序员4 小时前
分布式链路追踪:微服务可观测性的核心支柱
java·分布式·微服务·架构·wpf
我是华为OD~HR~栗栗呀4 小时前
华为OD-21届考研-Java面经
java·前端·c++·python·华为od·华为·面试
君逸臣劳5 小时前
玩Android Harmony next版,通过项目了解harmony项目快速搭建开发
android·harmonyos