JS实现audio音频剪裁剪切复制播放与上传(步骤详解)

背景是这样的,用户上传音频文件,可能只需要几十秒就够了,但是常规的音乐都要3~5分钟,80%的流量都是不需要的,要是就这么传上去,其实是流量的浪费,如果可以在前端就进行剪裁,也就是只取前面一段时间的音频,岂不是可以给公司省很多流量费用,前端的业务价值就体现了。

关键如何实现呢?

下面,就以“截取用户上传音频前3秒内容”的需求示意下如何借助Web Audio API实现音频的部分复制与播放功能。

一、不哔哔,直接正题

实现步骤如下。

1. File对象转ArrayBuffer

在Web网页中,用户选择的文件是个file对象,我们可以将这个文件对象转换成Blob、ArrayBuffer或者Base64。

在音频处理这里,都是使用ArrayBuffer这个数据类型。

代码如下所示,假设file类型的文件选择框的id是 'file'

file.onchange = function (event) {
 var file = event.target.files[0];
 // 开始识别
 var reader = new FileReader();
 reader.onload = function (event) {
 var arrBuffer = event.target.result;
 // arrBuffer就是包含音频数据的ArrayBuffer对象
 });
 reader.readAsArrayBuffer(file);
};

使用的是 readAsArrayBuffer() 方法,无论是MP3格式、OGG格式还是WAV格式,都可以转换成ArrayBuffer类型。

2. ArrayBuffer转AudioBuffer

这里的ArrayBuffer相对于把音频文件数组化了,大家可以理解为把音频文件分解成一段一段的,塞进了一个一个有地址的小屋子里,在计算机领域称为“缓冲区”,就是单词Buffer的意思。

所谓音频的剪裁,其实就是希望可以复制音频前面一段时间的内容。

但是问题来了,ArrayBuffer里面的数据并没有分类,统一分解了,想要准确提取某一截音频数据,提取不出来。

所以,才需要转换成AudioBuffer,纯粹的音频数据,方便提取。

AudioBuffer是一个仅仅包含音频数据的数据对象,是Web Audio API中的一个概念。

既然说到了Web Audio API,那我们就顺便……顺便……,想了想,还是不展开,因为太庞杂了,这Web Audio API至少比Web Animation API复杂了10倍,API之多,体量之大,世间罕见,想要完全吃透了,没有三年五载,啃不下来。

如果大家不是想要立志成为音视频处理专家,仅仅是临时解决一点小毛小病的问题,则不必深入,否则脑坑疼,使用MDN文档中的一些案例东拼西凑,基本的效果也能弄出来。

扯远了,回到这里。

AudioBuffer大家可以理解为音乐数据,那为什么叫AudioBuffer,不叫AudioData呢?

因为Buffer是个专有名词,直译为缓冲区,大家可以理解为高速公路,AudioBuffer处理数据更快,而且还有很多延伸的API,就像是高速公路上的服务区,有吃有喝还有加油的地方。

AudioData一看名字就是乡下土鳖,虽然接地气,但是,处理好几兆的数据的时候,就有些带不动了,就好像骑小电驴,在公速公路和乡道县道没多大区别,但是如果是开跑车,啧啧,乡下路就带不动了。

如何才能转换成AudioBuffer呢?

使用AudioContext对象的 decodeAudioData() 方法,代码如下:

var audioCtx = new AudioContext();

audioCtx.decodeAudioData(arrBuffer, function(audioBuffer) {
 // audioBuffer就是AudioBuffer
});

3. 复制AudioBuffer前3秒数据

AudioBuffer对象是一个音频专用Buffer对象,包含很多音频信息,包括:

duration
numberOfChannels
sampleRate

等。

包括一些音频声道数据处理方法,例如:

getChannelData()
copyFromChannel()
copyToChannel()

文档见这里: https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer

所以,实现的原理很简单,创建一个空的AudioBuffer,复制现有的通道数据前3秒的数据,然后复制的内容写入到这个空的AudioBuffer,于是我们就得到了一个剪裁后的音频Buffer数据了。

代码如下:

