Unity 实战项目 ☀️| Unity接入 百度语音识别 SDK!一篇文章搞定在Unity中实现语音识别!(万字完整教程)

6,588 阅读8分钟

本文已参与掘金创作者训练营第三期,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 呆呆敲代码的小Y 原创🙉
  • 📢未来很长,值得我们全力奔赴更美好的生活✨

📢前言

  • 最近有小伙伴在我之前写过的一篇怎样接入讯飞语音识别的文章下面留言
  • 说这篇文章有许多地方细节不到位,导致自己看不明白
  • 所以我就写了三篇系列文章从一个新手角度写了一篇接入讯飞语音的教程!在本文最后也有链接介绍!

注意:本文教程使用的Unity版本是2018.4.32,其他版本可能有出入,一般问题不大


🍉接入百度语音识别

  • 跟之前文章写过的接入讯飞语音识别一样,都要去官网下载对应的SDK
  • 所以我们去官网下载SDK,大家都是聪明人

百度语音SDK获取网址

  • 怎样获取SDK部分就不做过多介绍了,进入官网按照介绍
  • 注册登录获取语音识别的SDK就好了,然后创建一个应用 在这里插入图片描述 在这里插入图片描述 这样应用就创建成功了,这里要记住APPID,后面会用到! 在这里插入图片描述 找到SDK下载一个语音识别的SDK 在这里插入图片描述

那接下来就是获取到语音识别SDK后的部分了!


🍓Android Studio端操作步骤

1.工程开始

老规矩,新建项目,修改名字和路径,准备开始! 在这里插入图片描述 在这里插入图片描述

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

在这里插入图片描述


2.接入Unity的classes.jar包

  • 把Unity的class接入,路径在安装Unity客户端的路径下
  • Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes
  • 把Unity的jar包复制到AS的libs目录下,如下图: 在这里插入图片描述

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

  • 同样的方法,将百度语音的jar包也放进去
  • ar包位置就在我们在官网下载的SDK解压后的core->libs路径下
  • 直接也复制到AS的libs目录下就行! 在这里插入图片描述 效果如下: 在这里插入图片描述

4.关联两个classes.jar包

  • 选中两个jar包,右键Add As Libray…
  • 等待编译完就好了 在这里插入图片描述
  • 也可以右键iflytevoice,Open Module Settings
  • 将.jar文件手动添加,添加完了记得点apply应用一下 在这里插入图片描述 如果点击后这里显示已经有了这两个jar包,那说明就关联好了 在这里插入图片描述

5.添加libmsc.so

还是在我们下载的SDK目录下找到这个文件夹jniLibs 在这里插入图片描述

  • 然后把这个文件夹直接复制到AS的src->main目录下

如下图所示: 在这里插入图片描述


6.写SDK的接口

好了,到这一步才是写代码的阶段,前边做的几个步骤都是为了为最后的操作搭建一个"台子"

  • 我们接下来新建四个类,分别是CientBaiDuVoiceMainActivity、RecognHandler、RecognListener和GetActivity
  • 怎样新建就不说了,我这里还新建了两个文件夹Recogn和Util为了区分脚本类别,照着我这个做就行

直接看一下最终效果: 在这里插入图片描述 然后直接上各个脚本的代码,直接放进去就行 CientBaiDuVoiceMainActivity:

package com.example.baidu;


import android.content.Context;
import android.os.Bundle;
import android.util.Log;

import com.example.baidu.Recogn.RecognHandler;
import com.example.baidu.Recogn.RecognListener;
import com.unity3d.player.UnityPlayerActivity;

public class CientBaiDuVoiceMainActivity   {

    public static CientBaiDuVoiceMainActivity _instance;

    public static CientBaiDuVoiceMainActivity getInstance() {
        if (_instance == null) {
            _instance = new CientBaiDuVoiceMainActivity();
        }
        return _instance;
    }
    //语音识别
    RecognHandler mRecognHandler;

