手把手的操作——用java调用科大讯飞的离线语音识别dll实现离线识别(JNA实现)(二)

版权声明:原创文章本人保留一切权力,转载必须联系我 企鹅号2110127392 https://blog.csdn.net/weixin_43112746/article/details/82887426

上一篇的地址手把手的操作——用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=5ba4***"; //登录参数
		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;
	public static final int MSP_FAILED = 1;
	private static int MSP_AUDIO_SAMPLE_FIRS = 1;
	private static int MSP_AUDIO_SAMPLE_CONTINUE = 2;
	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 = 4;
	private static int MSP_REC_STATUS_COMPLETE = 5;
	
	private static File f_pcm 		 = null;
	private static byte[] pcm_data	 =null;
	private static int errcode		 = -1;
	private static String session_id = "";
	private static IntByReference ep_status  = new IntByReference(MSP_EP_LOOKING_FOR_SPEECH);
	private static IntByReference rec_status = new IntByReference(MSP_REC_STATUS_INCOMPLETE);
	private static IntByReference rss_status1 = new IntByReference(MSP_AUDIO_SAMPLE_FIRS);
	private static IntByReference err 		  = new IntByReference(errcode);

	public static final String ASR_RES_PATH = "fo|common.jet"; // 离线语法识别资源路径
	public static final String GRM_BUILD_PATH = "./msc/GrmBuilld_x64"; // 构建离线语法识别网络生成数据保存路径
	public static final String GRM_FILE = "./source/szx.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		
	};

里面的路径必须要跟我一样,尤其是 ASR_RES_PATH ,前面必须加"fo|"
有几个文件要拷贝过来啊,语法文件之类,我建立了一个source文件夹,都放里面了
现在的结构
在这里插入图片描述
【先一口气把main里面的全部改写完吧!】

public static void main(String[] args) {
		String login_config = "appid=5ba4*****"; //登录参数,要改成自己的
		asr_data = new UserData();
		int ret = 0;
		ret = VoiceLib.instance.MSPLogin(null, null, login_config); // 第一个参数为用户名,第二个参数为密码,传null即可,第三个参数是登录参数
		if (MSP_SUCCESS != ret) {
			System.out.println("登录失败!");
			exit();
		}
		System.out.println("登录成功!");
		System.out.println("构建离线识别语法网络...\n");
		ret = build_grammar(asr_data); // 第一次使用某语法进行识别,需要先构建语法网络,获取语法ID,之后使用此语法进行识别,无需再次构建

		if (MSP_SUCCESS != ret) {
			System.out.println("构建语法调用失败!\n");
			exit();
		}
		while (MSP_FAILED != asr_data.build_fini) {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				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("离线语法识别出错: \n");
			exit();
		}
	}

【注】词典我删掉了,和构建语法一样,需要的自己写喽

¥¥¥¥¥退出功能先封装一下¥¥¥¥¥¥

public static void exit(){
		VoiceLib.instance.MSPLogout();
		System.out.println("请按任意键退出...\n");
		
	}

【再来一口气】把要用的函数全部封装到VoiceLib中,等下直接调用


import com.sun.jna.Library;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.szxinfo.recognizer.CallbackUtil.GrammarCallBack;



public interface VoiceLib extends Library{
	String filePath = "mscx64";
	VoiceLib instance = (VoiceLib)Native.loadLibrary(filePath, VoiceLib.class);
	
	int MSPLogin(String usr, String pwd, String params);
	
    int QISRBuildGrammar (String cs2, String grm_content, int grm_cnt_len, String grm_build_params, GrammarCallBack grammarCallBack, Pointer pointer);
    
    String QISRSessionBegin(String grammarList, String asr_params, IntByReference errcode);
    
    int QISRAudioWrite(String session_id, byte[] every, int waveLen, int audioStatus, IntByReference ep_status1, IntByReference rec_status1);
	
