소스 검색

添加驰声语音评测

hwh97 6 년 전
부모
커밋
6a3ad7dcbc
38개의 변경된 파일1093개의 추가작업 그리고 169개의 파일을 삭제
  1. 59 14
      .idea/workspace.xml
  2. BIN
      android/.idea/caches/build_file_checksums.ser
  3. 2 3
      android/build.gradle
  4. BIN
      android/libs/flutter.jar
  5. 1 0
      android/src/main/assets/aiengine.provision
  6. 110 0
      android/src/main/java/com/chivox/AIEngine.java
  7. 0 0
      android/src/main/jniLibs/Msc.jar
  8. BIN
      android/src/main/jniLibs/arm64-v8a/libaiengine.so
  9. 0 0
      android/src/main/jniLibs/arm64-v8a/libmsc.so
  10. BIN
      android/src/main/jniLibs/armeabi-v5te/libaiengine.so
  11. BIN
      android/src/main/jniLibs/armeabi-v7a-neon/libaiengine.so
  12. BIN
      android/src/main/jniLibs/armeabi-v7a/libaiengine.so
  13. 0 0
      android/src/main/jniLibs/armeabi-v7a/libmsc.so
  14. BIN
      android/src/main/jniLibs/armeabi/libaiengine.so
  15. BIN
      android/src/main/jniLibs/mips/libaiengine.so
  16. BIN
      android/src/main/jniLibs/mips64/libaiengine.so
  17. BIN
      android/src/main/jniLibs/x86/libaiengine.so
  18. BIN
      android/src/main/jniLibs/x86_64/libaiengine.so
  19. 141 107
      android/src/main/kotlin/cn/i2edu/speech_plugin/SpeechPlugin.kt
  20. 17 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/App.kt
  21. 15 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Audio.kt
  22. 27 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/ChivoxResult.kt
  23. 47 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Detail.kt
  24. 13 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Fluency.kt
  25. 17 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Info.kt
  26. 13 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Params.kt
  27. 15 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Request.kt
  28. 49 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Result.kt
  29. 15 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Rhythm.kt
  30. 13 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Sdk.kt
  31. 13 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Time.kt
  32. 297 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/util/AIEngineHelper.java
  33. 120 0
      android/src/main/kotlin/cn/i2edu/speech_plugin/util/ChivoxAudioEvaluatorUtil.kt
  34. 21 19
      android/src/main/kotlin/cn/i2edu/speech_plugin/util/IFlyAudioEvaluatorUtil.kt
  35. 1 0
      example/.flutter-plugins-dependencies
  36. 1 0
      example/android/gradle.properties
  37. 73 24
      example/pubspec.lock
  38. 13 2
      lib/speech_plugin.dart

+ 59 - 14
.idea/workspace.xml

@@ -1,8 +1,46 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
+  <component name="AndroidLogFilters">
+    <option name="TOOL_WINDOW_LOG_LEVEL" value="debug" />
+    <option name="TOOL_WINDOW_CONFIGURED_FILTER" value="Show only selected application" />
+  </component>
   <component name="ChangeListManager">
     <list default="true" id="91fb8cbe-3946-4eca-a4cf-05dab52b73c6" name="Default Changelist" comment="">
+      <change afterPath="$PROJECT_DIR$/android/src/main/assets/aiengine.provision" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/jniLibs/arm64-v8a/libaiengine.so" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/jniLibs/armeabi-v5te/libaiengine.so" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/jniLibs/armeabi-v7a-neon/libaiengine.so" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/jniLibs/armeabi-v7a/libaiengine.so" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/jniLibs/armeabi/libaiengine.so" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/jniLibs/mips/libaiengine.so" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/jniLibs/mips64/libaiengine.so" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/jniLibs/x86/libaiengine.so" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/jniLibs/x86_64/libaiengine.so" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/App.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Audio.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/ChivoxResult.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Detail.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Fluency.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Info.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Params.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Request.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Result.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Rhythm.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Sdk.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Time.kt" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/util/AIEngineHelper.java" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/util/IFlyAudioEvaluatorUtil.kt" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/android/.idea/caches/build_file_checksums.ser" beforeDir="false" afterPath="$PROJECT_DIR$/android/.idea/caches/build_file_checksums.ser" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/android/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/android/build.gradle" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/android/libs/Msc.jar" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/jniLibs/Msc.jar" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/android/libs/arm64-v8a/libmsc.so" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/jniLibs/arm64-v8a/libmsc.so" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/android/libs/armeabi-v7a/libmsc.so" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/jniLibs/armeabi-v7a/libmsc.so" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/android/libs/flutter.jar" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/SpeechPlugin.kt" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/SpeechPlugin.kt" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/util/AudioEvaluatorUtil.kt" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/util/ChivoxAudioEvaluatorUtil.kt" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/example/android/gradle.properties" beforeDir="false" afterPath="$PROJECT_DIR$/example/android/gradle.properties" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/example/pubspec.lock" beforeDir="false" afterPath="$PROJECT_DIR$/example/pubspec.lock" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/lib/speech_plugin.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/speech_plugin.dart" afterDir="false" />
     </list>
     <ignored path="$PROJECT_DIR$/.dart_tool/" />
@@ -24,16 +62,16 @@
   <component name="ExecutionTargetManager" SELECTED_TARGET="792QAESFTC6MD" />
   <component name="FileEditorManager">
     <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
-      <file pinned="false" current-in-tab="true">
+      <file pinned="false" current-in-tab="false">
         <entry file="file://$PROJECT_DIR$/lib/speech_plugin.dart">
           <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="176">
-              <caret line="31" column="49" lean-forward="true" selection-start-line="31" selection-start-column="49" selection-end-line="31" selection-end-column="49" />
+            <state relative-caret-position="302">
+              <caret line="20" column="3" lean-forward="true" selection-start-line="20" selection-start-column="3" selection-end-line="20" selection-end-column="3" />
             </state>
           </provider>
         </entry>
       </file>
-      <file pinned="false" current-in-tab="false">
+      <file pinned="false" current-in-tab="true">
         <entry file="file://$PROJECT_DIR$/.gitignore">
           <provider selected="true" editor-type-id="text-editor">
             <state relative-caret-position="154">
@@ -60,8 +98,8 @@
     </option>
   </component>
   <component name="ProjectFrameBounds" extendedState="6">
-    <option name="x" value="522" />
-    <option name="y" value="28" />
+    <option name="x" value="395" />
+    <option name="y" value="42" />
     <option name="width" value="1346" />
     <option name="height" value="955" />
   </component>
@@ -71,8 +109,6 @@
       <foldersAlwaysOnTop value="true" />
     </navigator>
     <panes>
-      <pane id="PackagesPane" />
-      <pane id="Scope" />
       <pane id="ProjectPane">
         <subPane>
           <expand>
@@ -93,7 +129,9 @@
           <select />
         </subPane>
       </pane>
+      <pane id="Scope" />
       <pane id="AndroidView" />
+      <pane id="PackagesPane" />
     </panes>
   </component>
   <component name="PropertiesComponent">
@@ -132,7 +170,7 @@
     <frame x="-8" y="-8" width="1936" height="1056" extended-state="6" />
     <editor active="true" />
     <layout>
-      <window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.14658849" />
+      <window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.14658849" />
       <window_info id="Captures" order="1" side_tool="true" />
       <window_info id="Structure" order="2" side_tool="true" />
       <window_info id="Image Layers" order="3" />
@@ -142,12 +180,12 @@
       <window_info id="Capture Tool" order="7" />
       <window_info id="Favorites" order="8" side_tool="true" />
       <window_info anchor="bottom" id="Dart Analysis" order="0" weight="0.32936078" />