// 声道数量和采样率
var channels = audioBuffer.numberOfChannels;
var rate = audioBuffer.sampleRate;

// 截取前3秒
var startOffset = 0;
var endOffset = rate * 3;
// 3秒对应的帧数
var frameCount = endOffset - startOffset;

// 创建同样采用率、同样声道数量,长度是前3秒的空的AudioBuffer
var newAudioBuffer = new AudioContext().createBuffer(channels, endOffset - startOffset, rate);
// 创建临时的Array存放复制的buffer数据
var anotherArray = new Float32Array(frameCount);
// 声道的数据的复制和写入
var offset = 0;
for (var channel = 0; channel < channels; channel++) {
 audioBuffer.copyFromChannel(anotherArray, channel, startOffset);
 newAudioBuffer.copyToChannel(anotherArray, channel, offset);
}

// newAudioBuffer就是全新的复制的3秒长度的AudioBuffer对象

上面JavaScript代码中的变量 newAudioBuffer 就是全新的复制的3秒长度的AudioBuffer对象。

4. 使用newAudioBuffer做点什么?

其实应该是有了AudioBuffer对象后我们可以做点什么。

能做很多事情。

1) 如果希望直接播放

我们可以直接把AudioBuffer的数据作为音频数据进行播放

// 创建AudioBufferSourceNode对象
var source = audioCtx.createBufferSource();
// 设置AudioBufferSourceNode对象的buffer为复制的3秒AudioBuffer对象
source.buffer = newAudioBuffer;
// 这一句是必须的,表示结束,没有这一句没法播放,没有声音
// 这里直接结束,实际上可以对结束做一些特效处理
source.connect(audioCtx.destination);
// 资源开始播放
source.start();

2) 如果希望在<audio>元素中播放

这个还挺麻烦的。

<audio> 的src属性获取音频资源,再进行处理是简单的,网上的案例也很多。

但是,想要处理后的AudioBuffer再变成src让 <audio> 元素播放,嘿嘿,就没那么容易了。

我 (张鑫旭) 找了一圈,没有看到Web Audio API中有专门的“逆转录”方法。

唯一可行的路数就是根据AudioBuffer数据,重新构建原始的音频数据。研究了一番,转成WAV格式相对容易,想要转换成MP3格式比较麻烦,这里有个项目: https://github.com/higuma/mp3-lame-encoder-js 不过自己没验证过,不过看代码量,还挺惊人的。

因此,我们的目标还是转到WAV音频文件生成上吧,下面这段方法是从网上找的AudioBuffer转WAV文件的方法,以Blob数据格式返回。

// Convert AudioBuffer to a Blob using WAVE representation
function bufferToWave(abuffer, len) {
 var numOfChan = abuffer.numberOfChannels,
 length = len * numOfChan * 2 + 44,
 buffer = new ArrayBuffer(length),
 view = new DataView(buffer),
 channels = [], i, sample,
 offset = 0,
 pos = 0;

 // write WAVE header
 // "RIFF"
 setUint32(0x46464952);
 // file length - 8
 setUint32(length - 8);
 // "WAVE"
 setUint32(0x45564157);
 // "fmt " chunk
 setUint32(0x20746d66);
 // length = 16
 setUint32(16);
 // PCM (uncompressed)
 setUint16(1);
 setUint16(numOfChan);
 setUint32(abuffer.sampleRate);
 // avg. bytes/sec
 setUint32(abuffer.sampleRate * 2 * numOfChan);
 // block-align
 setUint16(numOfChan * 2);
 // 16-bit (hardcoded in this demo)
 setUint16(16);
 // "data" - chunk
 setUint32(0x61746164);
 // chunk length
 setUint32(length - pos - 4);   

 // write interleaved data
 for(i = 0; i < abuffer.numberOfChannels; i++)
 channels.push(abuffer.getChannelData(i));

 while(pos < length) {
  // interleave channels
 for(i = 0; i < numOfChan; i++) {
  // clamp
  sample = Math.max(-1, Math.min(1, channels[i][offset]));
  // scale to 16-bit signed int
  sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767)|0;
  // write 16-bit sample
  view.setInt16(pos, sample, true);
  pos += 2;
 }
 // next source sample
 offset++
 }

 // create Blob
 return new Blob([buffer], {type: "audio/wav"});

 function setUint16(data) {
 view.setUint16(pos, data, true);
 pos += 2;
 }

 function setUint32(data) {
 view.setUint32(pos, data, true);
 pos += 4;
 }
}

