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 相关文章推荐
JavaScript Array Flatten 与递归使用介绍
Oct 30 Javascript
jQuery中index()方法用法实例
Dec 27 Javascript
jQuery实现的手机发送验证码倒计时效果代码分享
Aug 24 Javascript
Vue2.x中的父组件传递数据至子组件的方法
May 01 Javascript
详解a++和++a的区别
Aug 30 Javascript
代码整洁之道(重构)
Oct 25 Javascript
Vue路由前后端设计总结
Aug 06 Javascript
微信小程序错误this.setData报错及解决过程
Sep 18 Javascript
微信小程序和H5页面间相互跳转代码实例
Sep 19 Javascript
IntelliJ IDEA编辑器配置vue高亮显示
Sep 26 Javascript
详细分析JavaScript中的深浅拷贝
Sep 17 Javascript
低门槛开发iOS、Android、小程序应用的前端框架详解
Oct 16 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
解析获取优酷视频真实下载地址的PHP源代码
2013/06/26 PHP
php中的观察者模式简单实例
2015/01/20 PHP
php实现的一个简单json rpc框架实例
2015/03/30 PHP
PHP的openssl加密扩展使用小结(推荐)
2016/07/18 PHP
PHP使用PDO调用mssql存储过程的方法示例
2017/10/07 PHP
php实现的支付宝网页支付功能示例【基于TP5框架】
2019/09/16 PHP
javascript parseInt 大改造
2009/09/27 Javascript
这段js代码得节约你多少时间
2011/12/20 Javascript
巧用js提交表单轻松解决一个页面有多个提交按钮
2013/11/17 Javascript
JS+DIV实现鼠标划过切换层效果的实例代码
2013/11/26 Javascript
优化javascript的执行效率一些方法总结
2013/12/25 Javascript
document.compatMode的CSS1compat使用介绍
2014/04/03 Javascript
JS中的form.submit()不能提交表单的错误原因
2014/10/08 Javascript
JavaScript中的标签语句用法分析
2015/02/10 Javascript
JS文字球状放大效果代码分享
2015/08/19 Javascript
javascript创建对象的几种模式介绍
2016/05/06 Javascript
JS中如何实现点击a标签返回页面顶部的问题
2017/01/19 Javascript
js+canvas实现动态吃豆人效果
2017/03/22 Javascript
Vue.js 踩坑记之双向绑定
2018/05/03 Javascript
请求时token过期自动刷新token操作
2020/09/11 Javascript
vue封装自定义指令之动态显示title操作(溢出显示,不溢出不显示)
2020/11/12 Javascript
Python中asyncore的用法实例
2014/09/29 Python
python实现类的静态变量用法实例
2015/05/08 Python
python 字典中文key处理,读取,比较方法
2018/07/06 Python
Pandas DataFrame 取一行数据会得到Series的方法
2018/11/10 Python
Python判断是否json是否包含一个key的方法
2018/12/31 Python
Django后端接收嵌套Json数据及解析详解
2019/07/17 Python
在python tkinter界面中添加按钮的实例
2020/03/04 Python
Anaconda+vscode+pytorch环境搭建过程详解
2020/05/25 Python
香港个人化生活购物网站:Ballyhoo Limited
2016/09/10 全球购物
工程部经理岗位职责
2013/12/08 职场文书
欢迎领导检查标语
2014/06/27 职场文书
环境日宣传活动总结
2014/07/09 职场文书
三分钟自我介绍演讲稿
2014/08/21 职场文书
离退休人员聘用协议书
2014/11/24 职场文书
使用PostGIS完成两点间的河流轨迹及流经长度的计算(推荐)
2022/01/18 PostgreSQL