Android 科大讯飞语音识别(详细步骤+源码)

简介: Android 科大讯飞语音识别(详细步骤+源码)

前言


语音识别在现在的APP中是常见的,但是通常小的项目中我们不会去费心思自己去做这一块的业务功能开发,常规的是接入第三方的SDK快速实现功能,比如百度、讯飞之类的,百度语音识别之前我已经写过了,本着雨露均沾的原则的,写这篇讯飞的SDK对接步骤,开始吧~


效果图

20200623171422756.png


点击


20200623171445894.png


识别中,识别到之后


20200623171514418.png


源码在文章最后面


正文


首先呢,你先去注册讯飞的开发者账号,点击讯飞开放平台前往注册,


20200622135538860.png


注册好之后你可以选择实名认证或者不认证都可以,然后登录进入控制台或者我的应用


20200622135733943.png


创建一个应用


20200622135929781.png


填写信息然后提交


20200622140322535.png


点击这个应用名称查看详细信息


20200622140528830.png


右边的是对接过程需要用到的值,APPID用SDK中,APIKey或APISecret适用于WebAPI调用方式,这两者有什么区别呢?SDK的话很好理解,复制一些文件到你的项目中,然后添加依赖,你就可以使用讯飞的SDK中的语音识别功能了,而WebAPI调用顾名思义是通过网络接口携带参数的方式去实现的,需要自己去做网络请求,本文中目前使用SDK的方式进行对接实现功能,如果有想看WebAPI调用方式的请在评论区留言,


OK,现在滑动到下面下载Android SDK


20200622141057889.png


下载到本地,然后解压,打开文件目录如下:


20200622141737218.png


然后就是创建你自己项目了,打开AS


20200622141850450.png


① 配置资源文件


我取名是XFASRDemo,然后Finish,等待项目创建好之后,复制文件中libs里面的三个文件,到项目的libs中


20200622142120160.png

20200622142431497.png


右键Mac.jar添加Add As Library


20200622142521127.png

20200622142558717.png


点击OK添加,添加好之后你的jar包就会有一个三角箭头,可以展开,这个时候你就可以使用里面的方法了。


20200622142645951.png


然后复制资源文件到main下面


20200622143939337.png

② 配置项目


创建SpeechApplication.java


20200622182305580.png


package com.llw.xfasrdemo;
import android.app.Application;
import com.iflytek.cloud.SpeechUtility;
public class SpeechApplication extends Application {
    @Override
    public void onCreate() {
        //   5ef048e1  为在开放平台注册的APPID  注意没有空格,直接替换即可
        SpeechUtility.createUtility(SpeechApplication.this, "appid=5ef048e1");
        super.onCreate();
    }
}


打开AndroidManifest.xml,增加权限配置


  <!--连接网络权限,用于执行云端语音能力 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <!--读取网络信息状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!--获取当前wifi状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>


配置SpeechApplication和注册权限


20200622182538460.png


然后修改app模块下面的build.gradle


20200623164220530.png

  sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
implementation files('libs/Msc.jar')


改完记得Sync一下


然后修改布局activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="识别到的内容"
        android:textColor="#000" />
    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="开始识别" />
</LinearLayout>


接下来就是MainActivity了


③ 编码


一、声明变量和初始化

  private static final String TAG = "MainActivity";
    private SpeechRecognizer mIat;// 语音听写对象
    private RecognizerDialog mIatDialog;// 语音听写UI
    // 用HashMap存储听写结果
    private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();
    private SharedPreferences mSharedPreferences;//缓存
    private String mEngineType = SpeechConstant.TYPE_CLOUD;// 引擎类型
    private String language = "zh_cn";//识别语言
    private TextView tvResult;//识别结果
    private Button btnStart;//开始识别
    private String resultType = "json";//结果内容数据格式


同时你要实现这个点击事件的监听


20200623164902429.png


实现一个方法

  @Override
    public void onClick(View v) {
    //写入点击之后处理逻辑
  }
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvResult = findViewById(R.id.tv_result);
        btnStart = findViewById(R.id.btn_start);
        btnStart.setOnClickListener(this);//实现点击监听
    }


