HarmonyOS Sample 之 AI能力应用之 报菜名儿 原创 精华
本文正在参与优质创作者激励
@toc
HarmonyOS Sample 之 AI能力应用之 报菜名儿
1.介绍
小的时候就看过一个节目,就报菜名,一直印象很深刻,“蒸羊羔、蒸熊掌、蒸鹿尾儿、烧花鸭、烧雏鸡儿、烧子鹅、卤煮咸鸭、酱鸡、腊肉、松花、小肚儿”,历历在目。
前几天,家里的小朋友在背诵校园童谣,背古诗,也得帮着儿,就想着可以结合AI能力开发一个小的应用,具备以下2个能力:
1.支持拍照,然后能提取出照片中的文字。(相机功能+通用文字提取能力)
2.能把提取的文字朗读出来,最后能通过语音进行控制。(Tts能力+Asr语音识别)
使用过程是这样的,
拍摄照片—>预览照片---->提取文字---->朗读内容---->实现语音控制菜单(可选)
2.效果展示
3.搭建环境
安装DevEco Studio,详情请参考DevEco Studio下载。
设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
下载源码后,
使用DevEco Studio 打开项目,需要在真机上运行,参见 使用远程真机运行应用 。
4.项目结构
5.代码讲解
5.1 拍摄照片介绍(相机功能)
照片拍摄的界面是MainAbilitySlice.java,主要是申请权限的代码,初始化组件然后调用相机工具类实现拍摄功能。相机工具类参考了官方给的AI相机的示例代码,CameraUtil.java 提供了拍摄画面预览、拍照、切换前后摄像头、保存照片的基础功能,核心代码如下:
1.初始化相机预览画面
/**
* 初始化屏幕
*
* @param surfaceContainer
*/
public void initSurface(Component surfaceContainer) {
HiLog.info(LABEL, "initSurface");
//相机屏幕
surfaceProvider = new SurfaceProvider(slice);
DirectionalLayout.LayoutConfig params =
new DirectionalLayout.LayoutConfig(
ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT);
surfaceProvider.setLayoutConfig(params);
//toTop --指定是否将Surface固定在最上面,值为true表示将Surface放在AGP容器组件的顶层,false表示相反。
surfaceProvider.pinToZTop(true);
surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack());
if (surfaceContainer instanceof ComponentContainer) {
((ComponentContainer) surfaceContainer).addComponent(surfaceProvider);
}
}
2.打开相机预览画面
private void openCamera() {
HiLog.info(LABEL, "openCamera");
//图像帧数据接收处理对象
imageReceiver = ImageReceiver.create(SCREEN_WIDTH, SCREEN_HEIGHT, ImageFormat.JPEG, IMAGE_RCV_CAPACITY);
//新图像接收监听
imageReceiver.setImageArrivalListener(this::saveImage);
//获取相机组件实例对象
CameraKit cameraKit = CameraKit.getInstance(slice.getApplicationContext());
if (cameraKit == null) {
// 处理cameraKit获取失败的情况
HiLog.error(LABEL, "CameraKit getInstance error");
}else{
//获取相机id列表
String[] cameraList = cameraKit.getCameraIds();
//获取前/后相机ID
String cameraId = cameraList.length > 1 && isCameraRear ? cameraList[1] : cameraList[0];
//相机状态回调
CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl();
//创建相机
cameraKit.createCamera(cameraId, cameraStateCallback, creamEventHandler);
}
}
/**
* 相机状态回调
* CameraStateCallbackImpl
*
* @since 2021-03-08
*/
class CameraStateCallbackImpl extends CameraStateCallback {
CameraStateCallbackImpl() {
}
@Override
public void onCreated(Camera camera) {
HiLog.info(LABEL, "onCreated");
//获取预览
previewSurface = surfaceProvider.getSurfaceOps().get().getSurface();
if (previewSurface == null) {
HiLog.info(LABEL, "create camera filed, preview surface is null");
return;
}
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException exception) {
HiLog.info(LABEL, "Waiting to be interrupted");
}
//提供配置相机的方法,例如添加或移除表面,以及设置相机运行模式。
CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();
//配置预览的Surfac
cameraConfigBuilder.addSurface(previewSurface);
//配置拍照的Surface
cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
//相机设备配置
camera.configure(cameraConfigBuilder.build());
cameraDevice = camera;
//相机已准备好,eventId=2,设置拍照/切换摄像头按钮可用
handler.sendEvent(2);
}
...
}
/**
* 相机预览画面回调
* 提供为表面操作添加或删除回调的方法。
*/
class SurfaceCallBack implements SurfaceOps.Callback {
@Override
public void surfaceCreated(SurfaceOps callbackSurfaceOps) {
if (callbackSurfaceOps != null) {
callbackSurfaceOps
.setFixedSize(surfaceProvider.getHeight(), surfaceProvider.getWidth());
}
openCamera();
}
@Override
public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {
}
}
3.实现拍照
/**
* 拍照
*/
public void takePicture(Component takePictureImage) {
HiLog.info(LABEL, "takePicture");
if (!takePictureImage.isEnabled()) {
HiLog.info(LABEL, "takePicture return");
return;
}
if (cameraDevice == null || imageReceiver == null) {
return;
}
//添加一个表面作为帧捕获输出。
FrameConfig.Builder framePictureConfigBuilder = cameraDevice.getFrameConfigBuilder(FRAME_CONFIG_PICTURE);
//添加图像接收Surface
framePictureConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
//提供用于帧捕获配置、图像处理和图像输出的方法。
FrameConfig pictureFrameConfig = framePictureConfigBuilder.build();
//开始单帧捕捉。
cameraDevice.triggerSingleCapture(pictureFrameConfig);
}
4.保存照片
/**
* 保存照片到存储设备
* 并复制一份到分布式文件系统
*
* @param receiver
*/
public void saveImage(ImageReceiver receiver) {
//文件起名
fileName = IMG_FILE_PREFIX + System.currentTimeMillis() + IMG_FILE_TYPE;
//外部存储的文件目录
targetFile = new File(slice.getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
try {
HiLog.info(LABEL, "filePath is " + targetFile.getCanonicalPath());
} catch (IOException e) {
HiLog.error(LABEL, "filePath is error");
}
ohos.media.image.Image image = receiver.readNextImage();
if (image == null) {
return;
}
ohos.media.image.Image.Component component = image.getComponent(ImageFormat.ComponentType.JPEG);
bytes = new byte[component.remaining()];
component.read(bytes);
try (FileOutputStream output = new FileOutputStream(targetFile)) {
output.write(bytes);
output.flush();
//保存参数
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("fileName", fileName);
dataMap.put("remaining", bytes);
//发送事件
InnerEvent event = InnerEvent.get(EVENT_IMAGESAVING_PROMTING, 1, dataMap);
handler.sendEvent(event);
//复制图片到分布式文件系统目录
DistributeFileUtil.copyPicToDistributedDir(slice, targetFile, fileName);
} catch (IOException e) {
HiLog.info(LABEL, "IOException, Save image failed");
}
}
5.相机底部缩略图的处理
用到了MyDrawTask.java工具类,它提供了将像素矩阵绘制到image组件的功能,同时处理了缩略图的大小等样式。
**
* 绘制任务
* 绘制一个照片缩略图
* @since 2021-03-08
*/
public class MyDrawTask implements Component.DrawTask {
private static final HiLogLabel LABEL = new HiLogLabel(LOG_APP, 0x00204, "=>" + MyDrawTask.class.getName());
private static final int HALF = 2;
private static final int STROKE_WIDTH = 3;
private static final int MAX_SIZE = 66;
private Component component;
private PixelMap pixelMap;
private RectFloat rectSrc;
/**
* MyDrawTask
*
* @param component component
*/
public MyDrawTask(Component component) {
HiLog.info(LABEL, "MyDrawTask");
this.component = component;
}
/**
* putPixelMap
* 以像素矩阵的形式提供图像
* @param pm Provides images in forms of pixel matrices
*/
public void putPixelMap(PixelMap pm) {
if (pm != null) {
pixelMap = pm;
int halfWidth = pixelMap.getImageInfo().size.width / HALF;
int halfHeight = pixelMap.getImageInfo().size.height / HALF;
int center = Math.min(halfWidth, halfHeight);
rectSrc = new RectFloat(halfWidth - center, halfHeight - center, halfWidth + center, halfHeight + center);
component.invalidate();
}
}
@Override
public void onDraw(Component component1, Canvas canvas) {
int halfWidth = component.getWidth() / HALF;
int halfHeight = component.getHeight() / HALF;
int center = Math.min(halfWidth, halfHeight);
if (center > MAX_SIZE) {
center = MAX_SIZE;
}
int radius = center - STROKE_WIDTH / HALF;
if (pixelMap != null) {
canvas.drawPixelMapHolderCircleShape(new PixelMapHolder(pixelMap), rectSrc, halfWidth, halfHeight, radius);
}
Paint roundPaint = new Paint();
roundPaint.setAntiAlias(true);
roundPaint.setStyle(Paint.Style.STROKE_STYLE);
roundPaint.setStrokeWidth(STROKE_WIDTH);
roundPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
roundPaint.setColor(Color.WHITE);
canvas.drawCircle(halfWidth, halfHeight, radius, roundPaint);
}
}
工具类中使用 EventHandler对象,实现跨线程通知的目的。
以上需要ohos.permission.CAMERA和ohos.permission.WRITE_MEDIA权限。
5.2 预览照片介绍(PixelMap的使用)
预览照片的界面是ImageAbilitySlice.java 主要是读取本地存储的照片后并做显示。
/**
* 本地读取照片并显示
*
* @param picPath
*/
private void setImage(String picPath) {
HiLog.info(LABEL, "setImage");
if (picPath != null && !picPath.isEmpty()) {
PixelMap pixelMap = PixelMapUtil.getPixelMapByLocalPath(null, picPath, 1);
bigImage.setPixelMap(pixelMap);
}
}
/**
* 将图片转换为PixeMap便于使用
*/
public static PixelMap getPixelMapByLocalPath(byte[] imageBytes, String imagePath, int rotateCount) {
ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
sourceOptions.formatHint = "image/jpg";
ImageSource imageSource;
if (imagePath != null && !imagePath.isEmpty()) {
imageSource = ImageSource.create(imagePath, sourceOptions);
} else if (imageBytes != null) {
imageSource = ImageSource.create(imageBytes, sourceOptions);
} else {
return null;
}
ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();
decodingOpts.desiredSize = new Size(0, 0);
//
decodingOpts.rotateDegrees = DEGREES_90 * rotateCount;
decodingOpts.desiredPixelFormat = PixelFormat.ARGB_8888;
return imageSource.createPixelmap(decodingOpts);
}
5.3 提取文字介绍(通用文字识别能力)
WordRecognition.java //抽取了通用文字识别能力的工具类
TextDetectorAbilitySlice.java //文字识别能力页
1.init方法,传递要识别的图片路径和其他参数
/**
* 设置参数方法
*
* @param context 页面
* @param picturePaths 图片集合
* @param eventHandler MyEventHandle对象
* @since 2021-01-21
*/
public void init(Context context, String[] picturePaths, EventHandler eventHandler) {
slice = context;
pictureLists = picturePaths;
handler = eventHandler;
}
2.开始文字识别
/**
* 文字识别方法
*
* @param picPath picPath
* @since 2021-01-21
*/
public void wordRecognition(String picPath) {
HiLog.debug(LABEL, "wordRecognition");
photoPath = picPath;
// 实例化ITextDetector接口
textDetector = VisionManager.getTextDetector(slice);
// 实例化VisionImage对象image,并传入待检测图片pixelMap
pixelMap = PixelMapUtil.getPixelMapByLocalPath(null, picPath, 1);
VisionImage image = VisionImage.fromPixelMap(pixelMap);
// 定义VisionCallback<Text>回调,异步模式下用到
VisionCallback<Text> visionCallback = getVisionCallback();
// 定义ConnectionCallback回调,实现连接能力引擎成功与否后的操作
ConnectionCallback connectionCallback = getConnectionCallback(image, visionCallback);
// 建立与能力引擎的连接
VisionManager.init(slice, connectionCallback);
}
3.实现识别结果的回调函数
/**
* 提供接收服务状态变化的回调方法,
* 当服务状态改变时调用该接口。
* @param image
* @param visionCallback
* @return
*/
private ConnectionCallback getConnectionCallback(VisionImage image, VisionCallback<Text> visionCallback) {
return new ConnectionCallback() {
@Override
public void onServiceConnect() {
HiLog.debug(LABEL, "onServiceConnect");
//存储给定图像中文本的检测结果。
Text text = new Text();
// 通过TextConfiguration配置textDetector()方法的运行参数
TextConfiguration.Builder builder = new TextConfiguration.Builder();
//MODE_IN模式表示SDK在调用者进程中直接调用OCR函数。
//MODE_OUT模式表示SDK作为客户端向HUAWEI HiAI Engine发送调用请求,由HUAWEI HiAI Engine进行OCR并将结果返回给调用方。
builder.setProcessMode(VisionConfiguration.MODE_IN);
builder.setDetectType(TextDetectType.TYPE_TEXT_DETECT_FOCUS_SHOOT);
builder.setLanguage(TextConfiguration.AUTO);
TextConfiguration config = builder.build();
textDetector.setVisionConfiguration(config);
// 调用ITextDetector的detect()方法
if (IS_SYNC) {
HiLog.debug(LABEL, "IS_SYNC");
//result不为0,说明当前OCR能力准备失败
result = textDetector.detect(image, text, null); // 同步
HiLog.debug(LABEL, "IS_SYNC,result:" + result);
//
sendResult(text.getValue());
} else {
HiLog.debug(LABEL, "!IS_SYNC");
//result=700:异步调用请求发送成功
result = textDetector.detect(image, null, visionCallback); // 异步
HiLog.debug(LABEL, "!IS_SYNC,result:" + result);
}
}
@Override
public void onServiceDisconnect() {
HiLog.debug(LABEL, "onServiceDisconnect");
// 释放
if ((IS_SYNC && result == 0 && textDetector != null)) {
textDetector.release();
}
if (pixelMap != null) {
pixelMap.release();
pixelMap = null;
}
//断开HUAWEI HiAI Engine服务。
VisionManager.destroy();
}
};
}
4.异步模式下的回调函数
/**
* 异步模式下回调
*
* @return
*/
private VisionCallback<Text> getVisionCallback() {
return new VisionCallback<Text>() {
@Override
public void onResult(Text text) {
HiLog.debug(LABEL, "onResult");
//异步情况下返回识别结果后发送
sendResult(text.getValue());
}
@Override
public void onError(int index) {
HiLog.debug(LABEL, "onError");
}
@Override
public void onProcessing(float value) {
HiLog.debug(LABEL, "onProcessing");
}
};
}
最后是通过EventHandler对象将识别的结果发送给TextDetectorAbilitySlice.java
进行界面展示。
5.4 朗读内容介绍(Tts能力)
Tts能力封装在TtsUtil.java工具类,核心类包括:
import ohos.ai.tts.TtsClient; // TTS客户端
import ohos.ai.tts.TtsListener; // TTS 监听接口
import ohos.ai.tts.TtsParams; // TTS参数
import ohos.ai.tts.constants.TtsEvent; // TTS事件
import ohos.utils.PacMap; // TTS依赖
1.初始化参数
/**
* 初始化参数
*
* @param context Slice对象
* @param eventHandler 事件传递对象
* @since 2021-01-21
*/
public void init(Context context, EventHandler eventHandler) {
slice = context;
handler = eventHandler;
//创建Tts客户端
TtsClient.getInstance().create(slice, ttsListener);
}
2.实例化事件监听
/**
* 实例化TTS 事件监听
*/
private TtsListener ttsListener = new TtsListener() {
@Override
public void onEvent(int eventType, PacMap pacMap) {
HiLog.info(LABEL, "onEvent...eventType:" + eventType);
// 定义TTS客户端创建成功的回调函数
if (eventType == TtsEvent.CREATE_TTS_CLIENT_SUCCESS) {
TtsParams ttsParams = new TtsParams();
//必填
ttsParams.setDeviceId(UUID.randomUUID().toString());
//语速
//ttsParams.setSpeed(8);
//ttsParams.setSpeaker(SPEAKER_XIAOYI);
//ttsParams.setVolume(4);
//Tts初始化结果
createSucc = TtsClient.getInstance().init(ttsParams);
//发送初始化成功事件
sendResult(createSucc + "", 4);
}
}
@Override
public void onSpeechStart(String utteranceId) {
HiLog.info(LABEL, "onSpeechStart...");
sendResult("开始朗读", 5);
}
@Override
public void onSpeechFinish(String utteranceId) {
HiLog.info(LABEL, "onSpeechFinish...");
sendResult("朗读完成", 6);
}
@Override
public void onStart(String utteranceId) {
HiLog.info(LABEL, "onStart...");
}
@Override
public void onProgress(String utteranceId, byte[] audioData, int progress) {
}
@Override
public void onFinish(String utteranceId) {
HiLog.info(LABEL, "onFinish...");
}
@Override
public void onError(String s, String s1) {
HiLog.info(LABEL, "onError...s:" + s + ",s1:" + s1);
}
@Override
public void onSpeechProgressChanged(String utteranceId, int progress) {
}
};
5.5 语音控制介绍(Asr能力)
自定义ASR工具类AsrUtil.java ,提供了Asr的相关功能,通过语音识别获取指定菜单指令,然后执行相应动作。Asr核心类如下:
import ohos.ai.asr.AsrIntent; // 提供ASR引擎执行时所需要传入的参数类
import ohos.ai.asr.util.AsrError; // 错误码的定义类
import ohos.ai.asr.AsrListener; // 加载语音识别Listener
import ohos.ai.asr.AsrClient; // 提供调用ASR引擎服务接口的类
import ohos.ai.asr.util.AsrResultKey; // ASR回调结果中的关键字封装类
1.初始化参数
/**
* 初始化参数
*
* @param context Slice对象
* @param eventHandler 事件传递对象
* @since 2021-01-21
*/
public void initAsrClient(Context context, EventHandler eventHandler) {
HiLog.debug(LABEL, "initAsrClient");
slice = context;
handler = eventHandler;
//release();
initAudioCapture();
initAsrClient();
}
2.实例化TTS 事件监听
//初始化 ASR 引擎时,调用者决定如何实现该接口。
private AsrListener asrListener = new MyAsrListener() {
@Override
public void onInit(PacMap params) {
HiLog.debug(LABEL, "onInit");
super.onInit(params);
//打开录音
if (!isAsrListener) {
HiLog.debug(LABEL, "!isAsrListener");
isAsrListener = true;
//新线程去处理,拾取PCM音频流交给asrClient
poolExecutor.submit(new AudioCaptureRunnable());
}
}
@Override
public void onError(int error) {
super.onError(error);
HiLog.error(LABEL, "error:" + (error == 6 ? "在设定的时间内没有语音输入时,在回调中会返回的结果码" : error));
}
@Override
public void onIntermediateResults(PacMap pacMap) {
super.onIntermediateResults(pacMap);
HiLog.debug(LABEL, "onIntermediateResults");
String result = pacMap.getString(AsrResultKey.RESULTS_INTERMEDIATE);
//发送识别结果
sendResult(result, 6);
}
@Override
public void onResults(PacMap results) {
super.onResults(results);
HiLog.debug(LABEL, "onResults");
}
@Override
public void onEnd() {
super.onEnd();
HiLog.debug(LABEL, "onEnd");
if (!recognizeOver) {
startListening();
}
}
};
/**
* 获取 PCM 音频流并在录制过程中将它们发送到 ASR 引擎。
*
* @since 2021-03-08
*/
private class AudioCaptureRunnable implements Runnable {
@Override
public void run() {
byte[] buffers = new byte[BYTES_LENGTH];
//录音开始
audioCapturer.start();
HiLog.debug(LABEL, "audioCapturer start!");
while (isAsrListener) {
//读取音频输入
int ret = audioCapturer.read(buffers, 0, BYTES_LENGTH);
if (ret >= 0) {
//将 PCM 音频流写入字节数组并执行语音识别。
asrClient.writePcm(buffers, BYTES_LENGTH);
}
}
}
}
3.初始化录音程序
/**
* 初始化录音程序
*/
private void initAudioCapture() {
HiLog.debug(LABEL, "initAudioCapture");
//初始化线程池
poolExecutor =
new ThreadPoolExecutor(
POOL_SIZE,
POOL_SIZE,
ALIVE_TIME,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(CAPACITY),
new ThreadPoolExecutor.DiscardOldestPolicy());
//提供音频流信息
AudioStreamInfo audioStreamInfo =
new AudioStreamInfo.Builder()
.encodingFormat(AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT)
.channelMask(AudioStreamInfo.ChannelMask.CHANNEL_IN_MONO)
.sampleRate(SAMPLE_RATE)
.build();
//提供录音所需的参数结构
AudioCapturerInfo audioCapturerInfo = new AudioCapturerInfo.Builder().audioStreamInfo(audioStreamInfo).build();
//初始化录音程序
audioCapturer = new AudioCapturer(audioCapturerInfo);
}
4.初始化 ASR 引擎服务客户端
/**
* 初始化 ASR 引擎服务客户端
*/
private void initAsrClient() {
HiLog.debug(LABEL, "initAsrClient");
//创建一个 AsrClient 实例
asrClient = AsrClient.createAsrClient(slice).orElse(null);
//任务分发器 TODO
TaskDispatcher taskDispatcher = slice.getMainTaskDispatcher();
taskDispatcher.asyncDispatch(
() -> {
if (asrClient != null) {
//初始化 ASR 引擎服务。
asrClient.init(setInitIntent(), asrListener);
}
});
}
//Asr引擎输入参数
private AsrIntent setInitIntent() {
HiLog.debug(LABEL, "setInitIntent");
//提供用于执行 ASR 引擎方法的输入参数。
AsrIntent initIntent = new AsrIntent();
//识别音频流
initIntent.setAudioSourceType(AsrIntent.AsrAudioSrcType.ASR_SRC_TYPE_PCM);
//
initIntent.setEngineType(AsrIntent.AsrEngineType.ASR_ENGINE_TYPE_LOCAL);
return initIntent;
}
4.开始录音和停止语音识别
public void startListening() {
HiLog.debug(LABEL, "startListening");
if (asrClient != null) {
//开始阅读和识别语音数据。
asrClient.startListening(setStartIntent());
}
sendResult("语音控制已开启---->^_^~~", 6);
recognizeOver = false;
isAsrListener = true;
}
/**
* 停止录音和识别
*/
public void stopListening() {
HiLog.debug(LABEL, "stopListening");
if (asrClient != null) {
// 取消识别
asrClient.stopListening();
}
//识别结束
recognizeOver = true;
//isAsrListener = false;
sendResult("语音控制已关闭---->@_@~~", 6);
}
发送事件通知
5.6 NluAbilitySlice.java 代码
/**
* 朗读文本
*/
private void speakingText() {
try {
if (initTtsResult) {
HiLog.debug(LABEL, "speakingText,text:" + inputText.getText());
stopSpeaking();
//朗读长文本,文本最大支持长度为100000,speakText最长512个字符
TtsClient.getInstance().speakLongText(inputText.getText(), null);
} else {
new ToastDialog(this).setText("Tts客户端初始化失败").show();
}
} catch (Exception e) {
HiLog.error(LABEL,"ERROR:"+e.getLocalizedMessage());
e.printStackTrace();
}
}
/**
* 停止朗读
*/
private void stopSpeaking() {
TtsClient.getInstance().stopSpeak();
}
匹配语音指令
//命令关键词
private static final Map<String, Boolean> COMMAND_MAP = new HashMap<>();
static {
COMMAND_MAP.put("朗读菜单", true);
COMMAND_MAP.put("关闭语音控制", true);
COMMAND_MAP.put("停止朗读", true);
COMMAND_MAP.put("获取分词", true);
COMMAND_MAP.put("词性标注", true);
COMMAND_MAP.put("提取关键字", true);
COMMAND_MAP.put("实体识别", true);
}
/**
* 匹配指令
*
* @param result
* @return
*/
private void matchWords(String result) {
if (result != null && !result.isEmpty()) {
if (result.indexOf('{') == -1) {
resultText.setText(result);
} else {
JSONObject jsonObject = JSONObject.parseObject(result);
JSONObject resultObject = new JSONObject();
if (jsonObject.getJSONArray("result").get(0) instanceof JSONObject) {
resultObject = (JSONObject) jsonObject.getJSONArray("result").get(0);
}
String resultWord = resultObject.getString("ori_word").replace(" ", "");
resultText.setText(resultWord);
boolean command = COMMAND_MAP.getOrDefault(resultWord, false);
HiLog.debug(LABEL, "command:" + command);
switch (resultWord) {
case "朗读菜单":
HiLog.debug(LABEL, resultWord);
speakingText();
break;
case "停止朗读":
HiLog.debug(LABEL, resultWord);
stopSpeaking();
break;
case "关闭语音控制":
HiLog.debug(LABEL, resultWord);
asrUtil.stopListening();
break;
case "获取分词":
HiLog.debug(LABEL, resultWord);
//分词能力
String requestData = "{\"text\":\"" + inputText.getText() + "\",\"type\":9223372036854775807}";
nluClientUtil.wordSegment(requestData);
break;
}
}
}
}
/**
* 事件Handle
*
* @since 2021-01-15
*/
public class MyEventHandler extends EventHandler {
MyEventHandler(EventRunner runner) throws IllegalArgumentException {
super(runner);
}
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
//事件类型
HiLog.debug(LABEL, "processEvent,eventId:" + event.eventId);
switch (event.eventId) {
//分词事件
case 0:
if (event.object instanceof List) {
Optional<List<String>> optionalStringList = TypeUtil.castList(event.object, String.class);
if (optionalStringList.isPresent() && optionalStringList.get().size() > 0
&& (!"no keywords".equals(optionalStringList.get().get(0)))) {
//获取分词结果列表
List<String> lists = optionalStringList.get();
resultText.setText(lists.toString());
}
}
break;
//Tts初始化成功事件
case 4:
Boolean success = Boolean.valueOf(event.object + "");
HiLog.debug(LABEL, "processEvent,initTtsResult:" + success);
initTtsResult = success;
break;
case 6:
String audio = String.valueOf(event.object);
//HiLog.debug(LABEL, "processEvent,audio:" + audio);
//匹配命令
matchWords(audio);
break;
default:
HiLog.debug(LABEL, "processEvent,String:");
String res = String.valueOf(event.object);
HiLog.debug(LABEL, "processEvent,res:" + res);
resultText.setText(res);
break;
}
}
}
6.完整代码
附件可以直接下载
很有意思的文章,要是能在文章中放音频就更好了(可惜放不得)
呵呵~~是的,不过可以真机玩玩~