1. 介绍 HarmonyOS为应用提供了丰富的AI(Artificial Intelligence)能力,支持开箱即用。开发者可以灵活、便捷地选择AI能力,让应用变得更加智能。语音播报(Text to Speech,下文简称TTS),基于华为智慧引擎(HUAWEI HiAI Engine)中的语音识别引擎,向开发者提供人工智能应用层API。该技术提供将文本转换为语音并进行播报的能力。
通过本项目,您将学习到AI语音播报、线程间通信和计时器的使用方法。项目具体示例如下:程序主体部分是一个可输入文本框,您可以在其中输入需要播报的文本文案,点击"语音播报"即可对文本进行播报,程序会同步记录语音播报的耗时。
2. 搭建HarmonyOS环境
我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。
- 安装DevEco Studio,详情请参考下载和安装软件。
- 设置DevEco Studio开发环境,DevEco Studio开发环境依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
- 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
- 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
- 开发者可以参考以下链接,完成设备调试的相关配置:
3. 语音播报 TTS提供将文本转换为语音并进行播报的能力。TTS的初始化代码如下所示:
步骤 1 - 创建TTS客户端
- private void initTtsEngine() {
- TtsClient.getInstance().create(this, ttsListener);
- }
复制代码
步骤 2 - 实现TTS客户端创建成功的回调函数
- public void onEvent(int eventType, PacMap pacMap) {
- HiLog.info(LABEL_LOG, "onEvent...");
- // 定义TTS客户端创建成功的回调函数
- if (eventType == TtsEvent.CREATE_TTS_CLIENT_SUCCESS) {
- TtsParams ttsParams = new TtsParams();
- ttsParams.setDeviceId(UUID.randomUUID().toString());
- initItsResult = TtsClient.getInstance().init(ttsParams);
- }
- }
复制代码
步骤 3 - 调用TtsClient.getInstance().speakText()方法对文本进行播报
- private void readText(Component component) {
- if (initItsResult) {
- HiLog.info(LABEL_LOG, "initItsResult is true, speakText");
- TtsClient.getInstance().speakText(infoText.getText(), null);
- } else {
- HiLog.error(LABEL_LOG, "initItsResult is false");
- }
- }
复制代码
4. 计时器和线程间通信
EventHandler是HarmonyOS用于处理线程间通信的一种机制,在开发过程中,开发者经常需要处理较为耗时的操作,但是又不希望当前的线程受到阻塞,此时,就可以使用EventHandler机制。例如本例中AI语音播报是在子线程9275中执行的,更新播报耗时是在UI主线程9015中执行的,日志如下所示:
- 02-20 11:26:56.916 9015-9015/com.huawei.codedemo I 01100/MainAbilitySlice: initItsResult is true, speakText
- 02-20 11:26:56.924 9015-9015/com.huawei.codedemo I 01100/MainAbilitySlice: onEvent...
- 02-20 11:26:56.926 9015-9864/com.huawei.codedemo I 01100/MainAbilitySlice: onStart...
- 02-20 11:26:57.111 9015-9275/com.huawei.codedemo I 01100/MainAbilitySlice: onSpeechStart...
- 02-20 11:26:57.117 9015-9015/com.huawei.codedemo I 01100/MainAbilitySlice: 播报耗时:0 s
- ...
- 02-20 11:27:09.575 9015-9015/com.huawei.codedemo I 01100/MainAbilitySlice: 播报耗时:12 s
- 02-20 11:27:09.934 9015-9860/com.huawei.codedemo I 01100/MainAbilitySlice: onSpeechFinish...
复制代码
对于这一场景,就需要使用到EventHandler线程间通信机制。例如,本例中开始发音的时候发送EVENT_MSG_TIME_COUNT事件,开始计时并更新UI页面,示例代码如下所示:
- [url=home.php?mod=space&uid=2735960]@Override[/url]
- public void onSpeechStart(String utteranceId) {
- // 开始计时
- HiLog.info(LABEL_LOG, "onSpeechStart...");
- if (timer == null && timerTask == null) {
- timer = new Timer();
- timerTask = new TimerTask() {
- public void run() {
- handler.sendEvent(EVENT_MSG_TIME_COUNT);
- }
- };
- timer.schedule(timerTask, 0, 1000);
- }
- }
复制代码
EventHandler更新UI页面的代码如下所示:
- private EventHandler handler = new EventHandler(EventRunner.current()) {
- @Override
- protected void processEvent(InnerEvent event) {
- switch (event.eventId) {
- case EVENT_MSG_TIME_COUNT:
- getUITaskDispatcher().delayDispatch(new Runnable() {
- @Override
- public void run() {
- time = time + 1;
- HiLog.info(LABEL_LOG, "播报耗时:" + Integer.toString(time) + " s");
- timeText.setText("播报耗时:" + Integer.toString(time) + " s");
- }
- }, 0);
- break;
- default:
- break;
- }
- }
- };
复制代码
5. 完整示例 基于AI能力的语音播报系统完整示例代码如下所示:
- package com.huawei.texttospeech.slice;
-
- import com.huawei.texttospeech.ResourceTable;
- import ohos.aafwk.ability.AbilitySlice;
- import ohos.aafwk.content.Intent;
- import ohos.agp.components.Button;
- import ohos.agp.components.Component;
- import ohos.agp.components.Text;
- import ohos.agp.components.TextField;
- import ohos.ai.tts.TtsClient;
- import ohos.ai.tts.TtsListener;
- import ohos.ai.tts.TtsParams;
- import ohos.ai.tts.constants.TtsEvent;
- import ohos.eventhandler.EventHandler;
- import ohos.eventhandler.EventRunner;
- import ohos.eventhandler.InnerEvent;
- import ohos.hiviewdfx.HiLog;
- import ohos.hiviewdfx.HiLogLabel;
- import ohos.utils.PacMap;
-
- import java.util.Timer;
- import java.util.TimerTask;
- import java.util.UUID;
-
- public class MainAbilitySlice extends AbilitySlice {
- private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "MainAbilitySlice");
- private TextField infoText;
- private Button readBtn;
- private Text timeText;
- private boolean initItsResult;
- private static final int EVENT_MSG_INIT = 0x1000001;
- private static final int EVENT_MSG_TIME_COUNT = 0x1000002;
- private int time = 0;
- private Timer timer = null;
- private TimerTask timerTask = null;
-
- private EventHandler handler = new EventHandler(EventRunner.current()) {
- @Override
- protected void processEvent(InnerEvent event) {
- switch (event.eventId) {
- case EVENT_MSG_TIME_COUNT:
- getUITaskDispatcher().delayDispatch(new Runnable() {
- @Override
- public void run() {
- time = time + 1;
- HiLog.info(LABEL_LOG, "播报耗时:" + Integer.toString(time) + " s");
- timeText.setText("播报耗时:" + Integer.toString(time) + " s");
- }
- }, 0);
- break;
- default:
- break;
- }
- }
- };
-
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(ResourceTable.Layout_ability_main);
- initView();
- initTtsEngine();
- }
-
- private void initView() {
- infoText = (TextField) findComponentById(ResourceTable.Id_text);
- readBtn = (Button) findComponentById(ResourceTable.Id_read_btn);
- timeText = (Text) findComponentById(ResourceTable.Id_time);
- readBtn.setClickedListener(this::readText);
- }
-
- private void initTtsEngine() {
- TtsClient.getInstance().create(this, ttsListener);
- }
-
- private void readText(Component component) {
- if (initItsResult) {
- HiLog.info(LABEL_LOG, "initItsResult is true, speakText");
- TtsClient.getInstance().speakText(infoText.getText(), null);
- } else {
- HiLog.error(LABEL_LOG, "initItsResult is false");
- }
- }
-
- private TtsListener ttsListener = new TtsListener() {
- @Override
- public void onEvent(int eventType, PacMap pacMap) {
- HiLog.info(LABEL_LOG, "onEvent...");
- // 定义TTS客户端创建成功的回调函数
- if (eventType == TtsEvent.CREATE_TTS_CLIENT_SUCCESS) {
- TtsParams ttsParams = new TtsParams();
- ttsParams.setDeviceId(UUID.randomUUID().toString());
- initItsResult = TtsClient.getInstance().init(ttsParams);
- }
- }
-
- @Override
- public void onStart(String utteranceId) {
- HiLog.info(LABEL_LOG, "onStart...");
- }
-
- @Override
- public void onProgress(String utteranceId, byte[] audioData, int progress) {
- }
-
- @Override
- public void onFinish(String utteranceId) {
- HiLog.info(LABEL_LOG, "onFinish...");
- }
-
- @Override
- public void onError(String s, String s1) {
- HiLog.info(LABEL_LOG, "onError...");
- }
-
- @Override
- public void onSpeechStart(String utteranceId) {
- // 开始计时
- HiLog.info(LABEL_LOG, "onSpeechStart...");
- if (timer == null && timerTask == null) {
- timer = new Timer();
- timerTask = new TimerTask() {
- public void run() {
- handler.sendEvent(EVENT_MSG_TIME_COUNT);
- }
- };
- timer.schedule(timerTask, 0, 1000);
- }
- }
-
- @Override
- public void onSpeechProgressChanged(String utteranceId, int progress) {
- }
-
- @Override
- public void onSpeechFinish(String utteranceId) {
- // 结束计时
- HiLog.info(LABEL_LOG, "onSpeechFinish...");
- timer.cancel();
- time = 0;
- timer = null;
- timerTask = null;
- }
- };
- }
复制代码
其中,页面布局文件为ability_main.xml,示例代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <DirectionalLayout
- xmlns:ohos="http://schemas.huawei.com/res/ohos"
- ohos:height="match_parent"
- ohos:width="match_parent"
- ohos:orientation="vertical">
-
- <Text
- ohos:height="match_content"
- ohos:width="match_content"
- ohos:margin="15vp"
- ohos:text="AI语音播报"
- ohos:text_size="23fp"
- ohos:top_margin="40vp"/>
-
- <TextField
- ohos:id="$+id:text"
- ohos:height="150vp"
- ohos:width="match_content"
- ohos:layout_alignment="horizontal_center"
- ohos:left_margin="20vp"
- ohos:multiple_lines="true"
- ohos:right_margin="20vp"
- ohos:text="华为创立于1987年,是全球领先的ICT(信息与通信)基础设施和智能终端提供商,我们致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界。"
- ohos:text_size="50"
- ohos:top_margin="20vp"
- />
-
- <DirectionalLayout
- xmlns:ohos="http://schemas.huawei.com/res/ohos"
- ohos:height="match_parent"
- ohos:width="match_parent"
- ohos:orientation="horizontal">
-
- <Button
- ohos:id="$+id:read_btn"
- ohos:height="35vp"
- ohos:width="80vp"
- ohos:background_element="$graphic:background_button"
- ohos:margin="15vp"
- ohos:text="语音播报"
- ohos:text_size="16fp"/>
-
- <Text
- ohos:id="$+id:time"
- ohos:height="35vp"
- ohos:width="150vp"
- ohos:margin="15vp"
- ohos:text="播报耗时:0 s"
- ohos:text_size="16fp"/>
- </DirectionalLayout>
-
- </DirectionalLayout>
复制代码
此外您还需在resource/base/graphic目录下添加background_button.xml
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
- ohos:shape="rectangle">
- <corners
- ohos:radius="40"/>
- <solid
- ohos:color="#e9e9e9"/>
- </shape>
复制代码 说明以上代码仅demo演示参考使用,产品化的代码需要考虑数据校验和国际化。
6. 恭喜你
通过对本教程的学习,你已经学会HarmonyOS AI语音播报、线程间通信和计时器的使用。