Websocket 推送音频文件流,前端播放及下载

这篇具有很好参考价值的文章主要介绍了Websocket 推送音频文件流,前端播放及下载。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Websocket 推送音频文件流,前端播放及下载</title>
  <script src="PCMPlayer.js"></script>
  <script src="base64.min.js"></script>
  <script src="pcmtoWav.js"></script>
</head>
<body>
<button onclick="sendMessage()">开始</button>
<button onclick="down()">下载</button>
<script>
var ws= null
var files = []

//创建实例
var player = new PCMPlayer({
	encoding: "16bitInt",
    channels: 1,
    sampleRate: 8000,
    flushingTime: 2000
});
window.onload = function() {
	openWebSocket()
}
function openWebSocket() {
	ws = new WebSocket("ws://*.*.*.*:*/**") // 创建WebSocket对象
    ws.binaryType = 'arraybuffer';

    ws.onopen = connectionOpen;
    ws.onmessage = messageReceived;
    ws.onerror = errorOccurred;
    ws.onclose = connectionClosed;
}
// 链接建立的时候触发
function connectionOpen (e) {
	console.log("Websocket 连接成功")
}
// 收到服务端发过来的消息时触发
function messageReceived (msg) {
	console.log("Websocket 返回信息")
	const data = msg.data;
	const dataAudio = new Uint8Array(data)
    player.feed(dataAudio)
    files.push(dataAudio)
}
// 异常时触发
function errorOccurred (e) {
	console.log("WebSocket 异常")
}
// 关闭客户端链接时触发
function connectionClosed (e) {
	console.log("WebSocket 连接断开")
    ws = null
}
// 发送信息
function sendMessage () {
	files = []
	// 如果是JSON,需要用JSON.stringify()转成字符串
    ws.send("开始")
}
// 下载文件
function down() {
	let length = 0;
    files.forEach(item => {
    	length += item.length;
    });
    let mergedArray = new Uint8Array(length);
    let offset = 0;
    files.forEach(item => {
    	mergedArray.set(item, offset);
        offset += item.length;
    });
	const link = document.createElement('a');
    link.href = pcmtoWav(window.btoa(String.fromCharCode(...mergedArray)), 8000, 1)
    link.download = new Date().getTime() + '.wav'
    link.click();
}
</script>
</body>
// PCMPlayer.js

function PCMPlayer(option) {
  this.init(option);
}

PCMPlayer.prototype.init = function (option) {
  var defaults = {
      encoding: '16bitInt',//编码格式
      channels: 1,//声道
      sampleRate: 8000,//采样率
      flushingTime: 1000//pcm数据刷新间隔
  };
  this.option = Object.assign({}, defaults, option);
  this.samples = new Float32Array();
  this.flush = this.flush.bind(this);
  this.interval = setInterval(this.flush, this.option.flushingTime);
  this.maxValue = this.getMaxValue();
  this.typedArray = this.getTypedArray();
  this.createContext();
};

PCMPlayer.prototype.getMaxValue = function () {
  var encodings = {
      '8bitInt': 128,
      '16bitInt': 32768,
      '32bitInt': 2147483648,
      '32bitFloat': 1
  }

  return encodings[this.option.encoding] ? encodings[this.option.encoding] : encodings['16bitInt'];
};

PCMPlayer.prototype.getTypedArray = function () {
  var typedArrays = {
      '8bitInt': Int8Array,
      '16bitInt': Int16Array,
      '32bitInt': Int32Array,
      '32bitFloat': Float32Array
  }

  return typedArrays[this.option.encoding] ? typedArrays[this.option.encoding] : typedArrays['16bitInt'];
};

PCMPlayer.prototype.createContext = function () {
  this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  this.gainNode = this.audioCtx.createGain();
  this.gainNode.gain.value = 1;
  this.gainNode.connect(this.audioCtx.destination);
  this.startTime = this.audioCtx.currentTime;
};

PCMPlayer.prototype.isTypedArray = function (data) {
  return (data.byteLength && data.buffer && data.buffer.constructor == ArrayBuffer);
};
// 播放原始pcm裸数据
PCMPlayer.prototype.feed = function (data) {
  if (!this.isTypedArray(data)) return;
  data = this.getFormatedValue(data);
  var tmp = new Float32Array(this.samples.length + data.length);
  tmp.set(this.samples, 0);
  tmp.set(data, this.samples.length);
  this.samples = tmp;
};
// 格式化
PCMPlayer.prototype.getFormatedValue = function (data) {
  var data = new this.typedArray(data.buffer),
      float32 = new Float32Array(data.length),
      i;
  for (i = 0; i < data.length; i++) {
      float32[i] = data[i] / this.maxValue;
  }
  return float32;
};
// 控制播放器音量
PCMPlayer.prototype.volume = function (volume) {
  this.gainNode.gain.value = volume;
};
// 销毁播放器实例
PCMPlayer.prototype.destroy = function () {
  if (this.interval) {
      clearInterval(this.interval);
  }
  this.samples = null;
  this.audioCtx.close();
  this.audioCtx = null;
};

