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 时会出现各种各样的坑,需要认真仔细,细节决定成败