    //语音识别初始化
    public void InitRecogn(Context context) {
        Log.i("@@@", "安卓端开始初始化语音识别了 ");
        RecognListener listener=new RecognListener();
        mRecognHandler=new RecognHandler(context,listener);
    }
    //开始语音识别
    public void StartRecogn() {
        mRecognHandler.Start();
    }
    //停止语音识别
    public void StopRecogn() {
        mRecognHandler.Stop();
    }
    //释放语音识别实例
    public void ReleaseRecogn() {
        mRecognHandler.Release();
        mRecognHandler=null;
    }


}

RecognHandler:

package com.example.baidu.Recogn;//自己的包名

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import com.baidu.speech.EventListener;
import com.baidu.speech.EventManager;
import com.baidu.speech.EventManagerFactory;
import com.baidu.speech.asr.SpeechConstant;

import org.json.JSONObject;

import java.util.LinkedHashMap;
import java.util.Map;
import static com.example.baidu.Util.GetActivity.getActivityByContext;

public class RecognHandler {

    private boolean mIsInit = false;
    private EventManager asr;
    private EventListener mEventLisener;

    public RecognHandler(Context context, EventListener listener) {
        if (mIsInit) {
            listener=null;
            return;
        }
        mIsInit = true;
        mEventLisener = listener;
        //动态申请权限
        initPermission(context);
        asr = EventManagerFactory.create(context, "asr");
        asr.registerListener(listener);
    }
    //开始识别
    public void Start() {
        Log.i("@@@", "点击开始语音识别了 ");
        Map<String, Object> params = new LinkedHashMap<String, Object>();
        // 基于SDK集成2.1 设置识别参数
        params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, true);
        params.put(SpeechConstant.DISABLE_PUNCTUATION, false);
        params.put(SpeechConstant.ACCEPT_AUDIO_DATA, false);
        params.put(SpeechConstant.PID, 1537); // 中文输入法模型,有逗号
        String json = null; // 可以替换成自己的json
        json = new JSONObject(params).toString(); // 这里可以替换成你需要测试的json
        asr.send(SpeechConstant.ASR_START, json, null, 0, 0);
    }
    //停止识别
    public void Stop() {
        asr.send(SpeechConstant.ASR_STOP, null, null, 0, 0);
    }
    //释放实例
    public void Release() {
        asr.unregisterListener(mEventLisener);
        mIsInit = false;
        asr=null;
    }

    /**
     * android 6.0 以上需要动态申请权限
     */
    private void initPermission(Context context) {
        String permissions[] = {Manifest.permission.RECORD_AUDIO,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.INTERNET,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };

        PackageManager pm = getActivityByContext(context).getPackageManager();
        boolean permission_readStorage = (PackageManager.PERMISSION_GRANTED ==
                pm.checkPermission("android.permission.RECORD_AUDIO", "com.cientx.tianguo"));
        boolean permission_network_state = (PackageManager.PERMISSION_GRANTED ==
                pm.checkPermission("android.permission.ACCESS_NETWORK_STATE", "com.cientx.tianguo"));
        boolean permission_internet = (PackageManager.PERMISSION_GRANTED ==
                pm.checkPermission("android.permission.INTERNET", "com.cientx.tianguo"));
        boolean permission_writeStorage = (PackageManager.PERMISSION_GRANTED ==
                pm.checkPermission("android.permission.WRITE_EXTERNAL_STORAGE", "com.cientx.tianguo"));

        if (!(permission_readStorage && permission_writeStorage && permission_network_state && permission_internet)) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                getActivityByContext(context).requestPermissions(permissions, 0x01);
            }
        }

    }

}

RecognListener:

package com.example.baidu.Recogn;//填写自己的包名

import android.util.Log;

import com.baidu.speech.EventListener;
import com.baidu.speech.asr.SpeechConstant;
import com.unity3d.player.UnityPlayer;

public class RecognListener implements EventListener {


    @Override
    public void onEvent(String name, String params, byte[] data, int i, int i1) {
        if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)) {
            Log.i("@@@", "进入开始语音识别的方法了 ");
            // 识别相关的结果都在这里
            if (params == null || params.isEmpty()) {
                return;
            }
            if (params.contains("\"partial_result\"")) {
                UnityPlayer.UnitySendMessage("NetLogic", "RecognResult", name + "&" + params);
                // 一句话的临时识别结果
            } else if (params.contains("\"final_result\"")) {
                UnityPlayer.UnitySendMessage("NetLogic", "RecognResult", name + "&" + params);
                // 一句话的最终识别结果
            } else {
                // 一般这里不会运行
                if (data != null) {
                }
            }
        } else {
            // 识别开始,结束,音量,音频数据回调
            if (params != null && !params.isEmpty()) {
            }
            if (data != null) {
            }
        }
    }
};


