完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="file" id="ipt">
<br>
<br>
<audio src="" id="audio" controls></audio>
<br>
<br>
<input type="file" id="ipt2">
</body>
<script>
const sliceSize = 1024 * 1024
ipt.onchange = function(e) {
const file = e.target.files[0];
const size = file.size;
const reader = new FileReader();
reader.onload = function(e) {
const res = e.target.result;
const audioCtx = new AudioContext();
audioCtx.decodeAudioData(res, function(audioBuffer) {
const { newAudioBuffer, frameCount } = getNewArrayBuffer(audioBuffer, audioCtx);
// 两种使用方式:
// 1. 给页面元素设置src
const blob = bufferToWave(newAudioBuffer, frameCount);
const audio = document.querySelector('#audio');
audio.src = window.URL.createObjectURL(blob);
// 2. 直接播放
// 创建AudioBufferSourceNode对象
// var source = audioCtx.createBufferSource();
// // 设置AudioBufferSourceNode对象的buffer为复制的3秒AudioBuffer对象
// source.buffer = newAudioBuffer;
// // 这一句是必须的,表示结束,没有这一句没法播放,没有声音
// // 这里直接结束,实际上可以对结束做一些特效处理
// source.connect(audioCtx.destination);
// // 资源开始播放
// source.start();
})
}
reader.readAsArrayBuffer(file);
}
function getNewArrayBuffer(audioBuffer, ctx) {
// 声道数量和采样率
var channels = audioBuffer.numberOfChannels;
var rate = audioBuffer.sampleRate;
// 截取前5秒
var startOffset = 0;
var endOffset = rate * 45;
// 5秒对应的帧数
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就是全新的复制的5秒长度的AudioBuffer对象
return {
newAudioBuffer,
frameCount
};
}
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;
}
}
// 也可使用file的slice对文件进行切片
const ipt2 = document.querySelector('#ipt2');
ipt2.onchange = function(e) {
const file = e.target.files[0];
const size = file.size;
console.log(size)
const list = [];
let n = 0;
let cur = 0;
while (n < size) {
list.push({
file: file.slice(n, n + sliceSize),
i: cur
})
n += sliceSize;
cur++
}
console.log(list)
const reader = new FileReader();
reader.readAsArrayBuffer(list[0].file);
reader.onload = function(e) {
const res = e.target.result;
const ctx = new AudioContext();
console.log(res, ctx);
var source = ctx.createBufferSource();
// 设置AudioBufferSourceNode对象的buffer为复制的3秒AudioBuffer对象
ctx.decodeAudioData(res, function(ab) {
console.log(ab, 'ab')
source.buffer = ab;
source.connect(ctx.destination);
source.start();
})
}
console.log(list, 'file-list')
}
</script>
</html>