【问题标题】:Web Audio API- onended event scopeWeb Audio API-onended 事件范围
【发布时间】:2015-06-12 04:09:09
【问题描述】:

我遇到了一个关于 Web Audio API AudioSourceBufferNode 及其 onended 事件的棘手问题。

基本上我想做的是有两个AudioSourceBufferNodes,当另一个完成时触发它们并继续来回播放。我知道一旦您调用 start(),AudioSourceBufferNodes 就已经完成了,并且它们被设计为在此之后进行垃圾收集。所以我试着像这样解决这个问题:

var source1;
var source2;

source1 = getSound(buffer1);
source2 = getSound(buffer2);
source1.start();

source1.onended = function(){
    source2 = getSound(buffer2);
    source2.start();
}

source2.onended = function(){
    source1 = getSound(buffer1);
    source1.start();
}

function getSound(buffer){
    var src = context.createBufferSource();
    src.buffer = buffer;
    src.connect(context.destination);
    return src;
}

这可能看起来很麻烦,但我有非常具体的理由来做整个来回播放的事情,所以我真的很想弄清楚这一点。无论如何,问题似乎是当我在source1.onended 回调中调用source2.start() 时,source2.onended 在结束时似乎听不到并且永远不会被调用。因此,如果我设法进入 source2.onended 回调,source1.onended 也不会听到新分配的 source1

所以我想我想知道的是,onended 事件的范围是什么,有没有更好的方法来完成我想要做的事情?

【问题讨论】:

    标签: javascript event-handling web-audio-api


    【解决方案1】:

    这不是范围问题。您将在第 5 行(在 getSound() 内部)创建一个新的 source2 对象,并设置其 onended 属性。但是,当调用 source1.onended 时,它会将 source2 设置为由 getSound 创建的 NEW 对象——它不会设置 onended。实际上,您需要在 onended 方法中设置 onended。

    var source1=null;
    var source2=null;
    
    function onendedSource1() {
        source2 = getSound(buffer2);
        source2.onended = onendedSource2;
        source2.start();
    }
    
    function onendedSource2() {
        source1 = getSound(buffer1);
        source1.onended = onendedSource1;
        source1.start();
    }
    
    function getSound(buffer){
        var src = context.createBufferSource();
        src.buffer = buffer;
        src.connect(context.destination);
        return src;
    }
    
    // kick it all off.
    source1 = getSound(buffer1);
    source1.onended = onendedSource1;
    source1.start();
    
    // note you could just replace the three lines above with one call to onendedSource2();
    

    请注意,使用“onended”将缓冲区链接在一起,如果它们打算真正相互邻接,则效果不佳 - 将会有很大的差距,因为音频在音频线程中完成播放,然后排队主线程上的消息,并且该消息通过事件队列。如果应该有间隙(例如,两首淡入/淡出的歌曲),那很好。

    【讨论】:

    • 啊,太好了。这就像一个魅力,谢谢你,也感谢你的解释。就其价值而言,只要我使用相对安全的缓冲区大小(4096 及以上),音频似乎就可以无缝播放。不过,出于好奇,有没有更传统的方法可以像这样将缓冲区链接在一起?
    • 这个怎么样,因为你知道每个缓冲区的持续时间?启动 source1 并安排 source2 在 source1 结束时启动(您知道)。让 source1 onended 事件在 source2 的末尾安排一个新的 source1,并让 source2 onended 事件在 source1 的末尾安排 source2。我认为这只有在源足够长的情况下才有效,以便您可以在当前播放的源结束之前处理 onended 事件。