二、动态权限请求


  /**
     * android 6.0 以上需要动态申请权限
     */
    private void initPermission() {
        String permissions[] = {Manifest.permission.RECORD_AUDIO,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.INTERNET,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        ArrayList<String> toApplyList = new ArrayList<String>();
        for (String perm : permissions) {
            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
                toApplyList.add(perm);
            }
        }
        String tmpList[] = new String[toApplyList.size()];
        if (!toApplyList.isEmpty()) {
            ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
        }
    }
    /**
     * 权限申请回调,可以作进一步处理
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        // 此处为android 6.0以上动态授权的回调,用户自行实现。
    }


在onCreate方法中调用


initPermission();//权限请求


三、语音监听


  /**
     * 初始化监听器。
     */
    private InitListener mInitListener = new InitListener() {
        @Override
        public void onInit(int code) {
            Log.d(TAG, "SpeechRecognizer init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showMsg("初始化失败,错误码:" + code + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案");
            }
        }
    };
    /**
     * 听写UI监听器
     */
    private RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() {
        public void onResult(RecognizerResult results, boolean isLast) {
            printResult(results);//结果数据解析
        }
        /**
         * 识别回调错误.
         */
        public void onError(SpeechError error) {
            showMsg(error.getPlainDescription(true));
        }
    };
  /**
     * 提示消息
     * @param msg
     */
    private void showMsg(String msg) {
        Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
    }


里面用到一个方法,用于解析监听到的结果


