HarmonyOS Sample 之 AI能力应用之 报菜名儿 原创 精华

Buty9147
发布于 2021-11-29 23:39
浏览
7收藏

本文正在参与优质创作者激励
@toc

HarmonyOS Sample 之 AI能力应用之 报菜名儿

1.介绍

小的时候就看过一个节目,就报菜名,一直印象很深刻,“蒸羊羔、蒸熊掌、蒸鹿尾儿、烧花鸭、烧雏鸡儿、烧子鹅、卤煮咸鸭、酱鸡、腊肉、松花、小肚儿”,历历在目。
前几天,家里的小朋友在背诵校园童谣,背古诗,也得帮着儿,就想着可以结合AI能力开发一个小的应用,具备以下2个能力:
1.支持拍照,然后能提取出照片中的文字。(相机功能+通用文字提取能力)
2.能把提取的文字朗读出来,最后能通过语音进行控制。(Tts能力+Asr语音识别)
使用过程是这样的,
拍摄照片—>预览照片---->提取文字---->朗读内容---->实现语音控制菜单(可选)

2.效果展示

 HarmonyOS Sample 之 AI能力应用之 报菜名儿-鸿蒙开发者社区

3.搭建环境

安装DevEco Studio,详情请参考DevEco Studio下载
设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境
下载源码后,
使用DevEco Studio 打开项目,需要在真机上运行,参见 使用远程真机运行应用

4.项目结构

 HarmonyOS Sample 之 AI能力应用之 报菜名儿-鸿蒙开发者社区
 HarmonyOS Sample 之 AI能力应用之 报菜名儿-鸿蒙开发者社区

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.完整代码

附件可以直接下载

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
AIAbilityDemo.zip 4.4M 95次下载
已于2021-11-29 23:41:43修改
6
收藏 7
回复
举报
2条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

很有意思的文章,要是能在文章中放音频就更好了(可惜放不得)

回复
2021-11-30 10:08:47
Buty9147
Buty9147 回复了 红叶亦知秋
很有意思的文章,要是能在文章中放音频就更好了(可惜放不得)

呵呵~~是的,不过可以真机玩玩~

回复
2021-11-30 17:18:33
回复
    相关推荐