Web Audio API- onended event scope

695 views Asked by At

I'm having a tricky issue with the Web Audio API AudioSourceBufferNode and its onended event.

Basically what I'd like to do is have two AudioSourceBufferNodes that are each triggered when the other one finishes and keep playing back-and-forth. I understand that AudioSourceBufferNodes are pretty much done once you call start() and they're designed to be garbage-collected after this. So I tried to work around that like so:

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;
}

This may look pretty cumbersome, but I have pretty specific reasons for doing the whole back-and-forth playing thing, so I'd really like to figure that out. At any rate, the problem seems to be that when I call source2.start() inside the source1.onended callback, source2.onended doesn't appear to hear when it ends and never gets called. So it also stands to reason that if I ever manage to get inside the source2.onended callback, source1.onended will not hear the newly-reassigned source1 either.

So I guess what I want to know is, what is the scope of the onended event, and is there a better way to accomplish what I'm trying to do?

1

There are 1 answers

2
cwilso On BEST ANSWER

This isn't a scope issue. You're creating a new source2 object at line 5 (inside getSound()), and setting its onended property. However, when source1.onended is called, it's setting source2 to a NEW object created by getSound - which will not have onended set. You need to set the onended inside your onended methods, in effect.

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();

Note that using "onended" to chain buffers together isn't going to work well if they are intended to truly abut each other - there will be a significant gap, as the audio finishes playing in the audio thread, then queues a message on the main thread, and that message makes its way through the event queue. If there's supposed to be a gap (like, two songs that fade in/out), it's fine.