四、数据解析


  /**
     * 数据解析
     *
     * @param results
     */
    private void printResult(RecognizerResult results) {
        String text = JsonParser.parseIatResult(results.getResultString());
        String sn = null;
        // 读取json结果中的sn字段
        try {
            JSONObject resultJson = new JSONObject(results.getResultString());
            sn = resultJson.optString("sn");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        mIatResults.put(sn, text);
        StringBuffer resultBuffer = new StringBuffer();
        for (String key : mIatResults.keySet()) {
            resultBuffer.append(mIatResults.get(key));
        }
        tvResult.setText(resultBuffer.toString());//听写结果显示
    }


里面用到一个JsonParser的工具类,需要手动去创建一个


202006231657433.png


代码如下:


package com.llw.xfasrdemo;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;
/**
 * Json结果解析类
 */
public class JsonParser {
  public static String parseIatResult(String json) {
    StringBuffer ret = new StringBuffer();
    try {
      JSONTokener tokener = new JSONTokener(json);
      JSONObject joResult = new JSONObject(tokener);
      JSONArray words = joResult.getJSONArray("ws");
      for (int i = 0; i < words.length(); i++) {
        // 转写结果词,默认使用第一个结果
        JSONArray items = words.getJSONObject(i).getJSONArray("cw");
        JSONObject obj = items.getJSONObject(0);
        ret.append(obj.getString("w"));
//        如果需要多候选结果,解析数组其他字段
//        for(int j = 0; j < items.length(); j++)
//        {
//          JSONObject obj = items.getJSONObject(j);
//          ret.append(obj.getString("w"));
//        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    } 
    return ret.toString();
  }
  public static String parseGrammarResult(String json) {
    StringBuffer ret = new StringBuffer();
    try {
      JSONTokener tokener = new JSONTokener(json);
      JSONObject joResult = new JSONObject(tokener);
      JSONArray words = joResult.getJSONArray("ws");
      for (int i = 0; i < words.length(); i++) {
        JSONArray items = words.getJSONObject(i).getJSONArray("cw");
        for(int j = 0; j < items.length(); j++)
        {
          JSONObject obj = items.getJSONObject(j);
          if(obj.getString("w").contains("nomatch"))
          {
            ret.append("没有匹配结果.");
            return ret.toString();
          }
          ret.append("【结果】" + obj.getString("w"));
          ret.append("【置信度】" + obj.getInt("sc"));
          ret.append("\n");
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
      ret.append("没有匹配结果.");
    } 
    return ret.toString();
  }
  public static String parseLocalGrammarResult(String json) {
    StringBuffer ret = new StringBuffer();
    try {
      JSONTokener tokener = new JSONTokener(json);
      JSONObject joResult = new JSONObject(tokener);
      JSONArray words = joResult.getJSONArray("ws");
      for (int i = 0; i < words.length(); i++) {
        JSONArray items = words.getJSONObject(i).getJSONArray("cw");
        for(int j = 0; j < items.length(); j++)
        {
          JSONObject obj = items.getJSONObject(j);
          if(obj.getString("w").contains("nomatch"))
          {
            ret.append("没有匹配结果.");
            return ret.toString();
          }
          ret.append("【结果】" + obj.getString("w"));
          ret.append("\n");
        }
      }
      ret.append("【置信度】" + joResult.optInt("sc"));
    } catch (Exception e) {
      e.printStackTrace();
      ret.append("没有匹配结果.");
    } 
    return ret.toString();
  }
  public static String parseTransResult(String json,String key) {
    StringBuffer ret = new StringBuffer();
    try {
      JSONTokener tokener = new JSONTokener(json);
      JSONObject joResult = new JSONObject(tokener);
      String errorCode = joResult.optString("ret");
      if(!errorCode.equals("0")) {
        return joResult.optString("errmsg");
      }
      JSONObject transResult = joResult.optJSONObject("trans_result");
      ret.append(transResult.optString(key));
      /*JSONArray words = joResult.getJSONArray("results");
      for (int i = 0; i < words.length(); i++) {
        JSONObject obj = words.getJSONObject(i);
        ret.append(obj.getString(key));
      }*/
    } catch (Exception e) {
      e.printStackTrace();
    }
    return ret.toString();
  }
}


五、参数配置


  /**
     * 参数设置
     *
     * @return
     */
    public void setParam() {
        // 清空参数
        mIat.setParameter(SpeechConstant.PARAMS, null);
        // 设置听写引擎
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
        // 设置返回结果格式
        mIat.setParameter(SpeechConstant.RESULT_TYPE, resultType);
        if (language.equals("zh_cn")) {
            String lag = mSharedPreferences.getString("iat_language_preference",
                    "mandarin");
            Log.e(TAG, "language:" + language);// 设置语言
            mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
            // 设置语言区域
            mIat.setParameter(SpeechConstant.ACCENT, lag);
        } else {
            mIat.setParameter(SpeechConstant.LANGUAGE, language);
        }
        Log.e(TAG, "last language:" + mIat.getParameter(SpeechConstant.LANGUAGE));
        //此处用于设置dialog中不显示错误码信息
        //mIat.setParameter("view_tips_plain","false");
        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        mIat.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("iat_vadbos_preference", "4000"));
        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        mIat.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("iat_vadeos_preference", "1000"));
        // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
        mIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("iat_punc_preference", "1"));
        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/iat.wav");
    }


六、代码组装


在onCreate中增加如下代码,进行初始化


    // 使用SpeechRecognizer对象,可根据回调消息自定义界面;
        mIat = SpeechRecognizer.createRecognizer(MainActivity.this, mInitListener);
        // 使用UI听写功能,请根据sdk文件目录下的notice.txt,放置布局文件和图片资源
        mIatDialog = new RecognizerDialog(MainActivity.this, mInitListener);
        mSharedPreferences = getSharedPreferences("ASR",
                Activity.MODE_PRIVATE);


然后再onClick中

  @Override
    public void onClick(View v) {
        if( null == mIat ){
            // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
            showMsg( "创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化" );
            return;
        }
        mIatResults.clear();//清除数据
        setParam(); // 设置参数
        mIatDialog.setListener(mRecognizerDialogListener);//设置监听
        mIatDialog.show();// 显示对话框
    }


最后在onDestory中

  @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != mIat) {
            // 退出时释放连接
            mIat.cancel();
            mIat.destroy();
        }
    }


这样逻辑就很清楚了


七、运行效果图


20200623171422756.png


点击


20200623171445894.png


识别中,识别到之后


20200623171514418.png


APK下载二维码


20210319161035957.png


④ 梳理


