HarmonyOS AI语音播报、线程间通信和计时器的使用
1. 项目介绍
HarmonyOS为应用提供了丰富的AI(Artificial Intelligence)能力,支持开箱即用。开发者可以灵活、便捷地选择AI能力,让应用变得更加智能。语音播报(Text to Speech,下文简称TTS),基于华为智慧引擎(HUAWEI HiAI Engine)中的语音识别引擎,向开发者提供人工智能应用层API。该技术提供将文本转换为语音并进行播报的能力。
通过本项目,您将学习到AI语音播报、线程间通信和计时器的使用方法。项目具体示例如下:程序主体部分是一个可输入文本框,您可以在其中输入需要播报的文本文案,点击"语音播报"即可对文本进行播报,程序会同步记录语音播报的耗时。
2. 语音播报
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");
}
}
—-结束
3. 计时器和线程间通信
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页面,示例代码如下所示:
@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);
}
}
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;
}
}
};
4. 完整示例
基于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演示参考使用,产品化的代码需要考虑数据校验和国际化。
点赞点赞