Promise Me It’s Over: A Web Audio Node Thing

Recently while working on Waveform Playlist, which can schedule multiple audio tracks playing with the web audio API, I ran into the problem of being able to stop and start my web audio playout in quick succession in javascript. This was needed when a user seeked to a different part of the playlist while it was currently playing. In brief, the code I had in Waveform Playlist to accomplish the above had looked something like this:

this.stop();
this.start(newStartTime);

The stop method was responsible for stopping each track in the playlist and clearing the old node graph, while the start method setup a new web audio node graph to continue playout from the new chosen starting time.

The result I got was

  1. playlist stopped
  2. playlist started
  3. … then playlist immediately stopped again?

I was confused for a moment, but then realized I had just refactored the project to use the newer onended callback provided by AudioBufferSourceNode to better manage cleanup when the audio had stopped/ended. Originally when the project had started in early 2013, this callback wasn’t available. This incorrect behaviour was the workings of the javascript event loop. For those needing a refresher on how the event loop schedules tasks to execute, this great article from last week can catch you up.

It turns out now, my stop function was called, then my start function, but then all the onended callbacks would execute right afterwards. To get around this problem and make sure I knew the moment when every track in the playlist had stopped, I wrapped the web audio nodes setup into a Promise. Again if you’re new to promises, here’s a great article to get started with.

Below is the function used to setup the web audio node graph.


setUpSource: function() {
    var sourcePromise;
    var that = this;

    this.source = this.ac.createBufferSource();
    this.source.buffer = this.buffer;

    sourcePromise = new Promise(function(resolve, reject) {
        //keep track of the buffer state.
        that.source.onended = function(e) {
            that.source.disconnect();
            that.fadeGain.disconnect();
            that.outputGain.disconnect();
            that.masterGain.disconnect();

            that.source = undefined;
            that.fadeGain = undefined;
            that.outputGain = undefined;
            that.masterGain = undefined;

            resolve();
        }
    });

    this.fadeGain = this.ac.createGain();
    //used for track volume slider
    this.outputGain = this.ac.createGain();
    //used for solo/mute
    this.masterGain = this.ac.createGain();

    this.source.connect(this.fadeGain);
    this.fadeGain.connect(this.outputGain);
    this.outputGain.connect(this.masterGain);
    this.masterGain.connect(this.destination);

    return sourcePromise;
}

The onended callback is fired both when the track plays out naturally, as well as when it’s manually stopped. Every time I scheduled a track to play, I would keep a reference to the Promise returned from the setup, something like this.

var playoutPromise = track.play(startTime, endTime)

Within the track’s play method setUpSource was called, which created the playoutPromise. Keeping a reference to a Promise for every track in the playlist, my newly updated code from before to seek to a different spot in the audio could now look something like

this.stop();
Promise.all(playoutPromises).then(this.start.bind(this, newStartTime));

This ensures that the start method will only run after all the onended callbacks for each track has finished executing.

Those are the basics to restarting multiple sounds using the web audio API! Now go back to making music!

Advertisements
Promise Me It’s Over: A Web Audio Node Thing

Thoughts on the Book “Instant Audio Processing with Web Audio”

I’ve recently just finished reading Packt Publishing’s book Instant Audio Processing with Web Audio by Chris Khoo.

Earlier this year I started looking into the new Web Audio API as I was busy researching how to build a timing-precise audio preview javascript library for work, which is available on my github account here. Web audio has opened many new possibilities in the browser and is quite exciting if you have any type of project that is sound-based or will incorporate a lot of audio.

As the API is relatively new and is currently being standardized, it is harder to come across a variety of useful examples to get started with. This book can get you up and running with many shorter code examples all set up with HTML/CSS. Overall it is a relatively short book at 76 pages, but provides a bonus section for download including another 26 pages of material.

I thought it was a little strange that the bonus section included the basic material such as initializing and playing back a sound sample. My first opinion was that the book spent too much time diving into talking about the “custom framework” that was used as scaffolding for the variety of examples included, and also diving immediately into UI presentation and function of the examples with HTML/jQuery. The basic usage of the API could have just been shown in the book via direct sequential javascript calls to get the fundamentals in mind first. The book becomes more valuable in its later chapters, however, when explaining and presenting ways to achieve audio ducking and build a 5-band equalizer. More visuals are then used to teach different types of audio fades and explain the examples using a graph of Web Audio API nodes.

Overall this book is a good place to refer to for more in-depth examples when learning the API and reading Web Audio’s working draft.

Thoughts on the Book “Instant Audio Processing with Web Audio”