一、介绍
小的时候就看过一个节目,就报菜名,一直印象很深刻,“蒸羊羔、蒸熊掌、蒸鹿尾儿、烧花鸭、烧雏鸡儿、烧子鹅、卤煮咸鸭、酱鸡、腊肉、松花、小肚儿”,历历在目。
前几天,家里的小朋友在背诵校园童谣,背古诗,也得帮着儿,就想着可以结合AI能力开发一个小的应用,具备以下2个能力:
- 支持拍照,然后能提取出照片中的文字。(相机功能+通用文字提取能力)
- 能把提取的文字朗读出来,最后能通过语音进行控制。(Tts能力+Asr语音识别)
使用过程是这样的,
拍摄照片—>预览照片---->提取文字---->朗读内容---->实现语音控制菜单(可选)
二、效果展示
三、搭建环境
安装DevEco Studio,详情请参考DevEco Studio下载。
设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
下载源码后,
使用DevEco Studio 打开项目,需要在真机上运行,参见 使用远程真机运行应用 。
四、项目结构
五、代码讲解
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;
- }
5.开始录音和停止语音识别
- 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;
- }
- }
- }
六、完整代码
附件可以直接下载
https://harmonyos.51cto.com/resource/1539