javascript audio mix

¿Es posible mezclar varios archivos de audio uno encima del otro, preferiblemente con JavaScript?



mix (1)

Quiero combinar clips de audio, en capas uno encima del otro para que se reproduzcan sincrónicamente y se guarden en un nuevo archivo de audio. Cualquier ayuda sería muy apreciada. Investigué en línea, pero no pude encontrar una respuesta definitiva sobre si muchas de las herramientas disponibles en lo que respecta a las bibliotecas de edición de audio Javascript (Mix.js, por ejemplo) son capaces.


Sí, es posible usar OfflineAudioContext() o AudioContext.createChannelMerger() y crear MediaStream . Ver Phonegap mezclando archivos de audio , Web Audio API .

Puede usar fetch() o XMLHttpRequest() para recuperar recursos de audio como ArrayBuffer , AudioContext.decodeAudioData() para crear un AudioBufferSourceNode partir de la respuesta; OfflineAudioContext() para renderizar audio combinado, AudioContext , AudioContext.createBufferSource() , AudioContext.createMediaStreamDestination() , MediaRecorder() para grabar el flujo; Promise.all() , Promise() constructor, .then() para procesar solicitudes asincrónicas para fetch() , AudioContext.decodeAudioData() , pasar Blob audio mixto resultante en el evento de MediaRecorder de MediaRecorder .

Conecte cada AudioContext AudioBufferSourceNode a OfflineAudioContext.destination , llame a .start() en cada nodo; llame a OfflineAudioContext.startRendering() ; cree un nuevo nodo AudioContext , conecte renderedBuffer ; llame a .createMediaStreamDestination() en AudioContext para crear un MediaStream partir de buffers de audio combinados, pase .stream a MediaRecorder() , en el evento de MediaRecorder de MediaRecorder , cree Blob URL de Blob de la mezcla de audio grabada con URL.createObjectURL() , que se puede descargar usando el elemento <a> con el atributo de download y href establecido en 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 and recording tracks.."; // stop playback and 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 for download.."; var audioDownload = URL.createObjectURL(blob); var a = 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>

Alternativamente, puede utilizar 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 a = 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) });