Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
303 views
in Technique[技术] by (71.8m points)

javascript - Mixing two audio buffers, put one on background of another by using web Audio Api

I want to mix two audio sources by put one song as background of another into single source.

for example, i have input :

<input id="files" type="file" name="files[]" multiple onchange="handleFilesSelect(event)"/>

And script to decode this files:

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]"
}

So how i can mix this sources into one source? For example i have two files, how i can put one source as background of another and put this mix into single source?

Another scenario: if i read input stream from microphone for example and i want to put this input on background song (some kind of karaoke) it is possible to do this work on client with html5 support? What about performance? Maybe better way to mix this audio sources on server side?

If it possible, so what the possible implementation of mixAudioSources function?

Thanks.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Two approach originally posted at Is it possible to mix multiple audio files on top of each other preferably with javascript, adjusted to process File objects at change event of <input type="file"> element.

The first approach utilizes OfflineAudioContext(), AudioContext.createBufferSource(), AudioContext.createMediaStreamDestination(), Promise constructor, Promise.all(), MediaRecorder() to mix audio tracks, then offer mixed audio file for download.

var div = document.querySelector("div");

function handleFilesSelect(input) {
  div.innerHTML = "loading audio tracks.. please wait";
  var files = Array.from(input.files);
  var duration = 60000;
  var chunks = [];
  var audio = new AudioContext();
  var mixedAudio = audio.createMediaStreamDestination();
  var player = new Audio();
  var context;
  var recorder;
  var description = "";
  
  player.controls = "controls";
  
  function get(file) {
    description += file.name.replace(/..*|s+/g, "");
    return new Promise(function(resolve, reject) {
      var reader = new FileReader;
      reader.readAsArrayBuffer(file);
      reader.onload = function() {
        resolve(reader.result)
      }
    })
  }

  function stopMix(duration, ...media) {
    setTimeout(function(media) {
      media.forEach(function(node) {
        node.stop()
      })
    }, duration, media)
  }

  Promise.all(files.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>
  <input id="files" 
         type="file" 
         name="files[]" 
         accept="audio/*" 
         multiple 
         onchange="handleFilesSelect(this)" />
  <div></div>
</body>

</html>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...