Is it possible to mix multiple audio files on top of each other preferably with javascript -
i want combine audio clips, layered on top of each other play synchronously , saved in new audio file. appreciated. i've done digging online, couldn't find definitive answer whether or not many of tools available far javascript audio editing librarys go (mix.js example) capable.
yes, possible using offlineaudiocontext()
or audiocontext.createchannelmerger()
, creating mediastream
. see phonegap mixing audio files , web audio api.
you can use fetch()
or xmlhttprequest()
retrieve audio resource arraybuffer
, audiocontext.decodeaudiodata()
create audiobuffersourcenode
response; offlineaudiocontext()
render merged audio, audiocontext
, audiocontext.createbuffersource()
, audiocontext.createmediastreamdestination()
, mediarecorder()
record stream; promise.all()
, promise()
constructor, .then()
process asynchronous requests fetch()
, audiocontext.decodeaudiodata()
, pass resulting mixed audio blob
@ stop
event of mediarecorder
.
connect each audiocontext
audiobuffersourcenode
offlineaudiocontext.destination
, call .start()
on each node; call offlineaudiocontext.startrendering()
; create new audiocontext
node, connect renderedbuffer
; call .createmediastreamdestination()
on audiocontext
create mediastream
merged audio buffers, pass .stream
mediarecorder()
, @ stop
event of mediarecorder
, create blob url
of blob
of recorded audio mix url.createobjecturl()
, can downloaded using <a>
element download
attribute , href
set blob url
.
var sources = ["https://upload.wikimedia.org/wikipedia/commons/b/be/" + "hidden_tribe_-_didgeridoo_1_live.ogg" , "https://upload.wikimedia.org/wikipedia/commons/6/6e/" + "micronesia_national_anthem.ogg"]; var description = "hiddentribeanthem"; var context; var recorder; var div = document.queryselector("div"); var duration = 60000; var chunks = []; var audio = new audiocontext(); var mixedaudio = audio.createmediastreamdestination(); var player = new audio(); player.controls = "controls"; function get(src) { return fetch(src) .then(function(response) { return response.arraybuffer() }) } function stopmix(duration, ...media) { settimeout(function(media) { media.foreach(function(node) { node.stop() }) }, duration, media) } promise.all(sources.map(get)).then(function(data) { var len = math.max.apply(math, data.map(function(buffer) { return buffer.bytelength })); context = new offlineaudiocontext(2, len, 44100); return promise.all(data.map(function(buffer) { return audio.decodeaudiodata(buffer) .then(function(buffersource) { var source = context.createbuffersource(); source.buffer = buffersource; source.connect(context.destination); return source.start() }) })) .then(function() { return context.startrendering() }) .then(function(renderedbuffer) { return new promise(function(resolve) { var mix = audio.createbuffersource(); mix.buffer = renderedbuffer; mix.connect(audio.destination); mix.connect(mixedaudio); recorder = new mediarecorder(mixedaudio.stream); recorder.start(0); mix.start(0); div.innerhtml = "playing , recording tracks.."; // stop playback , recorder in 60 seconds stopmix(duration, mix, recorder) recorder.ondataavailable = function(event) { chunks.push(event.data); }; recorder.onstop = function(event) { var blob = new blob(chunks, { "type": "audio/ogg; codecs=opus" }); console.log("recording complete"); resolve(blob) }; }) }) .then(function(blob) { console.log(blob); div.innerhtml = "mixed audio tracks ready download.."; var audiodownload = url.createobjecturl(blob); var = document.createelement("a"); a.download = description + "." + blob.type.replace(/.+\/|;.+/g, ""); a.href = audiodownload; a.innerhtml = a.download; document.body.appendchild(a); a.insertadjacenthtml("afterend", "<br>"); player.src = audiodownload; document.body.appendchild(player); }) }) .catch(function(e) { console.log(e) });
<!doctype html> <html> <head> </head> <body> <div>loading audio tracks.. please wait</div> </body> </html>
you can alternatively utilize audiocontext.createchannelmerger()
, audiocontext.createchannelsplitter()
var sources = ["/path/to/audoi1", "/path/to/audio2"]; var description = "mix"; var chunks = []; var channels = [[0, 1], [1, 0]]; var audio = new audiocontext(); var player = new audio(); var merger = audio.createchannelmerger(2); var splitter = audio.createchannelsplitter(2); var mixedaudio = audio.createmediastreamdestination(); var duration = 60000; var context; var recorder; var audiodownload; player.controls = "controls"; function get(src) { return fetch(src) .then(function(response) { return response.arraybuffer() }) } function stopmix(duration, ...media) { settimeout(function(media) { media.foreach(function(node) { node.stop() }) }, duration, media) } promise.all(sources.map(get)).then(function(data) { return promise.all(data.map(function(buffer, index) { return audio.decodeaudiodata(buffer) .then(function(buffersource) { var channel = channels[index]; var source = audio.createbuffersource(); source.buffer = buffersource; source.connect(splitter); splitter.connect(merger, channel[0], channel[1]); return source }) })) .then(function(audionodes) { merger.connect(mixedaudio); merger.connect(audio.destination); recorder = new mediarecorder(mixedaudio.stream); recorder.start(0); audionodes.foreach(function(node) { node.start(0) }); stopmix(duration, ...audionodes, recorder); recorder.ondataavailable = function(event) { chunks.push(event.data); }; recorder.onstop = function(event) { var blob = new blob(chunks, { "type": "audio/ogg; codecs=opus" }); audiodownload = url.createobjecturl(blob); var = document.createelement("a"); a.download = description + "." + blob.type.replace(/.+\/|;.+/g, ""); a.href = audiodownload; a.innerhtml = a.download; player.src = audiodownload; document.body.appendchild(a); document.body.appendchild(player); }; }) }) .catch(function(e) { console.log(e) });
Comments
Post a Comment