Unity-接入“科大讯飞”语音识别SDK(完整)

Unity接入讯飞的SDK使用

准备工具:
1.语音识别的基本知识
2.讯飞的官网注册信息并创建一个应用用于使用SDK

正文

一.Android Studio操作

1.工程开始

在AS新建一个工程,名字随便。然后新建一个module,起一个名字
File-new-new Module(下图)
在这里插入图片描述

2.建立相应文件夹

然后在java文件夹下新建两个文件夹便于管理语音识别跟语音唤醒,再新建一个MainActivity.class(下图)
在这里插入图片描述

3.接入Unity的ckasses.jar包

把Unity的class接入,路径在安装Unity客户端的路径下
Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes
在这里插入图片描述

4.接入讯飞语音的classess.jar包

将下载的讯飞SDK文件夹libs下的所有文件也复制到AS工程的libs下,与Unity的classes.jar一样,与下图一样即可
在这里插入图片描述

5.关联两个classes.jar包

右键libs文件夹下两个.jar文件,Add As Libray…
在这里插入图片描述
也可以右键iflytevoice,Open Module Settings
将.jar文件手动添加,添加完了记得点apply应用一下
在这里插入图片描述
在这里插入图片描述

6.添加libmsc.so

在main文件夹下新建一个Jnilibs文件夹,然后将libmsc.so添加进去。libmsc.so在讯飞SDK文件夹里的libs\armeabi-v7a下,最好连armeabi-v7a文件夹一起复制进去。效果如下
在这里插入图片描述

7.修改AndroidManifest文件

将app下的AndroidManifest的以下蓝色内容复制到我们module的AndroidManifest中,如下
在这里插入图片描述
改成这样就行了,这里注意一下,将这个的name改成自己之前建立的类名,我的是asr.asrPort
在这里插入图片描述

然后将以下代码添加到AndroidManifest中,这些代码是获取相应的权限,比如存取、录音机等权限
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" />

AndroidManifest算是配置完了

8.写SDK的接口

到了这一步,讯飞的SDK算是配置差不多了,接下来就是写方法使用了
先写语音识别的代码,在之前建立的asrPort类中写

package com.example.iflytekvoice.asr;

import android.os.Bundle;

import com.example.iflytekvoice.JsonParser;
import com.iflytek.cloud.RecognizerListener;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.SpeechUtility;
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.LinkedHashMap;

public class asrPort extends  UnityPlayerActivity{
    
    

    private SpeechRecognizer mIat;
    private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        //初始化
        SpeechUtility.createUtility(this, SpeechConstant.APPID + "=60307482");
        mIat = SpeechRecognizer.createRecognizer(this, null);
        //设置mIat的参数
        //表示是什么服务
        mIat.setParameter(SpeechConstant.DOMAIN, "iat");
        //设置语言
        mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
        //接受语言的类型
        mIat.setParameter(SpeechConstant.ACCENT, "mandarin");
        //使用什么样引擎
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);

    }
    RecognizerListener mRecognizerLis=new RecognizerListener() {
    
    
        @Override
        public void onVolumeChanged(int i, byte[] bytes) {
    
    

        }

        @Override
        public void onBeginOfSpeech() {
    
    

        }

        @Override
        public void onEndOfSpeech() {
    
    

        }

        @Override
        public void onResult(RecognizerResult recognizerResult, boolean b) {
    
    
            printResult(recognizerResult);
        }

        @Override
        public void onError(SpeechError speechError) {
    
    

        }

        @Override
        public void onEvent(int i, int i1, int i2, Bundle bundle) {
    
    

        }
    };
    //解析Json的方法
    //方法来自speechDemo->java->voicedemo->IatDemo中的printResult方法
    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));
        }
        //把消息发送给Unity场景中iFlytekASRController物体上的OnResult方法
        UnityPlayer.UnitySendMessage("iFlytekASRController", "OnResult", resultBuffer.toString());
    }

    public void beginListen(){
    
    
        //开始识别
        mIat.startListening(mRecognizerLis);
    }

    public void connected(){
    
    
        UnityPlayer.UnitySendMessage("iFlytekASRController","tryConnected","连通成功了");

    }
    public int beginTest(int a, int b){
    
    
        //交互测试
        return a+b;
    }


}


其中还加了一个解析Json的类,直接新建在java文件夹下就行。JsonParser如下

package com.example.iflytekvoice;

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();
	}
}

在asrPort后面加入的几个方法是用于后边与unity交互使用的,暂时写上即可,后边会用到。
到此为止,在asrPort中写完了语音识别的方法类,打包aar包给Unity导入用即可。

9.打包aar

上面在AS写的工程给Unity使用,选中module,右键选择"Make Module ‘iflytekvoice’"等待片刻

在这里插入图片描述

将aar包与AndroidManifest复制到自己一个文件夹中(如下)
在这里插入图片描述

