【问题标题】:Cloning audio source without having to download it again克隆音频源而无需再次下载
【发布时间】:2015-08-06 15:48:30
【问题描述】:

我正在使用 javascript 在浏览器中创建钢琴。为了让我同时多次播放同一个键,而不是只播放音频对象,我克隆它并播放克隆,否则我必须等待音频完成或重新启动它,我不这样做'不想。

我做过这样的事情:

var audioSrc = new Audio('path/');
window.onkeypress = function(event) {
    var currentAudioSrc = audioSrc.cloneNode();
    currentAudioSrc.play();
}

问题是,我在检查 chrome 的检查器时,发现每次克隆对象时,浏览器都会再次下载它

我检查了一些想要实现类似事情的人,发现他们中的大多数人都有和我一样的问题,他们重新下载了文件。我发现可以同时多次播放同一音频源的唯一示例是 SoundJs http://www.createjs.com/SoundJS

我尝试检查源代码,但无法弄清楚它是如何完成的。有什么想法吗?


【问题讨论】:

  • 是的,如果您不熟悉 SoundJS 架构,我知道这将是多么难以追踪。 WebAudioLoader._sendComplete 将加载的文件解码为 AudioBuffer,然后将其作为 _result 传递,在 AbstractPlugin._handlePreloadComplete 中通过将其存储在 _audioSources 哈希中进行处理(WebAudioPlugin 扩展 AbstractPlugin)。然后将此存储的 AudioBuffer 传递给每个新的 WebAudioSoundInstance 并用于创建音频节点。它与接受的答案相同,存储音频缓冲区并使用它来创建新的音频节点。

标签: javascript html audio


【解决方案1】:

使用 webAudioAPI 你可以做这样的事情:

  • 通过 XMLHttpRequest 下载一次文件。
  • 将响应附加到缓冲区
  • 创建一个新的 bufferSource 并在每次调用时播放它
  • 如果不支持 webAudioAPI (IE),则回退到您的第一个实现

window.AudioContext = window.AudioContext||window.webkitAudioContext;
if(!window.AudioContext)
  yourFirstImplementation();
else{
var buffer,
ctx = new AudioContext(),
gainNode = ctx.createGain();
gainNode.connect(ctx.destination);
var vol = document.querySelector('input');
vol.value = gainNode.gain.value;
vol.addEventListener('change', function(){
    gainNode.gain.value = this.value;
  }, false);

function createBuffer(){
  ctx.decodeAudioData(this.response, function(b) {
    buffer = b;
    }, function(e){console.warn(e)});
  var button = document.querySelector('button');
  button.addEventListener('click', function(){playSound(buffer)});
  button.className = 'ready';
  }

var file = 'https://dl.dropboxusercontent.com/s/agepbh2agnduknz/camera.mp3',
xhr = new XMLHttpRequest();
xhr.onload = createBuffer;
xhr.open('GET', file, true);
xhr.responseType = 'arraybuffer';
xhr.send();

function playSound(buf){
  var source = ctx.createBufferSource();
  source.buffer = buf;
  source.connect(gainNode);
  source.onended = function(){if(this.stop)this.stop(); if(this.disconnect)this.disconnect();}
  source.start(0);
  }
}

function yourFirstImplementation(){
  alert('webAudioAPI is not supported by your browser');
  }
button{opacity: .2;}
button.ready{opacity: 1};
<button>play</button>
<input type="range" max="5" step=".01" title="volume"/>

【讨论】:

  • 我无法停止播放:D
  • 有什么办法可以控制这个音频缓冲区的音量吗?
  • @DanielOrtiz 是的,当然。您将不得不使用增益节点。检查更新
【解决方案2】:

cloneNode 有一个布尔参数:

var dupNode = node.cloneNode(deep);
/*
  node
    The node to be cloned.
  dupNode
    The new node that will be a clone of node
  deep(Optional)
     true if the children of the node should also be cloned, or false to clone only the specified node.
*/

另请注意MDN:

Deep 是一个可选参数。如果省略,该方法就像 deep 的值为 true,默认使用深度克隆作为 默认行为。要创建浅克隆,必须将 deep 设置为 假的。

此行为已在最新规范中更改,如果省略, 方法将表现得好像 deep 的值为 false。虽然还是 可选的,您应该始终提供 deep 参数 向后和向前兼容性

所以,尝试使用deep = false来阻止下载资源:

var audioSrc = new Audio('path/');
window.onkeypress = function(event) {
    var currentAudioSrc = audioSrc.cloneNode(false);
    currentAudioSrc.play();
}

【讨论】:

  • 从我的测试来看,它不适用于 chrome 中的问题。您是否设法通过这种方式避免重新下载?
【解决方案3】:

手动加载并将二进制数据的Blob URL分配给src

<audio id="audioEl" data-src="audio.mp3"></audio>
var xhr = new XMLHttpRequest();
xhr.open('GET', audioEl.dataset.src);
xhr.responseType = 'blob';
xhr.onload = () => {
    audioEl.src = URL.createObjectURL(xhr.response);
};
xhr.send();

这样克隆时,只克隆内存中二进制数据的引用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-09
    • 2015-12-05
    • 2011-01-16
    • 1970-01-01
    • 2016-06-11
    相关资源
    最近更新 更多