Javascript 混合两个音频缓冲区,使用web音频Api将一个放在另一个的背景上
我想混合两个音频源,把一首歌作为另一首歌的背景放到一个源中 例如,我有以下输入:Javascript 混合两个音频缓冲区,使用web音频Api将一个放在另一个的背景上,javascript,html5-audio,web-audio-api,Javascript,Html5 Audio,Web Audio Api,我想混合两个音频源,把一首歌作为另一首歌的背景放到一个源中 例如,我有以下输入: <input id="files" type="file" name="files[]" multiple onchange="handleFilesSelect(event)"/> 和解码此文件的脚本: window.AudioContext = window.AudioContext || window.webkitAudioContext; var context = new window.
<input id="files" type="file" name="files[]" multiple onchange="handleFilesSelect(event)"/>
和解码此文件的脚本:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new window.AudioContext();
var sources = [];
var files = [];
var mixed = {};
function handleFilesSelect(event){
if(event.target.files.length <= 1)
return false;
files = event.target.files;
readFiles(mixAudioSources);
}
function readFiles(index, callback){
var freader = new FileReader();
var i = index ? index : 0;
freader.onload = function (e) {
context.decodeAudioData(e.target.result, function (buf) {
sources[i] = context.createBufferSource();
sources[i].connect(context.destination);
sources[i].buffer = buf;
if(files.length > i+1){
readFiles(i + 1, callback);
} else {
if(callback){
callback();
}
}
});
};
freader.readAsArrayBuffer(files[i]);
}
function mixAudioSources(){
//So on our scenario we have here two decoded audio sources in "sources" array.
//How we can mix that "sources" into "mixed" variable by putting "sources[0]" as background of "sources[1]"
}
window.AudioContext=window.AudioContext | | window.webkitadiocontext;
var context=new window.AudioContext();
风险值来源=[];
var文件=[];
var mixed={};
函数句柄文件选择(事件){
if(event.target.files.length i+1){
读取文件(i+1,回调);
}否则{
如果(回调){
回调();
}
}
});
};
readAsArrayBuffer(文件[i]);
}
函数mixAudioSources(){
//在我们的场景中,我们有两个解码的音频源在“源”数组中。
//如何通过将“sources[0]”作为“sources[1]的背景,将“sources”混合到“mixed”变量中
}
那么,我如何将这些源混合到一个源中呢?例如,我有两个文件,如何将一个源作为另一个源的背景,并将此混合放入单个源中
另一个场景:例如,如果我从麦克风读取输入流,并且我想将此输入放在背景歌曲(某种卡拉OK)上,是否可以在支持html5的客户端上执行此工作?性能如何?也许在服务器端混合这些音频源的更好方法
如果可能,那么mixAudioSources功能的可能实现是什么
谢谢。两种方法最初发布在,调整为在
元素的更改事件中处理文件
对象
第一种方法利用OfflineAudioContext()
,AudioContext.createBufferSource()
,AudioContext.createMediaStreamDestination()
,Promise
构造函数,Promise.all()
,MediaRecorder()
来混合音频曲目,然后提供混合音频文件供下载
var div=document.querySelector(“div”);
功能手柄文件选择(输入){
div.innerHTML=“正在加载音频曲目..请稍候”;
var files=Array.from(input.files);
风险值持续时间=60000;
var chunks=[];
var audio=新的AudioContext();
var mixedAudio=audio.createMediaStreamDestination();
var player=新音频();
var语境;
无功记录仪;
var description=“”;
player.controls=“controls”;
函数get(文件){
description+=file.name.replace(/\..*\s+/g,“”);
返回新承诺(功能(解决、拒绝){
var reader=新文件读取器;
reader.readAsArrayBuffer(文件);
reader.onload=函数(){
解析(reader.result)
}
})
}
功能停止混合(持续时间,…介质){
设置超时(功能(介质){
media.forEach(函数(节点){
node.stop()
})
},持续时间,媒体)
}
Promise.all(files.map(get)).then(函数(数据){
var len=Math.max.apply(数学,数据,映射(函数,缓冲区){
返回缓冲区
}));
上下文=新的OfflineAudioContext(2,len,44100);
返回Promise.all(data.map)(函数(缓冲区){
返回音频。解码音频数据(缓冲区)
.then(函数(缓冲源){
var source=context.createBufferSource();
source.buffer=缓冲源;
source.connect(context.destination);
返回source.start()
})
}))
.然后(函数(){
返回上下文。startRendering()
})
.then(函数(renderedBuffer){
返回新承诺(函数(解析){
var mix=audio.createBufferSource();
mix.buffer=renderedBuffer;
混合。连接(音频。目的地);
mix.connect(mixedAudio);
记录器=新的媒体记录器(mixedAudio.stream);
记录器。启动(0);
混合启动(0);
div.innerHTML=“播放和录制曲目..”;
//60秒后停止播放和录制
stopMix(持续时间、混合、记录器)
recorder.ondataavailable=功能(事件){
推送(事件数据);
};
recorder.onstop=功能(事件){
var blob=新blob(块{
“类型”:“音频/ogg;编解码器=作品”
});
控制台日志(“记录完成”);
解析(blob)
};
})
})
.then(函数(blob){
控制台日志(blob);
div.innerHTML=“混合音频曲目可供下载..”;
var audioDownload=URL.createObjectURL(blob);
var a=document.createElement(“a”);
a、 下载=description++“+blob.type.replace(/.+\/.+/g)”;
a、 href=音频下载;
a、 innerHTML=a.download;
文件.正文.附件(a);
a、 insertAdjacentHTML(“afterend”,“br>”);
player.src=音频下载;
document.body.appendChild(播放器);
})
})
.catch(函数(e){
控制台日志(e)
});
}
我只想补充一下的优秀答案,并在这里发布基于第二个场景答案的解决方案(第二个来源是麦克风输入)。实际上是客户卡拉OK。脚本:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new window.AudioContext();
var playbackTrack = null;
function handleFileSelect(event){
var file = event.files[0];
var freader = new FileReader();
freader.onload = function (e) {
context.decodeAudioData(e.target.result, function (buf) {
playbackTrack = context.createBufferSource();
playbackTrack.buffer = buf;
var karaokeButton = document.getElementById("karaoke_start");
karaokeButton.style.display = "inline-block";
karaokeButton.addEventListener("click", function(){
startKaraoke();
});
});
};
freader.readAsArrayBuffer(file);
}
function stopMix(duration, mediaRecorder) {
setTimeout(function(mediaRecorder) {
mediaRecorder.stop();
context.close();
}, duration, mediaRecorder)
}
function startKaraoke(){
navigator.mediaDevices.getUserMedia({audio: true,video: false})
.then(function(stream) {
var mixedAudio = context.createMediaStreamDestination();
var merger = context.createChannelMerger(2);
var splitter = context.createChannelSplitter(2);
var duration = 5000;
var chunks = [];
var channel1 = [0,1];
var channel2 = [1, 0];
var gainNode = context.createGain();
playbackTrack.connect(gainNode);
gainNode.connect(splitter);
gainNode.gain.value = 0.5; // From 0 to 1
splitter.connect(merger, channel1[0], channel1[1]);
var microphone = context.createMediaStreamSource(stream);
microphone.connect(splitter);
splitter.connect(merger, channel2[0], channel2[1]);
merger.connect(mixedAudio);
merger.connect(context.destination);
playbackTrack.start(0);
var mediaRecorder = new MediaRecorder(mixedAudio.stream);
mediaRecorder.start(1);
mediaRecorder.ondataavailable = function (event) {
chunks.push(event.data);
}
mediaRecorder.onstop = function(event) {
var player = new Audio();
player.controls = "controls";
var blob = new Blob(chunks, {
"type": "audio/mp3"
});
audioDownload = URL.createObjectURL(blob);
var a = document.createElement("a");
a.download = "karaokefile." + blob.type.replace(/.+\/|;.+/g, "");
a.href = audioDownload;
a.innerHTML = a.download;
player.src = audioDownload;
document.body.appendChild(a);
document.body.appendChild(player);
};
stopMix(duration, mediaRecorder);
})
.catch(function(error) {
console.log('error: ' + error);
});
}
和Html:
<input id="file"
type="file"
name="file"
accept="audio/*"
onchange="handleFileSelect(this)" />
<span id="karaoke_start" style="display:none;background-color:yellow;cursor:pointer;">start karaoke</span>
开始卡拉OK
这里是正在工作的plnkr示例:看看交叉衰落部分Hi。在本文中,与我的问题最接近的示例是同时播放两个源代码。这对我帮助不大,因为我需要上传一个源并保存到服务器上。我明白了,您希望您的用户能够预览他正在做的事情吗?如果是,可能是这样,否则可能在服务器上执行会更好。@OlegYudovich使用GainNode来减少选择为“background”plnkr plnkr.co/edit/5c36gNgO6bvtFGjDoh02?p=previewGreat-answer的音频曲目的音量。谢谢