smart_speech_sdk

智言语音评测Flutter sdk ,支持安卓(Android)、苹果(iOS)和鸿蒙(ohos) 三个平台。

合作联系人:18519378559

合作 邮箱: xuhaitao@samrt-speech.com

官方 网站:https://smart-speech.com/

一、集成步骤

支持平台:

  • Android API Level >=19 (Android 4.4及以上)
  • iOS 12 及以上版本
  • Ohos 12及以上版本,flutterHarmonyOS 化是基于flutter3.7.12版本开发

授权账号:

  • appId
  • appSecret

1. 在项目中引入SDK

  • 方式一,远程依赖集成(推荐) :
# pubspec.yaml 
# 远程依赖集成 
  dependencies:
    smart_speech_sdk: ^1.6.1
  • 方式二,本地集成
# pubspec.yaml 
# 本地集成
  dependencies:
    smart_speech_sdk:
        path: ***           #此处填写Flutter sdk在磁盘上的存放路径

2. 调用步骤/示例代码

graph LR
    A[创建评测类对象] --> B[设置公共参数]
    B --> C[初始化创建引擎create]
    C --> D[设置题型相关参数]
    
    D --> E[调用SDK录音]
    D --> F[传音频文件]
    
    E --> E1[startRecorder]
    E1 --> E2[stop]
    E2 --> E4[onResult获取结果]
    E1 --> E3[cancel]
    E3 --> G[取消评测 无结果]
    
    F --> F1[startFile wavPath]
    F1 --> E3
    F1 --> F4[发送完成后自动结束]
    F4 --> Z[onResult获取结果]
    
    G --> H[destroy]
    E4 --> H
    Z --> H

① 获取录音权限

使用前,flutter 代码需要获取录音权限,否则录音会失败。
如果有统一获取录音权限的方式(安卓、鸿蒙、iOS),请在调用前自行获取获取。或者通过Platform根据平台来分别获取不同系统的录音权限。

② 初始化引擎实例

import 'package:smart_speech_sdk/smart_speech_sdk.dart';  
import 'package:logger/logger.dart';

/// demo中写的一个全局单例,可以根据自己的业务进行处理
class GlobalEngine{
  static SmartSpeechSdk? _speechEvalSdkPlugin;
  static final logger  = Logger();
  static void loggerd(String msg){
    logger.d(TAG,msg);
  }

  static void loggere(String msg){
    logger.e(TAG,msg);
  }

  static void loggeri(String msg){
    logger.i(TAG,msg);
  }

  static void loggerw(String msg){
    logger.w(TAG,msg);
  }

  static void loggerv(String msg){
    logger.v(TAG,msg);
  }
}  

/// 创建引擎实例,并初使化
Future<void> createEngine() async {
    Map cfg = {
      "appId": "xxxxx-xxxx-xxxx-xxxx-xxxxxxxxx",  // appId 
      "appSecret": "xxxxxxxxxxxx",                //app Secret 
      "sampleRate":"16000",  //固定值
      //
      //  None,  不支持
      //  OralOnLine, 配置在线
      //
      "en": InitSettingOnline.OralOnLine.name,   //英文语种初使化配置,如果没有可以设置为None
      "zh": InitSettingOnline.OralOnLine.name,   //中文语种初使化配置,如果没有可以设置为None
      "ja": InitSettingOnline.OralOnLine.name,   //日文语种初使化配置,如果没有可以设置为None
      "userId":"111",  //用户的userId, 建议设置, 以方便区分不同的用户
      "serverUrl":"",  //服务器域名,通常不用设置
      "i18n":I18n.ZH.name,  // 国际化提示,默认提示中文
    };

    GlobalEngine._speechEvalSdkPlugin = SmartSpeechSdk();
    GlobalEngine._speechEvalSdkPlugin?.create(jsonEncode(cfg),RecordScoreCallBack(engineInitState: (String code,String msg){
      if(code == "00000"){
        GlobalEngine.loggere("engineInitState=========成功:::code:$code===msg:$msg");
        //创建引擎成功
      } else {
        GlobalEngine.loggere("engineInitState======失败===:::code:$code===msg:$msg");
        // 创建引擎失败
      }
    },onWsStart: (String taskId){
      //在线评测ws建立成功时回调
      GlobalEngine?.loggeri("onWsStart=====taskId:$taskId");
    },onStartRecording: (){
      //录音开始回调,不会等ws连接成功
      GlobalEngine?.loggeri("onStartRecording=================");
    },onStopSending: (){
      //录音停止时回调
      GlobalEngine?.loggeri("onStopSending=====");
    },onGetVolume: (double volume){
      //实时返回音量信息回调
      GlobalEngine?.loggeri("onGetVolume=====volume:$volume");
    },onSaveAudioFile: (String localPath){
      // 设置 setSaveAudio(true) 时,此方法会回调
      GlobalEngine?.loggeri("onSaveAudioFile=====localPath:$localPath");
    },onWarning: (String taskId,String code,String msg){
      //提醒、警告信息,根据自己业务端需求进行处理
      GlobalEngine?.loggerw("onWarning=====taskId:$taskId==code:$code==msg:$msg");
    },onError: (String taskId,String code,String msg){
      ////评测错误回调
      GlobalEngine?.loggere("onError=====taskId:$taskId==code:$code==msg:$msg");
    },onRealtimeResult: (String result){
      //realtime设置为true时,中/英文 sentence、chapter、freedom 题型,以及中文 poem、recite 题型 ,
      //会实时返回评测数据
      log("onRealtimeResult=====result:$result");
    },onResult: (String result,bool online){
      //评测返回结果回调,online=true为在线返回的结果
      log("onResult===result:$result");
    }));
}

