手把手的操作——用java呼叫科大訊飛的離線語音識別dll實現離線識別(JNA實現)(二)
上一篇講到了最難的地方,引數的轉換,這裡單獨寫出來 **
三、引數的轉換(難點)
** 注:本文是以訊飛提供的C語言例子作為模板改寫,語音來源於檔案 1、先分析提供的例子 本人使用的是VS2010 下載連結連結:https://pan.baidu.com/s/1CZX3k6nhsbLkuzB3mocyww 提取碼:6r5g 2.45G大小,需要安裝一段時間,因為用JNA只用看,這個版本夠了 【為了給我一樣對C/C++不瞭解的初階,大神輕噴】
執行之後 【檔案】–【開啟】–【專案/解決方案】–自己找檔案位置–【asr_offline_sample.vcxproj】
同樣的方法開啟標頭檔案 【檔案】–【開啟】–【檔案】–自己找檔案位置–【標頭檔案.h結尾的】
主要看例子asr_sample.c 在java中主要就是將這個檔案進行改寫,所以要先看懂 我自己看了很久,講下結構吧: 上面幾個是各項功能的函式,最下面是主函式main,跟java的結構很像吧 幾大主要功能 【登入】 【建立語法】 【建立詞典】 【語音識別】 【退出】
看上去很簡單啊,接下來就是一個一個在java中實現 我先建立了一個lib介面,裡面放呼叫的東西 (先把動態庫載入進來)
public interface VoiceLib extends Library{ VoiceLib instance = (VoiceLib)Native.loadLibrary("msc_x64", VoiceLib.class); }
#1登入 這個功能在(一)中已經有例子了,這裡重寫一遍就好
public interface VoiceLib extends Library{
VoiceLib instance = (VoiceLib)Native.loadLibrary("msc_x64", VoiceLib.class);
int MSPLogin(String usr, String pwd, String params);
}
新建主類xMsc 改寫登入功能
public static void main(String[] args) { String login_config = "appid=5ba4bc08"; //登入引數 UserData asr_data=null ; int ret = 0; ret = VoiceLib.instance.MSPLogin(null, null, login_config); //第一個引數為使用者名稱,第二個引數為密碼,傳null即可,第三個引數是登入引數 if (MSP_SUCCESS!= ret) { System.out.println("登入失敗!"); exit(); } }
上面先改寫下幾個常量:
public class xMsc {
public static final int SAMPLE_RATE_16K=16000;
public static final int SAMPLE_RATE_8K=8000;
public static final int MAX_GRAMMARID_LEN=32;
public static final int MAX_PARAMS_LEN=1024;
public static final int MSP_SUCCESS=0;
private static final int MSP_FAILED = 1;
private static int MSP_AUDIO_SAMPLE_FIRS=0x01;
private static int MSP_AUDIO_SAMPLE_CONTINUE=0x02;
private static int MSP_EP_LOOKING_FOR_SPEECH=0;
private static int MSP_REC_STATUS_INCOMPLETE=2;
private static int MSP_EP_AFTER_SPEECH= 3;
private static int MSP_AUDIO_SAMPLE_LAST= 0x04;
private static int MSP_REC_STATUS_COMPLETE = 5;
public static final String ASR_RES_PATH = "./source/common.jet"; //離線語法識別資源路徑
public static final String GRM_BUILD_PATH = "./GrmBuilld_x64"; //構建離線語法識別網路生成資料儲存路徑
public static final String GRM_FILE = "./source/call.bnf"; //構建離線識別語法網路所用的語法檔案
public static final String LEX_NAME = "contact"; //更新離線識別語法的contact槽(語法檔案為此示例中使用的call.bnf)
public static class UserData {
public static boolean build_fini; //標識語法構建是否完成
public static boolean update_fini; //標識更新詞典是否完成
public static int errcode; //記錄語法構建或更新詞典回撥錯誤碼
public static String grammar_id; //儲存語法構建返回的語法ID
};
裡面的修飾符等還需要再研究研究【待】 有幾個檔案要拷貝過來啊,語法檔案之類,我建立了一個source資料夾,都放裡面了 現在的結構 【先一口氣把main裡面的全部改寫完吧!】
public static void main(String[] args) {
String login_config = "appid=5ba4bc08"; //登入引數
UserData asr_data=null ;
int ret = 0;
ret = VoiceLib.instance.MSPLogin(null, null, login_config); //第一個引數為使用者名稱,第二個引數為密碼,傳null即可,第三個引數是登入引數
if (MSP_SUCCESS!= ret) {
System.out.println("登入失敗!");
exit();
}
System.out.println("登入成功!");
/*memset(&asr_data, 0, sizeof(UserData));*/
System.out.println("構建離線識別語法網路...\n");
ret = build_grammar(asr_data); //第一次使用某語法進行識別,需要先構建語法網路,獲取語法ID,之後使用此語法進行識別,無需再次構建
if (MSP_SUCCESS != ret) {
System.out.println("構建語法呼叫失敗!\n");
exit();
}
while (false != asr_data.build_fini)
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (MSP_SUCCESS != asr_data.errcode)
exit();
System.out.println("離線識別語法網路構建完成,開始識別...\n");
ret = run_asr(asr_data);
if (MSP_SUCCESS!= ret) {
System.out.println("離線語法識別出錯: %d \n");//+ret
exit();
}
System.out.println("請按任意鍵繼續\n");
//_getch();
System.out.println("更新離線語法詞典...\n");
ret = update_lexicon(asr_data); //當語法詞典槽中的詞條需要更新時,呼叫QISRUpdateLexicon介面完成更新
if (MSP_SUCCESS!= ret) {
System.out.println("更新詞典呼叫失敗!\n");
exit();
}
while (false != asr_data.update_fini)
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (MSP_SUCCESS != asr_data.errcode)
exit();
System.out.println("更新離線語法詞典完成,開始識別...\n");
ret = run_asr(asr_data);
if (MSP_SUCCESS!= ret) {
System.out.println("離線語法識別出錯: %d \n");
exit();
}
}
【注】有些功能還沒寫完,還需修改,最終版我會放到統一的資源中,現在只是初稿
¥¥¥¥¥退出先封裝一下¥¥¥¥¥¥
public static void exit(){
VoiceLib.instance.MSPLogout();
System.out.println("請按任意鍵退出...\n");
// _getch();
}
【再來一口氣】把要用的函式全部封裝到VoiceLib中,等下直接呼叫
package com.xinzhi;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.xinzhi.Util.GrammarCallBack;
import com.xinzhi.Util.LexiconCallBack;
public interface VoiceLib extends Library{
VoiceLib instance = (VoiceLib)Native.loadLibrary("msc_x64", VoiceLib.class);
int MSPLogin(String usr, String pwd, String params);
int QISRBuildGrammar (String grammarType, String grammarContent, int grammarLength, String params, GrammarCallBack callback, Pointer userData);
int QISRUpdateLexicon(String lexiconName, String lexiconContent, int lexiconLength, String params, LexiconCallBack callback, Pointer userData);
String QISRSessionBegin(String grammarList, String params, IntByReference errorCode);
int QISRAudioWrite(String sessionID, Object waveData, int waveLen, int audioStatus, IntByReference ep_status1, IntByReference rec_status1);
String QISRGetResult(String sessionID, int rsltStatus, int waitTime, int errorCode);
boolean QISRSessionEnd(String sessionID, String hints);
boolean MSPLogout();
}
【這裡的引數我都已經改好啦,等下到具體功能再解釋】
然後寫語法的
//重要分割線*********************************// 在訊飛提供的例子中,有一個回撥函式和一個構建語法函式,都要改寫,怎麼辦呢? 回撥函式我封裝到一個Util中(後面的詞典回撥一起了)
package com.xinzhi;
import com.sun.jna.Pointer;
import com.sun.jna.win32.StdCallLibrary.StdCallCallback;
public class Util {
//語法回撥函式的介面
public interface GrammarCallBack extends StdCallCallback{
int build_grm_cb(int ecode, String info, Pointer udata);
}
//語法回撥函式的實現
public static class GrammarCallBack_Realize implements GrammarCallBack{
@Override
public int build_grm_cb(int ecode, String info, Pointer udata) {
Pointer grm_data = udata;
if (null != grm_data) {
com.xinzhi.xMsc.UserData.build_fini = false;
com.xinzhi.xMsc.UserData.errcode = ecode;
}
if (com.xinzhi.xMsc.MSP_SUCCESS == ecode && null != info) {
System.out.println("構建語法成功! 語法ID:"+ info);
if (null != grm_data)
//_snprintf(grm_data->grammar_id, MAX_GRAMMARID_LEN - 1, info);
com.xinzhi.xMsc.UserData.grammar_id=info;
}
else {
System.out.println("構建語法失敗!%d"+ ecode);
}
return 0;
}
}
//詞典回撥函式的介面
public interface LexiconCallBack extends StdCallCallback{
int update_lex_cb(int ecode, String info, Pointer udata);
}
//詞典回撥函式的實現
public static class LexiconCallBack_Realize implements LexiconCallBack{
@Override
public int update_lex_cb(int ecode, String info, Pointer udata) {
Pointer lex_data = udata;
if (null != lex_data) {
com.xinzhi.xMsc.UserData.update_fini = false;
com.xinzhi.xMsc.UserData.errcode = ecode;
}
if (com.xinzhi.xMsc.MSP_SUCCESS == ecode)
System.out.println("更新詞典成功!\n");
else
System.out.println("更新詞典失敗!%d\n"+ ecode);
return 0;
}
}
}
裡面還有錯誤啊,回頭來改!
其中void,我使用Pointer來模擬*
再來寫語法的
//構建離線識別語法網路
public static int build_grammar(UserData udata){
File grm_file = null;
String grm_content = null;
int grm_cnt_len = 0;
String grm_build_params = null;
int ret = MSP_SUCCESS;
grm_file = new File(GRM_FILE);
if(!grm_file.exists()) {
System.out.println("開啟語法檔案失敗!");
return MSP_FAILED;
}
grm_cnt_len = (int) grm_file.length();
//按照字元讀取語法檔案,並將字元新增到grm_content中
FileInputStream in;
try {
in = new FileInputStream(grm_file);
BufferedReader buf=new BufferedReader(new InputStreamReader(new FileInputStream(grm_file)));
StringBuilder builder=new StringBuilder();
String data;
while((data=buf.readLine())!=null) {
builder.append(data+"\n");
}
grm_content=builder.toString();
} catch (Exception e) {
e.printStackTrace();
}
//初始化構建語法引數
grm_build_params="engine_type = local,asr_res_path = "+ASR_RES_PATH+
", sample_rate = "+SAMPLE_RATE_16K+", grm_build_path = "+GRM_BUILD_PATH;
//例項化回撥函式物件
Util.GrammarCallBack callback=new Util.GrammarCallBack_Realize();
//建立無型別空指標
Pointer udata1=Pointer.NULL;
//傳入引數,構建語法
ret =VoiceLib.instance.QISRBuildGrammar("bnf", grm_content, grm_cnt_len, grm_build_params, callback, udata1);
grm_content = null;
return ret;
}
QISRBuildGrammar(“bnf”, grm_content, grm_cnt_len, grm_build_params, callback, udata1); 主要是給這個函式配引數,現在還沒有測通,有測通的告訴我一聲! 解釋: 第一個 是檔案型別,不用說固定的 第二個 是語法內容,我用了檔案緩衝讀字元,按行讀取字元,注意用的是StringBuilder,因為要拼接,你直接用String卡死了不要找我哈。 第三個 是檔案長度,length一下就好 第四個 是引數,這裡初始化直接賦值就好,注意字串憑藉的引號問題 第五個就是回撥函式 第六個是那個無定向指標 現在報引數無效,錯誤碼23002,估計是後面兩個還不行,需要繼續調整,知道的指導一下,我繼續除錯了!