js实现mp3录音通过websocket实时传送+简易波形图效果


Posted in Javascript onJune 12, 2020

波形图:https://3water.com/article/188545.htm

废话:想不到我的第一篇博客是关于前端,作为一名后端的小菜,前端方面肯定还有很多不足之处,如果文章有任何问题欢迎指正。感谢大家。好了!废话不多说下面讲一下需求。

需求:公司要求实现web端的录音并通过websocket实时上传至java后台,而且能通过vlc实时播放,简单一点讲就是我用网页在那一边讲话,一个大喇叭就能实时把我的话播出去,这样是不是通俗易懂呀,而且呢公司要求用mp3格式。当然啦!为了知道自己在讲话需要一个波形图,这里主要实现前半部分功能,后半部分臣妾也做不到呀!后半部分的vlc播放呢如果大家想知道,可以留言,届时可以给大家指条明路

前端实现:

引入:

<script type="text/javascript" src="/js/recorder/recordmp3.js"></script>

这个跟大佬的js有点不一样,我在里面加了一点东西,而且在这个js里面引入了两个另外的js,lame.min.js和worker-realtime.js,这俩在大佬的代码里有

页面:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>
 <title>测试</title>
</head>
<body>
<button id="intercomBegin">开始对讲</button>
<button id="intercomEnd">关闭对讲</button>
<canvas id="casvased" style="width: 400px;height: 100px"></canvas>
</body>
<script type="text/javascript" src="/js/jquery-3.3.1.js"></script>
<script type="text/javascript" src="/js/recorder/recordmp3.js"></script>
<script type="text/javascript">
 var begin = document.getElementById('intercomBegin');
 var end = document.getElementById('intercomEnd');

 var canvas = document.getElementById("casvased");
 var canvasCtx = canvas.getContext("2d");

 var ws = null; //实现WebSocket

 var recorder;

 /*
 * WebSocket
 */
 function useWebSocket() {
  ws = new WebSocket("ws://127.0.0.1:8089/send/voice");
  ws.binaryType = 'arraybuffer'; //传输的是 ArrayBuffer 类型的数据
  ws.onopen = function () {
   console.log('握手成功');
   if (ws.readyState == 1) { //ws进入连接状态,则每隔500毫秒发送一包数据
    recorder.start();
   }
  };

  ws.onmessage = function (msg) {
   console.info(msg)
  }

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

 /*
 * 开始对讲
 */
 begin.onclick = function () {
  recorder = new MP3Recorder({
   debug: true,
   funOk: function () {
    console.log('点击录制,开始录音! ');
   },
   funCancel: function (msg) {
    console.log(msg);
    recorder = null;
   }
  });
 }

 /*
 * 关闭对讲
 */
 end.onclick = function () {
  if (ws) {
   ws.close();
   recorder.stop();
   console.log('关闭对讲以及WebSocket');
  }
 }

 var sendData = function() { //对以获取的数据进行处理(分包)
  var reader = new FileReader();
  reader.onload = e => {
   var outbuffer = e.target.result;
   var arr = new Int8Array(outbuffer);
   if (arr.length > 0) {
    var tmparr = new Int8Array(1024);
    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);
     }
    }
   }
  };
  recorder.getMp3Blob(function (blob) {
   reader.readAsArrayBuffer(blob);//这里拿到mp3格式的音频流写入到reader中
  })
 }; </script> </html>
recordmp3.js