从平台创建应用之后的到一个appid,然后新建一个项目,先配置AndroidManifest.xml,配置了静态权限和Application,在Application中对讯飞语音插件进行初始化,这样就可以影响到全局,而不是在某一个Activity的中onCreate进行初始化,当然你也可以这样做。然后初始化控件和讯飞的一些工具类,并实现点击监听,之后进行动态权限的获取,成功之后,进行语音监听(包括UI对话框)的接口实现,在返回值中做数据解析,得到需要的数据显示在页面的TextView上,然后再配置一些需要在调用语音识别之前需要的参数,比如语言类型和结果类型之类的,之后就是点击出现语音识别的UI对话框,当你说完之后会消失,并识别到你说的内容解析显示在页面上。至此,语音识别完成。


尾声


这里无非就是一些感想而已,其实实现功能的方法有很多,文章中只是其中之一而已,欢迎留言讨论,我是初学者-Study,山高水长,后会有期~

相关实践学习
一键创建和部署高分电影推荐语音技能
本场景使用天猫精灵技能应用平台提供的技能模板,在2-5分钟内,创建一个好玩的高分电影推荐技能,使用模板后无须代码开发,系统自动配置意图、实体等,新手0基础也可体验创建技能的乐趣。
达摩院智能语音交互 - 声纹识别技术
声纹识别是基于每个发音人的发音器官构造不同,识别当前发音人的身份。按照任务具体分为两种: 声纹辨认:从说话人集合中判别出测试语音所属的说话人,为多选一的问题 声纹确认:判断测试语音是否由目标说话人所说,是二选一的问题(是或者不是) 按照应用具体分为两种: 文本相关:要求使用者重复指定的话语,通常包含与训练信息相同的文本(精度较高,适合当前应用模式) 文本无关:对使用者发音内容和语言没有要求,受信道环境影响比较大,精度不高 本课程主要介绍声纹识别的原型技术、系统架构及应用案例等。 讲师介绍: 郑斯奇,达摩院算法专家,毕业于美国哈佛大学,研究方向包括声纹识别、性别、年龄、语种识别等。致力于推动端侧声纹与个性化技术的研究和大规模应用。
相关文章
|
3天前
|
开发工具 Android开发 git
Windows下载android2.2完整源码(转)
Windows下载android2.2完整源码(转)
21 3
|
3天前
|
Java 开发工具 Android开发
如何在Eclipse中查看Android源码或者第三方组件包源码(转)
如何在Eclipse中查看Android源码或者第三方组件包源码(转)
15 4
|
3天前
|
Java Android开发
Android12 双击power键启动相机源码解析
Android12 双击power键启动相机源码解析
29 0
|
1天前
|
JSON Java API
Android 深入Http(5)从Retrofit源码来看Http,最新Android开发面试解答
Android 深入Http(5)从Retrofit源码来看Http,最新Android开发面试解答
|
1天前
|
API Android开发 C++
【字节跳动大牛系列教学】Android源码剖析之Framwork层消息传递
【字节跳动大牛系列教学】Android源码剖析之Framwork层消息传递
|
3天前
|
Android开发
在android源码中编译ADW_Launcher
在android源码中编译ADW_Launcher
12 2
|
3天前
|
Android开发
Android 高通平台集成无源码apk示例
Android 高通平台集成无源码apk示例
17 0
|
1天前
|
前端开发 Android开发
Android架构组件JetPack之DataBinding玩转MVVM开发实战(四)
Android架构组件JetPack之DataBinding玩转MVVM开发实战(四)
Android架构组件JetPack之DataBinding玩转MVVM开发实战(四)
|
1天前
|
安全 Linux Android开发
Android最强保活黑科技的最强技术实现,2024年最新阿里资深Android开发带你搞懂Framework
Android最强保活黑科技的最强技术实现,2024年最新阿里资深Android开发带你搞懂Framework
Android最强保活黑科技的最强技术实现,2024年最新阿里资深Android开发带你搞懂Framework
|
1天前
|
算法 前端开发 Android开发
Android文字基线Baseline算法的使用讲解,Android开发面试题
Android文字基线Baseline算法的使用讲解,Android开发面试题
Android文字基线Baseline算法的使用讲解,Android开发面试题