帖子

Java通过JNA&麦克风调离线命令词识别

[复制链接]

该用户从未签到

2490 159****1928 发表于 2020-7-27 16:20:13 1#
while(true){
   放置资源(dll、jet、日志配置);
   通过JNA加载dll资源;
   登录MSPLogin;
   构建语法QISRBuildGrammar;
   更新语法词典QISRUpdateLexicon;
   开始会话QISRSessionBegin;
   while(true){
        写入音频QISRAudioWrite;
        if(持续大于6S)跳出循环;
   }
   获取识别结果QISRGetResult;
   结束会话QISRSessionEnd;
   退出MSPLogout
}

1.dll方法定义类
  1. package com.day.iFlyInterface.commonUtil.dll.asr;
  2. import java.util.Scanner;
  3. import javax.sound.sampled.AudioFormat;
  4. import javax.sound.sampled.AudioSystem;
  5. import javax.sound.sampled.DataLine;
  6. import javax.sound.sampled.TargetDataLine;
  7. import com.sun.jna.Library;
  8. import com.sun.jna.Native;
  9. import com.sun.jna.Pointer;
  10. import com.sun.jna.ptr.IntByReference;
  11. public class OfflineWindowsAsr {
  12.         /* char * 对应String ;;;int *对应IntByReference
  13.          * char * 对应String ;;;int 对应int;;;
  14.          * void * 对应Pointer;;;char *对应String ;;;int * 对应IntByReference;;;
  15.          * char * 对应String;;;
  16.          */
  17.         public interface MyDllInterface extends Library {
  18.                 MyDllInterface INSTANCE = (MyDllInterface)Native.loadLibrary("Asr_x64", MyDllInterface.class);
  19.                 //登录
  20.                 public int MSPLogin(String usr,String pwd,String params);
  21.                 //开始一次语音识别。
  22.                 public String QISRSessionBegin(String grammarList,String params,IntByReference errorCode);
  23.                 //写入本次识别的音频
  24.                 public int QISRAudioWrite(String sessionID, byte[] byteArrayAudioData,int waveLen,int audioStatus,
  25.                                 IntByReference epStatus,IntByReference recogStatus);
  26.                 //获取识别结果。
  27.                 public String QISRGetResult(String sessionID,IntByReference rsltStatus,int waitTime,
  28.                                 IntByReference errorCode);
  29.                 //结束本次语音识别。
  30.                 public int QISRSessionEnd(String sessionID,String hints);
  31.                 //获取当次语音识别信息,如上行流量、下行流量等
  32.                 public int QISRGetParam(String sessionID,String paramName,String paramValue,
  33.                                 IntByReference valueLen);
  34.                 //构建语法,生成语法ID。
  35.                 public int QISRBuildGrammar(String grammarType,String grammarContent,int grammarLength,
  36.                                 String params,GrammarCallBack grammarCallBack,Pointer userData);
  37.                 //更新本地语法词典。
  38.                 public int QISRUpdateLexicon(String lexiconName,String lexiconContent,int lexiconLength,
  39.                                 String params,LexiconCallBack callback,Pointer userData);
  40.                 //退出
  41.                 public int MSPLogout();
  42.         }
  43.         //录音相关参数
  44.         static AudioFormat audioFormat;
  45.         public static TargetDataLine targetDataLine;
  46.         private static final int CHUNCKED_SIZE = 1280;
  47.         public static void main(String args[]) throws Exception{
  48.                 System.out.println("y开始体验离线命令识别,n结束离线命令识别");
  49.                 Scanner input = new Scanner(System.in);
  50.                 String Sinput = input.next();
  51.                 long testtime = System.currentTimeMillis();
  52.                 if(Sinput.equals("y")){
  53.                         audioFormat = getAudioFormat(audioFormat);//构造具有线性 PCM 编码和给定参数的 AudioFormat。
  54.                         DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
  55.                         targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
  56.                         OfflineWindowsAsr offlineWindowsAsr=new OfflineWindowsAsr();
  57.                         AsrThread myThread=new AsrThread(offlineWindowsAsr);
  58.                         myThread.start();
  59.                         Scanner input_2 = new Scanner(System.in);
  60.                         String Sinput_2 = input_2.next();
  61.                         if(Sinput_2.equals("n")){
  62.                                 System.out.println("离线命令识别"+(System.currentTimeMillis()-testtime)/1000+"秒!");
  63.                                 System.exit(0);
  64.                         }
  65.                 }
  66.         }
  67.         //构造线程参数
  68.         private static AudioFormat getAudioFormat(AudioFormat audioFormat) {
  69.                 audioFormat=new AudioFormat(16000F, 16, 1,true,false);
  70.                 // true,false 指示是以 big-endian 顺序还是以 little-endian 顺序存储音频数据。
  71.                 return audioFormat;//构造具有线性 PCM 编码和给定参数的 AudioFormat。
  72.         }
  73. }

