Last year, I used a GoPro to record biking down Route 28 in Somerville, then used Microsoft Hyperlapse on the video. While the hyperlapse makes the ride look much more intense than reality, it’s not a particularly fun bike route.

While it’s pretty easy to get started generating sound with the Web Audio API, using the Audio Data API takes a bit more work. Unlike the Web Audio API, which is callback driven, the Audio Data API doesn’t help you manage it’s buffers, so you are responsible for keeping them full.

Adapting the example from the Mozilla Wiki, we will write a function to be run every 100 milliseconds and fill the buffer.

varsampleRate=44100;varaudio=newAudio();audio.mozSetup(1,sampleRate);

The API uses the Audio object for output. We create one, and configure it for output at a sample rate of 44,100.

varwritePosition=0;

We need to keep track of how many samples we’ve written in order to know how many samples to write.

We will try to keep ½ second of data in the output buffer, so we’ll never need to generate more than 44100 / 2 samples of data at a time. We allocate buffer to hold these samples. Samples that have been generated but not yet written will be kept in currentBuffer.

Each time the write() function is called, it loops until it has filled the output buffer with up to bufferSize samples of data. At any given time, the number of samples we want to have written to the output buffer is equal to playPosition + bufferSize. The first iteration through the loop, there may already be samples in currentBuffer. If this is the case, we can just write them to the output buffer. Otherwise, and on the second iteration of the loop, we need to generate some samples.

Once we have some samples, we write them to the output buffer, and keep track of the total number of samples written so far. The audio data remaining after the output buffer is filled will be kept in currentBuffer to be used on the next invocation. If we weren’t able to write as many samples as we wanted, we’re finished for this invocation.

Firefox 4 was the first browser to ship with built-in support for audio synthesis and manipulation with its Audio Data API. However, it also shipped with a bug which complicates writing JavaScript that uses the API. Fortunately, the nature of the bug makes it easy to detect and fix transparently.

The API relies heavily on Typed Arrays to work with audio data. Briefly, Typed Arrays provide fast, fixed-sized, and (obviously) typed arrays in JavaScript. Each array is backed by a mutable ArrayBuffer, which can be shared by multiple typed arrays.

Central to the API is the mozWriteAudio() method. This method adds an array of samples to the browser’s output buffer. It’s possible to fill this buffer completely, in which case some samples won’t be added and the return value will indicate the number of samples that actually were added. When this happens, it’s up to you to keep track of the remaining samples and make sure that they’re written eventually.

The Typed Array specification provides an easy way to do this with its subarray() method. This method works a lot like the regular Array.slice() method, but instead of creating a copy of the array elements, it simply creates a new window into the underlying region of memory which is what you want if you’re trying to write a low-latency audio application.

Ideally, we’d just take the return value from mozWriteAudio() and slice that many samples off the front of the sample array:

This is exactly what I tried, and then spent the better part of an hour trying to figure out why samples were being repeated, producing a stuttering effect. As it turns out, subarray() is just broken in Firefox 4.

This is exactly what happens in recent WebKit. Firefox 4, however, gives

1, 2, 3, 4
1, 2, 3
1, 2
1

This makes it look like Firefox is slicing off the end, but what is actually happening is that the byte offset for the new array is always relative to the beginning of the underlying buffer, but the length is being calculated correctly.

The Solution

Fortunately, we can fix this. This method is easy enough to implement in JavaScript, so we can replace the broken implementation with our own, corrected version.

With this in place, Firefox behaves correctly and produces the same output as WebKit. You can write code that uses subarray() without worrying about the effects of the bug and when Mozilla ships a fix, this patch won’t be applied.0>

Notes

The bug was introduced in revision 8323a963fd6c. This commit significantly rewrote the Typed Array support and references bug 636078, which is protected.

I started working with vectors in ActionScript 3 recently, and wanted to make sure I was using them in the most efficient way. I performed several rudimentary tests of various ways to fill a vector with values. None of the results were very surprising, but it's nice to have numbers to back up my assumptions.

Here’s what I discovered.

Don’t use push(). This is the slowest way I found to fill a vector. First, resizing the vector is going to have an impact on performance. Second, even if you need your vector to expand, push() is not the fastest option. Instead, always specify the index using a counter variable, e.g. vector[index++] = item;. This can take under 25 percent of the time it takes to push the same items.

Create the vector with enough capacity to hold all your items. If you know your vector will have 100,000 items in it, create a vector with that capacity. This can be twice as fast as growing the vector for every item.

Fixing the size of the vector doesn’t help performance. If you create a vector of a given size, and don’t resize the vector, it makes no difference whether the vector is fixed size or not.