您的位置:首页 > 移动开发 > Unity3D

Unity 接入 Android SDK-讯飞SDK实战

2017-12-15 13:12 375 查看

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 时会出现各种各样的坑,需要认真仔细,细节决定成败
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android unity sdk 讯飞 应用