chivox_aiengine.dart 10.1 KB
import 'package:flutter/services.dart';

import 'chivox_aiengine_platform_interface.dart';

class ChivoxAiengineResult {
  /// 本次评测唯一标识
  String tokenId = "";

  /// 是否是本次评测的最后一个结果
  bool isLast = false;

  /// 文本结果。当结果类型是EvalResult、Error、Vad、SoundIntensity、Other时,使用本字段获取文本结果。
  String? text;

  /// 二进制结果。当结果类型是BinaryResult时,使用本字段获取二进制数据。
  Uint8List? data;

  /// 如果录音文件保存成功,返回录音文件的路径,否则返回null。
  String? recFilePath;
}

class ChivoxAiengineResultListener {
  ChivoxAiengineResultListener(
      {void Function(ChivoxAiengineResult result)? onEvalResult,
      void Function(ChivoxAiengineResult result)? onBinaryResult,
      void Function(ChivoxAiengineResult result)? onError,
      void Function(ChivoxAiengineResult result)? onVad,
      void Function(ChivoxAiengineResult result)? onSoundIntensity,
      void Function(ChivoxAiengineResult result)? onOther}) {
    if (onEvalResult != null) this.onEvalResult = onEvalResult;
    if (onBinaryResult != null) this.onBinaryResult = onBinaryResult;
    if (onError != null) this.onError = onError;
    if (onVad != null) this.onVad = onVad;
    if (onSoundIntensity != null) this.onSoundIntensity = onSoundIntensity;
    if (onOther != null) this.onOther = onOther;
  }

  /// 用于接收评测分数结果
  void Function(ChivoxAiengineResult result) onEvalResult = (result) {};

  /// 用于接收二进制结果
  void Function(ChivoxAiengineResult result) onBinaryResult = (result) {};

  /// 用于接收错误
  void Function(ChivoxAiengineResult result) onError = (result) {};

  /// 用于接收语音活动检测结果
  void Function(ChivoxAiengineResult result) onVad = (result) {};

  /// 用于结果音强结果
  void Function(ChivoxAiengineResult result) onSoundIntensity = (result) {};

  /// 其他未处理的结果,在SDK正确实现的情况下,不会产生此结果
  void Function(ChivoxAiengineResult result) onOther = (result) {};
}

class ChivoxAiengine {
  static int _callbackCnt = 0;
  static String _nextCallbackId() {
    _callbackCnt++;
    final cnt = _callbackCnt;
    final unixTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
    return "${unixTime}_$cnt";
  }

  static bool _staticInited = false;
  static const _callbackChannel = MethodChannel("com.chivox.aiengine/callback");
  static final Map<String, void Function(List<dynamic>)> _callbacks = {};

  static void staticInit() {
    if (_staticInited) return;
    _staticInited = true;
    _callbackChannel.setMethodCallHandler((MethodCall call) async {
      if (call.method == "aiengine_callback") {
        String callbackId = call.arguments[0];
        _callbacks[callbackId]?.call(call.arguments);
      }
    });
  }

  bool _loadingOrLoaded = false;
  String? _engineId;

  ChivoxAiengine._() {
    staticInit();
  }

  /// 无用。忽略本接口
  static Future<String?> getPlatformVersion() async {
    return await ChivoxAienginePlatform.instance.getPlatformVersion();
  }

  /// 创建引擎
  /// 参数cfg:引擎配置,json字符串(同ios或android端sdk)
  /// 抛出异常:PlatformException
  static Future<ChivoxAiengine> create(String cfg) async {
    final engine = ChivoxAiengine._();
    await engine._load(cfg);
    return engine;
  }

  Future<void> _load(String cfg) async {
    if (_loadingOrLoaded) {
      throw PlatformException(
          code: "-1", message: "don't repeatedly load the engine");
    }
    _loadingOrLoaded = true;
    _engineId = await ChivoxAienginePlatform.instance.aiengineNew(cfg);
  }

  /// 销毁引擎
  /// 当应用程序不再需要使用评测引擎时,须销毁,否则引擎内部资源不会得到释放
  /// 抛出异常:PlatformException
  Future<void> destroy() async {
    final engineId = _engineId;
    if (engineId == null) return;
    _engineId = null;
    await ChivoxAienginePlatform.instance.aiengineDelete(engineId);
  }

