Archive for the ‘Random’ Category

A JavaScript spectrum analyzer

My friend asked me if I could make a music player with a spectrum analyzer for his web page. I’m like, I dunno? Can I?

I can make a spectrum analyzer in my sleep, and browsers can do some audio stuff these days, right? So, maybe?

I can make a spectrum analyzer in my sleep

Here’s how you make a spectrum analyzer. First, run your original signal through a few parallel bandpass filters. The range of human hearing spans about 10 octaves, from 20 Hz to 20k. If we want a 5-band display, we’ll place our bandpass filters at 2-octave intervals. This gives us centers at 40 Hz, 160 Hz, 640 Hz, 2.5k, and 10k.

Filter design is super interesting and fairly complex, but implementing a basic bandpass filter is shockingly simple.

Now we’ve got five (or whatever) new audio signals. We compute the power of each band and draw a bar for each one. There are some details around how you compute the actual size of the bars to make them look good, but that’s the main idea.

Easy! NEXT

Browsers can do some audio stuff these days, right?

Yes, but with difficulty. Dropping in an audio player is straightforward with the audio tag. Codecs are a bit muddled– Firefox won’t play MP3; Safari won’t play Vorbis. But in the grand scheme of browser quirks, this one is charmingly easy to deal with.

Here’s how you implement it:

<audio controls preload="auto">
<source src="/spectest/metal_star.ogg">
<source src="/spectest/metal_star.mp3">
</audio>

We specify both sources so that all browsers can play at least one. And presto:

This is cute, but to add a spectrum analyzer to our player, we need access to the raw PCM stream. The problem is thorny.

In Firefox

Firefox’s Audio Data API makes it easy. Your audio element will fire a MozAudioAvailable event for each little chunk of sound it plays. Create an event listener, and your callback can process the stream however you like. Like this (I use Cobra cause I like it):

var Biquad = new Cobra.Class({
  __init__: function(self, freq, q) {
    var omega = 2*Math.PI*freq/44100.0;
    var alpha = Math.sin(omega)*(2*q);

    self.b0 = alpha;
    self.b1 = 0.0;
    self.b2 = -alpha;
    self.a0 = 1 + alpha;
    self.a1 = -2*Math.cos(omega);
    self.a2 = 1 - alpha;

    self.y1 = self.y2 = self.x1 = self.x2 = 0.0;
  },

  next: function(self, x) {
    var y = (self.b0 / self.a0)*x +
      (self.b1 / self.a0)*self.x1 +
      (self.b2 / self.a0)*self.x2 -
      (self.a1 / self.a0)*self.y1 -
      (self.a2 / self.a0)*self.y2;
    self.y2 = self.y1;
    self.y1 = y;
    self.x2 = self.x1;
    self.x1 = x;
    return y;
  }
});

var bands = [
  new Biquad(40.0, 1.0),
  new Biquad(160.0, 1.0),
  new Biquad(640.0, 1.0),
  new Biquad(2560.0, 1.0),
  new Biquad(10240.0, 1.0)
];

function updateSpectrum(ev) {
  var fb = ev.frameBuffer;
  var sumsq = [0.0, 0.0, 0.0, 0.0, 0.0];

  for (var i = 0; i < fb.length/2; ++i) {
    /* average to get mono channel */
    var center = (fb[2*i] + fb[2*i+1])/2.0;

    /* feed to bp filters */
    for (var j = 0; j < 5; ++j) {
      var out = bands[j].next(center);
      sumsq[j] += out*out;
    }
  }

  for (var i = 0; i < 5; ++i) {
    /* calculate rms power */
    var rms = Math.sqrt(2.0 * sumsq[i] / fb.length);

    /* update bar */
    var db = 6.0 * Math.log(rms) / Math.log(2.0);
    var length = 450 + 10.0*db;
    if (length < 1) {
      length = 1;
    }
    var bar = document.getElementById("bar" + i);
    bar.style.width = length + "px";
  }
}

var audio = document.getElementById("player");
audio.addEventListener("MozAudioAvailable", updateSpectrum, false);

You can see it in action here.

Brilliant! Hopefully WebKit provides an equally easy solution.