10.修改aar与AndroidManifest

1.aar包中的文件如下,进入libs文件夹,将里面的classes.jar删掉,换成根目录下的这个classes.jar
将下图中的"假"删掉,"真"的剪切进去,只能留"真"的那一个,两个Unity打包时会报错
在这里插入图片描述在这里插入图片描述
然后修改aar包中的AndroidManifest,将 “android:label="这行删掉,这里是设置打包出来的apk名字,这里不删会与aar包外的那个AndroidManifest冲突(跟着做就对了,我都是摸爬滚打一堆bug改过来的…)
在这里插入图片描述
修改aar包外的AndroidManifest,只需要改package即可,这里改成与包内的不一样即可,但是在Unity playerSetting中的PackageName一定要与这里设置的一样,要不然unity掉不到AS中写的方法。如下图
在这里插入图片描述
在这里插入图片描述
到这里,在AS的操作就结束了,aar包也打出来了,在Unity中用即可,下面写在Unity中的操作步骤


二.Unity端的操作

1.导入aar包

在Unity的Assets内新建Plugins/Android文件夹,将aar于AndroidManifest文件放入进去即可。如下所示
在这里插入图片描述

2.搭建一个简易UI

示例如下。一个Text组件用于显示语音识别出来的内容,一个Button组件用于点击就开始进行语音识别
下面的Try是为了确认aar包在Unity中正常连通了,之前在AS里写了方法就是为了留给点击这个按钮后调用的
在这里插入图片描述

3.挂在iFlytekASRController物体上的脚本

代码如下,只有简单两个点击事件,和调用aar包内的方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SpeechManager : MonoBehaviour
{
    
    
    public Text ASRmsg;
    public Text tryTex;
    public Text aad;

    private Button TryBtn;
    private Button ASR_Btn;


    private AndroidJavaObject jo;

    
    private void Awake()
    {
    
    
        ASR_Btn = GameObject.Find("Speech/ASR_Btn").GetComponent<Button>();
        TryBtn = GameObject.Find("Test/TryBtn").GetComponent<Button>();

        AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
    }
    void Start()
    {
    
    
        ASR_Btn.onClick.AddListener(()=> {
    
    

            jo.Call("beginListen");

        });
        TryBtn.onClick.AddListener(tryConnect);
    }

    public void OnResult(string msg)
    {
    
    
        ASRmsg.text = msg;

    }

    /// <summary>
    /// 点击Try按钮
    /// </summary>
    public void tryConnect()
    {
    
    
        int aaa;
        aaa=jo.Call<int>("beginTest",2,3);
        aad.text = aaa.ToString();

        jo.Call("connected");
    }

    public void tryConnected(string tryMsg)
    {
    
    
        tryTex.text = tryMsg;

        Color ramColor = ColorRandom();
        tryTex.color = ramColor;
    }

    public Color ColorRandom()
    {
    
    
        float r = Random.Range(0f,1f);
        float g = Random.Range(0f, 1f);
        float b = Random.Range(0f, 1f);

        Color color = new Color(r, g, b);
        return color;
    }

}

要记得将脚本挂到这个物体上,因为也是在AS中写好的,也可以在AS那边改
在这里插入图片描述

4.调用方法

其中下图是固定写法,大家Unity这部分自己随意发挥,只要方法名与在AndroidStudio中写的一样即可,要不然调不到
在这里插入图片描述
①Unity内调用Android Studio内的方法
在这里插入图片描述
在这里插入图片描述


②Android Studio内调用Unity中的方法
在这里插入图片描述
在这里插入图片描述

5.修改Unity中 PlayerSetting

这里一定要与aar外的AndroidManifest中的pakeage名字一样才能调用aar包中写的方法
在这里插入图片描述

6.打包apk到真机测试即可


总结:到此为止,一个简单的科大讯飞语音识别的SDK就接入Unity可以使用了,至于怎么使用就随意发挥了。
文章不算短,但是真正的核心部分不多。看起来挺复杂,其实仔细一想也没干什么事。从Android方面不是很懂,连Android
Studio都不是很熟练,自己在网上摸索了一周左右,才学会打包aar,安卓与Unity简单交互,接入科大讯飞SDK等。
此文章主要写了在Unity接科大讯飞语音识别SDK的全过程,核心不多,但是每个细节都不能出错,因为我就是从bug堆里摸索出来的,毕竟安卓这方面实在差的太远了。

本来是想一篇文章写语音识别+语音唤醒的,一看太多了,就只写了语音识别,下篇文章写语音唤醒(只写语音唤醒的接口)。如果哪个地方没搞出来,或者我这哪里写的不太对都可以评论区告诉我哦,看到有时间就会回复的。

原创不易,全文无水印,若转载标记下原文。感谢!

猜你喜欢

转载自blog.csdn.net/zhangay1998/article/details/114115187