本文详解如何利用Web Audio API在前端实现音频文件的截取、播放及上传功能。通过将File对象转换为ArrayBuffer再解码为AudioBuffer,可精确提取指定时长的音频数据。教程涵盖从3秒音频截取、直接播放、生成WAV Blob链接到FormData上传的完整代码实现,帮助开发者节省服务器流量并提升用户体验。
前端音频剪裁的业务价值
在用户上传音频文件的场景中,往往只需保留几十秒的核心内容,而原始音乐文件通常长达3至5分钟,其中80%以上的数据属于冗余流量。若直接上传完整文件,不仅浪费带宽资源,也增加服务器负担。若能在前端完成音频剪裁,仅提取所需时长的片段进行上传,将显著降低流量成本,体现前端技术的实际业务价值。
具体该如何实现这一功能呢?
下文将以“截取用户上传音频的前3秒内容”为例,演示如何借助Web Audio API实现音频片段的复制与播放。
一、直接切入主题
具体实现步骤如下。
File对象转ArrayBuffer
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类型。
ArrayBuffer转AudioBuffer
2. 将ArrayBuffer转换为AudioBuffer
这里的ArrayBuffer相当于将音频文件数组化,可以理解为将音频文件分解成若干段,存入带有地址的“小屋子”中,在计算机领域称为“缓冲区”,即单词Buffer的含义。
所谓音频剪裁,本质上是希望复制音频前一段时间的内容。
然而,ArrayBuffer中的数据并未分类,而是统一分解,因此难以准确提取某一段音频数据。
因此,需要将其转换为AudioBuffer,这是一种纯粹的音频数据格式,便于提取。
AudioBuffer是一个仅包含音频数据的数据对象,属于Web Audio API中的概念。
既然提到Web Audio API,不妨顺便一提……想了想,还是不展开,因为该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
});
复制AudioBuffer前3秒数据
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对象。
使用newAudioBuffer进行播放与上传
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音频剪裁内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!