    String QISRGetResult(String session_id, IntByReference rss_status1, int waitTime, IntByReference err);
	
    int  QISRSessionEnd(String sessionID, String hints);
    
   // int GrammarCallBack(int ecode, String info, UserData udata);
    
	
    int  MSPLogout();

	



	
	

	
	
	
}

【这里的参数我都已经改好啦,等下到具体功能再解释】

然后写语法的

//重要分割线*********************************//
在讯飞提供的例子中,有一个回调函数和一个构建语法函数,都要改写,怎么办呢?
回调函数我封装到一个Util中(后面的词典回调一起了)

package com.xinzhi;


import com.sun.jna.Callback;
import com.sun.jna.Pointer;

public  class CallbackUtil {	

	//语法回调函数的接口
	 public static interface GrammarCallBack extends Callback{
		 
	    	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) {
			UserData g_udata=Util.fromPointer(udata, 0);
			if (null != g_udata) {
				
				xMsc.asr_data.build_fini = com.szxinfo.recognizer.xMsc.MSP_FAILED;
				xMsc.asr_data.errcode = ecode;
			} 
			if (com.szxinfo.recognizer.xMsc.MSP_SUCCESS == ecode && null != info) {
				System.out.println("构建语法成功! 语法ID:"+info);
				if (null != g_udata) {
					xMsc.asr_data.grammar_id=info;
				}
			}
			else {
				System.out.println("构建语法失败!错误代码:"+ ecode);
			}			
			return 0;
		}
		 
	 }
	 
}


里面还有错误啊,回头来改!
【终板ps】这里面的错误已经全部修改完成

其中void,我使用Pointer来模拟*

再来写语法的

//构建离线识别语法网络
	@SuppressWarnings("finally")
	public static int build_grammar(UserData asr_data2) {
		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_content中
		try {
			FileInputStream in = new FileInputStream(grm_file);
			InputStreamReader input = new InputStreamReader(in);
			char[] cbuf = new char[(int) grm_file.length()];
			@SuppressWarnings("unused")
			int data=0;
			data = input.read(cbuf);
			// 构建语法的内容传的是字符串,将char[]转成String
			StringBuilder builder = new StringBuilder();
			for (char c : cbuf) {
				builder.append(c);
			}
			grm_content = builder.toString();
			// 释放资源
			in.close();
			input.close();
			grm_file = null;

			// 初始化构建语法参数
			grm_build_params = "engine_type=local,asr_res_path=" + ASR_RES_PATH 
					+ ",sample_rate=" + SAMPLE_RATE_16K
					+ ",grm_build_path=" + GRM_BUILD_PATH + ",";

			// 实例化回调函数对象
			CallbackUtil.GrammarCallBack callback = new CallbackUtil.GrammarCallBack_Realize();
			// 语法长度为内容的长度
			grm_cnt_len = grm_content.length();
			// 传入参数,构建语法
			ret = VoiceLib.instance.QISRBuildGrammar("bnf", grm_content, grm_cnt_len, grm_build_params, callback,
					asr_data2.getPointer());

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			return ret;
		}
	}

QISRBuildGrammar(“bnf”, grm_content, grm_cnt_len, grm_build_params, callback, udata1);
主要是给这个函数配参数,现在还没有测通,有测通的告诉我一声!
【终板ps】已经全部测通!这里得到的语法id必须是call,如果你是一串字符串,说明读的不对!

解释:
第一个 是文件类型,不用说固定的
第二个 是语法内容,我用了文件缓冲读字符,按行读取字符,注意用的是StringBuilder,因为要拼接,你直接用String卡死了不要找我哈。
第三个 是文件长度,length一下就好
第四个 是参数,这里初始化直接赋值就好,注意字符串凭借的引号问题
第五个就是回调函数
第六个是那个无定向指针

现在报参数无效,错误码23002,估计是后面两个还不行,需要继续调整,知道的指导一下,我继续调试了!
【终板PS】没有正确的读call.bnf,终板更新已解决

