Android Studio集成讯飞语音导出可供Unity使用的jar/aar
因工作需要,把讯飞语音听写集成到unity,因为没有直接给unity调用的讯飞语音SDK,就想着通过android studio打包jar给unity调用
也是查了不少资料和demo,否则也无法完成需求,所以要感谢下其它的作者,但是因为过了一段时间才写的,所以参考了那个房个文章也不太记得了。如果有些看到部分相同资料的其它来源,请告诉我,我把出处加进来。
过了一遍就清晰很多,最后还加了些要注意的地方。
原码:https://github.com/yaopin002/ASXunFeiToUnity#as-unity
运行环境
Unity 2017.1.03,
Android Studio 2.3.3
Win 7
一、 创建Android Studio工程
1)第一步,点击File->New->New Project,打开"Create New Project"对话框,选择合适的Application name与Company Domain,保证Package name与Unity项目中的Bundle Idenifier一致
2)第二步,选择Phone and Tablet,并选择合适的Minimum SDK(也可以在创建后的build.gradle中设置)
3)第三步,选择“Empty Activity”
4)第四步,保持默认的Activity Name与Layout Name即可
5)最后,点击"Finish"创建工程
**以上是直接使用新创建的空activity,然后就需要下面第四个步骤--修改gradle把这个Application改成一个library
**另一个办法是直接创建一个Android Library,就不需要修改gralde了,但是空的library是不带空的activity的,所以需要手动创建activity
二、添加Unity的classes.jar引用和讯飞SDK及.so文件
1)把Unity引擎目录下中的”Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar“文件拷贝至Android Studio工程中的libs目录
2)把讯飞语音MSC.jar也拷贝至Android Studio工程中的libs目录
3)把以上jar报进行添加依赖动作
4)把讯飞语音armeabi和armeabi-v7a的so文件拷贝到main目录下的jniLibs文件夹下(没有就新建jniLibs)
5)在gradle的android目录下添加一段代码指定库文件(这个动作运行AS时需要,如果只是做Jar/aar包就可以不用),代码如下:
sourceSets.main{ jniLibs.srcDir 'src/main/jniLibs'}复制代码
三、编写Android侧代码(修改MainActivity代码)
public SpeechRecognizer speechRecognizer;
public SpeechSynthesizer speechSynthesizer;
private String ttsSpeakerName = "yefang";
private String ttsSpeakerPitch = "50";
MainActivity
// 写在onCreate这里面的代码,在unity初始化时一样会自动执行
//注意这里的appid为
SpeechUtility.createUtility(getApplicationContext(),"appid=58880d30");
initRecognizer();
}
//初始化
private void initRecognizer(){
//1.创建SpeechRecognizer对象,第二个参数:本地听写时传InitListener
speechRecognizer = SpeechRecognizer.createRecognizer(getApplicationContext(),mInitListener);
speechSynthesizer = SpeechSynthesizer.createSynthesizer(getApplicationContext(),mInitListener);
}
//初始化SpeechRecognizer对象的监听器
public InitListener mInitListener = new InitListener() {
@Override
public void onInit(int i) {
UnityPlayer.UnitySendMessage("Manager", "Result", "init success!");
}
};
public void setTTSSpeaker(String targetName) {
ttsSpeakerName = targetName;
}
public void setTTSPitch(String targetPitch) {
ttsSpeakerPitch = targetPitch;
}
public void doTTS(String ttsStr){
UnityPlayer.UnitySendMessage("MotionManager", "IsSpeaking", "true");
//设置发音人
speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME,ttsSpeakerName);
//合成语调 通过此参数,设置合成返回音频的语调。
speechSynthesizer.setParameter(SpeechConstant.PITCH,ttsSpeakerPitch);
//设置音量
speechSynthesizer.setParameter(SpeechConstant.VOLUME,"50");
int code = speechSynthesizer.startSpeaking(ttsStr, mTTSListener);
}
private SynthesizerListener mTTSListener = new SynthesizerListener() {
@Override
public void onSpeakBegin() {
}
@Override
public void onBufferProgress(int i, int i1, int i2, String s) {
}
@Override
public void onSpeakPaused() {
}
@Override
public void onSpeakResumed() {
}
@Override
public void onSpeakProgress(int i, int i1, int i2) {
}
@Override
public void onCompleted(SpeechError speechError) {
UnityPlayer.UnitySendMessage("MotionManager", "IsSpeaking", "false");
}
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
}
};
//开始听写
public void startSpeechListener(){
UnityPlayer.UnitySendMessage("Manager", "Result", "");
speechRecognizer.setParameter(SpeechConstant.DOMAIN, "iat");
speechRecognizer.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
speechRecognizer.setParameter(SpeechConstant.ACCENT, "mandarin");
speechRecognizer.startListening(mRecognizerListener);
}
public RecognizerListener mRecognizerListener = new RecognizerListener(){
@Override
public void onBeginOfSpeech() {
// TODO Auto-generated method stub
//UnityPlayer.UnitySendMessage("Manager", "Result", "onBeginOfSpeech");
}
@Override
public void onEndOfSpeech() {
// TODO Auto-generated method stub
//UnityPlayer.UnitySendMessage("Manager", "Result", "onEndOfSpeech");
//startSpeechListener();
UnityPlayer.UnitySendMessage("Manager", "SpeechEnd","");
}
@Override
public void onError(SpeechError arg0) {
// TODO Auto-generated method stub
//UnityPlayer.UnitySendMessage("Manager", "Result", "onError");
}
@Override
public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {
// TODO Auto-generated method stub
//UnityPlayer.UnitySendMessage("Manager", "Result", "onEvent");
}
@Override
public void onResult(RecognizerResult recognizerResult, boolean isLast) {
//UnityPlayer.UnitySendMessage("Manager", "Result", "listener");
printResult(recognizerResult);
//if(isLast)
//startSpeechListener();
}
@Override
public void onVolumeChanged(int arg0, byte[] arg1) {
//UnityPlayer.UnitySendMessage("Manager", "Result", "onVolumeChanged");
// TODO Auto-generated method stub
}
};
//解析
private void printResult(RecognizerResult results) {
String json = results.getResultString();
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"));
}
} catch (Exception e) {
e.printStackTrace();
}
//将解析结果“"result:" + ret.toString()”发送至“Manager”这个GameObject,中的“Result”函数
UnityPlayer.UnitySendMessage("Manager", "Result", ret.toString());
}
public void ShowToast(final String mStr2Show){
UnityPlayer.UnitySendMessage("Manager", "Result", "toast");
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(),mStr2Show,Toast.LENGTH_LONG).show();
}
});
}
四、修改build.gradle,设置工程导出为aar
1)apply plugin: 'com.android.application' 修改为 apply plugin: 'com.android.library'
2)删除 applicationId "com.zcode.unityandroidplugindemo"
3)修改后的build.gradle为
apply plugin: 'com.android.library'
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
minSdkVersion 18
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.1.1'
testCompile 'junit:junit:4.12'
compile files('libs/classes.jar')
}
五、修改AndroidManifest.xml
1)修改样式
我们需要在 AndroidManifest 中的 application 结点修改应用的主样式为系统样式,因为导出的 AAR 文件将不带自定义的样式,在我们的 Unity 项目中生成最终 apk 的时候会出现样式找不到的错误。
android:theme="@android:style/Theme.NoTitleBar"
同时需要删除res\Values目录下的styles.xml文件
备注:如果是直接创建的Android Library就不需要这一步。
2)在主 activity 结点下添加信息,否则在 Unity 导出 APK 时会报找不到manifest 文件的错误信息
备注:如果是直接创建的Android Library就需要添加activity节点,包括里面的intent-filter、action、category(但是上面那行meta-data可以不要)
六、导出供Unity使用的*.aar/jar文件
1)点击右上角的gradle projects找到对应module的Task里的other下的makeJar,双击运行,等一会儿后就完成
备注:要在gradle根目录下添加如下两个方法才会在gradle projects出现makeJar(内容需依据需要进行修改):
task makeJar(type: Copy) {delete 'build/libs/speechrecognizer.jar'from('build/intermediates/bundles/release/')into('build/libs/')include('classes.jar')rename ('classes.jar', 'speechrecognizer.jar')}makeJar.dependsOn(build)复制代码
2)也可以通过Terminal执行命令:gradlew makeJar,会使所有Module都执行,如下图:
2 如下目录找到aar(aar文件里最外层就有一个classes.jar)
3 如下目录直接找到jar包
4 3)如果是要调用aar文件,就需使用压缩软件打开aar文件删除libs目录下unity的classes.jar文件(因为会和unity自带的classes.jar冲突),并且要删除其它jar文件和jni目录下的so文件(因为要拷贝到unity指定的目录下,下面步骤会介绍)
如果使用classes.jar就直接拷贝到Unity(拷贝前建议改个名)
Unity导入插件,并调用
一、创建Unity工程
二、导入插件到Unity工程中
1)新建目录:Plugins-Android-bin/libs
2)拷贝aar/jar文件、AndroidManifest.xml文件、so文件至如下目录注意:安卓5.0以上系统需要armeabi-v7a,不然会出现21002的错误
5 三、编写测试代码
1)新建一个cs脚本文件
2)在脚本中编写调用Android侧代码
using UnityEngine;using System.Collections;public class XunFeiTest : MonoBehaviour{ private string showResult = ""; //UnityGUI控制是利用一类被称为OnGUI()的函数,只要在控制脚本激活的状态下 //OnGUI()函数可以在每帧调用,就像Update( )函数一样。 void OnGUI () { //新建一个button,指定文本和高度 if (GUILayout.Button ("startRecognizer", GUILayout.Height (100))) { //这两行代码一般不用改 AndroidJavaClass jc = new AndroidJavaClass ("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo = jc.GetStatic("currentActivity");
//调用方法
jo.Call ("startSpeechListener");
}
//创建一个TextArea,参数一:显示的内容,参数二:指定宽度
GUILayout.TextArea (showResult, GUILayout.Width (200));
}
//这是AS中指定的方法名,AS一旦有发送消息,这里就会传进来结果
public void Result (string recognizerResult)
{
showResult += recognizerResult; //把结果连接起来
}
}
四、设置并运行APK
1)设置Bundle Identifier(保持与插件的包名+类名一致)
2)设置合适的Minimum API Level(保持与插件)
五、测试
结尾
1.确定所有的aar库中的android:minSdkVerion与android:targetSdkVersion一致
2.android studio包名要和unity包名一致
3.unity的OnGUI()启动方法要大写
4.theme要改为android系统样式android:theme="@android:style/Theme.NoTitleBar",或者不要theme也可以
5.删除res下的style.xml
6.修改build.gradle:一改一删
7.务必真机测试,虚拟机会报错
8.res下activity_main.xml里的这种代码"app:layout_constraintBottom_toBottomOf="parent""要删除,不然会报错
9.AS不需要添加动态权限申请,否则会报activity找不到的错
以上如有错误请高人指正