Unity 接入 Android SDK-讯飞SDK实战

Unity 接入 Android SDK-讯飞SDK实战

系列篇 四
所有篇幅
Unity Android 交互

Unity Android 交互 二

Unity Android 交互 三 多个 Module分别生成 aar 导入Unity自动合并 AndroidManifest.xml

Unity 接入 Android SDK - 讯飞SDK 实战

本篇以接入一个讯飞 SDK 为例实际操作一下
首先到讯飞官网注册并下载 SDK 讯飞官网
进入 SDK 下载中心,

这里写图片描述

随便申请一个应用,提交即可
这里写图片描述

这里写图片描述

然后下载 SDK

下载解压如下
这里写图片描述

libs 下为 SDK需要的 Msc .jar 包和 .so 库
这里写图片描述

SDK 准备完成,开始 打开 Android Studio
1. 首先新建一个 主 Module 作为程序主入口
2. new -> Module 创建一个 Phone 工程
这里写图片描述

然后就是参考 这里写链接内容
清除所有无用配置和资源,删除没用的依赖库
修改 build.gradle 设置为导出aar

apply plugin: 'com.android.library'

导入 Unity 自带 classes.jar 库,并添加依赖
修改 MainActivity 继承于 UnityPlayerActivity

package com.testSdk.demo;

import android.os.Bundle;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

public class MainActivity extends UnityPlayerActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
}

修改 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.testSdk.demo">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:supportsRtl="true"
        >
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

3.主Module准备就绪,我将讯飞SDK 接入到一个新的 Module中,目的是解耦合,将讯飞SDK 所有配置和逻辑都放在 Module 中。
新建Module,作为一个 Android Library
这里写图片描述
项目名 xunfen 包名 com.xunfens.demo
老规矩清除所有无用资源配置,清除依赖

4.将 Msc.jar 放到 libs下,open Module Setting 打开设置依赖
这里写图片描述

5.将 讯飞 SDK 下 libs 中其他 .so 文件放入 src -> main -> jniLibs 下,如果没有 jniLibs 文件夹则创建一个
这里写图片描述

6.在讯分包名文件夹下新建一个 SynthesizerVoice.java
按照 SDK 文档说明接入 语音合成SDK
代码如下

package com.xunfens.demo;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.widget.Toast;
import android.os.Bundle;

import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechSynthesizer;
import com.iflytek.cloud.SynthesizerListener;
import com.iflytek.speech.*;
import com.iflytek.*;
import com.iflytek.cloud.SpeechUtility;

public class SynthesizerVoice {
    // 单利
    private static SynthesizerVoice instance;

    // 暂存 UnityPlayerActivity 的 Context
    private static Context unityContext;
    // 暂存 UnityPlayerActivity 的 Activity
    private static Activity unityActivity;

    // 语音合成对象
    private SpeechSynthesizer speechSynthesizer;

    // 默认发言人
    private String voicer = "xiaoyan";

    // 引擎类型
    private String mEngineType = SpeechConstant.TYPE_CLOUD;

    // 获取单利
    public static SynthesizerVoice getInstance()
    {
        if (instance == null)
        {
            instance = new SynthesizerVoice();
        }

        return instance;
    }

    // 构造函数
    public SynthesizerVoice()
    {
    }

    // 在 MainActivity 中调用初始化,传入 MainActivity.this
    public void init(Context _context) {
        // 获取到 UnityPlayerActivity 的 Context 和 Activity
        unityContext = _context.getApplicationContext();
        unityActivity = (Activity) _context;

        // 初始化讯飞 SDK
        // "appid=" + "5a30c837" 注意:"appid=" 中等号前后都不要加任何字符必须紧按着 5a30c837 为我当前应用的appid
        // 创建应用会生成一个唯一的 appid
        SpeechUtility.createUtility(unityContext, "appid=" + "5a30c837");

        // 初始化语音合成对象,传入 Context 和 监听回调
        speechSynthesizer = SpeechSynthesizer.createSynthesizer(_context, mTtsInitListener);
    }