In WebKit

Nope! WebKit is implementing the Web Audio API. This API sorta works like a modular. You create chains of AudioNode objects which act as sound sources or processors. There are a bunch of prefab AudioNodes, or you can write your own.

Chrome implements at least part of the Web Audio API. So do Safari nightlies, and I presume Safari 6 will as well.

My plan was to connect an audio element to a custom AudioNode, which would use the same spectrum analyzer code from the Firefox example. This plan was foiled when I discovered that the MediaElementAudioSourceNode, designed to provide integration with the audio and video tags, is silently unimplemented in Chrome. You can create one, but it will just feed you a steady stream of zeroes.

For now, it appears that the preferred solution is to re-implement the audio tag yourself using an XHR and an AudioBufferSource. Which is insane.

Our updateSpectrum only changes slightly:

function updateSpectrum(ev) {
  var fb = ev.inputBuffer;
  var outb = ev.outputBuffer;
  var sumsq = [0.0, 0.0, 0.0, 0.0, 0.0];
  var inputL = fb.getChannelData(0);
  var inputR = fb.getChannelData(1);
  var outputL = outb.getChannelData(0);
  var outputR = outb.getChannelData(1);

  for (var i = 0; i < inputL.length; ++i) {
    /* average to get mono channel */
    var center = (inputL[i] + inputR[i])/2.0;

    /* copy to output */
    outputL[i] = inputL[i];
    outputR[i] = outputR[i];

    /* feed to bp filters */
    for (var j = 0; j < 5; ++j) {
      var out = bands[j].next(center);
      sumsq[j] += out*out;
    }
  }

  for (var i = 0; i < 5; ++i) {
    /* calculate rms amplitude */
    var rms = Math.sqrt(sumsq[i] / inputL.length);

    /* update bar */
    var db = 6.0 * Math.log(rms) / Math.log(2.0);
    var length = 450 + 10.0*db;
    if (length < 1) {
      length = 1;
    }
    var bar = document.getElementById("bar" + i);
    bar.style.width = length + "px";
  }
}

But we need a bunch of new rigmarole to wire up the sound:

var context = new webkitAudioContext();

var processor = context.createJavaScriptNode(512, 1, 1);
processor.onaudioprocess = updateSpectrum;

/* get the sound via http */
var req = new XMLHttpRequest();
req.open("GET", "/spectest/metal_star.mp3", true);
req.responseType = "arraybuffer";
req.onload = function() {
  var audio = context.createBuffer(req.response, false);
  var source = context.createBufferSource();
  source.buffer = audio;
  source.noteOn(0.0);
  source.connect(processor);
  processor.connect(context.destination);
}
req.send();

And here’s the Chrome edition of the demo (warning: autoplays).

Opinions

I quite like the Mozilla Audio Data API. The Web Audio API strikes me as overdesigned.

I hate Flash with the fury of a thousand suns scorned, but given this Byzantine thicket, I understand why developers use it.

Also

If you like the song in the demos, download it here.

Ikea-hacking a better workspace

What’s better than slightly improving your workspace? Not much! Inspired by this Ikea hack, I tore down my old Jerker desk and set upon building a new one.

 

Goodbye Jerker, hello Besta

The original hacker’s basic idea was to start with a Besta shelf unit, which has all kinds of add-ons. Saw your shelves down a little and mount them on rails from a matching drawer unit. I added a keyboard stand to mine, made out of a wall shelf and some Vika Kaj adjustable table legs. I also installed some Dioder multicolored LEDs, just cause they’re awesome.

The brilliant part is that, while you need to be able to reach all your controllers, you don’t need to reach them all at the same time. You can store them compactly tucked into the unit and just slide out the ones you want.  As awesome as my Jerker desk was, this setup has a significantly smaller footprint while keeping all my gadgets accessible.