PCMPlayer.prototype.flush = function () {
  if (!this.samples.length) return;
  var bufferSource = this.audioCtx.createBufferSource(),
      length = this.samples.length / this.option.channels,
      audioBuffer = this.audioCtx.createBuffer(this.option.channels, length, this.option.sampleRate),
      audioData,
      channel,
      offset,
      i,
      decrement;

  for (channel = 0; channel < this.option.channels; channel++) {
      audioData = audioBuffer.getChannelData(channel);
      offset = channel;
      decrement = 50;
      for (i = 0; i < length; i++) {
          audioData[i] = this.samples[offset];
          /* fadein */
          if (i < 50) {
              audioData[i] = (audioData[i] * i) / 50;
          }
          /* fadeout*/
          if (i >= (length - 51)) {
              audioData[i] = (audioData[i] * decrement--) / 50;
          }
          offset += this.option.channels;
      }
  }

  if (this.startTime < this.audioCtx.currentTime) {
    this.startTime = this.audioCtx.currentTime;
  }
  bufferSource.buffer = audioBuffer;
  bufferSource.connect(this.gainNode);
  bufferSource.start(this.startTime);
  this.startTime += audioBuffer.duration;
  this.samples = new Float32Array();
};
// pcmtoWav

function pcmtoWav(pcmsrt, sampleRate, numChannels, bitsPerSample) {
  //参数->(base64编码的pcm流,采样频率,声道数,采样位数)
  let header = {
    // OFFS SIZE NOTES
    chunkId: [0x52, 0x49, 0x46, 0x46], // 0    4    "RIFF" = 0x52494646
    chunkSize: 0, // 4    4    36+SubChunk2Size = 4+(8+SubChunk1Size)+(8+SubChunk2Size)
    format: [0x57, 0x41, 0x56, 0x45], // 8    4    "WAVE" = 0x57415645
    subChunk1Id: [0x66, 0x6d, 0x74, 0x20], // 12   4    "fmt " = 0x666d7420
    subChunk1Size: 16, // 16   4    16 for PCM
    audioFormat: 1, // 20   2    PCM = 1
    numChannels: numChannels || 1, // 22   2    Mono = 1, Stereo = 2...
    sampleRate: sampleRate || 16000, // 24   4    8000, 44100...
    byteRate: 0, // 28   4    SampleRate*NumChannels*BitsPerSample/8
    blockAlign: 0, // 32   2    NumChannels*BitsPerSample/8
    bitsPerSample: bitsPerSample || 16, // 34   2    8 bits = 8, 16 bits = 16
    subChunk2Id: [0x64, 0x61, 0x74, 0x61], // 36   4    "data" = 0x64617461
    subChunk2Size: 0, // 40   4    data size = NumSamples*NumChannels*BitsPerSample/8
  }
  function u32ToArray(i) {
    return [i & 0xff, (i >> 8) & 0xff, (i >> 16) & 0xff, (i >> 24) & 0xff]
  }
  function u16ToArray(i) {
    return [i & 0xff, (i >> 8) & 0xff]
  }
  let pcm = Base64.toUint8Array(pcmsrt)
  header.blockAlign = (header.numChannels * header.bitsPerSample) >> 3
  header.byteRate = header.blockAlign * header.sampleRate
  header.subChunk2Size = pcm.length * (header.bitsPerSample >> 3)
  header.chunkSize = 36 + header.subChunk2Size

  let wavHeader = header.chunkId.concat(
    u32ToArray(header.chunkSize),
    header.format,
    header.subChunk1Id,
    u32ToArray(header.subChunk1Size),
    u16ToArray(header.audioFormat),
    u16ToArray(header.numChannels),
    u32ToArray(header.sampleRate),
    u32ToArray(header.byteRate),
    u16ToArray(header.blockAlign),
    u16ToArray(header.bitsPerSample),
    header.subChunk2Id,
    u32ToArray(header.subChunk2Size)
  )
  let wavHeaderUnit8 = new Uint8Array(wavHeader)

  let mergedArray = new Uint8Array(wavHeaderUnit8.length + pcm.length)
  mergedArray.set(wavHeaderUnit8)
  mergedArray.set(pcm, wavHeaderUnit8.length)
  let blob = new Blob([mergedArray], { type: 'audio/wav' })
  let blobUrl = window.URL.createObjectURL(blob)
  return blobUrl
}
// base64.min.js
/**
 * Minified by jsDelivr using Terser v5.15.1.
 * Original file: /npm/js-base64@3.7.5/base64.js
 *
 * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
 */
