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


Posted in Javascript onJuly 28, 2020

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的,如下图所示:

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

凡事支持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

使用截图示意如下:

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

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

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

Javascript 相关文章推荐
js cookies实现简单统计访问次数
Nov 24 Javascript
js加强的经典分页实例
Mar 15 Javascript
javascript实现汉字转拼音代码分享
Apr 20 Javascript
浅析JavaScript Array和string的转换(推荐)
May 20 Javascript
Bootstrap模态框禁用空白处点击关闭
Oct 20 Javascript
AngularJS指令用法详解
Nov 02 Javascript
详解JavaScript中return的用法
May 08 Javascript
微信小程序实现缓存根据不同的id来进行设置和读取缓存
Jun 12 Javascript
JavaScript实现JSON合并操作示例【递归深度合并】
Sep 07 Javascript
写gulp遇到的ES6问题详解
Dec 03 Javascript
js中实例与对象的区别讲解
Jan 21 Javascript
layui的表单提交以及验证和修改弹框的实例
Sep 09 Javascript
JavaScript中window和document用法详解
Jul 28 #Javascript
vue中echarts引入中国地图的案例
Jul 28 #Javascript
vue 监听窗口变化对页面部分元素重新渲染操作
Jul 28 #Javascript
JavaScript文档加载模式以及元素获取
Jul 28 #Javascript
javascript实现贪吃蛇小游戏
Jul 28 #Javascript
Element DateTimePicker日期时间选择器的使用示例
Jul 27 #Javascript
Js图片点击切换轮播实现代码
Jul 27 #Javascript
You might like
php iconv() : Detected an illegal character in input string
2010/12/05 PHP
国外十大最流行的PHP框架排名
2013/07/04 PHP
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
2016/12/14 PHP
jQuery 研究心得 取得属性的值
2007/11/30 Javascript
IE浏览器兼容Firefox的JS脚本的代码
2008/10/23 Javascript
jquery实现居中弹出层代码
2010/08/25 Javascript
手机端网页点击链接触发自动拨打或保存电话的示例代码
2014/08/15 Javascript
深入理解js中this的用法
2016/05/28 Javascript
javascript显示系统当前时间代码
2016/12/29 Javascript
通过jsonp获取json数据实现AJAX跨域请求
2017/01/22 Javascript
简单实现bootstrap选项卡效果
2017/02/08 Javascript
docker中编译nodejs并使用nginx启动
2017/06/23 NodeJs
Angular4学习笔记之准备和环境搭建项目
2017/08/01 Javascript
p5.js入门教程之小球动画示例代码
2018/03/15 Javascript
原生js封装的ajax方法示例
2018/08/02 Javascript
webstorm+vue初始化项目的方法
2018/10/18 Javascript
Nuxt.js SSR与权限验证的实现
2018/11/21 Javascript
Python3.2中的字符串函数学习总结
2015/04/23 Python
Python简单进程锁代码实例
2015/04/27 Python
django主动抛出403异常的方法详解
2019/01/04 Python
一文了解Python并发编程的工程实现方法
2019/05/31 Python
python flask框架实现传数据到js的方法分析
2019/06/11 Python
Python 日期区间处理 (本周本月上周上月...)
2019/08/08 Python
Python如何读写二进制数组数据
2020/08/01 Python
Wiggle中国:英国骑行、跑步、游泳 & 铁三运动装备专卖网店
2016/08/02 全球购物
PurCotton全棉时代官网:100%天然棉花生产的生活护理用品
2016/11/18 全球购物
Lookfantastic阿联酋官网:英国知名美妆护肤购物网站
2020/05/26 全球购物
会计出纳员的自我评价
2014/01/15 职场文书
人力资源主管的岗位职责
2014/03/15 职场文书
优秀求职信
2014/05/29 职场文书
大学感恩节活动策划方案
2014/10/11 职场文书
2014年学校体育工作总结
2014/12/08 职场文书
保管员岗位职责
2015/02/14 职场文书
Memcached介绍及php-memcache扩展安装
2021/04/01 PHP
深入解读Java三大集合之map list set的用法
2021/11/11 Java/Android
Windows 11要来了?微软文档揭示Win11太阳谷 / Win10有两个不同版本
2021/11/21 数码科技