最后是识别,这里坑太多,不会的下面留言

// 进行离线语法识别
	public static int run_asr(UserData udata) {
		String asr_params;
		String rec_rslt = null;
		String asr_audiof = null;

		long pcm_count = 0;
		long pcm_size;
		@SuppressWarnings("unused")
		int last_audio = 0;
		int aud_stat = 1;

		asr_audiof = get_audio_file();

		f_pcm = new File(asr_audiof);

		if (!f_pcm.exists()) {
			System.out.println("打开语音文件失败!\n");
			run_error();
		}
		pcm_size = f_pcm.length();

		try {
			@SuppressWarnings("resource")
			InputStream in = new FileInputStream(f_pcm);
			pcm_data = new byte[(int) pcm_size];
			@SuppressWarnings("unused")
			int n = 0;
			n = in.read(pcm_data);

		} catch (Exception e1) {
			e1.printStackTrace();
		}

		// 离线语法识别参数设置
		asr_params = "engine_type=local,asr_res_path=" + ASR_RES_PATH + ",sample_rate=" + SAMPLE_RATE_16K
				+ ",grm_build_path=" + GRM_BUILD_PATH + ",local_grammar=" + udata.grammar_id
				+ ",result_type=plain,result_encoding=GB2312,";

		session_id = VoiceLib.instance.QISRSessionBegin(null, asr_params, err);
		if (null == session_id) {
			run_error();
		}

		System.out.println("开始识别...\n");

		while (true) {
			int len = 6400;

			if (pcm_size < 12800) {
				len = (int) pcm_size;
				last_audio = 1;
			}
			aud_stat = MSP_AUDIO_SAMPLE_CONTINUE;// 0x02
			if (0 == pcm_count) {
				aud_stat = MSP_AUDIO_SAMPLE_FIRS;// 0x01
			}
			if (len <= 0) {
				break;
			}
			System.out.print(">");
		

			errcode = VoiceLib.instance.QISRAudioWrite(session_id, pcm_data, len, aud_stat, ep_status, rec_status);

			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}


			if (MSP_SUCCESS != errcode) {
				run_error();
				break;
			}
			pcm_count += (long) len;
			pcm_size -= (long) len;

			// 检测到音频结束
			if (MSP_EP_AFTER_SPEECH == ep_status.getValue()) {
				break;
			}
		}

		// 主动点击音频结束
		VoiceLib.instance.QISRAudioWrite(session_id, null, 0, MSP_AUDIO_SAMPLE_LAST, ep_status, rec_status);
		pcm_data = null;


		// 获取识别结果
		while (MSP_REC_STATUS_COMPLETE != rss_status1.getValue() && MSP_SUCCESS == err.getValue()) {

			rec_rslt = VoiceLib.instance.QISRGetResult(session_id, rss_status1, 0, err);
		}

		System.out.println("\n识别结束:\n");
		System.out.println("=============================================================\n");
		if (null != rec_rslt) {

			String str = rec_rslt.substring(rec_rslt.indexOf("input=") + 6);
			System.out.println("识别结果为:\n" + str);
		} else {
			System.out.println("没有识别结果!");
			System.out.println("=============================================================\n");
			run_exit();
		}
		return MSP_SUCCESS;
	}

附加几个小方法:

	public static void run_error() {
		if (null != pcm_data) {
			pcm_data = null;
		}
		if (null != f_pcm) {
			f_pcm = null;
		}
	}

	public static int run_exit() {
		VoiceLib.instance.QISRSessionEnd(session_id, null);
		return errcode;
	}

至此,全部完工!
识别结果如下:
在这里插入图片描述

代码是plain解析的,截图是xml的,改下参数就好,编码别动,GB2312,不要utf-8

猜你喜欢

转载自blog.csdn.net/weixin_43112746/article/details/82887426