!function(t,n){var r,e;"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(r=t.Base64,(e=n()).noConflict=function(){return t.Base64=r,e},t.Meteor&&(Base64=e),t.Base64=e)}("undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:this,(function(){"use strict";var t,n="3.7.5",r="function"==typeof atob,e="function"==typeof btoa,o="function"==typeof Buffer,u="function"==typeof TextDecoder?new TextDecoder:void 0,i="function"==typeof TextEncoder?new TextEncoder:void 0,f=Array.prototype.slice.call("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="),c=(t={},f.forEach((function(n,r){return t[n]=r})),t),a=/^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/,d=String.fromCharCode.bind(String),s="function"==typeof Uint8Array.from?Uint8Array.from.bind(Uint8Array):function(t){return new Uint8Array(Array.prototype.slice.call(t,0))},l=function(t){return t.replace(/=/g,"").replace(/[+\/]/g,(function(t){return"+"==t?"-":"_"}))},h=function(t){return t.replace(/[^A-Za-z0-9\+\/]/g,"")},p=function(t){for(var n,r,e,o,u="",i=t.length%3,c=0;c<t.length;){if((r=t.charCodeAt(c++))>255||(e=t.charCodeAt(c++))>255||(o=t.charCodeAt(c++))>255)throw new TypeError("invalid character found");u+=f[(n=r<<16|e<<8|o)>>18&63]+f[n>>12&63]+f[n>>6&63]+f[63&n]}return i?u.slice(0,i-3)+"===".substring(i):u},y=e?function(t){return btoa(t)}:o?function(t){return Buffer.from(t,"binary").toString("base64")}:p,A=o?function(t){return Buffer.from(t).toString("base64")}:function(t){for(var n=[],r=0,e=t.length;r<e;r+=4096)n.push(d.apply(null,t.subarray(r,r+4096)));return y(n.join(""))},b=function(t,n){return void 0===n&&(n=!1),n?l(A(t)):A(t)},g=function(t){if(t.length<2)return(n=t.charCodeAt(0))<128?t:n<2048?d(192|n>>>6)+d(128|63&n):d(224|n>>>12&15)+d(128|n>>>6&63)+d(128|63&n);var n=65536+1024*(t.charCodeAt(0)-55296)+(t.charCodeAt(1)-56320);return d(240|n>>>18&7)+d(128|n>>>12&63)+d(128|n>>>6&63)+d(128|63&n)},B=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,x=function(t){return t.replace(B,g)},C=o?function(t){return Buffer.from(t,"utf8").toString("base64")}:i?function(t){return A(i.encode(t))}:function(t){return y(x(t))},m=function(t,n){return void 0===n&&(n=!1),n?l(C(t)):C(t)},v=function(t){return m(t,!0)},U=/[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g,F=function(t){switch(t.length){case 4:var n=((7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3))-65536;return d(55296+(n>>>10))+d(56320+(1023&n));case 3:return d((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return d((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},w=function(t){return t.replace(U,F)},S=function(t){if(t=t.replace(/\s+/g,""),!a.test(t))throw new TypeError("malformed base64.");t+="==".slice(2-(3&t.length));for(var n,r,e,o="",u=0;u<t.length;)n=c[t.charAt(u++)]<<18|c[t.charAt(u++)]<<12|(r=c[t.charAt(u++)])<<6|(e=c[t.charAt(u++)]),o+=64===r?d(n>>16&255):64===e?d(n>>16&255,n>>8&255):d(n>>16&255,n>>8&255,255&n);return o},E=r?function(t){return atob(h(t))}:o?function(t){return Buffer.from(t,"base64").toString("binary")}:S,D=o?function(t){return s(Buffer.from(t,"base64"))}:function(t){return s(E(t).split("").map((function(t){return t.charCodeAt(0)})))},R=function(t){return D(T(t))},z=o?function(t){return Buffer.from(t,"base64").toString("utf8")}:u?function(t){return u.decode(D(t))}:function(t){return w(E(t))},T=function(t){return h(t.replace(/[-_]/g,(function(t){return"-"==t?"+":"/"})))},Z=function(t){return z(T(t))},j=function(t){return{value:t,enumerable:!1,writable:!0,configurable:!0}},I=function(){var t=function(t,n){return Object.defineProperty(String.prototype,t,j(n))};t("fromBase64",(function(){return Z(this)})),t("toBase64",(function(t){return m(this,t)})),t("toBase64URI",(function(){return m(this,!0)})),t("toBase64URL",(function(){return m(this,!0)})),t("toUint8Array",(function(){return R(this)}))},O=function(){var t=function(t,n){return Object.defineProperty(Uint8Array.prototype,t,j(n))};t("toBase64",(function(t){return b(this,t)})),t("toBase64URI",(function(){return b(this,!0)})),t("toBase64URL",(function(){return b(this,!0)}))},P={version:n,VERSION:"3.7.5",atob:E,atobPolyfill:S,btoa:y,btoaPolyfill:p,fromBase64:Z,toBase64:m,encode:m,encodeURI:v,encodeURL:v,utob:x,btou:w,decode:Z,isValid:function(t){if("string"!=typeof t)return!1;var n=t.replace(/\s+/g,"").replace(/={0,2}$/,"");return!/[^\s0-9a-zA-Z\+/]/.test(n)||!/[^\s0-9a-zA-Z\-_]/.test(n)},fromUint8Array:b,toUint8Array:R,extendString:I,extendUint8Array:O,extendBuiltins:function(){I(),O()},Base64:{}};return Object.keys(P).forEach((function(t){return P.Base64[t]=P[t]})),P}));

文章来源地址https://www.toymoban.com/news/detail-601735.html

到了这里,关于Websocket 推送音频文件流,前端播放及下载的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot集成WebSocket,实现后台向前端推送信息

    在一次项目开发中,使用到了Netty网络应用框架,以及MQTT进行消息数据的收发,这其中需要后台来将获取到的消息主动推送给前端,于是就使用到了MQTT,特此记录一下。 WebSocket协议是基于TCP的一种新的网络协议。它实现了客户端与服务器全双工通信,学过计算机网络都知道

    2024年01月16日
    浏览(48)
  • Springboot整合WebSocket实现主动向前端推送消息

            在上篇文章tcp编程中,我们实现了C++客户端与java服务器之间的通信,客户端发送了一个消息给服务器,今天我们要实现基于WebSocket实现服务器主动向前端推送消息,并且以服务器接收到C++客户端的消息主动向前端推送消息的触发条件。 WebSocket 的诞生背景       

    2024年03月16日
    浏览(43)
  • webSocket推送太快导致前端渲染卡顿问题优化

    优化思路: 把webSocket接收到的数据用一个数组存起来,达到一定长度再统一渲染,可根据推送数据的速度适当调解数组长度限制,如果一段时间内改数组长度打不要渲染条件,就用定时器之间渲染

    2024年02月03日
    浏览(40)
  • 前端实现消息推送、即时通信、SSE、WebSocket、http简介

    服务端主动向客户端推送消息,使客户端能够即时接收到信息。 场景 页面接收到点赞,消息提醒 聊天功能 弹幕功能 实时更新数据功能 短轮询 浏览器(客户端)每隔一段时间向服务器发送http请求,服务器端在收到请求后,不论是否有数据更新,都直接进行响应。 本质:客

    2024年02月16日
    浏览(43)
  • 多个数据webSocket推送太快导致前端渲染卡顿问题优化

    作者代码写的不怎么样,谅解!主要思路就是把websocket接收到的数据用一个数组暂存起来,达到一定数量一起修改统一渲染,可根据项目数据推送数据的速度适当调解数组大小,然后再加了一个可能一段时间内都到不到数组达标渲染数量,就使用定时器直接做渲染,防止数据

    2024年02月12日
    浏览(49)
  • Python Flask 后端向前端推送信息——轮询、SSE、WebSocket

    后端向前端推送信息,通知任务完成 轮询 SSE WebSocket 请求方式 HTTP HTTP TCP长连接 触发方式 轮询 事件 事件 优点 实现简单易兼容 实现简单开发成本低 全双工通信,开销小,安全,可扩展 缺点 消耗较大 不兼容IE 传输数据需二次解析,开发成本大 适用场景 服务端向客户端单向

    2023年04月19日
    浏览(91)
  • websocket 实现后端主动前端推送数据、及时通讯(vue3 + springboot)

    WebSocket 是一种全双工通信协议,用于在 Web 浏览器和服务器之间建立持久的连接。 WebSocket 协议由 IETF 定为标准,WebSocket API 由 W3C 定为标准。 一旦 Web 客户端与服务器建立连接,之后的全部数据通信都通过这个连接进行。 可以互相发送 JSON、XML、HTML 或图片等任意格式的数据

    2024年03月17日
    浏览(52)
  • Thinkphp5.0 安装使用Workerman实现websocket前后端通信,后端主动推送消息到前端

    安装使用Workerman实现websocket前后端通信,后端主动推送消息到前端,实现后端有数据更新时,前端页面自动更新数据。 我使用的是基于Thinkphp5.0的ThinkCMF5.0。 安装: 启动: public目录下放置的server.php文件,注意里面的配置必须按照你的Worker控制器来: woker控制器: 后端主动推

    2024年02月16日
    浏览(55)
  • WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链接,但不容易直接完成实时的消息推送功能,如聊天室、后台信

    2024年02月13日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包