WAV格式的兼容性还是很6的,如下图所示:

凡事支持Web Audio API的浏览器都支持WAV格式,所以,技术上完全可行。

下面这段JS可以得到剪裁后的WAV音频的Blob数据格式:

var blob = bufferToWave(newAudioBuffer, frameCount);

有了Blob数据,接下来事情就简单了。

我们可以直接把Blob数据转换成URL,可以使用 URL.createObjectURL() 生成一个Blob链接。

假设页面上有如下HTML代码:

<audio id="audio" controls=""></audio>

则如下设置,就可以点击上面的 <audio> 元素进行播放了。

audio.src = URL.createObjectURL(blob);

如果要转换成Base64地址,可以这么处理:

var reader2 = new FileReader();
reader2.onload = function(event){
 audio.src = event.target.result;
};
reader2.readAsDataURL(blob);

3) 如果希望上传剪裁的音频

有了Blob数据,上传还不是洒洒水的事情。

可以使用FormData进行传输,例如:

var formData = new FormData();
formData.append('audio', blob);
// 请求走起
var xhr = new XMLHttpRequest();
xhr.open('POST', this.cgiGetImg, true);
// 请求成功
xhr.onload = function () {
};
// 发送数据
xhr.send(formData);

有demo可以进行效果体验的,您可以狠狠地点击这里: 用户上传的MP3音频剪裁并播放demo

使用截图示意如下:

本文地址: https://www.zhangxinxu.com/wordpress/?p=9505

