最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Is it possible to mix multiple audio files on top of each other preferably with javascript - Stack Overflow

programmeradmin3浏览0评论

I want to bine audio clips, layered on top of each other so that they play synchronously and are saved in a new audio file. Any help would be much appreciated. I've done some digging online, but couldn't find a definitive answer as to whether or not many of the tools available as far as Javascript audio editing librarys go (Mix.js for example) are capable.

I want to bine audio clips, layered on top of each other so that they play synchronously and are saved in a new audio file. Any help would be much appreciated. I've done some digging online, but couldn't find a definitive answer as to whether or not many of the tools available as far as Javascript audio editing librarys go (Mix.js for example) are capable.

Share Improve this question edited Mar 25, 2021 at 21:03 miken32 42.7k16 gold badges121 silver badges172 bronze badges asked Nov 13, 2016 at 2:58 Ethan StepanianEthan Stepanian 1411 gold badge1 silver badge6 bronze badges 4
  • 1 Is it just a one time thing? Does it need to be done programmatically? Why preferably JS? – dgo Commented Nov 13, 2016 at 3:35
  • Also - if the answers to my above questions are 'yes', 'no', ... Then use a DAW. Plenty of freeware and open source options available. For what you want to do, pretty easy, low learning curve. – dgo Commented Nov 13, 2016 at 16:06
  • After you answer questions above .... is this from a browser or nodejs ? – Scott Stensland Commented Nov 13, 2016 at 20:30
  • The dev I was planning on delegating this to was most fortable using javascript, but that’s no longer a priority. This needs to be done programmatically. If it can be done on the front-end, that would be best, but I understand the solution might need to be on the backend. – Ethan Stepanian Commented Nov 15, 2016 at 15:49
Add a ment  | 

1 Answer 1

Reset to default 12

Yes, it is possible using OfflineAudioContext() or AudioContext.createChannelMerger() and creating a MediaStream. See Phonegap mixing audio files , Web Audio API.

You can use fetch() or XMLHttpRequest() to retrieve audio resource as an ArrayBuffer, AudioContext.decodeAudioData() to create an AudioBufferSourceNode from response; OfflineAudioContext() to render merged audio, AudioContext, AudioContext.createBufferSource(), AudioContext.createMediaStreamDestination() , MediaRecorder() to record stream; Promise.all(), Promise() constructor, .then() to process asynchronous requests to fetch(), AudioContext.decodeAudioData(), pass resulting mixed audio Blob at stop event of MediaRecorder.

Connect each AudioContext AudioBufferSourceNode to OfflineAudioContext.destination, call .start() on each node; call OfflineAudioContext.startRendering(); create new AudioContext node, connect renderedBuffer; call .createMediaStreamDestination() on AudioContext to create a MediaStream from merged audio buffers, pass .stream to MediaRecorder(), at stop event of MediaRecorder, create Blob URL of Blob of recorded audio mix with URL.createObjectURL(), which can be downloaded using <a> element with download attribute and href set to Blob URL.

var sources = ["https://upload.wikimedia/wikipedia/mons/b/be/"
               + "Hidden_Tribe_-_Didgeridoo_1_Live.ogg"
               , "https://upload.wikimedia/wikipedia/mons/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 plete");
            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>

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 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)
  });
发布评论

评论列表(0)

  1. 暂无评论