zoukankan      html  css  js  c++  java
  • 微信小程序语音与讯飞语音识别接口(Java),Kronopath/SILKCodec,ffmpeg处理silk,pcm,wav转换

    项目需求,需要使用讯飞的语音识别接口,将微信小程序上传的录音文件识别成文字返回

    首先去讯飞开放平台中申请开通语音识别功能

    在这里面下载sdk,然后解压,注意appid与sdk是关联的,appid在初始化接口时候需要

    由于是在Linux上开发,所以需要将.so文件和.dll文件上传到Linux服务器上安装的jdk/lib/amd64里面,要不会报引擎错误,window环境直接放在项目跟目录就行.

    由于微信小程序上传的文件格式是silk的,而讯飞接口能识别wav 格式的文件,所以需要将小程序上传的silk文件转成wav的格式

    由于小程序上传的silk文件是变异的silk(小程序上传的silk文件中在编码头多添加了一个字节)文件,所以需要将他处理成正常的silk文件

    由于项目是运行在Linux上,所以写了一个简单的shell脚本以供java程序调用处理

    这个脚本的作用是删除输入文件中#!SILK_V3所在行的第一个字节

    好了,文件处理完了,现在就是格式转换了

    经调研,发现一般是先将silk文件转换成pcm,这里使用的是Kronopath/SILKCodec,下载到linux服务器上,然后在SILK_SDK_SRC_ARM里执行

    make lib
    make decoder

    执行之后会生成命令行工具decoder

    使用方法:

    ./decoder  要转换文件.silk   要生成文件.pcm

    执行完上面代码就会生成.pcm文件,然后就是将pcm转成wav格式了,这里使用的是ffmpeg,没有安装的可以参考一下

    ubuntu14.04安装ffmpeg:http://blog.csdn.net/leezha/article/details/77849286

    阿里云linux安装ffmpeg:http://blog.csdn.net/baijinwen/article/details/77235725

    安装ffmpeg可能出现的问题:http://blog.51cto.com/zlyang/1709508

    为了保证语音识别的准确性,使用一下代码识别生成的wav文件,讯飞接口识别结果最好

    ffmpeg -f s16le -ar 12k -ac 2 -i /path/to/pcm -f wav -ar 16k -ac 1 /path/to/wav

    下面就是java的讯飞语音接口开发了,直接贴代码

    package com.example.utils;
    
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by songzs on 2017/12/12.
    * 封装的转码工具类 */ public class FFMPEGUtil { public static String silk2Pcm(String inputfile,String outputfile){ List<String> commend = new ArrayList<String>(); commend.add("/usr/local/silk2pcm_tool/SILKCodec/SILK_SDK_SRC_ARM/./decoder"); commend.add(inputfile); commend.add(outputfile); StringBuffer test=new StringBuffer(); for(int i=0;i<commend.size();i++) test.append(commend.get(i)+" "); System.out.println("decoder命令:"+test+""); exec(test); return outputfile; } public static String pcm2Wav(String inputfile,String outputfile){ //ffmpeg -f s16le -ar 12k -ac 2 -i /path/to/pcm -f wav -ar 16k -ac 1 /path/to/wav List<String> commend = new ArrayList<String>(); commend.add("ffmpeg"); commend.add("-f"); commend.add("s16le"); commend.add("-ar"); commend.add("12k"); commend.add("-ac"); commend.add("2"); commend.add("-i"); commend.add(inputfile); commend.add("-f"); commend.add("wav"); commend.add("-ar"); commend.add("16k"); commend.add("-ac"); commend.add("1"); commend.add(outputfile); StringBuffer test=new StringBuffer(); for(int i=0;i<commend.size();i++) test.append(commend.get(i)+" "); System.out.println("ffmpeg命令:"+test+""); exec(test); return outputfile; } public static String silk_remove_word(String filepath){ List<String> commend = new ArrayList<String>(); commend.add("/home/workspace/./test.sh"); commend.add(filepath); StringBuffer test=new StringBuffer(); for(int i=0;i<commend.size();i++) test.append(commend.get(i)+" "); System.out.println("test命令:"+test+""); exec(test); return filepath; } private static void exec(StringBuffer test){ try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec(test.toString()); InputStream stderr = proc.getErrorStream(); InputStreamReader isr = new InputStreamReader(stderr); BufferedReader br = new BufferedReader(isr); String line = null; while ( (line = br.readLine()) != null) ; } catch (Exception e) { e.printStackTrace(); } } }

     语音结果处理工具类(代码简陋,见谅)

    package com.example.utils;
    
    import com.alibaba.fastjson.JSON;
    
    import java.util.List;
    import java.util.Map;
    
    /**
     * Created by songzs on 2017/12/15.
     */
    public class SR2Words {
    
        public static String sr2words(String jsonString){
            StringBuffer sb = new StringBuffer();
            String[] split = jsonString.split("}]}]}");
            for (int i = 0; i < split.length; i++) {
                String s = split[i] + "}]}]}";
                System.out.println(s);
                Map parse = (Map) JSON.parse(s);
                List<Map> ws = (List<Map>) parse.get("ws");
                for (int i1 = 0; i1 < ws.size(); i1++) {
                    List<Map> cw = (List<Map>)ws.get(i1).get("cw");
                    String w = cw.get(0).get("w").toString();
                    sb.append(w);
                }
    
            }
            return sb.toString();
        }
    }

    小程序录音文件上传与讯飞语音识别

    package com.example.service.impl;
    
    import com.example.service.XunFeiService;
    import com.example.utils.FFMPEGUtil;
    import com.example.utils.SR2Words;
    import com.example.utils.SRTool;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    /**
     * Created by songzs on 2017/12/12.
     */
    @Service
    public class XunFeiServiceImpl implements XunFeiService {
    
        @Override
        public Map<String,String> speechRecognition(MultipartFile multi) {
            Map<String,String> map =new HashMap<>();
            UUID uuid = UUID.randomUUID();
            String path = "/home/workspace/audio";
            String fileName = uuid.toString()+".silk";
            //临时silk文件
            String tempFile = "/home/workspace/audio/"+uuid.toString()+".silk";
            //中间过渡pcm文件
            String pcmFile = "/home/workspace/audio/"+uuid.toString()+".pcm";
            //可识别的wav文件
            String wavFile = "/home/workspace/audio/"+uuid.toString()+".wav";
            File file = new File(path,fileName);
            try {
                multi.transferTo(file);
            } catch (IOException e) {
                e.printStackTrace();
            }
            /*移除临时silk文件首字节start*/
            //标准silk文件
            String silkFile = FFMPEGUtil.silk_remove_word(tempFile);
            /*移除临时silk文件首字节end*/
            //silk文件转换成pcm文件
            String silk2Pcm = FFMPEGUtil.silk2Pcm(silkFile, pcmFile);
            //pcm文件转换成wav文件
            String pcm2Wav = FFMPEGUtil.pcm2Wav(silk2Pcm, wavFile);
            //讯飞语音识别接口识别wav音频文件,转成文字返回
            SRTool sr = new SRTool();
            String words = null;
            try {
                words = sr.voice2words(pcm2Wav);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("讯飞识别的语音结果:"+words);
            if("".equals(words)){
                System.out.println("讯飞识别的语音结果:null");
                map.put("status","error");
                map.put("content","对不起,请您在描述一遍!");
                return map;
            }
            String result = SR2Words.sr2words(words);
            System.out.println("讯飞识别的语音结果:"+result);
            map.put("status","success");
            map.put("content",result);
            return map;
        }
    }

    讯飞语音识别工具类

    package com.example.utils;
    
    import com.iflytek.cloud.speech.*;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.ArrayList;
    
    /**
     * Created by songzs on 2017/12/4.
     */
    public class SRTool {
    
        private int perWaitTime = 100;
    
        private StringBuffer mResult = new StringBuffer();
    
        static {
            SpeechUtility.createUtility("appid=********");//申请的appid
        }
    
        public String voice2words(String fileName) throws InterruptedException, IOException {
            return to(fileName);
        }
    
        public String to(String fileName) throws InterruptedException, IOException {
    
            File file = new File(fileName);
            if(!file.exists()){
                throw new RuntimeException("要读取的文件不存在");
            }
            FileInputStream fis = new FileInputStream(file);
            int len = 0;
            byte[] buf = new byte[fis.available()];
            fis.read(buf);
            fis.close();
    
            //1.创建SpeechRecognizer对象
            SpeechRecognizer mIat = SpeechRecognizer.createRecognizer();
            //2.设置听写参数,详见《MSC Reference Manual》SpeechConstant类
            mIat.setParameter(SpeechConstant.DOMAIN, "iat");
            mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
            mIat.setParameter(SpeechConstant.ACCENT, "mandarin ");
            mIat.setParameter(SpeechConstant.AUDIO_SOURCE, "-1");
            //3.开始听写
            mIat.startListening(mRecoListener);
    
            //voiceBuffer为音频数据流,splitBuffer为自定义分割接口,将其以4.8k字节分割成数组
            ArrayList<byte[]> buffers = splitBuffer(buf, buf.length, 4800);
            for (int i = 0; i < buffers.size(); i++) {
                // 每次写入msc数据4.8K,相当150ms录音数据
                mIat.writeAudio(buffers.get(i), 0, buffers.get(i).length);
            }
            mIat.stopListening();
    
            while (mIat.isListening()) {
                Thread.sleep(perWaitTime);
            }
            return mResult+"";
        }
    
        /**
         * 将字节缓冲区按照固定大小进行分割成数组
         *
         * @param buffer 缓冲区
         * @param length 缓冲区大小
         * @param spsize 切割块大小
         * @return
         */
        private ArrayList<byte[]> splitBuffer(byte[] buffer, int length, int spsize) {
            ArrayList<byte[]> array = new ArrayList<byte[]>();
            if (spsize <= 0 || length <= 0 || buffer == null
                    || buffer.length < length)
                return array;
            int size = 0;
            while (size < length) {
                int left = length - size;
                if (spsize < left) {
                    byte[] sdata = new byte[spsize];
                    System.arraycopy(buffer, size, sdata, 0, spsize);
                    array.add(sdata);
                    size += spsize;
                } else {
                    byte[] sdata = new byte[left];
                    System.arraycopy(buffer, size, sdata, 0, left);
                    array.add(sdata);
                    size += left;
                }
            }
            return array;
        }
    
        //听写监听器
        private RecognizerListener mRecoListener = new RecognizerListener() {
            public void onResult(RecognizerResult results, boolean isLast) {
                System.out.println("Result:" + results.getResultString());
                mResult.append(results.getResultString());
            }
    
            //会话发生错误回调接口
            public void onError(SpeechError error) {
                System.out.println(error.getErrorCode()+"=========="+error.getErrorDesc());
                System.out.println(error);
            }
    
            //开始录音
            public void onBeginOfSpeech() {
            }
    
            //音量值0~30
            public void onVolumeChange(int volume) {
            }
    
            @Override
            public void onVolumeChanged(int i) {
    
            }
    
            @Override
            public void onEndOfSpeech() {
    
            }
    
            @Override
            public void onEvent(int i, int i1, int i2, String s) {
    
            }
        };
    }

    *小程序上传接口必须是https请求,所以可能需要搭建https,相关内容可以参考我上一篇文章

  • 相关阅读:
    数据库中的 索引
    JQuery中ajax请求
    如何优化广告,提高点击率
    常用的正则表达式
    全栈工程师之路中级篇之小程序开发前言
    ionic入门教程第四课使用$controllerProvider按需加载controller
    全栈工程师之路中级篇之小程序开发第一章第一节注册小程序
    ionic入门教程第三课在项目中使用requirejs分离controller文件和server文件
    ionic入门教程第五课举例子说明异步回调$q及$q在项目中的用法
    求两个整数的最大公约数和最小公倍数
  • 原文地址:https://www.cnblogs.com/SongG-blogs/p/8044474.html
Copyright © 2011-2022 走看看