(function (exports) {

 var MP3Recorder = function (config) {

  var recorder = this;
  config = config || {};
  config.sampleRate = config.sampleRate || 44100;
  config.bitRate = config.bitRate || 128;

  navigator.getUserMedia = navigator.getUserMedia ||
   navigator.webkitGetUserMedia ||
   navigator.mozGetUserMedia ||
   navigator.msGetUserMedia;

  if (navigator.getUserMedia) {
   navigator.getUserMedia({
     audio: true
    },
    function (stream) {
     var context = new AudioContext(),
      microphone = context.createMediaStreamSource(stream),
      processor = context.createScriptProcessor(16384, 1, 1),//bufferSize大小,输入channel数,输出channel数
      mp3ReceiveSuccess, currentErrorCallback;

     var height = 100;
     var width = 400;

     const analyser = context.createAnalyser()
     analyser.fftSize = 1024
     //连接到音频源
     microphone.connect(analyser);
     analyser.connect(context.destination);

     const bufferLength = analyser.frequencyBinCount // 返回的是 analyser的fftsize的一半
     const dataArray = new Uint8Array(bufferLength);

     function draw() {
      canvasCtx.clearRect(0, 0, width, height); //清除画布
      analyser.getByteFrequencyData(dataArray); // 将当前频率数据复制到传入其中的Uint8Array
      const requestAnimFrame = window.requestAnimationFrame(draw) || window.webkitRequestAnimationFrame(draw);
      canvasCtx.fillStyle = '#000130';
      canvasCtx.fillRect(0, 0, width, height);
      let barWidth = (width / bufferLength) * 2;
      let barHeight;
      let x = 0;
      let c = 2
      for (let i = 0; i < bufferLength; i++) {
       barHeight = c+(dataArray[i]/400)*height;
       canvasCtx.fillStyle = 'rgb(0, 255, 30)';
       canvasCtx.fillRect(x, height / 2 - barHeight / 2, barWidth, barHeight);
       x += barWidth + 1;
      }
     }

     draw();

     useWebSocket();
     config.sampleRate = context.sampleRate;
     processor.onaudioprocess = function (event) {
      //边录音边转换
      var array = event.inputBuffer.getChannelData(0);
      realTimeWorker.postMessage({cmd: 'encode', buf: array});
      sendData();
     };

     var realTimeWorker = new Worker('/js/recorder/worker-realtime.js');
     realTimeWorker.onmessage = function (e) {
      switch (e.data.cmd) {
       case 'init':
        log('初始化成功');
        if (config.funOk) {
         config.funOk();
        }
        break;
       case 'end':
        log('MP3大小:', e.data.buf.length);
        if (mp3ReceiveSuccess) {
         mp3ReceiveSuccess(new Blob(e.data.buf, {type: 'audio/mp3'}));
        }
        break;
       case 'error':
        log('错误信息:' + e.data.error);
        if (currentErrorCallback) {
         currentErrorCallback(e.data.error);
        }
        break;
       default:
        log('未知信息:', e.data);
      }
     };

     recorder.getMp3Blob = function (onSuccess, onError) {
      currentErrorCallback = onError;
      mp3ReceiveSuccess = onSuccess;
      realTimeWorker.postMessage({cmd: 'finish'});
     };

     recorder.start = function () {
      if (processor && microphone) {
       microphone.connect(processor);
       processor.connect(context.destination);
       log('开始录音');
      }
     }

     recorder.stop = function () {
      if (processor && microphone) {
       microphone.disconnect();
       processor.disconnect();
       log('录音结束');
      }
     }

     realTimeWorker.postMessage({
      cmd: 'init',
      config: {
       sampleRate: config.sampleRate,
       bitRate: config.bitRate
      }
     });
    },
    function (error) {
     var msg;
     switch (error.code || error.name) {
      case 'PERMISSION_DENIED':
      case 'PermissionDeniedError':
       msg = '用户拒绝访问麦客风';
       break;
      case 'NOT_SUPPORTED_ERROR':
      case 'NotSupportedError':
       msg = '浏览器不支持麦客风';
       break;
      case 'MANDATORY_UNSATISFIED_ERROR':
      case 'MandatoryUnsatisfiedError':
       msg = '找不到麦客风设备';
       break;
      default:
       msg = '无法打开麦克风,异常信息:' + (error.code || error.name);
       break;
     }
     if (config.funCancel) {
      config.funCancel(msg);
     }
    });
  } else {
   if (config.funCancel) {
    config.funCancel('当前浏览器不支持录音功能');
   }
  }

  function log(str) {
   if (config.debug) {
    console.log(str);
   }
  }
 }

 exports.MP3Recorder = MP3Recorder;

})(window);

后端websocket:
这里实现的是保存为mp3文件

package com.jetosend.common.socket;

import com.jetosend.common.utils.Utils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.Hashtable;
import java.util.Map;

@ServerEndpoint("/send/{key}")
@Component
public class ServerSocket {

 private static final Map<String, Session> connections = new Hashtable<>();
 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

 /***
  * @Description:打开连接
  * @Param: [id, 保存对方平台的资源编码
  * session]
  * @Return: void
  * @Author: Liting
  * @Date: 2019-10-10 09:22
  */
 @OnOpen
 public void onOpen(@PathParam("key") String id, Session session) {
  System.out.println(id + "连上了");
  connections.put(id, session);
 }