复制代码


2.麦克风录音线程
  1. package com.day.iFlyInterface.commonUtil.dll.asr;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileNotFoundException;
  6. import java.io.IOException;
  7. import java.io.InputStreamReader;
  8. import java.util.Arrays;

  9. import javax.sound.sampled.AudioInputStream;

  10. import com.day.iFlyInterface.commonUtil.dll.asr.OfflineWindowsAsr.MyDllInterface;
  11. import com.sun.jna.Memory;
  12. import com.sun.jna.Pointer;
  13. import com.sun.jna.ptr.IntByReference;
  14. public class AsrThread  extends Thread{
  15.         OfflineWindowsAsr offlineWindowsAsr=null;
  16.         AsrThread(OfflineWindowsAsr offlineWindowsAsr){
  17.                 this.offlineWindowsAsr=offlineWindowsAsr;
  18.         }
  19.         public void run(){
  20.                 while(true){
  21.                         try{
  22.                                 //String login_config="appid =写您自己的appid; //登录参数
  23.                                 String login_config = "appid = 你自己的, work_dir = ./dllLib/asr";//获取当前目录,注意存放路径
  24.                                 //第一个参数为用户名,第二个参数为密码,传NULL即可,第三个参数是登录参数
  25.                                 int ret = MyDllInterface.INSTANCE.MSPLogin(null,null,login_config);
  26.                                 //System.out.println("登录返回参数:"+ret);

  27.                                 //System.out.println("构建离线识别语法网络...");
  28.                                 //读取call.bnf的内容
  29.                                 FileInputStream fileInputStream=new FileInputStream("./call.bnf");
  30.                                 //因为call.bnf是ANSI编码,所以这里指定GBK
  31.                                 InputStreamReader inputStreamReader =new InputStreamReader (fileInputStream,"GB2312");
  32.                                 BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
  33.                                 String grammarContent="";
  34.                                 String temp="";
  35.                                 while((temp=bufferedReader.readLine())!=null){
  36.                                         grammarContent=grammarContent+temp+"\n";
  37.                                 }
  38.                                 //System.out.println("语法内容:\n"+grammarContent);

  39.                                 //这里长度一定要用grammarContent.getBytes().length
  40.                                 int grammarLength=grammarContent.getBytes().length;
  41.                                 //System.out.println("语法内容长度"+grammarLength);
  42.                                 GrammarCallBack grammarCallBack=new GrammarCallBackImpl();
  43.                                 Pointer userData=new Memory(1000000);
  44.                                 String params="engine_type = local,asr_res_path = fo|res/asr/common.jet,sample_rate = 16000,grm_build_path = res/asr/GrmBuilld_x64,";
  45.                                 ret=MyDllInterface.INSTANCE.QISRBuildGrammar("bnf",grammarContent,grammarLength,params,grammarCallBack,userData);
  46.                                 //System.out.println("构建语法返回的参数"+ret);
  47.                                 Thread.sleep(500);

  48.                                 //System.out.println("更新离线语法词典...");
  49.                                 String lexiconName="contact";
  50.                                 String lex_content="丁伟\n黄辣椒";
  51.                                 int lexiconLength=lex_content.getBytes().length;
  52.                                 //System.out.println(lexiconLength);
  53.                                 String lexParams="engine_type=local,asr_res_path = fo|res/asr/common.jet, sample_rate = 16000,grm_build_path =res/asr/GrmBuilld_x64, grammar_list =call";
  54.                                 LexiconCallBack callback=new LexiconCallBackImpl();
  55.                                 Pointer lexUserData=new Memory(968000);
  56.                                 //ret=MyDllInterface.INSTANCE.QISRUpdateLexicon(lexiconName,lex_content,lexiconLength,lexParams,callback,lexUserData);
  57.                                 //System.out.println("更新离线语法词典返回的参数"+ret);
  58.                                 Thread.sleep(500);
  59.                                 
  60.                                 IntByReference errorCode = new IntByReference(-1);
  61.                                 params="engine_type = local,asr_res_path = fo|res/asr/common.jet, sample_rate = 16000,grm_build_path = res/asr/GrmBuilld_x64, local_grammar = call,result_type = json, result_encoding = UTF8,";
  62.                                 String session_id= MyDllInterface.INSTANCE.QISRSessionBegin(null,params, errorCode);
  63.                                 //System.out.println("返回的会话id:"+session_id);

  64.                                 IntByReference epStatus=new IntByReference(-1);
  65.                                 IntByReference recogStatus=new IntByReference(-1);
  66.                                 File file=new File("./zMusic/pcm/Asr/Software.pcm");
  67.                                 int frameSize = 10*640; //每次发送音频大小
  68.                                 int intervel = 160;
  69.                                 int MSP_AUDIO_SAMPLE_INIT = 0x00;//初始化帧
  70.                                 int SP_AUDIO_SAMPLE_FIRST = 0x01;//第一帧
  71.                                 int MSP_AUDIO_SAMPLE_CONTINUE  = 0x02;//中间帧
  72.                                 int MSP_AUDIO_SAMPLE_LAST  = 0x04;//最后帧
  73.                                 int sendStage=0x01;
  74.                                 Pointer waveData = new Memory(96800);
  75.                                 long startTime=System.currentTimeMillis();
  76.                                 //System.out.println(startTime);
  77.                                 System.out.println("请开始说话...");
  78.                                 Thread.sleep(500);
  79.                                 try (FileInputStream fs = new FileInputStream(file)) {
  80.                                         while(true){
  81.                                                 long endTime=System.currentTimeMillis();
  82.                                                 //System.err.println(fs.available());
  83.                                                 byte[] byteArrayAudioData = new byte[frameSize];
  84.                                                 offlineWindowsAsr.targetDataLine.open(offlineWindowsAsr.audioFormat);
  85.                                                 offlineWindowsAsr.targetDataLine.start();
  86.                                                 //int len =fs.read(buffer);
  87.                                                 int len = new AudioInputStream(offlineWindowsAsr.targetDataLine).read(byteArrayAudioData);
  88.                                                 if(len!=-1){
  89.                                                         /*byte[] tempAnother=Arrays.copyOf(buffer, len);
  90.                                                         for(long z=0;z<tempAnother.length;z++){
  91.                                                                 waveData.setByte(z, tempAnother[(int) z]);
  92.                                                         }*/
  93.                                                         //System.out.println(tempAnother.toString());
  94.                                                         //System.out.println(len);
  95.                                                         //System.out.println(waveData.nativeValue(waveData));
  96.                                                         ret = MyDllInterface.INSTANCE.QISRAudioWrite(session_id,byteArrayAudioData,len,sendStage,epStatus,recogStatus);
  97.                                                         //System.err.println(epStatus.getValue()+"----"+recogStatus.getValue());
  98.                                                         //System.err.println(sendStage);
  99.                                                 }
  100.                                                 sendStage=0x02;
  101.                                                 //endTime-startTime>6000    ;  len==-1 //30000不说话报错10114
  102.                                                 if (endTime-startTime>6000) {
  103.                                                         System.out.println("本次识别结束...");
  104.                                                         //System.out.println("空帧....");
  105.                                                         //停止录音
  106.                                                         offlineWindowsAsr.targetDataLine.stop();
  107.                                                         offlineWindowsAsr.targetDataLine.close();
  108.                                                         byte b='b';
  109.                                                         sendStage=0x04;
  110.                                                         waveData=new Memory(100000);
  111.                                                         waveData.setByte(0, b);
  112.                                                         ret = MyDllInterface.INSTANCE.QISRAudioWrite(session_id,"".getBytes(),0,sendStage,epStatus,recogStatus);
  113.                                                         //System.out.println("csid="+session_id+",错误码"+err_code+",长度"+len+",aus="+sendStage);
  114.                                                         System.out.println("最后一帧返回的错误码:"+ret+",即将执行退出...");
  115.                                                         //System.err.println(epStatus.getValue()+"----"+recogStatus.getValue());
  116.                                                         //System.err.println(sendStage);
  117.                                                         //Thread.sleep(10000);
  118.                                                         IntByReference rsltStatus=new IntByReference(-1);
  119.                                                         //这里必须停顿,必须停顿些时间,才能获取识别结果,否则为null
  120.                                                         Thread.sleep(2000);
  121.                                                         //System.err.println(recogStatus.getValue());
  122.                                                         //获取结果
  123.                                                         String result=MyDllInterface.INSTANCE.QISRGetResult(session_id,rsltStatus,0,errorCode);
  124.                                                         System.err.println("命令词识别结果:"+result);
  125.                                                         //System.err.println("n错误码"+errorCode.getValue());
  126.                                                         //终止
  127.                                                         MyDllInterface.INSTANCE.QISRSessionEnd(session_id, "正常终止");
  128.                                                         break;  //文件读完,跳出循环
  129.                                                 }
  130.                                                 if(ret!=0){
  131.                                                         System.out.println("出错了..."+ret);
  132.                                                 }else{
  133.                                                         //System.out.println("没问题..."+ret);
  134.                                                 }
  135.                                                 //如果用麦克风唤醒,则不用传这个参数,会提高识别率
  136.                                                 Thread.sleep(intervel); //模拟人说话时间间隙,10帧的音频时长为200ms
  137.                                         }
  138.                                 } catch (FileNotFoundException e) {
  139.                                         e.printStackTrace();
  140.                                 } catch (IOException e) {
  141.                                         e.printStackTrace();
  142.                                 }
  143.                                 int tempCode=MyDllInterface.INSTANCE.MSPLogout();
  144.                                 System.out.println("最终的执行退出,返回的错误码:"+tempCode);
  145.                         }catch(Exception e){
  146.                                 e.printStackTrace();
  147.                         }
  148.                 }
  149.         }
  150. }