-      <window_info anchor="bottom" id="Run" order="1" weight="0.3299435" />
+      <window_info anchor="bottom" id="Run" order="1" weight="0.329718" />
       <window_info anchor="bottom" id="TODO" order="2" />
       <window_info anchor="bottom" id="Android Profiler" order="3" show_stripe_button="false" />
-      <window_info anchor="bottom" id="Logcat" order="4" />
+      <window_info active="true" anchor="bottom" id="Logcat" order="4" visible="true" weight="0.329718" />
       <window_info anchor="bottom" id="Debug" order="5" />
-      <window_info anchor="bottom" id="Terminal" order="6" visible="true" weight="0.329718" />
+      <window_info anchor="bottom" id="Terminal" order="6" weight="0.329718" />
       <window_info anchor="bottom" id="Event Log" order="7" side_tool="true" />
       <window_info anchor="bottom" id="Flutter Performance" order="8" side_tool="true" />
       <window_info anchor="bottom" id="Version Control" order="9" />
@@ -184,8 +222,15 @@
     </entry>
     <entry file="file://$PROJECT_DIR$/lib/speech_plugin.dart">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="176">
-          <caret line="31" column="49" lean-forward="true" selection-start-line="31" selection-start-column="49" selection-end-line="31" selection-end-column="49" />
+        <state relative-caret-position="302">
+          <caret line="20" column="3" lean-forward="true" selection-start-line="20" selection-start-column="3" selection-end-line="20" selection-end-column="3" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/android/src/main/kotlin/cn/i2edu/speech_plugin/SpeechPlugin.kt">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="154">
+          <caret line="136" selection-start-line="136" selection-end-line="136" />
         </state>
       </provider>
     </entry>

BIN
android/.idea/caches/build_file_checksums.ser


+ 2 - 3
android/build.gradle