 /**
  * 接收消息
  */
 @OnMessage
 public void onMessage(@PathParam("key") String id, InputStream inputStream) {
  System.out.println("来自" + id);
  try {
   int rc = 0;
   byte[] buff = new byte[100];
   while ((rc = inputStream.read(buff, 0, 100)) > 0) {
    byteArrayOutputStream.write(buff, 0, rc);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 /**
  * 异常处理
  *
  * @param throwable
  */
 @OnError
 public void onError(Throwable throwable) {
  throwable.printStackTrace();
  //TODO 日志打印异常
 }

 /**
  * 关闭连接
  */
 @OnClose
 public void onClose(@PathParam("key") String id) {
  System.out.println(id + "断开");
  BufferedOutputStream bos = null;
  FileOutputStream fos = null;
  File file = null;
  try {
   file = new File("D:\\testtest.mp3");

   //输出流
   fos = new FileOutputStream(file);

   //缓冲流
   bos = new BufferedOutputStream(fos);

   //将字节数组写出
   bos.write(byteArrayOutputStream.toByteArray());
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   if (bos != null) {
    try {
     bos.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
   if (fos != null) {
    try {
     fos.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }

  connections.remove(id);
 }

实现效果:

js实现mp3录音通过websocket实时传送+简易波形图效果 

总结

到此这篇关于js实现mp3录音通过websocket实时传送+简易波形图效果的文章就介绍到这了,更多相关js实现mp3录音内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
Extjs 继承Ext.data.Store不起作用原因分析及解决
Apr 15 Javascript
jQuery使用技巧简单汇总
Apr 18 Javascript
通过JQuery将DIV的滚动条滚动到指定的位置方便自动定位
May 05 Javascript
javascript实现获取cookie过期时间的变通方法
Aug 14 Javascript
jquery学习总结(超级详细)
Sep 04 Javascript
jQuery如何获取同一个类标签的所有值(默认无法获取)
Sep 25 Javascript
前端框架Vue.js构建大型应用浅析
Sep 12 Javascript
jquery dataview数据视图插件使用方法
Dec 23 Javascript
bootstrap为水平排列的表单和内联表单设置可选的图标
Feb 15 Javascript
vue多页面开发和打包正确处理方法
Apr 20 Javascript
JS实现获取数组中最大值或最小值功能示例
Mar 02 Javascript
详解vue-cli3开发Chrome插件实践
May 29 Javascript
学前端,css与javascript重难点浅析
Jun 11 #Javascript
微信小程序 scroll-view的使用案例代码详解
Jun 11 #Javascript
基于ajax及jQuery实现局部刷新过程解析
Sep 12 #jQuery
微信小程序scroll-view实现滚动到锚点左侧导航栏点餐功能(点击种类,滚动到锚点)
Jun 11 #Javascript
多页vue应用的单页面打包方法(内含打包模式的应用)
Jun 11 #Javascript
VUE页面中通过双击实现复制表格中内容的示例代码
Jun 11 #Javascript
vue scroll滚动判断的实现(是否滚动到底部、滚动方向、滚动节流、获取滚动区域dom元素)
Jun 11 #Javascript
You might like
使用数据库保存session的方法
2006/10/09 PHP
mysql 的 like 问题,超强毕杀记!!!
2007/01/18 PHP
php简单实现多语言切换的方法
2016/05/09 PHP
PHP基于DateTime类解决Unix时间戳与日期互转问题【针对1970年前及2038年后时间戳】
2018/06/13 PHP
学习ExtJS Panel常用方法
2009/10/07 Javascript
让网页跳转到指定位置的jquery代码非书签
2013/09/06 Javascript
JS增加行复制行删除行的实现代码
2013/11/09 Javascript
原生JS操作网页给p元素添加onclick事件及表格隔行变色
2013/12/01 Javascript
深入理解Javascript作用域与变量提升
2013/12/09 Javascript
JavaScript获取当前网页最后修改时间的方法
2015/04/03 Javascript
基于jquery实现即时检查格式是否正确的表单
2016/05/06 Javascript
详解vue-cli开发环境跨域问题解决方案
2017/06/06 Javascript
详解如何使用 vue-cli 开发多页应用
2017/12/16 Javascript
基于vue-cli vue-router搭建底部导航栏移动前端项目
2018/02/28 Javascript
vue 项目打包通过命令修改 vue-router 模式 修改 API 接口前缀
2018/06/13 Javascript
vue 2.8.2版本配置刚进入时候的默认页面方法
2018/09/21 Javascript
javascript匿名函数中的'return function()'作用
2018/10/15 Javascript
es6基础学习之解构赋值
2018/12/10 Javascript
javascript面向对象三大特征之封装实例详解
2019/07/24 Javascript
nodejs实现UDP组播示例方法
2019/11/04 NodeJs
Element Rate 评分的使用方法
2020/07/27 Javascript
[12:29]《一刀刀一天》之DOTA全时刻19:蝙蝠骑士田伯光再度不举
2014/06/10 DOTA
python判断端口是否打开的实现代码
2013/02/10 Python
python select.select模块通信全过程解析
2017/09/20 Python
Python socket实现多对多全双工通信的方法
2019/02/13 Python
python如果快速判断数字奇数偶数
2019/11/13 Python
TensorFlow基本的常量、变量和运算操作详解
2020/02/03 Python
西班牙自行车和跑步商店:Alltricks
2018/07/07 全球购物
为女性购买传统的印度服装和婚纱:Kalkifashion
2019/07/22 全球购物
中医临床专业自我鉴定范文
2014/01/15 职场文书
关于美容院的活动方案
2014/08/14 职场文书
技术员个人工作总结
2015/03/03 职场文书
高中家长意见怎么写
2015/06/03 职场文书
拉贝日记观后感
2015/06/05 职场文书
vue如何批量引入组件、注册和使用详解
2021/05/12 Vue.js
pytorch加载预训练模型与自己模型不匹配的解决方案
2021/05/13 Python