复制代码




3.构建语法接口定义
  1. package com.day.iFlyInterface.commonUtil.dll.asr;
  2. import com.sun.jna.Pointer;
  3. import com.sun.jna.win32.StdCallLibrary.StdCallCallback;
  4. public interface GrammarCallBack extends StdCallCallback {
  5.         public int build_grm_cb(int errorCode,String info,Pointer userData);
  6. }
复制代码




4.构建语法接口实现类
  1. package com.day.iFlyInterface.commonUtil.dll.asr;
  2. import com.sun.jna.Pointer;
  3. public class GrammarCallBackImpl implements GrammarCallBack{
  4.         public int build_grm_cb(int errorCode,String info, Pointer userData) {
  5.                 System.err.println("构建语法返回的ID信息...:"+info+"错误码"+errorCode+"\n");
  6.                 return 0;
  7.         }        
  8. }
复制代码




5.更新词典接口
  1. package com.day.iFlyInterface.commonUtil.dll.asr;
  2. import com.sun.jna.Pointer;
  3. import com.sun.jna.win32.StdCallLibrary.StdCallCallback;
  4. public interface LexiconCallBack extends StdCallCallback{
  5.         public int LexiconCallBack(int errorCode,String info,Pointer userData);        
  6. }
复制代码



6.更新词典实现类
  1. package com.day.iFlyInterface.commonUtil.dll.asr;
  2. import com.sun.jna.Pointer;
  3. public class LexiconCallBackImpl implements LexiconCallBack{
  4.         @Override
  5.         public int LexiconCallBack(int errorCode, String info, Pointer userData) {
  6.                 System.err.println("更新词典返回的信息...:"+info+"错误码:"+errorCode+"\n");
  7.                 return 0;
  8.         }
  9. }
复制代码