一、什么是ASR

  • 自动语音识别(Automatic Speech Recognition,简称 ASR)是一种将人类语音转换为文本的技术。其目标是让计算机“听懂”人类的语言。

二、OpenHarmony如何实现ASR

  • 本项目参考开源项目https://github.com/k2-fsa/sherpa-onnx实现。通过三方推理框架onnx运行时运行推理模型获取输出,最后在应用侧将模型输出在屏幕上显示

    sherpa-onnx 是由 Next-gen Kaldi 团队开发的一个开源项目,旨在提供高效的离线语音识别和语音合成解决方案。它支持多种平台,包括 Android、iOS、Raspberry Pi 等,能够在没有网络连接的情况下进行实时语音处理。该项目依赖于 ONNX Runtime 框架,提供从语音到文本(ASR)、文本到语音(TTS)以及语音活动检测(VAD)等功能,适用于各类嵌入式系统和移动设备。

  • 源项目是基于HarmonyOS开发,OH4.1支持不友好,会闪退,所以笔者进行了兼容性适配,目前在开发者手机4.1/5.0都能运行。

项目流程图介绍

img

三、项目实操

3.1、环境准备

设备环境:开发者手机二代(版本:B710或B613)、开发者手机一代(B613)

开发环境:DevEco Studio 4.1 Release(构建版本:4.1.0.400)

SDK版本:4.1.9.2 Release full sdk

源码地址:https://gitee.com/MIKECODE/lavalphone_asr_demo.git

  • 选用低版本DevEco和低版本SDK是为了构建兼容OH4.1的hap包

3.2、修改代码

首先使用git clone https://gitee.com/MIKECODE/lavalphone_asr_demo.git 获取代码并用DevEco4.1打开项目文件。

  • 源项目缺少神经网络模型和签名。缺少签名无法安装,缺少神经网络模型会闪退。

3.2.1、获取神经网络模型

文件目录为如下结构即设置完成

└───entry
    └───src
        └───main
            ├───ets
            └───resources
                ├───base
                ├───en_US
                │───zh_CN
                └───rawfile
                    └───sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20 //onnx神经网络模型文件

3.2.2、项目签名

  1. 打开项目结构(快捷键Ctrl+Alt+Shift+S)

    img

  1. 勾选自动签名并确定,等待同步完成

    img

3.3、运行代码

  • 确保以上步骤完成,用数据线连接设备和pc,点击run,应用即可安装至设备。
麦克风识别页-模型加载中麦克风识别页-模型加载完成麦克风识别页-实时推理麦克风识别页-停止推理

img

img

img

img

文件识别页-模型加载完成文件识别页-选择文件文件识别页-推理中文件识别页-推理完成

img

img

img

img

使用说明

  1. 首先打开应用,等待模型初始化。
  2. 页面默认停留在麦克风识别页,点击Start,开始实时语音识别
  3. 点击Stop停止语音识别
  4. 再点击Save recorded audio,可以选择文件保存路径,文件名以.wav结尾。
  5. 点击下方From file,切换到从文件识别页,点击Select .wav file (16kHz),
  6. 选择需要进行语音识别的wav格式音频文件,点击右上角的确认
  7. 会返回页面并进行推理
  8. 等待推理完成,

四、自定义神经网络模型如何修改

  1. 下载支持的模型,支持的模型不止这些,原作者只列出了这些,可以自行尝试,下载地址https://github.com/k2-fsa/sherpa-onnx/releases/tag/asr-models

    function getModelConfig(type: number): OnlineModelConfig {
      const modelConfig = new OnlineModelConfig();
      switch (type) {
        case 0: {
          const modelDir = 'sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20';
          modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'zipformer';
          break;
        }
    
        case 1: {
          const modelDir = 'sherpa-onnx-lstm-zh-2023-02-20';
          modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-11-avg-1.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-11-avg-1.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-11-avg-1.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'lstm';
          break;
        }
    
        case 2: {
          const modelDir = 'sherpa-onnx-lstm-en-2023-02-17';
          modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'lstm';
          break;
        }
    
        case 3: {
          const modelDir = 'icefall-asr-zipformer-streaming-wenetspeech-20230615';
          modelConfig.transducer.encoder = `${modelDir}/exp/encoder-epoch-12-avg-4-chunk-16-left-128.int8.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/exp/decoder-epoch-12-avg-4-chunk-16-left-128.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/exp/joiner-epoch-12-avg-4-chunk-16-left-128.onnx`;
          modelConfig.tokens = `${modelDir}/data/lang_char/tokens.txt`;
          modelConfig.modelType = 'zipformer2';
          break;
        }
    
        case 4: {
          const modelDir = 'icefall-asr-zipformer-streaming-wenetspeech-20230615';
          modelConfig.transducer.encoder = `${modelDir}/exp/encoder-epoch-12-avg-4-chunk-16-left-128.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/exp/decoder-epoch-12-avg-4-chunk-16-left-128.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/exp/joiner-epoch-12-avg-4-chunk-16-left-128.onnx`;
          modelConfig.tokens = `${modelDir}/data/lang_char/tokens.txt`;
          modelConfig.modelType = 'zipformer2';
          break;
        }
    
        case 5: {
          const modelDir = 'sherpa-onnx-streaming-paraformer-bilingual-zh-en';
          modelConfig.paraformer.encoder = `${modelDir}/encoder.int8.onnx`;
          modelConfig.paraformer.decoder = `${modelDir}/decoder.int8.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'paraformer';
          break;
        }
    
        case 6: {
          const modelDir = 'sherpa-onnx-streaming-zipformer-en-2023-06-26';
          modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1-chunk-16-left-128.int8.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1-chunk-16-left-128.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1-chunk-16-left-128.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'zipformer2';
          break;
        }
    
        case 7: {
          const modelDir = 'sherpa-onnx-streaming-zipformer-fr-2023-04-14';
          modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-29-avg-9-with-averaged-model.int8.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-29-avg-9-with-averaged-model.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-29-avg-9-with-averaged-model.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'zipformer';
          break;
        }
    
        case 8: {
          const modelDir = 'sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20';
          modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'zipformer';
          break;
        }
    
        case 9: {
          const modelDir = 'sherpa-onnx-streaming-zipformer-zh-14M-2023-02-23'
          modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'zipformer';
          break;
        }
    
        case 10: {
          const modelDir = 'sherpa-onnx-streaming-zipformer-en-20M-2023-02-17';
          modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'zipformer';
          break;
        }
    
        case 14: {
          const modelDir = 'sherpa-onnx-streaming-zipformer-korean-2024-06-16';
          modelConfig.transducer.encoder = `${modelDir}/encoder-epoch-99-avg-1.int8.onnx`;
          modelConfig.transducer.decoder = `${modelDir}/decoder-epoch-99-avg-1.onnx`;
          modelConfig.transducer.joiner = `${modelDir}/joiner-epoch-99-avg-1.int8.onnx`;
          modelConfig.tokens = `${modelDir}/tokens.txt`;
          modelConfig.modelType = 'zipformer';
          break;
        }
        default: {
          console.log(`Please specify a supported type. Given type ${type}`);
        }
      }
      return modelConfig;
    }
    
  2. 将下载的模型解压移动至项目的的rawfile目录

  3. 修改StreamingAsrWorker.ets的174行。将type修改为实际下载的模型的数值,此处以8为例。

    img

  4. 确保以上步骤完成,用数据线连接设备和pc,点击run,应用即可安装至设备。

五、其他

本项目开源地址:https://gitee.com/MIKECODE/lavalphone_asr_demo
本项目release版本已经上传开发者手机分发中心,可以直接下载体验。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