问题描述
我对该主题进行了为期 2 天的广泛研究,但没有真正有效的解释作品。
所以流程如下:
- 将
mp3
(商店购买)cbr 320
加载到wavesurfer
- 应用您需要的所有更改
- 将处理后的结果下载回
mp3
文件(不使用服务器)
我见过可以做到这一点的在线应用程序,没有任何东西传输到服务器,一切都发生在浏览器中。
当我们检查 wavesurfer
时,我们可以访问这些:
目标是使用 wavesurfer
中已有的引用来生成下载 mp3
。
据我所知,这可以通过 MediaRecorder
、WebCodecs API
或一些库(例如 lamejs
)来完成。
我试图找到如何使用两种第一种方法来做到这一点的工作示例,但没有运气。我还尝试使用 lamejs
使用他们在 git 上提供的示例来执行此操作,但我从库中收到了难以调试的错误,很可能与提供错误输入有关。
到目前为止,我只能使用以下脚本下载 wav
文件:
handlecopyRegion = (region,instance) => {
var segmentDuration = region.end - region.start;
var originalBuffer = instance.backend.buffer;
var emptySegment = instance.backend.ac.createBuffer(
originalBuffer.numberOfChannels,Math.ceil(segmentDuration * originalBuffer.sampleRate),originalBuffer.sampleRate
);
for (var i = 0; i < originalBuffer.numberOfChannels; i++) {
var chanData = originalBuffer.getChannelData(i);
var emptySegmentData = emptySegment.getChannelData(i);
var mid_data = chanData.subarray(
Math.ceil(region.start * originalBuffer.sampleRate),Math.ceil(region.end * originalBuffer.sampleRate)
);
emptySegmentData.set(mid_data);
}
return emptySegment;
};
bufferToWave = (abuffer,offset,len) => {
var numOfChan = abuffer.numberOfChannels,length = len * numOfChan * 2 + 44,buffer = new ArrayBuffer(length),view = new DataView(buffer),channels = [],i,sample,pos = 0;
// write WAVE header
setUint32(0x46464952); // "RIFF"
setUint32(length - 8); // file length - 8
setUint32(0x45564157); // "WAVE"
setUint32(0x20746d66); // "fmt " chunk
setUint32(16); // length = 16
setUint16(1); // PCM (uncompressed)
setUint16(numOfChan);
setUint32(abuffer.sampleRate);
setUint32(abuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
setUint16(numOfChan * 2); // block-align
setUint16(16); // 16-bit (hardcoded in this demo)
setUint32(0x61746164); // "data" - chunk
setUint32(length - pos - 4); // chunk length
// write interleaved data
for (i = 0; i < abuffer.numberOfChannels; i++)
channels.push(abuffer.getChannelData(i));
while (pos < length) {
for (i = 0; i < numOfChan; i++) {
// interleave channels
sample = Math.max(-1,Math.min(1,channels[i][offset])); // clamp
sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0; // scale to 16-bit signed int
view.setInt16(pos,true); // update data chunk
pos += 2;
}
offset++; // next source sample
}
// 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,true);
pos += 4;
}
};
const cutSelection = this.handlecopyRegion(
this.wavesurfer.regions.list.cut,this.wavesurfer
);
const blob = this.bufferToWave(cutSelection,cutSelection.length);
// you can Now download wav from the blob
有没有办法避免制作 wav
并立即制作 mp3
并下载它,或者如果没有制作 mp3
从那个 wav
,如果可以,它如何完成了吗?
我主要尝试使用 wavesurfer.backend.buffer
作为输入,因为这个引用是 AudioBuffer
并且访问 .getChannelData(0|1)
会给你 left
和 right
通道。但是没有完成任何事情,也许我想错了。
解决方法
好的,下面是我们需要做的步骤:
- 从 wavesurfer 播放器获取
buffer
数据 - 分析
buffer
以获得通道数(立体声或单声道)、通道数据和采样率。 - 使用
lamejs
库将缓冲区转换为 MP3blob
文件 - 然后我们可以从 blob 获取下载链接
这是一个快速的DEMO
还有JS代码:
function downloadMp3() {
var MP3Blob = analyzeAudioBuffer(wavesurfer.backend.buffer);
console.log('here is your mp3 url:');
console.log(URL.createObjectURL(MP3Blob));
}
function analyzeAudioBuffer(aBuffer) {
let numOfChan = aBuffer.numberOfChannels,btwLength = aBuffer.length * numOfChan * 2 + 44,btwArrBuff = new ArrayBuffer(btwLength),btwView = new DataView(btwArrBuff),btwChnls = [],btwIndex,btwSample,btwOffset = 0,btwPos = 0;
setUint32(0x46464952); // "RIFF"
setUint32(btwLength - 8); // file length - 8
setUint32(0x45564157); // "WAVE"
setUint32(0x20746d66); // "fmt " chunk
setUint32(16); // length = 16
setUint16(1); // PCM (uncompressed)
setUint16(numOfChan);
setUint32(aBuffer.sampleRate);
setUint32(aBuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
setUint16(numOfChan * 2); // block-align
setUint16(16); // 16-bit
setUint32(0x61746164); // "data" - chunk
setUint32(btwLength - btwPos - 4); // chunk length
for (btwIndex = 0; btwIndex < aBuffer.numberOfChannels; btwIndex++)
btwChnls.push(aBuffer.getChannelData(btwIndex));
while (btwPos < btwLength) {
for (btwIndex = 0; btwIndex < numOfChan; btwIndex++) {
// interleave btwChnls
btwSample = Math.max(-1,Math.min(1,btwChnls[btwIndex][btwOffset])); // clamp
btwSample = (0.5 + btwSample < 0 ? btwSample * 32768 : btwSample * 32767) | 0; // scale to 16-bit signed int
btwView.setInt16(btwPos,true); // write 16-bit sample
btwPos += 2;
}
btwOffset++; // next source sample
}
let wavHdr = lamejs.WavHeader.readHeader(new DataView(btwArrBuff));
//Stereo
let data = new Int16Array(btwArrBuff,wavHdr.dataOffset,wavHdr.dataLen / 2);
let leftData = [];
let rightData = [];
for (let i = 0; i < data.length; i += 2) {
leftData.push(data[i]);
rightData.push(data[i + 1]);
}
var left = new Int16Array(leftData);
var right = new Int16Array(rightData);
//STEREO
if (wavHdr.channels===2)
return bufferToMp3(wavHdr.channels,wavHdr.sampleRate,left,right);
//MONO
else if (wavHdr.channels===1)
return bufferToMp3(wavHdr.channels,data);
function setUint16(data) {
btwView.setUint16(btwPos,data,true);
btwPos += 2;
}
function setUint32(data) {
btwView.setUint32(btwPos,true);
btwPos += 4;
}
}
function bufferToMp3(channels,sampleRate,right = null) {
var buffer = [];
var mp3enc = new lamejs.Mp3Encoder(channels,128);
var remaining = left.length;
var samplesPerFrame = 1152;
for (var i = 0; remaining >= samplesPerFrame; i += samplesPerFrame) {
if (!right)
{
var mono = left.subarray(i,i + samplesPerFrame);
var mp3buf = mp3enc.encodeBuffer(mono);
}
else {
var leftChunk = left.subarray(i,i + samplesPerFrame);
var rightChunk = right.subarray(i,i + samplesPerFrame);
var mp3buf = mp3enc.encodeBuffer(leftChunk,rightChunk);
}
if (mp3buf.length > 0) {
buffer.push(mp3buf);//new Int8Array(mp3buf));
}
remaining -= samplesPerFrame;
}
var d = mp3enc.flush();
if(d.length > 0){
buffer.push(new Int8Array(d));
}
var mp3Blob = new Blob(buffer,{type: 'audio/mpeg'});
//var bUrl = window.URL.createObjectURL(mp3Blob);
// send the download link to the console
//console.log('mp3 download:',bUrl);
return mp3Blob;
}
如果您对代码有任何疑问,请告诉我