@@ -30,7 +30,6 @@ android {
     sourceSets {
         main {
             java.srcDirs += 'src/main/kotlin'
-            jniLibs.srcDirs = ['libs']
         }
     }
     defaultConfig {
@@ -44,8 +43,8 @@ android {
 
 dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-    compileOnly  files('libs/flutter.jar')
     implementation 'com.googlecode.mp4parser:isoparser:1.0-RC-37'
-    implementation files('libs/Msc.jar')
+    api "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
     implementation 'com.alibaba:fastjson:1.1.71.android'
+    implementation fileTree(dir: 'src\\main\\jniLibs', include: ['*.aar', '*.jar'], exclude: [])
 }

BIN
android/libs/flutter.jar


+ 1 - 0
android/src/main/assets/aiengine.provision

@@ -0,0 +1 @@
+ハフハホヒフフヘフマフマフマフホノヒノハニ厖ノヘニヒヒハ圀マ厠マヌヒニニフ圀フ圀卻ハ數ヒ悁ネヌ厦ヘフフフ悚ニ數ヌ悍ヘ悚棘棡ノ尹圀墹ヘノヒ劒棍ホ榾槃ノヌヒホフニホニネ敝マ劈フ屓

+ 110 - 0
android/src/main/java/com/chivox/AIEngine.java

@@ -0,0 +1,110 @@
+package com.chivox;
+
+public final class AIEngine {
+
+    static {
+        System.loadLibrary("aiengine");
+    }
+
+    /**
+     * 引擎回调接口
+     */
+    public interface aiengine_callback {
+        /**
+         * 抽象函数,调用时需重写函数实体
+         * @param id 返回id
+         * @param type 返回类型
+         * @param data 返回数据
+         * @param size 数据大小
+         * @return 返回值
+         */
+        public abstract int run(byte[] id, int type, byte[] data, int size);
+    }
+
+    /** 回调返回消息类型 - json */
+    public static int AIENGINE_MESSAGE_TYPE_JSON = 1;
+    /** 回调返回消息类型 - bin */
+    public static int AIENGINE_MESSAGE_TYPE_BIN = 2;
+
+    public static int AIENGINE_OPT_GET_VERSION = 1;
+    public static int AIENGINE_OPT_GET_MODULES = 2;
+    public static int AIENGINE_OPT_GET_TRAFFIC = 3;
+    public static int AIENGINE_OPT_SET_WIFI_STATUS = 4;
+    public static int AIENGINE_OPT_GET_PROVISION = 5;
+    public static int AIENGINE_OPT_GET_SERIAL_NUMBER = 6;
+    
+    
+    /**
+     * 创建引擎
+     * @param cfg 配置信息
+     * @param context androidContext对象,非android平台直接传null
+     * @return 0:失败
+     *    引擎对象值:成功
+     */
+    public static native long aiengine_new(String cfg, Object context);
+
+    /**
+     * 移除引擎
+     * @param engine 引擎对象值
+     * @return 返回值
+     */
+    public static native int aiengine_delete(long engine);
+    
+    /**
+     * 启动引擎
+     * @param engine 引擎对象值
+     * @param param 参数配置字符串
+     * @param id 存储引擎生成的id字符
+     * @param callback aiengine_callback对象
+     * @param context androidContext对象,非android平台直接传null
+     * @return 返回值
+     */
+    public static native int aiengine_start(long engine, String param, byte[] id, aiengine_callback callback, Object context);
+   
+    /**
+     * 向引擎缓冲区填入语音数据
+     * @param engine 引擎对象值
+     * @param data 数据
+     * @param size 数据大小
+     * @return 返回值
+     */
+    public static native int aiengine_feed(long engine, byte[] data, int size);
+    
+    /**
+     * 停止引擎
+     * @param engine 引擎对象值
+     * @return 返回值
+     */
+    public static native int aiengine_stop(long engine);
+
+    /**
+     * 评分重试 
+     * @param engine 引擎对象值
+     * @param id 存储引擎生成的id字符
+     * @param callback aiengine_callback对象
+     * @param context androidContext对象,非android平台直接传null
+     * @return 返回值
+     */
+    public static native int aiengine_redo(long engine, byte[] id, aiengine_callback callback, Object context);
+    
+    /**
+     * 取消引擎, 用于出现异常情况下,用户可以取消引擎
+     * @param engine 引擎对象值
+     * @return 返回值
+     */
+    public static native int aiengine_cancel(long engine);
+    public static native int aiengine_log(long engine, String log);
+    public static native int aiengine_opt(long engine, int opt, byte[] data, int size);
+
+    public static native int aiengine_get_last_error_code();
+    public static native int aiengine_get_last_sub_error_code();
+    public static native String aiengine_get_last_error_text();
+    
+    /**
+     * 获取设备ID
+     * @param device_id 存储id的缓冲byte[]空间
+     * @param context androidContext对象,非android平台直接传null
+     * @return 返回值
+     */
+    public static native int aiengine_get_device_id(byte[] device_id, Object context);
+}

+ 0 - 0
android/libs/Msc.jar → android/src/main/jniLibs/Msc.jar


BIN
android/src/main/jniLibs/arm64-v8a/libaiengine.so


+ 0 - 0
android/libs/arm64-v8a/libmsc.so → android/src/main/jniLibs/arm64-v8a/libmsc.so


BIN
android/src/main/jniLibs/armeabi-v5te/libaiengine.so


BIN
android/src/main/jniLibs/armeabi-v7a-neon/libaiengine.so


BIN
android/src/main/jniLibs/armeabi-v7a/libaiengine.so


+ 0 - 0
android/libs/armeabi-v7a/libmsc.so → android/src/main/jniLibs/armeabi-v7a/libmsc.so


BIN
android/src/main/jniLibs/armeabi/libaiengine.so


BIN
android/src/main/jniLibs/mips/libaiengine.so


BIN
android/src/main/jniLibs/mips64/libaiengine.so


BIN
android/src/main/jniLibs/x86/libaiengine.so


BIN
android/src/main/jniLibs/x86_64/libaiengine.so


+ 141 - 107
android/src/main/kotlin/cn/i2edu/speech_plugin/SpeechPlugin.kt

@@ -3,12 +3,14 @@ package cn.i2edu.speech_plugin
 import android.app.Activity
 import android.content.Context
 import android.text.TextUtils
-import android.widget.Toast
 import cn.i2edu.dubbing_lib.bean.SimpleWord
-import cn.i2edu.dubbing_lib.util.AudioEvaluatorUtil
-import cn.i2edu.dubbing_lib.util.EvaluatorCallBack
+import cn.i2edu.dubbing_lib.util.ChivoxAudioEvaluatorUtil
+import cn.i2edu.dubbing_lib.util.ChivoxEvaluatorCallBack
 import cn.i2edu.speech_plugin.audioUtils.audioEvaluator.resultParse.ReadSentenceResult
 import cn.i2edu.speech_plugin.audioUtils.audioEvaluator.resultParse.XmlResultParser
+import cn.i2edu.speech_plugin.model.chivox.ChivoxResult
+import cn.i2edu.speech_plugin.util.IFlyAudioEvaluatorUtil
+import cn.i2edu.speech_plugin.util.IFlyEvaluatorCallBack
 import com.alibaba.fastjson.JSON
 import com.googlecode.mp4parser.authoring.Movie
 import com.googlecode.mp4parser.authoring.Track
@@ -25,136 +27,168 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler
 import io.flutter.plugin.common.MethodChannel.Result
 import io.flutter.plugin.common.PluginRegistry.Registrar
 import java.io.File
+import java.io.FileInputStream
 import java.io.RandomAccessFile
 import java.util.*
+import com.chivox.AIEngine.*
 
-class SpeechPlugin: MethodCallHandler {
+class SpeechPlugin : MethodCallHandler {
 
     companion object {
         private lateinit var context: Context
         private lateinit var channel: MethodChannel
         private var evaluatorType: Int = 0
+        private var TAG: String = "SpeechPlugin"
 
         @JvmStatic
         fun registerWith(registrar: Registrar) {
             if (registrar.activity() == null) return
+            System.loadLibrary("aiengine")
             channel = MethodChannel(registrar.messenger(), "speech_plugin")
             context = registrar.activeContext()
             channel.setMethodCallHandler(SpeechPlugin())
         }
     }
 
-  override fun onMethodCall(call: MethodCall, result: Result) {
-    if (call.method == "getPlatformVersion") {
-      result.success("Android ${android.os.Build.VERSION.RELEASE}")
-    } else if (call.method == "initSpeechSdk") {
-      SpeechUtility.createUtility(context, SpeechConstant.APPID +"=5da1549c")
-      result.success(true)
-    } else if (call.method == "evaluatorByAudio") {
-        val pathEvaluatorDecode = call.argument<String>("pathEvaluatorDecode")!!
-        val index = call.argument<Int>("index")!!
-        val videoId = call.argument<String>("videoId")!!
-        val recordPath = call.argument<String>("recordPath")!!
-        val en = call.argument<String>("en")!!
-        evaluatorType = call.argument<Int>("evaluatorType")!!
+    override fun onMethodCall(call: MethodCall, result: Result) {
+        when {
+            call.method == "getPlatformVersion" -> result.success("Android ${android.os.Build.VERSION.RELEASE}")
+            call.method == "initSpeechSdk" -> {
+                SpeechUtility.createUtility(context, SpeechConstant.APPID + "=5da1549c")
+                result.success(true)
+            }
+            call.method == "evaluatorByAudio" -> {
+                val pathEvaluatorDecode = call.argument<String>("pathEvaluatorDecode")!!
+                val index = call.argument<Int>("index")!!
+                val videoId = call.argument<String>("videoId")!!
+                val recordPath = call.argument<String>("recordPath")!!
+                val en = call.argument<String>("en")!!
+                val sdkType = call.argument<Int>("sdkType")!!
+                evaluatorType = call.argument<Int>("evaluatorType")!!
 
-        evaluatorRecord(pathEvaluatorDecode, index, recordPath, en, false)
-    } else if (call.method == "evaluatorByMp4") {
-        val pathEvaluatorDecode = call.argument<String>("pathEvaluatorDecode")!!
-        val index = call.argument<Int>("index")!!
-        val videoId = call.argument<String>("videoId")!!
-        val recordPath = call.argument<String>("recordPath")!!
-        val en = call.argument<String>("en")!!
-        evaluatorType = call.argument<Int>("evaluatorType")!!
+                evaluatorRecord(pathEvaluatorDecode, index, recordPath, en, false, if (sdkType == 1) SpeechSdk.Chivox else SpeechSdk.IFly)
+            }
+            call.method == "evaluatorByMp4" -> {
+                val pathEvaluatorDecode = call.argument<String>("pathEvaluatorDecode")!!
+                val index = call.argument<Int>("index")!!
+                val videoId = call.argument<String>("videoId")!!
+                val recordPath = call.argument<String>("recordPath")!!
+                val en = call.argument<String>("en")!!
+                val sdkType = call.argument<Int>("sdkType")!!
+                evaluatorType = call.argument<Int>("evaluatorType")!!
 
-        evaluatorVideoRecord(pathEvaluatorDecode, index, videoId, recordPath, en)
-    } else {
-      result.notImplemented()
+                evaluatorVideoRecord(pathEvaluatorDecode, index, videoId, recordPath, en, if (sdkType == 1) SpeechSdk.Chivox else SpeechSdk.IFly)
+            }
+            else -> result.notImplemented()
+        }
     }
-  }
 
-  private fun evaluatorVideoRecord(pathEvaluatorDecode: String, index: Int, videoId: String, recordPath: String, en: String) {
-    try {
-      val movie = MovieCreator.build (recordPath)
-      val audioTracks = arrayListOf<Track>()
-      for (t in movie.tracks){
-        if (t.handler == "soun") {
-          audioTracks.add(t)
+    private fun evaluatorVideoRecord(pathEvaluatorDecode: String, index: Int, videoId: String, recordPath: String, en: String, sdk: SpeechSdk) {
+        try {
+            val movie = MovieCreator.build(recordPath)
+            val audioTracks = arrayListOf<Track>()
+            for (t in movie.tracks) {
+                if (t.handler == "soun") {
+                    audioTracks.add(t)
+                }
+            }
+            val result = Movie()
+            if (audioTracks.size > 0) {
+                val array = arrayOfNulls<Track>(audioTracks.size)
+                audioTracks.toArray(array)
+                result.addTrack(AppendTrack(*array))
+            }
+            val out = DefaultMp4Builder().build(result)
+            try {
+                val fc = RandomAccessFile("$pathEvaluatorDecode/${videoId}_$index.aac", "rw").channel
+                out.writeContainer(fc)
+                fc.close()
+                evaluatorRecord(pathEvaluatorDecode, index, "$pathEvaluatorDecode/${videoId}_$index.aac", en, true, sdk)
+            } catch (e: Exception) {
+                e.printStackTrace()
+            }
+        } catch (e: Exception) {
+            e.printStackTrace()
         }
-      }
-      val result =  Movie()
-      if (audioTracks.size > 0) {
-        val array = arrayOfNulls<Track>(audioTracks.size)
-        audioTracks.toArray(array)
-        result.addTrack(AppendTrack(*array))
-      }
-      val out = DefaultMp4Builder().build(result)
-      try {
-        val fc = RandomAccessFile("$pathEvaluatorDecode/${videoId}_$index.aac", "rw").channel
-        out.writeContainer(fc)
-        fc.close()
-        evaluatorRecord(pathEvaluatorDecode, index, "$pathEvaluatorDecode/${videoId}_$index.aac", en, true)
-      } catch ( e: Exception) {
-        e.printStackTrace()
-      }
-    }catch (e: Exception) {
-      e.printStackTrace()
     }
-  }
 
-  private fun evaluatorRecord(pathEvaluatorDecode: String, index: Int, recordPath: String, en: String, isVideo: Boolean) {
-    val dir = File(pathEvaluatorDecode)
-    if (!dir.exists()) dir.mkdirs()
-    val decodePath = pathEvaluatorDecode + UUID.randomUUID().toString() + ".pcm"
-    AudioEvaluatorUtil.getInstance(context = context)
-            .setEvaluatorCallBack(object : EvaluatorCallBack {
-              override fun onResult(result: EvaluatorResult) {
-                (context as Activity).runOnUiThread {
-                  if (isVideo) {
-                    try {
-                      File(recordPath).delete()
-                    } catch (e: java.lang.Exception) {
-                      e.printStackTrace()
-                    }
-                  }
-                  val builder = StringBuilder()
-                  builder.append(result.resultString)
-                  if (!TextUtils.isEmpty(builder)) {
-                    val resultParser = XmlResultParser()
-                    val parseResult = resultParser.parse(builder.toString()) as ReadSentenceResult
-//                    Toast.makeText(context,
-//                            "识别成功,结果:${if (parseResult.is_rejected) "您在乱读哦" else "您没乱读结果为:${parseResult.total_score}"}", Toast.LENGTH_SHORT).show()
-                    val formatWords = arrayListOf<SimpleWord>()
-                    for (sentence in parseResult.sentences) {
-                      val words = sentence.words
-                      for (word in words) {
-                        // 过滤掉sil、 silv 表示静音, fil 表示噪音
-                        if (word.content == "sil" || word.content == "silv" || word.content == "fil")
-                          continue
-                        formatWords.add(SimpleWord(word.content, word.total_score))
-                      }
-                    }
+    private fun evaluatorRecord(pathEvaluatorDecode: String, index: Int, recordPath: String, en: String, isVideo: Boolean, sdk: SpeechSdk) {
+        val dir = File(pathEvaluatorDecode)
+        if (!dir.exists()) dir.mkdirs()
+        val decodePath = pathEvaluatorDecode + UUID.randomUUID().toString() + ".pcm"
 
-                    channel.invokeMethod("evaluatorResult",
-                            mapOf("index" to index,"score" to (if(parseResult.is_rejected) null else parseResult.total_score), "integrity_score" to (if(parseResult.is_rejected) null else parseResult.integrity_score),
-                                    "accuracy_score" to (if(parseResult.is_rejected) null else parseResult.accuracy_score), "fluency_score" to (if(parseResult.is_rejected) null else parseResult.fluency_score),
-                                    "words" to JSON.toJSONString(formatWords)))
-                  } else {
-//                    Toast.makeText(context, "识别失败,结果为空", Toast.LENGTH_SHORT).show()
-                    channel.invokeMethod("evaluatorResult", mapOf("index" to index,"score" to null, "integrity_score" to null, "accuracy_score" to null, "fluency_score" to null))
-                  }
-                }
-              }
+        if (sdk == SpeechSdk.Chivox) {
+            ChivoxAudioEvaluatorUtil.getInstance(context)
+                    .setEvaluatorCallBack(object: ChivoxEvaluatorCallBack {
+                        override fun onResult(result: ChivoxResult) {
+                            (context as Activity).runOnUiThread {
+                                val formatWords = arrayListOf<SimpleWord>()
+                                for (detail in result.result.details) {
+                                    formatWords.add(SimpleWord(detail.char, detail.score / 20.0f))
+                                }
+                                channel.invokeMethod("evaluatorResult",
+                                        mapOf("index" to index, "score" to result.result.overall / 20.0f, "integrity_score" to result.result.integrity / 20.0f,
+                                                "accuracy_score" to result.result.accuracy / 20.0f, "fluency_score" to result.result.fluency.overall / 20.0f,
+                                                "words" to JSON.toJSONString(formatWords)))
+                            }
+                        }
 
-              override fun onError(error: SpeechError) {
-                (context as Activity).runOnUiThread {
-                  //                  Toast.makeText(activity.applicationContext, "识别失败,错误描述:${error.errorDescription},错误码:${error.errorCode}",
-//                          Toast.LENGTH_SHORT).show()
-                  channel.invokeMethod("evaluatorResult", mapOf("index" to index,"score" to null))
-                }
-              }
-            })
-            ?.startEvaluator(en, recordPath, decodePath, evaluatorType)
-  }
+                        override fun onError(message: String) {
+                            (context as Activity).runOnUiThread {
+                                channel.invokeMethod("evaluatorResult", mapOf("index" to index, "score" to null, "integrity_score" to null, "accuracy_score" to null, "fluency_score" to null))
+                            }
+                        }
+                    })
+                    ?.startEvaluator(en, recordPath, decodePath)
+        } else {
+            IFlyAudioEvaluatorUtil.getInstance(context = context)
+                    .setEvaluatorCallBack(object : IFlyEvaluatorCallBack {
+                        override fun onResult(result: EvaluatorResult) {
+                            (context as Activity).runOnUiThread {
+                                if (isVideo) {
+                                    try {
+                                        File(recordPath).delete()
+                                    } catch (e: java.lang.Exception) {
+                                        e.printStackTrace()
+                                    }
+                                }
+                                val builder = StringBuilder()
+                                builder.append(result.resultString)
+                                if (!TextUtils.isEmpty(builder)) {
+                                    val resultParser = XmlResultParser()
+                                    val parseResult = resultParser.parse(builder.toString()) as ReadSentenceResult
+                                    val formatWords = arrayListOf<SimpleWord>()
+                                    for (sentence in parseResult.sentences) {
+                                        val words = sentence.words
+                                        for (word in words) {
+                                            // 过滤掉sil、 silv 表示静音, fil 表示噪音
+                                            if (word.content == "sil" || word.content == "silv" || word.content == "fil")
+                                                continue
+                                            formatWords.add(SimpleWord(word.content, word.total_score))
+                                        }
+                                    }
 
+                                    channel.invokeMethod("evaluatorResult",
+                                            mapOf("index" to index, "score" to (if (parseResult.is_rejected) null else parseResult.total_score), "integrity_score" to (if (parseResult.is_rejected) null else parseResult.integrity_score),
+                                                    "accuracy_score" to (if (parseResult.is_rejected) null else parseResult.accuracy_score), "fluency_score" to (if (parseResult.is_rejected) null else parseResult.fluency_score),
+                                                    "words" to JSON.toJSONString(formatWords)))
+                                } else {
+                                    channel.invokeMethod("evaluatorResult", mapOf("index" to index, "score" to null, "integrity_score" to null, "accuracy_score" to null, "fluency_score" to null))
+                                }
+                            }
+                        }
+
+                        override fun onError(error: SpeechError) {
+                            (context as Activity).runOnUiThread {
+                                channel.invokeMethod("evaluatorResult", mapOf("index" to index, "score" to null))
+                            }
+                        }
+                    })
+                    ?.startEvaluator(en, recordPath, decodePath, evaluatorType)
+        }
+    }
 }
+
+enum class SpeechSdk {
+    IFly, Chivox,
+}

+ 17 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/App.kt

@@ -0,0 +1,17 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class App(
+    @JSONField(name = "alg")
+    val alg: String = "",
+    @JSONField(name = "applicationId")
+    val applicationId: String = "",
+    @JSONField(name = "sig")
+    val sig: String = "",
+    @JSONField(name = "timestamp")
+    val timestamp: String = "",
+    @JSONField(name = "userId")
+    val userId: String = ""
+)

+ 15 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Audio.kt

@@ -0,0 +1,15 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Audio(
+    @JSONField(name = "audioType")
+    val audioType: String = "",
+    @JSONField(name = "channel")
+    val channel: Int = 0,
+    @JSONField(name = "sampleBytes")
+    val sampleBytes: Int = 0,
+    @JSONField(name = "sampleRate")
+    val sampleRate: Int = 0
+)

+ 27 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/ChivoxResult.kt

@@ -0,0 +1,27 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class ChivoxResult(
+    @JSONField(name = "applicationId")
+    val applicationId: String = "",
+    @JSONField(name = "dtLastResponse")
+    val dtLastResponse: String = "",
+    @JSONField(name = "eof")
+    val eof: Int = 0,
+    @JSONField(name = "params")
+    val params: Params = Params(),
+    @JSONField(name = "recordId")
+    val recordId: String = "",
+    @JSONField(name = "result")
+    val result: Result = Result(),
+    @JSONField(name = "sdk")
+    val sdk: Sdk = Sdk(),
+    @JSONField(name = "time")
+    val time: Time = Time(),
+    @JSONField(name = "tokenId")
+    val tokenId: String = "",
+    @JSONField(name = "uuid")
+    val uuid: String = ""
+)

+ 47 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Detail.kt

@@ -0,0 +1,47 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Detail(
+    @JSONField(name = "accent")
+    val accent: Int = 0,
+    @JSONField(name = "beginindex")
+    val beginindex: Int = 0,
+    @JSONField(name = "char")
+    val char: String = "",
+    @JSONField(name = "conn_ref")
+    val connRef: Int = 0,
+    @JSONField(name = "conn_type")
+    val connType: Int = 0,
+    @JSONField(name = "dur")
+    val dur: Int = 0,
+    @JSONField(name = "end")
+    val end: Int = 0,
+    @JSONField(name = "endindex")
+    val endindex: Int = 0,
+    @JSONField(name = "fluency")
+    val fluency: Int = 0,
+    @JSONField(name = "indict")
+    val indict: Int = 0,
+    @JSONField(name = "pause_ref")
+    val pauseRef: Int = 0,
+    @JSONField(name = "pause_score")
+    val pauseScore: Int = 0,
+    @JSONField(name = "score")
+    val score: Int = 0,
+    @JSONField(name = "senseref")
+    val senseref: Int = 0,
+    @JSONField(name = "sensescore")
+    val sensescore: Int = 0,
+    @JSONField(name = "start")
+    val start: Int = 0,
+    @JSONField(name = "stressref")
+    val stressref: Int = 0,
+    @JSONField(name = "stressscore")
+    val stressscore: Int = 0,
+    @JSONField(name = "toneref")
+    val toneref: Int = 0,
+    @JSONField(name = "tonescore")
+    val tonescore: Int = 0
+)

+ 13 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Fluency.kt

@@ -0,0 +1,13 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Fluency(
+    @JSONField(name = "overall")
+    val overall: Int = 0,
+    @JSONField(name = "pause")
+    val pause: Int = 0,
+    @JSONField(name = "speed")
+    val speed: Int = 0
+)

+ 17 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Info.kt

@@ -0,0 +1,17 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Info(
+    @JSONField(name = "clip")
+    val clip: Int = 0,
+    @JSONField(name = "snr")
+    val snr: Double = 0.0,
+    @JSONField(name = "tipId")
+    val tipId: Int = 0,
+    @JSONField(name = "trunc")
+    val trunc: Int = 0,
+    @JSONField(name = "volume")
+    val volume: Int = 0
+)

+ 13 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Params.kt

@@ -0,0 +1,13 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Params(
+    @JSONField(name = "app")
+    val app: App = App(),
+    @JSONField(name = "audio")
+    val audio: Audio = Audio(),
+    @JSONField(name = "request")
+    val request: Request = Request()
+)

+ 15 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Request.kt

@@ -0,0 +1,15 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Request(
+    @JSONField(name = "coreType")
+    val coreType: String = "",
+    @JSONField(name = "rank")
+    val rank: Int = 0,
+    @JSONField(name = "refText")
+    val refText: String = "",
+    @JSONField(name = "tokenId")
+    val tokenId: String = ""
+)

+ 49 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Result.kt

@@ -0,0 +1,49 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Result(
+    @JSONField(name = "accuracy")
+    val accuracy: Int = 0,
+    @JSONField(name = "delaytime")
+    val delaytime: Int = 0,
+    @JSONField(name = "details")
+    val details: List<Detail> = listOf(),
+    @JSONField(name = "en_prob")
+    val enProb: Int = 0,
+    @JSONField(name = "fluency")
+    val fluency: Fluency = Fluency(),
+    @JSONField(name = "forceout")
+    val forceout: Int = 0,
+    @JSONField(name = "info")
+    val info: Info = Info(),
+    @JSONField(name = "integrity")
+    val integrity: Int = 0,
+    @JSONField(name = "is_en")
+    val isEn: Int = 0,
+    @JSONField(name = "overall")
+    val overall: Int = 0,
+    @JSONField(name = "pretime")
+    val pretime: Int = 0,
+    @JSONField(name = "pron")
+    val pron: Int = 0,
+    @JSONField(name = "rank")
+    val rank: Int = 0,
+    @JSONField(name = "res")
+    val res: String = "",
+    @JSONField(name = "rhythm")
+    val rhythm: Rhythm = Rhythm(),
+    @JSONField(name = "systime")
+    val systime: Int = 0,
+    @JSONField(name = "textmode")
+    val textmode: Int = 0,
+    @JSONField(name = "usehookw")
+    val usehookw: Int = 0,
+    @JSONField(name = "useref")
+    val useref: Int = 0,
+    @JSONField(name = "version")
+    val version: String = "",
+    @JSONField(name = "wavetime")
+    val wavetime: Int = 0
+)

+ 15 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Rhythm.kt

@@ -0,0 +1,15 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Rhythm(
+    @JSONField(name = "overall")
+    val overall: Int = 0,
+    @JSONField(name = "sense")
+    val sense: Int = 0,
+    @JSONField(name = "stress")
+    val stress: Int = 0,
+    @JSONField(name = "tone")
+    val tone: Int = 0
+)

+ 13 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Sdk.kt

@@ -0,0 +1,13 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Sdk(
+    @JSONField(name = "protocol")
+    val protocol: String = "",
+    @JSONField(name = "source")
+    val source: String = "",
+    @JSONField(name = "version")
+    val version: String = ""
+)

+ 13 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/model/chivox/Time.kt

@@ -0,0 +1,13 @@
+package cn.i2edu.speech_plugin.model.chivox
+
+
+import com.alibaba.fastjson.annotation.JSONField
+
+data class Time(
+    @JSONField(name = "callback")
+    val callback: String = "",
+    @JSONField(name = "connect")
+    val connect: String = "",
+    @JSONField(name = "request")
+    val request: String = ""
+)

+ 297 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/util/AIEngineHelper.java

@@ -0,0 +1,297 @@
+package cn.i2edu.speech_plugin.util;
+
+import android.content.Context;
+import android.os.Environment;
+import android.util.Log;
+
+import com.chivox.AIEngine;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+//import org.apache.http.HttpResponse;
+//import org.apache.http.NameValuePair;
+//import org.apache.http.client.HttpClient;
+//import org.apache.http.client.entity.UrlEncodedFormEntity;
+//import org.apache.http.client.methods.HttpPost;
+//import org.apache.http.impl.client.DefaultHttpClient;
+//import org.apache.http.message.BasicNameValuePair;
+//import org.apache.http.params.BasicHttpParams;
+//import org.apache.http.util.EntityUtils;
+
+public class AIEngineHelper {
+
+	private static String TAG = "AIEngineHelper";
+	private static int BUFFER_SIZE = 4096;
+
+	private static String readFileAsString(File file) throws IOException {
+		String line;
+		StringBuffer sb = new StringBuffer();
+		BufferedReader br = new BufferedReader(new FileReader(file));
+		while ((line = br.readLine()) != null) {
+			sb.append(line);
+		}
+
+		br.close();
+		return sb.toString();
+	}
+
+	private static void writeFileAsString(File file, String str) throws IOException {
+		FileWriter fw = new FileWriter(file);
+		fw.write(str);
+		fw.close();
+	}
+
+	private static void writeFileAsBytes(File file, byte [] bytes) throws IOException {
+		FileOutputStream fstream = new FileOutputStream(file);
+		BufferedOutputStream stream = new BufferedOutputStream(fstream);
+		stream.write(bytes);
+		stream.close();
+		fstream.close();
+	}
+
+	private static void removeDirectory(File directory) {
+		if (directory.isDirectory()) {
+			File[] files = directory.listFiles();
+			for (int i = 0; i < files.length; i++) {
+				if (files[i].isDirectory()) {
+					removeDirectory(files[i]);
+				}
+				files[i].delete();
+			}
+			directory.delete();
+		}
+	}
+
+	/**
+	 * extract resource once, the resource should in zip formatting
+	 *
+	 * @param context
+	 * @param name
+	 * @return return resource directory contains resources for native aiengine cores on success, otherwise return null
+	 */
+	public static String extractResourceOnce(Context context, String name, boolean unzip) {
+
+		try {
+			if (unzip) {
+				String pureName = name.replaceAll("\\.[^.]*$", "");
+
+				File filesDir = getFilesDir(context);
+				File targetDir = new File(filesDir, pureName);
+
+				String md5sum = md5sum(context.getAssets().open(name));
+
+				File md5sumFile = new File(targetDir, ".md5sum");
+				if (targetDir.isDirectory()) {
+					if (md5sumFile.isFile()) {
+						String md5sum2 = readFileAsString(md5sumFile);
+						if (md5sum2.equals(md5sum)) {
+							return targetDir.getAbsolutePath(); /* already extracted */
+						}
+					}
+
+					removeDirectory(targetDir); /* remove old dirty resource */
+				}
+
+				unzip(context.getAssets().open(name), targetDir);
+				writeFileAsString(md5sumFile, md5sum);
+
+				return targetDir.getAbsolutePath();
+			} else {
+				File targetFile = new File(getFilesDir(context), name);
+				copyInputStreamToFile(context.getAssets().open(name), targetFile);
+				return targetFile.getAbsolutePath();
+			}
+		} catch (Exception e) {
+			Log.e(TAG, "failed to extract resource", e);
+		}
+
+		return null;
+	}
+
+	/**
+	 * register device once
+	 * @param appKey
+	 * @param secretKey
+	 * @param userId
+	 * @return return serialNumber on success, otherwise return null
+	 */
+	public static String registerDeviceOnce(Context context, String appKey, String secretKey, String userId) {
+		File filesDir = getFilesDir(context);
+		File serialNumberFile = new File(filesDir, "aiengine.serial");
+		String serialNumber = "";
+		String serialNumberInfo = "";
+		if (serialNumberFile.isFile()) {
+			try {
+				serialNumber = readFileAsString(serialNumberFile);
+				Log.d(TAG, "yes");
+				return serialNumber;
+			} catch (IOException e) {
+				/* ignore */
+			}
+		}
+		String sig = String.format("{\"appKey\":\"%s\",\"secretKey\":\"%s\",\"userId\":\"%s\"}", appKey,secretKey,userId);
+		JSONObject sig_json = null;
+		try {
+			sig_json = new JSONObject(sig);
+		} catch (JSONException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		byte cfg_b[] = Arrays.copyOf(sig_json.toString().getBytes(), 1024);
+		int ret = AIEngine.aiengine_opt(0, 6, cfg_b, 1024);
+		if (ret > 0) {
+			serialNumberInfo = new String(cfg_b, 0, ret);
+		} else {
+			serialNumberInfo = new String(cfg_b);
+		}
+		try {
+			serialNumber = (new JSONObject(serialNumberInfo)).getString("serialNumber");
+		} catch (JSONException e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+			return "";
+		}
+		if(serialNumber.length()==0){
+			return "";
+		}
+		try {
+			writeFileAsString(serialNumberFile, serialNumber);
+		} catch (Exception e1) {
+			/* ignore */
+		}
+		return serialNumber;
+	}
+
+	public static File getFilesDir(Context context) {
+
+		File targetDir = null;
+
+		if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+			// targetDir = context.getExternalFilesDir(null); // not support android 2.1
+			targetDir = new File(Environment.getExternalStorageDirectory(), "Android/data/" + context.getApplicationInfo().packageName + "/files");
+			if (!targetDir.exists()) {
+				targetDir.mkdirs();
+			}
+		}
+
+		if (targetDir == null || !targetDir.exists()) {
+			targetDir = context.getFilesDir();
+		}
+
+		return targetDir;
+	}
+
+	private static void unzip(InputStream is, File targetDir)
+			throws IOException {
+
+		ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is,
+				BUFFER_SIZE));
+
+		ZipEntry ze;
+		while ((ze = zis.getNextEntry()) != null) {
+			if (ze.isDirectory()) {
+				new File(targetDir, ze.getName()).mkdirs();
+			} else {
+
+				File file = new File(targetDir, ze.getName());
+				File parentdir = file.getParentFile();
+				if (parentdir != null && (!parentdir.exists())) {
+					parentdir.mkdirs();
+				}
+
+				int pos;
+				byte[] buf = new byte[BUFFER_SIZE];
+				OutputStream bos = new FileOutputStream(file);
+				while ((pos = zis.read(buf, 0, BUFFER_SIZE)) > 0) {
+					bos.write(buf, 0, pos);
+				}
+				bos.flush();
+				bos.close();
+
+				Log.d(TAG, file.getAbsolutePath());
+			}
+		}
+
+		zis.close();
+		is.close();
+	}
+
+	private static void copyInputStreamToFile(InputStream is, File file)
+			throws Exception {
+		int bytes;
+		byte[] buf = new byte[BUFFER_SIZE];
+
+		FileOutputStream fos = new FileOutputStream(file);
+		while ((bytes = is.read(buf, 0, BUFFER_SIZE)) > 0) {
+			fos.write(buf, 0, bytes);
+		}
+
+		is.close();
+		fos.close();
+	};
+
+	private static String sha1(String message) {
+		try {
+			MessageDigest md = MessageDigest.getInstance("SHA-1");
+			md.update(message.getBytes(), 0, message.length());
+			return bytes2hex(md.digest());
+		} catch (Exception e) {
+			/* ignore */
+		}
+		return null;
+	}
+
+	private static String bytes2hex(byte[] bytes) {
+		StringBuffer sb = new StringBuffer(bytes.length * 2);
+		for (int i = 0; i < bytes.length; i++) {
+			int v = bytes[i] & 0xff;
+			if (v < 16) {
+				sb.append('0');
+			}
+			sb.append(Integer.toHexString(v));
+		}
+		return sb.toString();
+	}
+
+	private static String md5sum(InputStream is) {
+		int bytes;
+		byte buf[] = new byte[BUFFER_SIZE];
+		try {
+			MessageDigest md = MessageDigest.getInstance("MD5");
+			while ((bytes = is.read(buf, 0, BUFFER_SIZE)) > 0) {
+				md.update(buf, 0, bytes);
+			}
+			is.close();
+			return bytes2hex(md.digest());
+		} catch (Exception e) {
+			/* ignore */
+		}
+		return null;
+	}
+
+	public static String getProvisionTmpFile(Context context)
+	{
+		File filesDir = getFilesDir(context);
+		String str = filesDir.getAbsolutePath() + "/aiengine.provision.temp";
+
+		Log.d(TAG, "provison tmp file path: " + str);
+
+		return str;
+	}
+}

+ 120 - 0
android/src/main/kotlin/cn/i2edu/speech_plugin/util/ChivoxAudioEvaluatorUtil.kt

@@ -0,0 +1,120 @@
+package cn.i2edu.dubbing_lib.util
+
+import android.content.Context
+import android.util.Log
+import cn.i2edu.speech_plugin.audioUtils.audiotransfer.DecodeEngine
+import cn.i2edu.speech_plugin.audioUtils.audiotransfer.DecodeOperateInterface
+import cn.i2edu.speech_plugin.model.chivox.ChivoxResult
+import cn.i2edu.speech_plugin.util.AIEngineHelper
+import com.alibaba.fastjson.JSON
+import com.chivox.AIEngine
+import com.chivox.AIEngine.*
+import java.io.File
+import java.io.FileInputStream
+
+interface ChivoxEvaluatorCallBack {
+    fun onResult(result: ChivoxResult)
+    fun onError(message: String)
+}
+
+class ChivoxAudioEvaluatorUtil private constructor(val context: Context) {
+
+    private var chivoxEvaluatorCallBack: ChivoxEvaluatorCallBack? = null
+    private var engine: Long? = null
+    private val TAG: String = "ChivoxAudioEvaluator"
+
+    companion object {
+        @Volatile
+        private var instance: ChivoxAudioEvaluatorUtil? = null
+
+        fun getInstance(context: Context) = instance
+                ?: synchronized(this) {
+                    instance
+                            ?: ChivoxAudioEvaluatorUtil(context)
+                                    .also {
+                                    }
+                                    .also { instance = it }
+                }
+    }
+
+    fun startEvaluator(en: String, filePath: String, decodedFilePath: String) {
+        val provisionPath = AIEngineHelper.extractResourceOnce(context, "aiengine.provision", false)
+        val logFilePath = AIEngineHelper.getFilesDir(context).path
+        engine = aiengine_new(
+                "{\"appKey\": \"157775873600002d\"," +
+                        " \"secretKey\": \"a6c766845f9a83974aed16f103e60621\"," +
+                        " \"provision\": \"$provisionPath\"," +
+                        " \"prof\":{ \"enable\": 0, \"output\": \"$logFilePath/log.txt\"}," +
+                        "\"cloud\": {\"server\": \"ws://cloud.chivox.com\"}}", context)
+
+        if (engine == null || engine == 0L) {
+            chivoxEvaluatorCallBack?.onError("new aiengine fail")
+            return
+        } else {
+            val param = "{\"coreProvideType\": \"cloud\"," +
+                    " \"app\": {\"userId\": \"i2_school_sign\"}," +
+                    " \"audio\": {\"audioType\": \"wav\", \"channel\": 1, \"sampleBytes\": 2, \"sampleRate\": 16000}," +
+                    " \"request\": {\"coreType\": \"en.sent.score\",\"refText\":\"$en\", \"rank\": 100}}"
+            val tokenId = ByteArray(64)
+            val callback = aiengine_callback { id, type, data, size ->
+                if (type == AIENGINE_MESSAGE_TYPE_JSON) {
+                    try {
+                        val jsonString = String(data, 0, size).trim { it <= ' ' }
+                        Log.d(TAG, jsonString)// 回结果是json格式
+                        val result = JSON.parseObject(jsonString, ChivoxResult::class.java) //返回值
+                        chivoxEvaluatorCallBack?.onResult(result)
+                    } catch (e: java.lang.Exception) {
+                        chivoxEvaluatorCallBack?.onError("parse json object failed ${e.message}")
+                    }
+                }
+                onDestory()
+                0
+            }
+            aiengine_start(engine!!, param, tokenId, callback, context)
+
+            Thread {
+                DecodeEngine.getInstance(context).beginDecodeMusicFile(filePath, decodedFilePath, 0, 16000, 1, 2, object : DecodeOperateInterface {
+                    override fun updateDecodeProgress(decodeProgress: Int) {
+                    }
+
+                    override fun decodeSuccess() {
+                        try {
+                            val file = File(decodedFilePath)
+                            val fileInputStream = FileInputStream(file)
+                            val bytes = ByteArray(1024)
+                            var byteCount = fileInputStream.read(bytes)
+                            while (byteCount > 0) {
+                                aiengine_feed(engine!!, bytes, byteCount)
+                                Thread.sleep(10)
+                                byteCount = fileInputStream.read(bytes)
+                            }
+                            aiengine_stop(engine!!)
+                            file.delete()
+                            fileInputStream.close()
+                        } catch (e: Exception) {
+                            e.printStackTrace()
+                            chivoxEvaluatorCallBack?.onError("aiengine_feed failed ${e.message}")
+                            onDestory()
+                        }
+                    }
+
+                    override fun decodeFail() {
+                        chivoxEvaluatorCallBack?.onError("decode fail")
+                        onDestory()
+                    }
+                })
+            }.start()
+        }
+    }
+
+    fun setEvaluatorCallBack(callback: ChivoxEvaluatorCallBack): ChivoxAudioEvaluatorUtil? {
+        this.chivoxEvaluatorCallBack = callback
+        return instance
+    }
+
+    fun onDestory() {
+        engine?.let {
+            aiengine_delete(engine!!)
+        }
+    }
+}

+ 21 - 19
android/src/main/kotlin/cn/i2edu/speech_plugin/util/AudioEvaluatorUtil.kt → android/src/main/kotlin/cn/i2edu/speech_plugin/util/IFlyAudioEvaluatorUtil.kt

@@ -1,4 +1,4 @@
-package cn.i2edu.dubbing_lib.util
+package cn.i2edu.speech_plugin.util
 
 import android.content.Context
 import android.os.Bundle
@@ -9,34 +9,36 @@ import com.iflytek.cloud.*
 import java.io.File
 import java.io.FileInputStream
 
-interface EvaluatorCallBack {
+interface IFlyEvaluatorCallBack {
     fun onResult(result: EvaluatorResult)
     fun onError(error: SpeechError)
 }
 
-class AudioEvaluatorUtil private constructor(val context: Context){
+class IFlyAudioEvaluatorUtil private constructor(val context: Context) {
 
-    private var evaluatorCallBack: EvaluatorCallBack? = null
+    private var iFlyEvaluatorCallBack: IFlyEvaluatorCallBack? = null
     private var speechEvaluator: SpeechEvaluator? = null
     private val Tag: String = "AudioEvaluatorUtil"
 
     companion object {
-        @Volatile private var instance: AudioEvaluatorUtil? = null
+        @Volatile
+        private var instance: IFlyAudioEvaluatorUtil? = null
+
         fun getInstance(context: Context) = instance
                 ?: synchronized(this) {
-            instance
-                    ?: AudioEvaluatorUtil(context)
-                    .also {
-                        it.speechEvaluator = SpeechEvaluator.createEvaluator(context, null)
-                    }
-                    .also { instance = it }
-        }
+                    instance
+                            ?: IFlyAudioEvaluatorUtil(context)
+                                    .also {
+                                        it.speechEvaluator = SpeechEvaluator.createEvaluator(context, null)
+                                    }
+                                    .also { instance = it }
+                }
     }
 
     fun startEvaluator(en: String, filePath: String, decodedFilePath: String, evaluatorType: Int) {
         speechEvaluator?.setParameter(SpeechConstant.LANGUAGE, "en_us")
         speechEvaluator?.setParameter(SpeechConstant.ISE_CATEGORY, "read_sentence")
-        speechEvaluator?.setParameter(SpeechConstant.AUDIO_SOURCE,"-1")
+        speechEvaluator?.setParameter(SpeechConstant.AUDIO_SOURCE, "-1")
         if (evaluatorType == 1) {
             speechEvaluator?.setParameter("plev", "0")
         }
@@ -44,7 +46,7 @@ class AudioEvaluatorUtil private constructor(val context: Context){
         if (!File(decodedFilePath).exists()) {
             File(decodedFilePath).createNewFile()
         }
-        Thread{
+        Thread {
             DecodeEngine.getInstance(context).beginDecodeMusicFile(filePath, decodedFilePath, 0, 16000, 1, 2, object : DecodeOperateInterface {
                 override fun updateDecodeProgress(decodeProgress: Int) {
                 }
@@ -60,7 +62,7 @@ class AudioEvaluatorUtil private constructor(val context: Context){
 
                             override fun onResult(p0: EvaluatorResult, p1: Boolean) {
                                 Log.d(Tag, p0.toString())
-                                evaluatorCallBack?.onResult(p0)
+                                iFlyEvaluatorCallBack?.onResult(p0)
                                 file.delete()
                                 onDestory()
                             }
@@ -76,7 +78,7 @@ class AudioEvaluatorUtil private constructor(val context: Context){
 
                             override fun onError(p0: SpeechError) {
                                 Log.d(Tag, p0.toString())
-                                evaluatorCallBack?.onError(p0)
+                                iFlyEvaluatorCallBack?.onError(p0)
                                 file.delete()
                                 onDestory()
                             }
@@ -97,14 +99,14 @@ class AudioEvaluatorUtil private constructor(val context: Context){
 
                 override fun decodeFail() {
                     val speechError = SpeechError(1, "评测失败,请稍后再试!")
-                    evaluatorCallBack?.onError(speechError)
+                    iFlyEvaluatorCallBack?.onError(speechError)
                 }
             })
         }.start()
     }
 
-    fun setEvaluatorCallBack(callBack: EvaluatorCallBack): AudioEvaluatorUtil?{
-        this.evaluatorCallBack = callBack
+    fun setEvaluatorCallBack(callBackIFly: IFlyEvaluatorCallBack): IFlyAudioEvaluatorUtil? {
+        this.iFlyEvaluatorCallBack = callBackIFly
         return instance
     }
 

+ 1 - 0
example/.flutter-plugins-dependencies

@@ -0,0 +1 @@
+{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"speech_plugin","dependencies":[]}]}

+ 1 - 0
example/android/gradle.properties

@@ -1,2 +1,3 @@
 org.gradle.jvmargs=-Xmx1536M
 
+android.enableR8=true

+ 73 - 24
example/pubspec.lock

@@ -1,41 +1,69 @@
 # Generated by pub
 # See https://dart.dev/tools/pub/glossary#lockfile
 packages:
+  archive:
+    dependency: transitive
+    description:
+      name: archive
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.0.11"
+  args:
+    dependency: transitive
+    description:
+      name: args
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.5.2"
   async:
     dependency: transitive
     description:
       name: async
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "2.3.0"
+    version: "2.4.0"
   boolean_selector:
     dependency: transitive
     description:
       name: boolean_selector
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.5"
   charcode:
     dependency: transitive
     description:
       name: charcode
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.2"
   collection:
     dependency: transitive
     description:
       name: collection
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.14.11"
+  convert:
+    dependency: transitive
+    description:
+      name: convert
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.1"
+  crypto:
+    dependency: transitive
+    description:
+      name: crypto
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.3"
   cupertino_icons:
     dependency: "direct main"
     description:
       name: cupertino_icons
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "0.1.2"
+    version: "0.1.3"
   flutter:
     dependency: "direct main"
     description: flutter
@@ -46,39 +74,53 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  image:
+    dependency: transitive
+    description:
+      name: image
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.4"
   matcher:
     dependency: transitive
     description:
       name: matcher
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "0.12.5"
+    version: "0.12.6"
   meta:
     dependency: transitive
     description:
       name: meta
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.1.7"
+    version: "1.1.8"
   path:
     dependency: transitive
     description:
       name: path
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.6.4"
   pedantic:
     dependency: transitive
     description:
       name: pedantic
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.8.0+1"
+  petitparser:
+    dependency: transitive
+    description:
+      name: petitparser
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.4.0"
   quiver:
     dependency: transitive
     description:
       name: quiver
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.5"
   sky_engine:
@@ -90,7 +132,7 @@ packages:
     dependency: transitive
     description:
       name: source_span
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.5.5"
   speech_plugin:
@@ -104,50 +146,57 @@ packages:
     dependency: transitive
     description:
       name: stack_trace
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.9.3"
   stream_channel:
     dependency: transitive
     description:
       name: stream_channel
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.0"
   string_scanner:
     dependency: transitive
     description:
       name: string_scanner
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.5"
   term_glyph:
     dependency: transitive
     description:
       name: term_glyph
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.0"
   test_api:
     dependency: transitive
     description:
       name: test_api
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "0.2.5"
+    version: "0.2.11"
   typed_data:
     dependency: transitive
     description:
       name: typed_data
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.6"
   vector_math:
     dependency: transitive
     description:
       name: vector_math
-      url: "https://pub.dartlang.org"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.8"
+  xml:
+    dependency: transitive
+    description:
+      name: xml
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "3.5.0"
 sdks:
-  dart: ">=2.2.2 <3.0.0"
+  dart: ">=2.4.0 <3.0.0"

+ 13 - 2
lib/speech_plugin.dart

@@ -11,6 +11,7 @@ class SpeechPlugin {
   StreamController _onEvaluatorChange;
   Stream<Map> get evaluator => _onEvaluatorChange.stream;
   static int evaluatorType = 0;
+  static SpeechSdk speechSdk = SpeechSdk.IFly;
 
   static SpeechPlugin get instance => _instance;
 
@@ -23,8 +24,12 @@ class SpeechPlugin {
     return _channel.invokeMethod("initSpeechSdk");
   }
 
-  void updateEvaluatorType(int type) {
-    SpeechPlugin.evaluatorType = type; // 1为开启全维度,0为关闭
+  void updateEvaluatorType({int plevType}) {
+    SpeechPlugin.evaluatorType = plevType; // 1为开启全维度,0为关闭
+  }
+
+  void updateEvaluatorSdkType({SpeechSdk sdkType}) {
+    SpeechPlugin.speechSdk = sdkType; // 1为开启chivox,0为讯飞
   }
 
  Future<Map> evaluatorByAudio(String pathEvaluatorDecode, int index, String videoId, String recordPath, String en, {int evaluatorType}) {
@@ -35,6 +40,7 @@ class SpeechPlugin {
       "recordPath": recordPath,
       "en": en,
       "evaluatorType": evaluatorType ?? SpeechPlugin.evaluatorType,
+      "sdkType": speechSdk.index,
     });
  }
 
@@ -46,6 +52,7 @@ class SpeechPlugin {
      "recordPath": recordPath,
      "en": en,
      "evaluatorType": evaluatorType ?? SpeechPlugin.evaluatorType,
+     "sdkType": speechSdk.index,
    });
  }
 
@@ -73,3 +80,7 @@ class SpeechPlugin {
   }
 
 }
+
+enum SpeechSdk {
+  IFly, Chivox,
+}