  /// 启动一次评测
  /// 参数audioSrc: 选择录音模式。其格式如下:
  ///   {
  ///     "srcType": string, // [必填项] 音频源:可填"innerRecorder"-sdk内部录音, "outerFeed"-用于外部输入音频
  ///     "innerRecorderParam": { // [可选项] 内部录音参数
  ///       "duration": int, // [可选项] 录音时长,默认永久
  ///       "channel": int,  // [可选项] 通道数,默认1
  ///       "sampleBytes": int, // [可选项] 采样字节,默认2
  ///       "sampleRate": int, // [可选项] 采样率,默认16000
  ///       "saveFile": string, // [可选项] 保存录音文件的路径,默认不保存
  ///     }
  ///   }
  /// 参数param: 语音评测参数,json字符串(同ios或android端sdk)
  /// 参数listener: 用于接收评测结果的listener对象
  /// 抛出异常:PlatformException
  ///
  /// 注意:对于ios平台,sdk内部并没有设置AudioSessionCategory,应用层须自行设置其为一个支持录音的值。
  Future<void> start(Map<String, dynamic> audioSrc, String param,
      ChivoxAiengineResultListener listener) async {
    final engineId = _engineId;
    if (engineId == null) {
      throw PlatformException(
          code: "-1", message: "the engine has not been loaded yet");
    }

    final callbackId = _nextCallbackId();
    _callbacks[callbackId] = (List<dynamic> args) {
      var result = ChivoxAiengineResult();
      String type = args[1];
      result.isLast = args[2];
      result.tokenId = args[3];
      result.text = args[4];
      result.data = args[5];
      result.recFilePath = args[6];
      if (type == "evalResult") {
        listener.onEvalResult(result);
      } else if (type == "binResult") {
        listener.onBinaryResult(result);
      } else if (type == "error") {
        listener.onError(result);
      } else if (type == "vad") {
        listener.onVad(result);
      } else if (type == "soundIntensity") {
        listener.onSoundIntensity(result);
      } else {
        listener.onOther(result);
      }

      if (result.isLast || type == "error") {
        _callbacks.remove(callbackId);
      }

      // TODO 超过一定时间没有回调,则强行移除回调
    };

    await ChivoxAienginePlatform.instance
        .aiengineStart(engineId, audioSrc, param, callbackId);
    return;
  }

  /// 输入音频数据 (仅outerFeed模式可使用)
  /// 抛出异常:PlatformException
  Future<void> feed(Uint8List bytes, int length) async {
    final engineId = _engineId;
    if (engineId == null) {
      throw PlatformException(
          code: "-1", message: "the engine has not been loaded yet");
    }
    await ChivoxAienginePlatform.instance.aiengineFeed(engineId, bytes, length);
  }

  /// 结束录音 (或结束音频数据)
  /// 本方法调用后将会进入等待评测结果状态,一旦评测结果产生后,将通过start方法的listener参数回调给应用层。
  /// 抛出异常:PlatformException
  Future<void> stop() async {
    final engineId = _engineId;
    if (engineId == null) {
      throw PlatformException(
          code: "-1", message: "the engine has not been loaded yet");
    }
    await ChivoxAienginePlatform.instance.aiengineStop(engineId);
  }

  /// 取消全部评测
  /// 本方法调用后,sdk内部取消所有正在进行的评测
  /// 抛出异常:PlatformException
  Future<void> cancel() async {
    final engineId = _engineId;
    if (engineId == null) {
      throw PlatformException(
          code: "-1", message: "the engine has not been loaded yet");
    }
    await ChivoxAienginePlatform.instance.aiengineCancel(engineId);
  }

  static Future<String?> getDeviceId() async {
    return await ChivoxAienginePlatform.instance.getDeviceId();
  }

  /// 激活设备,并返回序列号
  /// 本方法调用后,sdk会把序列号存储在文件系统中。
  /// 应用层下次调用本方法sdk会直接从文件系统中取出该值返回给应用层。
  /// 应用层可调用clearSavedSerialNumber方法清除sdk存储的序列号。
  /// 参数input: 结构同ios或android端sdk
  /// 返回: 结构同ios或andorid端sdk
  /// 抛出异常:PlatformException
  static Future<String?> getSerialNumber(
      ChivoxAiengine? engine, Map input) async {
    return await ChivoxAienginePlatform.instance
        .getSerialNumber(engine?._engineId, input);
  }

  /// 清除sdk在文件系统中存储的序列号
  /// 抛出异常:PlatformException
  static Future<bool?> clearSavedSerialNumber(String appKey) async {
    return await ChivoxAienginePlatform.instance.clearSavedSerialNumber(appKey);
  }

  /// 某些appkey支持二次授权功能,本方法提供该功能
  /// 抛出异常:PlatformException
  static Future<String?> getProvision(ChivoxAiengine? engine, Map input) async {
    return await ChivoxAienginePlatform.instance
        .getProvision(engine?._engineId, input);
  }

  /// 工具方法:提取sdk离线资源包到一个目录中
  /// 参数assetPrefix: 离线资源包在flutter assets中的前缀
  /// 参数assetNames: 需要提取的资源包的名称列表
  /// 参数targetDir: 目标目录,资源包会解压于此
  /// 参数progressHint: 解压进度回调
  /// 抛出异常:PlatformException
  static Future<void> extractRes(String assetPrefix, List<String> assetNames,
      String targetDir, Function(double) progressHint) async {
    staticInit();
    final callbackId = _nextCallbackId();
    _callbacks[callbackId] = (List<dynamic> args) {
      progressHint(args[1]);
    };

    await ChivoxAienginePlatform.instance
        .extractRes(assetPrefix, assetNames, targetDir, callbackId);
    _callbacks.remove(callbackId);
  }

  /// 工具方法:根据传入的资源根目录和资源名称列表,加载离线资源配置
  /// 参数resRootDir: 离线资源包的根目录
  /// 参数resNames: 指定需要哪些离线资源包
  /// 返回: 一个json字符串,描述了离线资源配置信息。该json用于引擎加载时提供离线配置信息。
  /// 抛出异常:PlatformException
  static Future<String?> loadNativeCfg(
      String resRootDir, List<String> resNames) {
    return ChivoxAienginePlatform.instance.loadNativeCfg(resRootDir, resNames);
  }
}