2017-03-02 60 views
8

我想通过混合两种音频源,把一首歌曲作为另一个后台为单一来源。混合两种音频缓冲,把一个在另一个背景下,通过使用网络音频阿比

例如,我有输入:

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

而且脚本这个文件解码:

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

所以,我怎么可以搭配这个源到一个源?例如,我有两个文件,我怎么能把一个源作为另一个源的背景,并把这个混合成单一源?

另一种情况:如果我从麦克风读取输入流,例如我想将此输入放在背景歌曲(某种卡拉OK)上,可以在html5支持的客户端上完成此项工作?性能如何?也许更好的方法来混合服务器端的这些音频源?

如果有可能,所以什么可能的实现mixAudioSources功能?

谢谢。

+0

你尝试过这么远吗? –

+1

看看交叉衰减部分https://www.html5rocks.com/en/tutorials/webaudio/intro/ –

+0

嗨。在本文中,最接近我的问题的例子是同时播放两个来源。对我没有太大帮助,因为我需要将一个单一来源上传并保存在服务器上。 –

回答

3

两种方法最初发布在Is it possible to mix multiple audio files on top of each other preferably with javascript,调整为处理File对象在change事件<input type="file">元素。

第一种方法利用OfflineAudioContext()AudioContext.createBufferSource()AudioContext.createMediaStreamDestination()Promise构造,Promise.all()MediaRecorder()混合音轨,然后提供下载的混合音频文件。

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>

第二种方法使用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使用GainNode降低选定为“背景” plnkr plnkr.co/edit/5c36gNgO6bvtFGjDoh02?p=preview – guest271314

+0

大答案音轨的音量。谢谢 –