前言
项目要求在离线环境下实现基本的命令词识别,并且需要接入后台进行命令词意图识别,而后台使用Java编写,简单来说就是Java平台下的中文离线语音识别。刚开始调研时我试用了Sphinx4[1],但是效果一般,然后我把目光转向国内的语音识别平台如科大讯飞[2]。科大讯飞的功能还算齐全,但最大的问题就是离线语音识别不支持Java平台,只有C/C++版本。所以我需要实现一个Java与C进行通信的接口。Java调用C函数最普遍的方法可能是JNI(Java Native Interface)。然而我认为这个方法过于复杂,需要自己定义接口。还有其他方法如JNA[3](Java Native Access),然而我尝试后均以失败告终,最后我尝试了SWIG[4]。SWIG是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具,简单高效满足需求。
正文
方案选择
Java+SWIG+科大讯飞,平台为Ubuntu 17.04
准备工作
-
从讯飞开放平台下载离线命令词识别SDK,得到以下文件
-
根据doc文件夹下的MSC_Novice_Manual_for_Linux.pdf建立一个demo工程,确认样例运行没有问题
-
从网上下载SWIG的安装包或者使用Ubuntu自带apt-get获取SWIG。
sudo apt install swig
STEP1
创建链接文件如asr.i,文件中定义接口
vim asr.i
/* File: asr.i */
%module asr
%{
int main();
%}
int main();
并使用SWIG输出asr.java,asrJNI.java和asr_wrap.c
swig swig -java asr.i
注意:如果你需要将此文件定义在一个包中如util那么此命令应加上-package参数
swig -java -package util.asr asr.i
STEP2
编译所有.c文件包括刚才生成的asr_wrap.c。
注意:编译asr_wrap.c时需要头文件jni.h和jni_md.h。这两个头文件可以在Java环境安装目录下找到,我的路径为
/usr/lib/jvm/java-1.8.0-openjdk-amd64/include/
/usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux
使用gcc命令分别编译
gcc -fPIC -c asr_wrap.c -I/usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -I/usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux
gcc -fPIC -c -I../include asr_record_sample.c -o asr_sample.o
使用ld命令链接所有.o文件
ld -G -I../include asr_wrap.o asr_sample.o -o ../libs/x64/libasr.so -L../libs/x64 -lmsc -lrt -ldl -lpthread -lasound
最后得到libasr.so文件
STEP3
编写简单的Java程序进行测试
vim run.java
/*run.java*/
public class run {
static {
System.loadLibrary("asr");
}
public static void main(String argv[]) {
asr.main();
}
}
编译并运行Java程序
javac *.java
java run
结语
整个过程发现除了编译的部分繁琐了些,其他实现还是比较容易,通过SWIG实现Java和C之间的通讯是一个比较可行的解决方案。