1. 程式人生 > >手把手的操作——用java呼叫科大訊飛的離線語音識別dll實現離線識別(JNA實現)(二)

手把手的操作——用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,估計是後面兩個還不行,需要繼續調整,知道的指導一下,我繼續除錯了!