当前位置: 首页 > news >正文

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

  • 个人主页:VON
  • 文章所属专栏:从0开始的开源鸿蒙6.0.0
  • 个人抖音:清洒

目录

一、前言

二、实践

2.1、原理分析

2.2、创建文本朗读引擎

2.3、监听播报状态

核心作用

各部分说明

变量定义

初始化方法 initListener()

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

异常捕获结构

创建引擎的核心调用

创建成功后的处理

2.5、初始化监听器和引擎

2.6、使用引擎

三、测试

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

1.初始化与事件监听

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

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.type 为 1 时,通过 emitter 触发名为 eventId 的事件(可能用于通知上层逻辑合成已完成)。
  • onStop 回调

    onStop(requestId: string, response: textToSpeech.StopResponse) {if (response.type === 1) {emitter.emit("eventId");}
    }
    
    • 触发时机:语音合成被主动停止时调用(如调用了停止接口)。
    • 逻辑:与 onComplete 类似,当 response.type 为 1 时,触发 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:错误信息(如果创建失败,会包含错误详情;成功则为 null 或 undefined
      • 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 监听器捕获 onComplete 或 onStop 事件(参考前面的 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();}
}

http://www.dtcms.com/a/490034.html

相关文章:

  • Spring AI 1.0 GA 深度解析:Java生态的AI革命已来
  • Linux网络之----TCP网络编程
  • 【零基础学习CAPL语法】——writeLineEx() 函数
  • 计算机网络数据链路层
  • 做网站选什么专业门户网站开发步骤博客
  • 论文写作 24: 全文保持同样的节奏
  • 洛谷 P1438 无聊的数列 题解
  • iOS混淆与IPA加固实战手记,如何构建苹果应用防反编译体系
  • 想抓PostgreSQL里的慢SQL?pg_stat_statements基础黑匣子和pg_stat_monitor时间窗,谁能帮你更准揪出性能小偷?
  • 把 iOS 26 的「Liquid Glass」带进 React Native
  • 基于物理信息的神经网络求解偏微分方程反问题的综合优化策略
  • 工地佩戴安全帽检测-目标检测数据集
  • 广东网站备案查询系统企业网站带后台
  • 知名的集团门户网站建设费用我要自学网网站建设
  • 2025 年 10 月科技前沿全景:从量子跃迁到生命重构的文明拐点
  • scene graph generation 用到的vg150数据集groundtruth数据预处理,展示和保存
  • 【Qt开发】多元素类控件(一)-> QListWidget
  • 【Mybatis从入门到入土】ResultMap映射、多表查询与缓存机制全解析
  • Springboot整合MinIO文件服务(windows版本)
  • HarmonyOS Next 项目完整学习指南
  • vscode离线下载依赖
  • Python 高效清理 Excel 空白行列:从原理到实战
  • 算法11.0
  • 工业级串口通信设计
  • 盐山网站建设广西网上办事大厅
  • 郑州高端网站制作团队大连本地网
  • Linux网络的应用层自定义协议
  • leetcode 2598 执行操作后的最大MEX
  • FFmpeg 基本API avio_read函数内部调用流程分析
  • 【计算机网络】HTTP协议核心知识梳理