If you are considering building this hack yourself, here are some tips:

  • Sawing the shelves down to size was surprisingly easy, although it takes a while and my arm got kind of tired. You can use the bottom of the drawer from which you took the rails as a sizing guide– it’s exactly the right width. Since the shelves are fiberboard, the side facing down as you saw is going to get a little ugly, so plan accordingly.
  • Mounting the rails on the shelves was slightly more difficult than I anticipated.  First, it’s easy to get confused about how to orient the rails.  Second, it’s a little tricky to drill holes in such a thin piece.
  • The most difficult part was installing the other half of the rails into the shelf– it’s really hard to operate a screwdriver around the rear holes. Ironically, this is the part actually sanctioned by Ikea.
  • Binder clips can keep your cables neat as you slide stuff in and out.

My latest band-crush

It’s Suz:

Information seems to be scarce and in Italian.  I’m not even sure if Suz is the name of the band or the singer.  But I’m quite enjoying this record.

Siouxsie vs New Order

Everyone loves a mashup:

In other news, I’m on SoundCloud now! Let’s be friends.

Pandora Potluck

For when you have guests.

1. Everyone writes down the name of one artist (in secret).

2. Create a new Pandora station from all the listed artists.

3. No song skips!  (Thumbs up are OK.)

Happy 909 Day

Last year Tom at Music Thing (RIP)1 had a nice bit for 808 day.  Those are big shoes to fill, but I couldn’t let 9.09.09 pass without a mention.

I tried to think of some quotes about the 909 but drew a blank.  The 808 was as common in hip-hop as in techno, while the 909 is exclusively a house and techno machine.  I enjoy listening to hip-hop, but I don’t really feel connected to hip-hop culture.  But I do feel like techno, and electronic music more generally, is part of me, not just something to consume but something to participate in.  Perhaps that why I am so fond of the 909.

Here’s Daft Punk:

Here’s Jeff Mills:

1. RIP to the blog, not to Tom.  As far as I know.

MS Paint MPC

Sorta hard to explain, so just check it out.

MS Paint Adventures is one part webcomic, one part crowd-sourced old school adventure game.  You probably should read/play the current story/game from the beginning.

Le Roi (Du Pop) Est Mort

I know I am late to the party, but I was out in the woods when it happened.

Curiously, the best blogging I read on the subject was written by Ta-Nehisi Coates a week before Jackson’s death:

Mike used to be beautiful. My sister Kelly just knew she was marrying him. And he danced so smooth and easy. I hate to think that what gave him that ability, was the same thing that ruined him.

After you start producing music everything sounds different. I rediscovered his music several years ago and was floored by the arrangement, production, songwriting, everything.  I know some of that credit is due to guys like Quincy Jones and Bruce Swedien, but remember that MJ wrote a lot of his best songs.

If you write, perform, or produce music, you can learn something from Michael Jackson.

This is my favorite MJ video:

I like the suits.  I like the lean.  I like the hottie with the fan.  I even like the cheesy coin flip.  I love the D-50 bassline.*  The staccato delivery is now a staple of modern R&B– listen to Beyoncé’s “Crazy In Love” for example.

Here is a short history of the moonwalk (HT: The Midnight Man):

*OK, I have no idea what kit they used.  It could be a D-50 though.

Five pieces of gear and how they survived a coffee spill

5. Korg padKontrol. The bottom left pad occasionally fails to send a note off.  I usually map that pad to the kick drum, which fortunately is not important in electronic music.

4. Faderfox LC2. The channel 4/channel 10/solo button is gummed up.  The other controls just feel so damn nice, making the sticky one even worse in comparison.  It feels like the button might loosen up after pounding on it some more so I am optimistic.

3. M-Audio Keystation Pro. I had to wipe down the keys.  After bangin on low D a few times, it’s no worse for the wear.  One of the knobs is too loose but that predates the coffee.

2. NI Audio Kontrol 1. No noticeable ill effects, although I think this guy was pretty well out of the danger zone.

1. The plank that I put on my keyboard stand to hold my laptop. I just wiped it down and bam, good as new.

A quick update

1. Live 7 is good.

2. The Faderfox LC2 is really good.

3. A clean install of Windows only seems like a good idea before you do it.

3a. Damn serial numbers and activations.

3b. I am almost ready to kick Buzz to the curb.

3b(i). Emphasis on “almost.”

4. I am working on a new audio software project.

4a. I do not yet know how serious it is.

4b. Boost.Python is a godsend.