    // 设置参数
    private void setParam() {
        // 清空参数
        speechSynthesizer.setParameter(SpeechConstant.PARAMS, null);
        // 根据合成引擎设置相应参数
        if (mEngineType.equals(SpeechConstant.TYPE_CLOUD)) {
            speechSynthesizer.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
            // 设置在线合成发音人
            speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, voicer);
            //设置合成语速
            speechSynthesizer.setParameter(SpeechConstant.SPEED, "50");
            //设置合成音调
            speechSynthesizer.setParameter(SpeechConstant.PITCH, "50");
            //设置合成音量
            speechSynthesizer.setParameter(SpeechConstant.VOLUME, "50");
        } else {
            speechSynthesizer.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
            // 设置本地合成发音人 voicer为空,默认通过语记界面指定发音人。
            speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, "");
            /**
             * TODO 本地合成不设置语速、音调、音量,默认使用语记设置
             * 开发者如需自定义参数,请参考在线合成参数设置
             */
        }
        //设置播放器音频流类型
        speechSynthesizer.setParameter(SpeechConstant.STREAM_TYPE, "5");
        // 设置播放合成音频打断音乐播放,默认为true
        speechSynthesizer.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");

        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        // 注:AUDIO_FORMAT参数语记需要更新版本才能生效
        speechSynthesizer.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        //speechSynthesizer.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/tts.wav");
    }

    // 外部调用开始播放
    public void StartSpeaking() {
        // 需要合成声音的文字
        String text = "哈哈哈哈哈哈哈哈哈哈快说出来";
        // 设置参数
        setParam();

        // 开始播放
        int code = speechSynthesizer.startSpeaking(text, mTtsListener);

        if (code != ErrorCode.SUCCESS) {
            showToast("语音合成失败,错误码: " + code);
        } else {
            showToast("语音合成成功啦");
        }
    }


    // 播放回调
    private SynthesizerListener mTtsListener = new SynthesizerListener() {
        @Override
        public void onSpeakBegin() {
        }

        @Override
        public void onSpeakPaused() {
        }

        @Override
        public void onSpeakResumed() {
        }

        @Override
        public void onBufferProgress(int percent, int beginPos, int endPos,
                                     String info) {
            // 合成进度
        }

        @Override
        public void onSpeakProgress(int percent, int beginPos, int endPos) {
            // 播放进度
        }

        @Override
        public void onCompleted(SpeechError error) {
            if (error == null) {
                showToast("播放完成");
            } else if (error != null) {
                showToast(error.getPlainDescription(true));
            }
        }

        @Override
        public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
            // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
            // 若使用本地能力,会话id为null
            //  if (SpeechEvent.EVENT_SESSION_ID == eventType) {
            //      String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
            //      Log.d(TAG, "session id =" + sid);
            //  }
        }
    };

    /**
     * 初始化监听。
     */
    private InitListener mTtsInitListener = new InitListener() {
        @Override
        public void onInit(int code) {
            //Log.d(TAG, "InitListener init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showToast("初始化失败,错误码:" + code);
            } else {
                // 初始化成功,之后可以调用startSpeaking方法
                // 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成,
                // 正确的做法是将onCreate中的startSpeaking调用移至这里
                showToast("初始化成功");
            }
        }
    };

    // 传入 meg,弹出一个 Toast 操作
    public static void showToast(final String meg) {
        unityActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(unityContext, meg, Toast.LENGTH_LONG).show();
            }
        });
    }

    // 弹出一个提示窗口,窗口需要的文字信息从strings.xml 里面获取,点击确认关闭
    public static void showAlertDialog(final String _title, final String _content) {
        unityActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(unityActivity);
                builder.setTitle(_title).setMessage(_content).setPositiveButton("Down", null);
                builder.show();
            }
        });
    }
}

代码注释很详细就不细说了
在 AndroidManifest.xml 中配置权限,需要的权限在 SDK 示例代码和文档中都可以找到,下面配置的是所有 讯飞 SDK 需要的一些权限,
有一些不是 语音合成 SDK 需要的,我就赖得删选,全部放进来了

添加前AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xunfens.demo" >
</manifest>

添加后 AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xunfens.demo" >


    <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.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

</manifest>

xunfen Module 准备结束,在 主Module 加入对 xunfen Module 的依赖,然后在 主Module 的 MainActivity 中调用 xunfen 的初始化和调用
修改 MainActivity

package com.testSdk.demo;

import android.os.Bundle;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

// 引入xunfen 库
import com.xunfens.demo.SynthesizerVoice;

public class MainActivity extends UnityPlayerActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 获取 xunfen 中SynthesizerVoice 单利
        SynthesizerVoice synthesizerVoice = SynthesizerVoice.getInstance();
        // 初始化 xunfen 将 MainActivity.this 传递进去
        synthesizerVoice.init(MainActivity.this);
    }

    // 在Unity 中调用说话
    public void StartSpeaking()
    {
        // 获取单利
        SynthesizerVoice synthesizerVoice = SynthesizerVoice.getInstance();
        // 调用说话
        synthesizerVoice.StartSpeaking();
    }
}

准备就绪
执行 Buidl -> Rebuild Project,生成 aar
找到 主Module 和 xunfen Module 生成 aar

找到 主 Module 生成 aar, app-debug.aar,删除 libs 下 classes.jar
将 AndroidManifest.xml 复制出来
这里写图片描述

找到 xunfen Module 生成的 aar
这里写图片描述

将 app-debug.aar, AndroidManifest.xml, 和 xunfen-debug.aar 一起放入到 Unity工程 Plugins/Android 目录下
这里写图片描述

此处有坑,最好执行以下 Reimport重新导入以下
这里写图片描述

生成 APK 安装到模拟器上测试

在 MainActivity onCreate 方法中执行初始化,
运行游戏启动显示 Toast 初始化成功
这里写图片描述

这里写图片描述

点击 Speak 按钮
这里写图片描述

等待说完
这里写图片描述

测试成功

由于没有Android 真机测试,只能在 MUMU 模拟器上测

接入 SDK 时会出现各种各样的坑,需要认真仔细,细节决定成败

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值