浏览器录音发送二进制流案例(websocket通信)

  var begin = document.getElementsByClassName('start-button');
  var wsUrl = 'wss://ws-rtasr.hivoice.cn/v1/ws';//服务地址
  var wstime = '1626325549809'//访问时间戳
  var appkey = '45gn7md5n44aak7a57rdjud3b5l4xdgv75saomys'//用户的appkey
  var sign = 'EE1CB17678086F0ED8F83B22B31444045206138E6A67FDCE9331BF91B291F344'//签名
  var ws = null; //实现WebSocket 
  var record = null; //多媒体对象,用来处理音频
  var str = ''; //识别的文字
  var state = 'STOPING'; //状态
  var fixedArr = []; //固定翻译数组
  var fixedNum = -1; //固定翻译数组下标
  var flag = false; //自动提交下标
  var SubTxt = []; //固定翻译数组
  var timer;//定时器
  localStorage.setItem('SubTxt',JSON.stringify(SubTxt))
  function init(rec) {
    record = rec;
  }

  //录音对象
  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: 48000, //输入采样率
      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);
        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);
        // console.log(data);
        var offset = 0;
        for (var i = 0; i < bytes.length; i++, offset += 2) {
          var s = Math.max(-1, Math.min(1, bytes[i]));
          data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
        }
        return new Blob([data]);
      }
    };

    var sendData = function () { //对以获取的数据进行处理(分包)
      var reader = new FileReader();
      reader.onload = e => {
        // console.log(e);
        var outbuffer = e.target.result;
        // var arr = new Int8Array(outbuffer);
        // console.log(arr);
        ws.send(outbuffer);
        ws.send(JSON.stringify({
          "type": "heartbeat"
        }));
      };
      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);
      // console.log(inputBuffer);
      sendData();
    }
  }


  /*
   * WebSocket
   */
  function useWebSocket() {
    // ws = new WebSocket(`${wsUrl}?time=${wstime}&appkey=${appkey}&sign=${sign}`);
    ws = new WebSocket(`ws://59.110.116.36/v1/ws?time=1642471777442&appkey=45gn7md5n44aak7a57rdjud3b5l4xdgv75saomys&sign=D54B7D7AFE27A7DC7CE389AF8E232B502C1F9326BBE34C1F51B42AB03679F32D`);
    ws.binaryType = 'arraybuffer'; //传输的是 ArrayBuffer 类型的数据
    ws.onopen = function () {
      console.log('握手成功');
      ws.send(JSON.stringify({
        "type": "start",
        "data": {
          "domain":"general",
          "sample": "16k",
          "lang": "cn",
          "punctuation": "true",
          "post_proc": "true",
          "server_vad": "true",
          "max_start_silence": "5000",
          "max_end_silence": "500",
          "user_id": "userid"
        }
      }))
      if (ws.readyState == 1) {
        record.start();
      }
    };

    ws.onmessage = function (msg) {
      let data = JSON.parse(msg.data);
      // console.log(data);
      if (data.type == 'fixed') {
        fixedArr.push(data.text)
        str += data.text;
        $("#result_output").html(str);
      }else{
        $("#result_output").html(str+data.text);
      }
      if (data.end == true && data.code ==0) {
        ws.close();
        record.stop();
      }
      console.log(str);
    }

    ws.onerror = function (err) {
      console.info(err)
    }
  }

  /*
   * 开始对讲 
   */
  $('.start-button').click(function () {
    // console.log(123123);
    if (state === 'STOPING') {
      $('.start-button').text('结束识别')
      state = 'STARTING'
    }else{
      $('.start-button').text('开始识别')
      state = 'STOPING'
      if (ws) {
        ws.send(JSON.stringify({
          "type": "end"
        }))
      }
      record.stop();
      return;
    }
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
    if (!navigator.getUserMedia) {
      alert('浏览器不支持音频输入');
    } else {
      navigator.getUserMedia({
          audio: true
        },
        function (mediaStream) {
          init(new Recorder(mediaStream));
          console.log('开始对讲');
          useWebSocket();
        },
        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 () {
  //   ws.send(JSON.stringify({
	//     "type": "end"
  //   }))
  //   record.stop();
  // }
  function sent_confirm(){
    if (flag) {
      return ;
    }
    if ($("#first_sent").val()!='') {
      
      if (fixedNum>=fixedArr.length) {
        return;
      }
      let p_sent = document.createElement('p');
      p_sent.innerText=$("#first_sent").val();
      document.getElementById("recordingslist").appendChild(p_sent);
      SubTxt.push($("#first_sent").val())
      localStorage.setItem('SubTxt',JSON.stringify(SubTxt))
      $("#recordingslist").scrollTop($("#recordingslist")[0].scrollHeight)
      $("#first_sent").val('')
      $('.confirm').val('获取');
      return;
    }else{
      
      if (fixedNum<=fixedArr.length-2) {
        fixedNum++;
      }else{
        return;
      }
      $("#first_sent").val(fixedArr[fixedNum]);
      // 清楚之前存在的高亮
      str = str.replace('<strong style="color:yellow">','')
      str = str.replace('</strong>','')
      let p = $("#first_sent").val()
      let newp = '<strong style="color:yellow">' +$("#first_sent").val()+'</strong>'
      var reg = new RegExp("(.*)"+p,"g");
      // 替换最后一个匹配项
      str = str.replace(reg,`$1${newp}`)
      console.log(str);
      $("#result_output").html(str);
      $('.confirm').val('提交');
      // $("#result_output").scrollTop($("#result_output")[0].scrollHeight)
    }
  }
  function cheakboxbtn() {
    if (!flag){
      flag = true;
      if ($("#first_sent").val()!='') {
        let p_sent = document.createElement('p');
        p_sent.innerText=$("#first_sent").val();
        document.getElementById("recordingslist").appendChild(p_sent);
        SubTxt.push($("#first_sent").val())
        localStorage.setItem('SubTxt',JSON.stringify(SubTxt))
        $("#recordingslist").scrollTop($("#recordingslist")[0].scrollHeight)
        $("#first_sent").val('')
      }
      timer = setInterval(() => {
        if (fixedNum<fixedArr.length-1){
          fixedNum++;
          let p_sent = document.createElement('p');
          p_sent.innerText=fixedArr[fixedNum];
          document.getElementById("recordingslist").appendChild(p_sent);
          SubTxt.push(fixedArr[fixedNum])
          localStorage.setItem('SubTxt',JSON.stringify(SubTxt))
          $("#recordingslist").scrollTop($("#recordingslist")[0].scrollHeight)
        }
      }, 1000);
    }
    else {
        flag = false;
        clearInterval(timer)
    }
  }
  function sent_restore(){
    if ($("#first_sent").val()!='') {
      $("#first_sent").val(fixedArr[fixedNum]);
    }
  }
  function sent_clear(){
    $("#recordingslist").html('');
  }
  function wordgen(){
    console.log(123132);
    $("#recordingslist").wordExport("会议记录");
  }
<!--
 * @Date: 2022-01-12 10:15:34
 * @LastEditors: yunshuo
 * @LastEditTime: 2022-01-19 11:37:10
-->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link href="index.css" type="text/css" rel="stylesheet">
    <link rel="stylesheet" href="./css/bootstrap.min.css">
    <link rel="stylesheet" href="./css/meeting.css">
    <link rel="stylesheet" href="./css/voicedictation.css">
    <link rel="stylesheet" href="./css/reset.css">

</head>

<body>
<div>
  
</div>
    <div class="container">
        <div class="demoBody">
            <div class="lBody">
                <div class="lTitle">
                    <i></i>
                    <p>会议内容分段修改</p>
                    <a class="conference" href="conference.html" target="_blank">会议记录</a>
                </div>
                <div class="lMessage">
                    <div class="lDetail">
                        <div class="lDetail-content"><textarea name="" id="first_sent"  class="record" cols="" rows=""></textarea></div>
                        <div class="lMessage-bottom">
                            <div >
                                <label for="u3_input"></label>
                                <!--20190715 add οnclick='cheakboxbtn()'-->
                                <input id="u3_input" type="checkbox" value="checkbox" onclick='cheakboxbtn()'/>自动确认
                            </div>
                            <div class="divRight2" >
                                <input class="btn2 confirm" onclick="sent_confirm()" type="button" value="获取"/>
                                <input class="btn2" onclick="sent_restore()" type="button" value="恢复"/> 
                            </div>  
                        </div>
                      
                    </div>
                    <div class="lDetail lDetail2">
                        <div class="lDetail2-title"><span class="buttomTitle">会议实时语音转换:</span></div>
                        <!--20190711 add disabled="disabled"-->
                        <div class="lDetail2-content"><div name="" id="result_output" class="record record2" style="height:300px;overflow-y:auto" disabled="disabled"></div>  </div>
                        <div class="lDetail2-bottom"> 
                            <div class="service-item service-item-taste">
                                <div class="service-item-content service-item-taste-content">
                                    <div class="start-taste flex-display-1">
                                        <div class="start-taste-left">
                                            <div class="start-taste-button">
                                                <button class="taste-button start-button">开始识别</button>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>                   
                </div>
                
            </div>
            <div class="rBody">
                <div class="rTitle">
                    <i></i>
                    <p>会议记录</p>
                </div>
                <div class="lMessage lMessage-right" id="recordingslist" style="line-height:26px">
                </div>
                <div class="button-group-right">
                   <button id="btn_start" class="btn" onclick="wordgen()">生成word文档</button>
                    <button id="btn_clear" class="btn" onclick="sent_clear()">清除</button> 
                </div>
                
            </div>
        </div>
    </div>
    <!-- <form id="wordform" method="POST" action="http://62.234.66.244/speech/speech/wordgen.do">
     <input id="data" name="data" type="hidden" value="/">   
    </form> -->
    <script src="jquery.js"></script>
<script src="hmac-sha256.js"></script>
<script src="enc-base64-min.js"></script>
<script type="text/javascript" src="./js/FileSaver.js"></script>
<script type="text/javascript" src="./js/jquery.wordexport.js"></script>

<!-- <script type="text/javascript" src="./recorder.js"></script> -->
<!-- <script src="index.js"></script> -->
<script src="index1.js"></script>
</body>
</html>
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值