GetActivity:

package com.example.baidu.Util;//自己的包名

        import android.app.Activity;
        import android.content.Context;
        import android.content.ContextWrapper;

public class GetActivity {


    public static Activity getActivityByContext(Context context){
        while(context instanceof ContextWrapper){
            if(context instanceof Activity){
                return (Activity) context;
            }
            context = ((ContextWrapper) context).getBaseContext();
        }
        return null;
    }
}

这四个脚本就是核心类,语音识别就是AS端这几个脚本实现的! 下面来看一下AndroidManifest怎样修改吧!


7.修改AndroidManifest文件

直接把我这个代码复制到自己的AndroidManifest中就好了

  • 这里要注意两个问题
  • package要填写正确,填写自己的!
  • 下面的APPIDAPI_KEYSECRET_KEY都要填写自己在百度语音识别平台上创建的那个应用的
  • 如果忘记了就往上翻看一下哦,这个要在百度语音平台看一下自己的!每个人的都不同~
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.xxx.xxx">   <!-- 填写自己的包名 -->
    <!-- 通用权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
    <!-- 语音识别权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 
    <!-- 语音合成权限 -->
 
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
 
    <application android:allowBackup="true">
        <activity android:name=".Wakeup.WakeUpResult"></activity>
        <activity android:name=".Wakeup.WakeupHandler" />
        
        <activity
            android:name=".CientBaiDuVoiceMainActivity"
            android:launchMode="singleTask" />
 <!-- 请填写真实的APP_ID API_KEY SECRET_KEY -->
        <meta-data
            android:name="com.baidu.speech.APP_ID"
            android:value="xxxxx" />
        <meta-data
            android:name="com.baidu.speech.API_KEY"
            android:value="xxxxx" />
        <meta-data
            android:name="com.baidu.speech.SECRET_KEY"
            android:value="xxxxx" />
 
        <service
            android:name="com.baidu.speech.VoiceRecognitionService"
            android:exported="false" />
    </application>
 
</manifest>

8.打包aar

  • 好了,如果确定上面一步里的包名、ID和KEY等都填写正确了
  • 那在AS端的操作也就算完成了,接下来就是打包成aar给Unity使用就好了!
  • 按照下图所示,先选中baidu这个文件夹,然后Build - > Make Module "baidu"
  • 开始这三布之前先点击右边那个小按钮编译一下最好! 在这里插入图片描述
  • 然后就会看到baidu这个文件夹下多了一个Build文件夹!
  • 我们把下图中这个aar文件复制出来就好了,准备下一步中放到Unity中使用!! 在这里插入图片描述

🍑Unity端操作步骤

1.新建一个Unity工程

打开UnityHub新建一个Unity工程,我这里使用的Unity版本是2018.4.32 在这里插入图片描述


2.导入aar包

  • UnityAssets文件夹下新建一个文件夹 Plugins->Android
  • 别问为什么,照做就好啦~
  • 然后将我们在AS端打包的aar文件放到Unity中,就如下图所示: 在这里插入图片描述

3.简单搭建一个UI用做测试

新建一个画布,里面放两个Button按钮和一个Text文本就好了! 在这里插入图片描述

3.新建脚本写代码!

  • 这里我们新建一个空游戏对象,名字改为NetLogic
  • 名字一定要是这个,因为我们这次AS给Unity交互采用的是发消息机制UnityPlayer.UnitySendMessage
  • 是通过名字来找到这个对象的!(因为我写的讯飞语音中使用的是代理模式Proxy,所以这里换个方法体验一下!) 在这里插入图片描述
  • 然后新建一个脚本BD,代码如下,将两个ButtonText文本拖到脚本上即可!

如下图: 在这里插入图片描述