到此这篇关于JS实现audio音频剪裁剪切复制播放与上传的文章就介绍到这了,更多相关js audio音频剪裁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用JavaScript为Kindeditor自定义按钮增加Audio标签

    流程比较简单,主要有以下步骤: 注册插件(按钮.Lang.htmlTags.插件脚本) 基于media插件代码修改 注册插件 首先,全局配置kindeditor参数: KindEditor.lang({ audio : 'MP3' }); KindEditor.options.htmlTags['audio'] = ['src','controls','autoplay','type']; KindEditor.options.htmlTags['source'] = ['src','contr

  • 基于JavaScript实现简单的音频播放功能

    现效果如下: 由于我这边不需要其他按钮,就没写 数据是由后台提供,在这做了个小列子 后台代码 public ActionResult MusicPlayer(int musicId=0) { MusicPlayerModel model = new MusicPlayerModel(); switch (musicId) { default: model.MusicName = "Believe-动画<海贼王>"; model.CoverImg = "/Conte

  • JS实现audio音频剪裁剪切复制播放与上传(步骤详解)

    背景是这样的,用户上传音频文件,可能只需要几十秒就够了,但是常规的音乐都要3~5分钟,80%的流量都是不需要的,要是就这么传上去,其实是流量的浪费,如果可以在前端就进行剪裁,也就是只取前面一段时间的音频,岂不是可以给公司省很多流量费用,前端的业务价值就体现了. 关键如何实现呢? 下面,就以"截取用户上传音频前3秒内容"的需求示意下如何借助Web Audio API实现音频的部分复制与播放功能. 一.不哔哔,直接正题 实现步骤如下. 1. File对象转ArrayBuffer 在Web网

  • JS+Struts2多文件上传实例详解

    本文实例为大家分享了JS Struts2多文件上传的具体代码,供大家参考,具体内容如下 1.JSP页面: JS控制增加删除多个上传文件框,代码如下: <%@ page language="java" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C

  • Mongo复制集同步验证的实例详解

    mongo复制集同步验证的实例详解 第一步:在主节点上插入一条数据 Sql代码 rs0:PRIMARY> use imooc switched to db imooc rs0:PRIMARY> db.imooc.insert({"name":"imooc"}) WriteResult({ "nInserted" : 1 }) 第二步:在从节点查看数据,看是否同步 Sql代码 rs0:SECONDARY> use imooc sw

  • Node.js开发教程之基于OnceIO框架实现文件上传和验证功能

    OnceIO 是 OnceDoc 企业内容(网盘)的底层Web框架,它可以实现模板文件.静态文件的全缓存,运行起来完全不需要I/O操作,并且支持客户端缓存优化,GZIP压缩等(只在第一次压缩),拥有非常好的性能,为您节约服务器成本.它的模块化功能,可以让你的Web进行分布式存储,即一个扩展包里即包含前端.后端和数据库定义,只需通过添加/删除目录的方式就可实现功能删减,实现真正的模块化扩展.这里是介绍如何使用OnceIO的一系列文章. 在这一章节中,我们将为大家演示如何使用 OnceIO 实现文件

  • js监听键盘事件的方法_原生和jquery的区别详解

    经常需要监听键盘的事件,以便做更好的操作,基本原理是:监听全局键盘,每一个键盘,当用户按下某一按键时,返回对应的键值,然后再判断用户按下了哪一科按键,键值对应按键的名称在最下面列出,自行比对.去以下介绍两种不同的方式 原生键盘监听事件:按下一次按键,分为三个过程,按下-按住-松开 onkeydown:某个键被按下 onkeypress:某个键盘的键被按下或按住 onkeyup:某个键盘的键被松开 使用方法,一般来说,键盘监听是直接在整个页面文档上进行监听的,也就是document上 以下是原生的

  • Linux 中的文件复制cp命令和scp命令详解

    Linux 中的文件复制cp命令和scp命令详解 在使用操作系统的使用过程中,常常需要复制文件到本地或者传输文件到其他电脑上,这时候用到两个命令cp和scp. cp命令用来复制文件或者目录.scp是secure copy的简写,用来在Linux下进行加密的远程传输文件或者目录. cp和scp是Linux中功能强大且常用的的命令,下面就介绍下cp和scp两个命令的使用. cp命令 cp命令可以复制一个文件,可以是单个文件复制也可以是整个目录复制,命令的使用方式: cp [options] sour

  • JS写XSS cookie stealer来窃取密码的步骤详解

    JavaScript是web中最常用的脚本开发语言,js可以自动执行站点组件,管理站点内容,在web业内实现其他有用的函数.JS可以有很多的函数可以用做恶意用途,包括窃取含有密码等内容的用户cookie. Cookie是站点请求和保持特定访问页面的信息.Cookie含有访问的方式.时间.用户名密码等认证信息等.当用户访问给定站点时,必须使用cookie:如果攻击者可以拦截cookie,就可以利用cookie窃取用户的一些信息.对某个特定的域名,使用JS可以保存或修改用户的cookie.也就是说,

  • JS求1到任意数之间的所有质数的方法详解

    何为质数: 只能被1 和 自身 整除的数; 方法: 利用js中求模, 看是否有余数. ---> 3%2 = 1; 5%2 = 3......... 代码如下: function test (n) { // 判断一个数是否能被自身小的正整数(除开1和自身)整除.如果能整除则不是质数,否则反之. for(var k = 2;k < n;k++) { if(n % k === 0) { return false; } } return ture; } 以上方法是为判断一个数是否为质数; 那如何判断1

  • JavaScript复制变量三种方法实例详解

    这篇文章主要介绍了JavaScript复制变量三种方法实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 直接将一个变量赋给另一个变量时,系统并不会创造一个新的变量,而是将原变量的地址赋给了新变量名.举个栗子: 复制代码 复制代码 let obj = { a: 1, b: 2, }; let copy = obj; obj.a = 5; console.log(copy.a); // Result // a = 5; // 更改obj的值,

  • vue使用AES.js的步骤详解

    AES加密的使用 对数据传输加密.解密处理---AES.js 第一步: 在vue中安装crypto-js依赖 npm install crypto-js --save-dev 第二步: 在static目录下新建一个AES.js文件,例如: 第三步: 在AES.js中填写如下代码 import CryptoJS from "crypto-js"; // npm install crypto-js --save-dev //随机生成指定数量的32进制key export default {

随机推荐