1,掉后端接口选择模式。
2,分为实时和非实时语音识别。(没有后端配合看不到效果展示)
3,点击开始,ws建联,sendData对获取的数据处理,分包处理每包1024.tmparr式处理过的音频。
4,点击关闭,链接断开。
5,再次点击开始,刷新文本框。
6,上传音频文件识别。(file音频文件做处理模拟蹦字效果)
7,再次使用录音功能时,先清除file上传的内容。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta name="apple-mobile-web-capable" content="yes">
        <title>ASR demo</title>
        <style type="text/css">
            html body {
                margin: 0;
                padding: 0;
            }
            .manage {
                display: flex;
                justify-content: space-between;
            }
            .comments {
                width: 100%; /*自动适应父布局宽度*/
                overflow: auto;
                word-break: break-all;
                /*在ie中解决断行问题(防止自动变为在一行显示,主要解决ie兼容问题,ie8中当设宽度为100%时,文本域类容超过一行时,
                当我们双击文本内容就会自动变为一行显示,所以只能用ie的专有断行属性“word-break或word-wrap”控制其断行)*/
            }
            img{
                display: none;
            }
           
        </style>
    </head>
    <body>
        <div class="manage">
            <div id="controls">
                <button id="intercomBegin">开始</button>
                <button id="intercomEnd">关闭</button>
            </div><br>
            <!-- <input type="button" onclick="" value="下载"> -->
            <a href="音频地址" downlown=""></a>
           <!-- 上传音频 accept打开系统文件目录  -->
            <div class="file">
                <form id="file-rec" method="POST" action="#" target="myIframe" enctype="multipart/form-data">
                    <input type="file" id="fileInput" accept=".pcm,.wav" multiple="multiple"/>
                    <div id="audio1"></div>  <!-- 音频播放器 -->
                    <button id="upload">上传</button>

                </form>
                <iframe style="display: none" id="myIframe" name="myIframe" class="iframes"></iframe>
            </div>
        </div>
        <div>选择模式</div>
        <form id="article-foot" class="article-foot"> 
        </form><br/>

    </body>
    <!-- 引入组件 -->
    <script src="./js/jquery.min.js"></script> 
    <script src="./js/toastr.min.js"></script>   <!-- 基于jquery的非阻塞的消息提示插件 -->
    <link rel="stylesheet" href="./css/toastr.min.css">  
    <script src="./js/HZRecorder.js"></script>
    <!-- <script src="./js/audio.js"></script> -->


    <script type="text/javascript">
        var result = ''
        var textResult = document.getElementById("textResult");

        var begin = document.getElementById('intercomBegin');
        var end = document.getElementById('intercomEnd');
		
        var ws = null; //实现WebSocket 
        var record = null; //多媒体对象,用来处理音频
        var interval;   //定时器
        
        function init(rec) {  //init初始化
            record = rec;
            console.log(record)
        }
         //生成唯一标识符
        function uuid() {
            var s = [];
            var hexDigits = "0123456789abcdef";
            for (var i = 0; i < 36; i++) {
                s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
            }
            s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
            s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
            s[8] = s[13] = s[18] = s[23] = "-";

            var uuid = s.join("");
            return uuid;
        }
        uuid() // "ffb7cefd-02cb-4853-8238-c0292cf988d5"
        var uuid = uuid()
        //console.log(uuid)

    

   /*ajax请求数据选择模式  */
        window.onload=function() {
            $.ajax({
            type: "GET",
            async: false,
            url: "http://10.45.38.250:8998/asr/non_real/selectModel" ,
            dataType: "json",
                success: function (data) {
                    //console.log(data)
                    if (data.length == 0) {
                    //如果查出来的数据为空,则div隐藏
                        $(".article-foot").hide();
                    } else {
                        $(".article-foot").show();
                       // console.log(data)
                        var html = ''
                        $.each(data,function(i,n){
					    html += "<input type=\"checkbox\" name=\"items\" value=\""+n.type+"\">" + n.name + "</input>" + "<br>"
                         +"<textarea  id=\"textResult\" class=\"comments\" rows=\"10\" cols=\"10\"></textarea>"
                        
                       // console.log(html)

					});
                        $(".article-foot").html(html);
                        
                    }
                }
            });
        }
          /* message提示 */
          function showMessage(message,type,time) {
            let str = ''
            switch (type) {
                case 'success':
                    str = '<div class="success-message" style="width: 300px;height: 40px;text-align: center;background-color:#daf5eb;;color: rgba(59,128,58,0.7);position: fixed;left: 43%;top: 10%;line-height: 40px;border-radius: 5px;z-index: 9999">\n' +
                        '    <span class="mes-text">'+message+'</span></div>'
                    break;
                case 'error':
                    str = '<div class="error-message" style="width: 300px;height: 40px;text-align: center;background-color: #f5f0e5;color: rgba(238,99,99,0.8);position: fixed;left: 43%;top: 10%;line-height: 40px;border-radius: 5px;;z-index: 9999">\n' +
                        '    <span class="mes-text">'+message+'</span></div>'
            }
            $('body').append(str)
            setTimeout(function () {
                $('.'+type+'-message').remove()
            },time)
        }    
            /*
        * 开始对讲
        */
        begin.onclick = function() {
            navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
            //点击开始清空文本框
           // clearFile()
            result=''
            var comments = document.getElementsByClassName('comments')
            console.log(comments)
            for (var i = 0; i < comments.length; i++) {
                        comments[i].innerHTML = ''
                }     
            if (!navigator.getUserMedia) {
                alert('浏览器不支持音频输入');
            } else {
                navigator.getUserMedia({
                audio: true
            },
            //判断
            function(mediaStream) {
                init(new Recorder(mediaStream));
                console.log(mediaStream)
                console.log('开始对讲');
                showMessage('建立连接中','success',1000)
                useWebSocket();
                //如果ws链接还在,就断开ws
                if(ws == true) {
                    ws.close()
                }

            },

            function(error) {
                console.log(error);
                switch (error.message || error.name) {
                    case 'PERMISSION_DENIED':  
                    case 'PermissionDeniedError':  
                        console.info('用户拒绝提供信息。');  
                        break;  
                    case 'NOT_SUPPORTED_ERROR':  
                    case 'NotSupportedError':  
                        console.info('浏览器不支持硬件设备。');  
                        break;  
                    case 'MANDATORY_UNSATISFIED_ERROR':  
                    case 'MandatoryUnsatisfiedError':  
                        console.info('无法发现指定的硬件设备。');  
                        break;  
                        default:  
                        console.info('无法打开麦克风。异常信息:' + (error.code || error.name));  
                        break;  
                        }  
                    }
                )
            }
        }
 
        /*
        * 关闭对讲
        */
        end.onclick = function() {

            if (ws) {
              
                ws.send(JSON.stringify({'msgType': 'audioEnd'}))
                console.log('关闭对讲以及WebSocket');
                //ws.send(JSON.stringify({'msgType': 'end'}))
               //ws.close();
                record.stop();
               
            }
            clearInterval(interval)
        }

        /* file文件上传 */   
        //上传音频
        $("#fileInput").change(function(){
            var file = $("#fileInput").get(0).files;
            console.log()
            console.log($("#fileInput").get(0))
            console.log(file);
            for(var i = 0;i<file.length;i++){
                var path = URL.createObjectURL(file[i]); //用于创建 URL 的 File 对象.
                //console.log(path)
                //console.log(file[i])
            }
        });

        $("#upload").click(function(){
           // console.log((new Date().getTime()))
            result = ''
            var comments = document.getElementsByClassName('comments')
            console.log(comments)
            for (var i = 0; i < comments.length; i++) {
                        comments[i].innerHTML = ''
                } 
            //ajax上传至后台
             // uploadAudio(formData,arr);
            //调用websocket
            useWebSocket();
        });
        /* 清除file上传的文件内容 */
        function clearFile(){          
            $('#fileInput').val('')
            var file = $("#fileInput").get(0).files
            //console.log(file)
        }


        //Recorder
        var Recorder = function(stream) {
            var sampleBits = 16; //输出采样数位 8, 16
            var sampleRate = 16000; //输出采样率(单位时间内对模拟信号采样的多少)
            var context = new AudioContext();
            var audioInput = context.createMediaStreamSource(stream);
            var recorder = context.createScriptProcessor(4096, 1, 1);
            //录音模型
            var audioData = {
                size: 0, //录音文件长度
                buffer: [], //录音缓存
                inputSampleRate: 16000, //输入采样率
                inputSampleBits: 16, //输入采样数位 8, 16
                outputSampleRate: sampleRate, //输出采样数位
                oututSampleBits: sampleBits, //输出采样率
                clear: function() {
                    this.buffer = [];
                    this.size = 0;
                },
                input: function(data) {
                    this.buffer.push(new Float32Array(data));
                    this.size += data.length;		
                },
                compress: function() { //合并压缩
                    //合并
                    var data = new Float32Array(this.size);
                    var offset = 0;
                    for (var i = 0; i < this.buffer.length; i++) {
                        data.set(this.buffer[i], offset);
                        offset += this.buffer[i].length;
                    }
                    //压缩
                    var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
                    //console.log(compression)
                    var length = data.length / compression;
                    var result = new Float32Array(length);
                    var index = 0,
                    j = 0;
                    while (index < length) {
                        result[index] = data[j];
                        j += compression;
                        index++;
                    }
                    return result;
                },
               encodePCM: function() {
                    var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
                    var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
                    var bytes = this.compress();
                    var dataLength = bytes.length * (sampleBits / 8);
                    var buffer = new ArrayBuffer(dataLength);
                    var data = new DataView(buffer);
                    var offset = 0;
                    for (var i = 0; i < bytes.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                     //数据视图指定字节偏移量位置处设置 Int16 值
                        data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                    }
                    return new Blob([data]);
                } 
            };
 
            var sendData = function() { //对以获取的数据进行处理(分包),每包最大1024
                var reader = new FileReader(); // FileReader接口提供了读取文件的方法和包含读取 结果的事件模型
                reader.onload = e => {
                    var outbuffer = e.target.result; //e是监听到时间回调返回的event对象,event.target 是事件属性,可返回事件的目标节点
                    var arr = new Int8Array(outbuffer);
                    if (arr.length > 0) {
                        var tmparr = new Int8Array(1024); //Int8Array:8位有符号整数
                        var j = 0;
                        for (var i = 0; i < arr.byteLength; i++) {
                            tmparr[j++] = arr[i];
                            if (((i + 1) % 1024) == 0) {
                            
                                ws.send(tmparr); //处理过的音频数据

                                if (arr.byteLength - i - 1 >= 1024) {
                                    tmparr = new Int8Array(1024);
                                } else {
                                    tmparr = new Int8Array(arr.byteLength - i - 1);
                                }
                                j = 0;
                            }
                            if ((i + 1 == arr.byteLength) && ((i + 1) % 1024) != 0) {
                        
                               ws.send(tmparr);
                            }
                        }
                    }
                };
                reader.readAsArrayBuffer(audioData.encodePCM());
                audioData.clear();//每次发送完成则清理掉旧数据
            };
			
            this.start = function() {
                audioInput.connect(recorder);
                recorder.connect(context.destination);
            }
 
            this.stop = function() {
                recorder.disconnect();
            }
 
            this.getBlob = function() {
                return audioData.encodePCM();
            }
            this.clear = function() {
                audioData.clear();
            }
			
            recorder.onaudioprocess = function(e) {
                var inputBuffer = e.inputBuffer.getChannelData(0);
                audioData.input(inputBuffer);
        
                sendData();
                console.log('发送音频流')
            }
        }
        /*  */

        /*
        * WebSocket
        */
        function useWebSocket() {
            ws = new WebSocket("ws://10.45.38.250:8998/asr/audio/recognition");
           // console.log(ws)
           // ws.binaryType = 'arraybuffer'; //传输的是 ArrayBuffer 类型的数据
            ws.onopen = function() { //连接成功建立的回调方法
               // console.log((new Date().getTime()))
                ws.binaryType='text'
                var s = $("input[name='items']")
                var arr= []
                s.each(function(i) {
                    if(this.checked == true) {
                        arr.push(this.value)
                    }
                })
                ws.send(JSON.stringify({'asrType' : arr, 'msgType' : 'init' }))
                console.log('发送初始化');
                //sleep(5000)
                //console.log(1111111)     
                if (ws.readyState == 1) { //ws进入连接状态,则每隔40毫秒发送一包数据
                    var file = $("#fileInput").get(0).files;  //判断音频文件的
                   // console.log(file[0])  //undefined+
                  if("undefined" == typeof file[0]) {
                    interval =setInterval(() => {
                        record.start()                       
                    },40)
                  }else {
                    var fr = new FileReader();
                    var filename = file[0].name;
                    var buf
                    fr.readAsArrayBuffer(file[0]);
                    fr.addEventListener("loadend",(e) => {
                        buf = e.target.result;
                        //console.log(buf.byteLength)
                        //var temp =0;                       
                        for (var i = 0; i < buf.byteLength; i += 3840) {
                            var arr = buf.slice(i,i+3840);
                           // console.log(arr)
                            //temp = i;
                            ws.send(arr)
                            //console.log(arr.byteLength)
                        }
                        //sleep(buf.byteLength / 1280 * 40 )
                        ws.send(JSON.stringify({'msgType': 'audioEnd'}))
                        console.log('发送sendEnd')
                        //清除file文件
                        clearFile()
                    },false);
                   
                  }                 
                }
            };
			//接收到消息的回调方法
            ws.onmessage = function(MesssageEvent) {
                //返回结果
                //console.log((new Date().getTime()))
                //console.log(MesssageEvent)
                var jsonStr = MesssageEvent.data;
                //console.log(jsonStr)
                var jso = JSON.parse(jsonStr)
                //console.log(jso)
                var inputs = document.getElementsByName("items")
                for (var i = 0; i < inputs.length; i ++) {  //遍历数组
                    if(inputs[i].value == jso.type){
                        if(jso.type == 'real'){
                            if(jso.action == 'final') {
                                //console.log(result)
                                result =( result + jso.result) + "\r\n"  //回车和换行
                            }
                            if(jso.action == 'partial') {
                                inputs[i].nextElementSibling.nextElementSibling.innerHTML  = result + jso.result //获取下个兄弟元素
                            } 
                        }
                        if(jso.type == 'non_real'){
                            if(jso.action == 'final'){
                                inputs[i].nextElementSibling.nextElementSibling.innerHTML += jso.result + "\r\n"
                            }
                        }
                    }
                }
            }
		//连接关闭的回调方法
            ws.onerror = function(err) {
                console.info(err)
                textResult.innerHTML = ''
            }
            //关闭websocket连接
            ws.onclose = function (msg) {
                console.info(msg);
        };
        //文本框根据输入内容自适应高度
        var autoTextarea = function (elem, extra, maxHeight) {
        //判断elem是否为数组
        if (elem.length > 0) {
            for (var i = 0; i < elem.length; i++) {
                e(elem[i]);
            }
        } else {
            e(elem);
        }

        function e(elem) {
            extra = extra || 0;
            var isFirefox = !!document.getBoxObjectFor || 'mozInnerScreenX' in window,
                isOpera = !!window.opera && !!window.opera.toString().indexOf('Opera'),
                addEvent = function (type, callback) {
                    elem.addEventListener ?
                        elem.addEventListener(type, callback, false) :
                        elem.attachEvent('on' + type, callback);
                },
                getStyle = elem.currentStyle ? function (name) {
                    var val = elem.currentStyle[name];

                    if (name === 'height' && val.search(/px/i) !== 1) {
                        var rect = elem.getBoundingClientRect();
                        return rect.bottom - rect.top -
                            parseFloat(getStyle('paddingTop')) -
                            parseFloat(getStyle('paddingBottom')) + 'px';
                    }
                    ;

                    return val;
                } : function (name) {
                    return getComputedStyle(elem, null)[name];
                },
                minHeight = parseFloat(getStyle('height'));

            elem.style.resize = 'none';

            var change = function () {
                var scrollTop, height,
                    padding = 0,
                    style = elem.style;

                if (elem._length === elem.value.length) return;
                elem._length = elem.value.length;

                if (!isFirefox && !isOpera) {
                    padding = parseInt(getStyle('paddingTop')) + parseInt(getStyle('paddingBottom'));
                }
                ;
                scrollTop = document.body.scrollTop || document.documentElement.scrollTop;

                elem.style.height = minHeight + 'px';
                if (elem.scrollHeight > minHeight) {
                    if (maxHeight && elem.scrollHeight > maxHeight) {
                        height = maxHeight - padding;
                        style.overflowY = 'auto';
                    } else {
                        height = elem.scrollHeight - padding;
                        style.overflowY = 'hidden';
                    }
                    ;
                    style.height = height + extra + 'px';
                    scrollTop += parseInt(style.height) - elem.currHeight;
                    document.body.scrollTop = scrollTop;
                    document.documentElement.scrollTop = scrollTop;
                    elem.currHeight = parseInt(style.height);
                }
                ;
            };

            addEvent('propertychange', change);
            addEvent('input', change);
            addEvent('focus', change);
            change();
        }
    };
        }

    </script>
 
</html>