using LitJson;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BD : MonoBehaviour
{
    AndroidJavaObject m_AndroidPluginObj;
    AndroidJavaClass _androidJC;
    AndroidJavaObject m_Android;
    public Text mRecognRes;
    public Button startASR_Btn;
    public Button stopASR_Btn;

    void Start()
    {
        AndroidJavaClass _androidJC = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        if (_androidJC == null)
        {
            Debug.Log("JNI initialization failure.");
            return;
        }
        m_AndroidPluginObj = _androidJC.GetStatic<AndroidJavaObject>("currentActivity");
        startASR_Btn.onClick.AddListener(StartRecogn);
        stopASR_Btn.onClick.AddListener(StopRecogn);

        Invoke("InitAsr", 3);
    }

    public void InitAsr()
    {
        AndroidJavaClass jc = new AndroidJavaClass("com.example.baidu.CientBaiDuVoiceMainActivity");//包名加类名
        AndroidJavaObject m_Android = jc.CallStatic<AndroidJavaObject>("getInstance");
        if (m_Android != null)
        {
            m_Android.Call("InitRecogn", m_AndroidPluginObj);
        }
        else
            Debug.Log("AndroidPlugin is Null");
    }
    public void StartRecogn()
    {
        AndroidJavaClass jc = new AndroidJavaClass("com.example.baidu.CientBaiDuVoiceMainActivity");
        AndroidJavaObject m_Android = jc.CallStatic<AndroidJavaObject>("getInstance");
        if (m_Android != null)
        {
            m_Android.Call("StartRecogn");
        }
        else
            Debug.Log("AndroidPlugin is Null");
    }
    public void StopRecogn()
    {
        AndroidJavaClass jc = new AndroidJavaClass("com.example.baidu.CientBaiDuVoiceMainActivity");
        AndroidJavaObject m_Android = jc.CallStatic<AndroidJavaObject>("getInstance");
        if (m_Android != null)
        {
            m_Android.Call("StopRecogn");
        }
        else
            Debug.Log("AndroidPlugin is Null");
    }

    /// <summary>
    /// 百度语音识别结果反馈
    /// </summary>
    /// <param name="res"></param>
    void RecognResult(string res)
    {
        string[] ress = res.Split('&');
        JsonData jsonData = JsonMapper.ToObject(ress[1]);
        string resStr = "";
        if (jsonData["result_type"].ToString() == "partial_result")
        {
            resStr = "临时识别结果:";
        }
        else
        {
            resStr = "最终识别结果:";
        }

        resStr += jsonData["best_result"].ToString();
        mRecognRes.text = resStr;
    }
}

  • 脚本中定义了两个Button点击事件和四个方法

  • 分别是语音识别初始化开始语音识别停止语音识别语音识别内容接收

  • 这里值得一提的是,接收语音识别内容的方法,得到的是Json字符

  • 我们需要解析完才能看到我们想要的内容

  • 那这里就需要导入一个解析Json的dll文件——LitJSON

  • 还要把它放到Plugins目录下

  • 那我这里也是在网上下载了然后放到这里使用了! 在这里插入图片描述

  • 然后代码中还要引用一下命名空间,上述脚本代码中也添加了,这里只是简单说一下!

using LitJson;

4.打包成apk真机测试

  • 这里只需要将产品名改成AndroidManifest中的Package包名即可
  • 就比如我这里的包名就改为baidu就可以了,然后Build打包就行
  • 我自己真机测试过了,是可以识别到的! 在这里插入图片描述

🥭源码工程下载

  • 本文教程源码工程下载链接在这
  • 可以直接下载打开使用

Unity接入百度语音源码工程下载


💬总结

  • 那到这里就结束了,本文对百度语音接入做了一个教程

相比前面三篇接入讯飞语音的要写的少,因为整理过程是有些相似的!

  • 在实际操作过程中,会出现一些问题是肯定的
  • 包括我在做这个百度语音识别的时候也遇到了困难
  • 因为我之前做过讯飞的语音识别,所以感觉百度的应该也很简单,道理应该差不多
  • 但是我实际操作起来,包括自己写代码和写文章总结发现,这两者差别还是挺大的!
  • 反正遇到困难就解决就好了,多尝试几次找一下哪里出现问题
  • 这个过程就是程序员必备的,也是最脑壳痛的问题,加油!