2017-03-02 1 views
8

Je veux mélanger deux sources audio en mettant une chanson comme arrière-plan d'une autre source unique.Mélanger deux tampons audio, en mettre un sur le fond d'un autre en utilisant web Audio Api

par exemple, j'ai entrée:

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

Et script pour décoder les fichiers:

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

Alors, comment je peux mélanger ces sources en une seule source? Par exemple, j'ai deux fichiers, comment puis-je mettre une source en arrière-plan d'un autre et mettre ce mélange en source unique? Un autre scénario: si je lis le flux d'entrée du microphone par exemple et que je veux mettre cette entrée sur la chanson de fond (une sorte de karaoké), il est possible de faire ce travail sur le client avec le support html5? Qu'en est-il des performances? Peut-être meilleur moyen de mélanger ces sources audio sur le côté serveur?

Si c'est possible, alors quelle est l'implémentation possible de mixAudioSources?

Merci.

+0

Qu'avez-vous essayé jusqu'à présent? –

+1

Jetez un oeil à la section de fondu enchaîné https://www.html5rocks.com/fr/tutoriels/webaudio/intro/ –

+0

Salut. Dans cet article, l'exemple le plus proche de ma question est de jouer deux sources en même temps. Ne m'aide pas tellement parce que j'ai besoin d'une seule source à télécharger et à enregistrer sur le serveur. –

Répondre

3

Deux approche initialement publié à Is it possible to mix multiple audio files on top of each other preferably with javascript, ajusté pour traiter File objets à change événement de <input type="file"> élément.

La première approche utilise OfflineAudioContext(), AudioContext.createBufferSource(), AudioContext.createMediaStreamDestination(), constructeur Promise, Promise.all(), MediaRecorder() mixer les pistes audio, puis offrent un fichier audio mixte pour le téléchargement.

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>

La seconde approche utilise AudioContext.createChannelMerger(), AudioContext.createChannelSplitter()

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

 
function handleFilesSelect(input) { 
 

 
    div.innerHTML = "loading audio tracks.. please wait"; 
 
    var files = Array.from(input.files); 
 
    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; 
 
    var description = ""; 
 

 
    player.controls = "controls"; 
 

 
    function get(file) { 
 
    description += file.name.replace(/\..*|\s+/g, ""); 
 
    console.log(description); 
 
    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) { 
 
     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, index) { 
 
      node.start(0) 
 
      }); 
 
      
 
      div.innerHTML = "playing and recording tracks.."; 
 
      
 
      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) 
 
    }); 
 
}
<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
</head> 
 

 
<body> 
 
    <input id="files" 
 
     type="file" 
 
     name="files[]" 
 
     accept="audio/*" 
 
     multiple onchange="handleFilesSelect(this)" /> 
 
    <div></div> 
 
</body> 
 

 
</html>

+0

@OlegYudovich Utilisation de GainNode pour réduire le volume de la piste audio sélectionnée comme "arrière-plan" plnkr plnkr.co/edit/5c36gNgO6bvtFGjDoh02?p=preview – guest271314

+0

Grande réponse. Merci –