前言
項目要求在離線環境下實現基本的命令詞識別,並且需要接入后台進行命令詞意圖識別,而后台使用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之間的通訊是一個比較可行的解決方案。