初始化状态回调方法 engineInitState 对应状态码:

在线
"00000","success"
"13010", "缺少网络权限"
"13011", "无网络连接"
"10002", "参数错误"
"11011", "signature错误"
"11012", "appid不可用"
"11013", "appid不存在"
"11014", "生成token错误"
"11015", "非法的当前时间"

③ 设置参数

评测参数
  • 使用 eval.setKEY(VALUE); 的方法设置评测参数
  • 必填参数如缺失或赋值超出范围,将在 onError 中返回错误码、错误信息
  • 选填参数如缺失,将取默认值;如赋值超出范围,将取默认值,并在 onWarning 中返回错误码、错误信息
示例代码
void engineStart(){
    if(speechText.isNotEmpty){
      /// 公共 必传参数
      GlobalEngine._speechEvalSdkPlugin?.setLangType(LangType.enUS); // 设置评测的语种 zhHans,enUS,jaJP
      GlobalEngine._speechEvalSdkPlugin?.setSampleRate(16000);        //采样率,只能是16000
      
      ///公共非必传参数,可不设置(有默认值),或者根据需求进行设置
      GlobalEngine._speechEvalSdkPlugin?.setAudioUrl(false);  //是否返回音频文件,默认为false,即服务端不保存音频
      GlobalEngine._speechEvalSdkPlugin?.setSaveAudio(true);   //设置保存音频(本地磁盘)
      GlobalEngine._speechEvalSdkPlugin?.setLooseness(4);  //宽松度,取值范围[0,9],默认为4
      GlobalEngine._speechEvalSdkPlugin?.setRatio(1.0);    //拉伸系数,取值范围[0.8,1.5],默认为1
      GlobalEngine._speechEvalSdkPlugin?.setScale(100);    //分制,取值范围[1,100],默认为100
      GlobalEngine._speechEvalSdkPlugin?.setConnectTimeout(50);   //连接超时时间,单位秒,默认为15秒
      GlobalEngine._speechEvalSdkPlugin?.setResponseTimeout(50);  //评测超时时间,单位秒,默认为15秒
      GlobalEngine._speechEvalSdkPlugin?.setRealtime(false);      //是否实时返回结果,默认为false
      GlobalEngine._speechEvalSdkPlugin?.setMaxPrefixSilenceMs(0); //最大前置静音时长,单位毫秒,默认0,不开启
      GlobalEngine._speechEvalSdkPlugin?.setMaxSuffixSilenceMs(0); //最大后置静音时长,单位毫秒,默认0,不开启
      GlobalEngine._speechEvalSdkPlugin?.setUserId("");     //用户id,默认为空,建议设置, 以方便区分不同的用户
      GlobalEngine._speechEvalSdkPlugin?.setMini(0); //是否返回全量数据,默认0,0返回全量数据,1为返回精简数据
      GlobalEngine._speechEvalSdkPlugin?.setPrecision("0.1"); //评测精度,取值范围{"0","0.1","0.25","0.5","1"}
      GlobalEngine._speechEvalSdkPlugin?.setPauseInterval(400);  //停顿间隔时间设置,单位毫秒,默认为400
      GlobalEngine._speechEvalSdkPlugin?.setClientData(""); //客户端自定义参数,默认"", 建议不要传过大的数据
     
      /// 设置题型参数,各个题型的参数设置方式请参考对应题型的接口参数文档
      Map paramsJson ={
        "mode": "word",
        "refText":"good morning."
      };
      GlobalEngine.loggerd("paramsjson:$paramsJson");
      GlobalEngine._speechEvalSdkPlugin?.setParamsJson(jsonEncode(paramsJson));
      GlobalEngine._speechEvalSdkPlugin?.startRecorder();
  }

④ 开始/停止/取消评测

  • 录音方式
GlobalEngine._speechEvalSdkPlugin?.startRecorder();
//此时开始使用麦克风接收音频
// ...
//接收完成后手动停止录音完成评测,或者开启后置静音检测,检测到后置静音会自动停止,如果没有调用,超过最大录音时长时会自动停止。
GlobalEngine._speechEvalSdkPlugin?.stop(); 

//如果不想要此次评测的结果,可以调用cancel方法
GlobalEngine._speechEvalSdkPlugin?.cancel(); 
  • 发送本地录音文件方式
//开始
 String wavePath = "/sdcard/xxx/abc.wav"
 GlobalEngine._speechEvalSdkPlugin?.startFile(wavePath);
//传入wav或pcm格式音频数据,要求为16000Hz、16Bit、单声道音频文件
 GlobalEngine._speechEvalSdkPlugin?.setFileFormat("wav");
//自动读取音频文件并发送,完成后自动停止评测,不需要调整stop();

//如果不想要此次评测的结果,可以调用cancel方法取消,即不返回当前评测结果
 GlobalEngine._speechEvalSdkPlugin?.cancel(); 

处理评测结果

参考 ② , 在 listener 中处理评测结果

释放评测资源

//一般可以放在Activity.onDestroy中调用
GlobalEngine._speechEvalSdkPlugin?.destroy();

3. 混淆机制

//安卓项目中,如果要做混淆,排除SDK做混淆即可
-keep class com.zhiyan.**{*;}

二、常见问题

1.Flutter SDK 支持的最小系统版本号是多少?

答:Android版本SDK目前支持4.4及以上版本(即19),iOS 支持 12及以上的版本,鸿蒙sdk支持12(5.0.0)及以上的版本

2.libc++_shared.so 冲突解决办法

在工程的build文件中加入如下代码

 android {
     packagingOptions {
          pickFirst 'lib/*/libc++_shared.so'
     }
 }

3.如果使用代码混淆功能时,不能正常评测

在proguard-rules.pro文件中检查是否添加了如下规则:

-keep class com.zhiyan.**{*;}

6.语音评测支持哪些应用平台?

目前语音评测支持:Android、iOS、HarmonyOS、Web、小程序、Linux、Windows、词典笔、RTOS等应用平台

7.语音评测支持的音频格式有哪些?

音频格式介绍,请查看 支持音频格式 文档

8.错误码及相关的解决方案

错误码介绍,请查看 状态码 文档

9.语音评测支持的题型、文本、音频时长限制、结果及各字段的含义

语音引擎支持题型、文本、音频时长限制,请查看 题型文档 和 参数介绍 文档

10.为什么返回结果中没有音频 audioUrl地址,或者audioUrl地址为空?

需要在评测前传递audioUrl=true ,结果中才会返回audioUrl音频地址

11.评测音频地址存储多久?

默认保留20天,如果需要更长时间存储,建议下载至自己的服务器,保存音频 需要给评分引擎设置audioUrl=true。

12.如果某次打分有问题,需要智言协助分析,需要提供哪些信息?

提供智言返回的 audioUrl 即可,或者提供taskId

13.使用麦克风录制的音频,没有朗读任何内容,此时评测为何会有分数?

录音音频的环境中有噪声,噪声中的部分声音与评测文本中的发音相同,所以出现有分数的情况,但分数是偏低的。

14.评测参数中,设置userId的作用是什么?是否需要设置?

userId用来标识不同的用户,建议使用评测的时候,将产品中的userId设置成和产品登录账号相关联的内容。若有用户反馈评分方面的问题,智言可以快速定位到评分数据进行分析。

15.单词为什么没有流利度得分?

流利度这项维度只有句子、段落等 这种长文本的题型才有,单词发音只要朗读清晰、饱满、正确,即可正常打分。

16.语音评测Licence 是如何计数的?

请求token或者获取激活码后,即用掉一个Licence,同一个设备多次向服务器发起注册请求,只计一次。

17.语音评测并发是如何计算的?

并发就是某一时刻同时在进行录音评测的请求个数。

18.语音评测调用次数是否包含失败的情况?

语音评测调用次数,只包含服务端成功响应的次数,失败的次数不会计算在内。

19.句子评测和段落评测中,为何只读了部分内容,但流利度很很高?

流利度是对已朗读的部分进行评价,这种未朗读完的情况,完整度的得分是偏低的。所以总分在这种情况下不会出现高分。