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 相关文章推荐
window.event快达到全浏览器支持了,以后使用就方便了
Nov 30 Javascript
博客侧边栏模块跟随滚动条滑动固定效果的实现方法(js+jquery等)
Mar 24 Javascript
javascipt:filter过滤介绍及使用
Sep 10 Javascript
谈谈JavaScript类型系统之Math
Jan 06 Javascript
全面解析Javascript无限添加QQ好友原理
Jun 15 Javascript
利用Angular2 + Ionic3开发IOS应用实例教程
Jan 15 Javascript
基于datepicker定义自己的angular时间组件的示例
Mar 14 Javascript
Angular5中提取公共组件之radio list的实例代码
Jul 10 Javascript
Vue实现调节窗口大小时触发事件动态调节更新组件尺寸的方法
Sep 15 Javascript
Vue.js 中 axios 跨域访问错误问题及解决方法
Nov 21 Javascript
Element Input输入框的使用方法
Jul 26 Javascript
微信小程序实现简单购物车功能
Dec 30 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安装xdebug/php安装pear/phpunit详解步骤(图)
2013/12/22 PHP
PHP实现简单的新闻发布系统实例
2015/07/28 PHP
Yii2框架引用bootstrap中日期插件yii2-date-picker的方法
2016/01/09 PHP
PHP结合Redis+MySQL实现冷热数据交换应用案例详解
2019/07/09 PHP
初探jquery——表单应用范例
2007/02/20 Javascript
javascript 强制刷新页面的实现代码
2009/12/13 Javascript
jQuery.extend 函数详解
2012/02/03 Javascript
jQuery中get和post方法传值测试及注意事项
2014/08/08 Javascript
ECMAScript6中Map/WeakMap详解
2015/06/12 Javascript
JS模式之单例模式基本用法
2015/06/30 Javascript
CSS图片响应式 垂直水平居中
2015/08/14 Javascript
jQuery使用$.ajax进行异步刷新的方法(附demo下载)
2015/12/04 Javascript
学习JavaScript设计模式之装饰者模式
2016/01/19 Javascript
JS与Ajax Get和Post在使用上的区别实例详解
2016/06/08 Javascript
浅谈Vue的基本应用
2016/12/27 Javascript
详解微信小程序开发之——wx.showToast(OBJECT)的使用
2017/01/18 Javascript
jquery实现一个全局计时器(商城可用)
2017/06/30 jQuery
vue中路由参数传递可能会遇到的坑
2017/12/07 Javascript
vue.js页面加载执行created,mounted的先后顺序说明
2020/11/07 Javascript
Python常用列表数据结构小结
2014/08/06 Python
Python面向对象之静态属性、类方法与静态方法分析
2018/08/24 Python
Python中staticmethod和classmethod的作用与区别
2018/10/11 Python
解决使用export_graphviz可视化树报错的问题
2019/08/09 Python
解决django无法访问本地static文件(js,css,img)网页里js,cs都加载不了
2020/04/07 Python
python/golang实现循环链表的示例代码
2020/09/14 Python
Django restful framework生成API文档过程详解
2020/11/12 Python
HTML5中meta属性的使用方法
2016/02/29 HTML / CSS
健身场所或家用健身设备:Life Fitness
2017/11/01 全球购物
美国单身专业人士在线约会网站:EliteSingles
2019/03/19 全球购物
党员活动日总结
2014/05/05 职场文书
好学生评语大全
2014/05/05 职场文书
中国梦演讲稿5分钟
2014/08/19 职场文书
通知的写法
2015/04/23 职场文书
2015年消防工作总结
2015/04/24 职场文书
2015年安置帮教工作总结
2015/05/22 职场文书
golang中实现给gif、png、jpeg图片添加文字水印
2021/04/26 Golang