准备工具:
1.语音识别的基本知识
2.讯飞的官网注册信息并创建一个应用用于使用SDK
正文
在AS新建一个工程,名字随便。然后新建一个module,起一个名字
File-new-new Module(下图)
然后在java文件夹下新建两个文件夹便于管理语音识别跟语音唤醒,再新建一个MainActivity.class(下图)
把Unity的class接入,路径在安装Unity客户端的路径下
Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes
将下载的讯飞SDK文件夹libs下的所有文件也复制到AS工程的libs下,与Unity的classes.jar一样,与下图一样即可
右键libs文件夹下两个.jar文件,Add As Libray…
也可以右键iflytevoice,Open Module Settings
将.jar文件手动添加,添加完了记得点apply应用一下
在main文件夹下新建一个Jnilibs文件夹,然后将libmsc.so添加进去。libmsc.so在讯飞SDK文件夹里的libs\armeabi-v7a下,最好连armeabi-v7a文件夹一起复制进去。效果如下
将app下的AndroidManifest的以下蓝色内容复制到我们module的AndroidManifest中,如下
改成这样就行了,这里注意一下,将这个的name改成自己之前建立的类名,我的是asr.asrPort
然后将以下代码添加到AndroidManifest中,这些代码是获取相应的权限,比如存取、录音机等权限
AndroidManifest算是配置完了
到了这一步,讯飞的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导入用即可。
上面在AS写的工程给Unity使用,选中module,右键选择"Make Module ‘iflytekvoice’"等待片刻
将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的Assets内新建Plugins/Android文件夹,将aar于AndroidManifest文件放入进去即可。如下所示
示例如下。一个Text组件用于显示语音识别出来的内容,一个Button组件用于点击就开始进行语音识别
下面的Try是为了确认aar包在Unity中正常连通了,之前在AS里写了方法就是为了留给点击这个按钮后调用的
代码如下,只有简单两个点击事件,和调用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;
}
///
/// 点击Try按钮
///
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那边改
其中下图是固定写法,大家Unity这部分自己随意发挥,只要方法名与在AndroidStudio中写的一样即可,要不然调不到
①Unity内调用Android Studio内的方法
这里一定要与aar外的AndroidManifest中的pakeage名字一样才能调用aar包中写的方法
总结:到此为止,一个简单的科大讯飞语音识别的SDK就接入Unity可以使用了,至于怎么使用就随意发挥了。
文章不算短,但是真正的核心部分不多。看起来挺复杂,其实仔细一想也没干什么事。从Android方面不是很懂,连Android
Studio都不是很熟练,自己在网上摸索了一周左右,才学会打包aar,安卓与Unity简单交互,接入科大讯飞SDK等。
此文章主要写了在Unity接科大讯飞语音识别SDK的全过程,核心不多,但是每个细节都不能出错,因为我就是从bug堆里摸索出来的,毕竟安卓这方面实在差的太远了。
本来是想一篇文章写语音识别+语音唤醒的,一看太多了,就只写了语音识别,下篇文章写语音唤醒(只写语音唤醒的接口)。如果哪个地方没搞出来,或者我这哪里写的不太对都可以评论区告诉我哦,看到有时间就会回复的。
可下载整套资源文件讯飞语音识别+唤醒DemoS.zip