Wednesday, January 14, 2015

I Am Not Musical, But JavaScript Is

A while back I wrote about the Internet Arcade, a collection of classic arcade games that can be played in your web browser. They play without any special plugins because the games are being emulated in JavaScript. It's great, except that most of them have no sound. This got me thinking about how limited JavaScript is in terms of sound generation and I started looking around for some answers.

I have used SoundManager 2 for many years in order to play audio clips in web pages, but I suspect that would be of little use when emulating an old arcade game. I own two full-size classic arcade games (BurgerTime and Dig Dug) and I realize that most of their sounds are just playing some frequency for some time through some simple speaker. There were no MP3's stored in the memory chips on those old boards.

I don't understand any of this.
So why can't we make simple sounds with JavaScript? I could play sounds at whatever frequency I wanted using BASIC on an IBM PCjr back in 1989. Have we gone backwards?

No, it turns out that we can generate sounds using JavaScript, with quite a bit of complexity actually. The Web Audio API is an amazing toolbox for audio generation in the browser...but it is still just a draft. Fortunately, the draft specification has been implemented in recent versions of the major browsers, with the exception of Internet Explorer of course.

I read the specification Too much audio stuff for me to understand. I am not musical in the least. I played the saxophone for one year in elementary school and I remember absolutely nothing about it. I don't have an expensive sound system in my home or car, and I believe that the tiny cheap speakers that come built-in to most monitors are just fine for my listening needs. Not musical at all.

I needed to find some library that could simplify things a bit. I found Tone.js and started to play around with it. Sure enough, I could use JavaScript to create a terribly annoying sound that would not end until I closed the page.

My goal, however, was to make a page that could play a recognizable song of some sort. I found this great resource that has transcribed the music from the original Super Mario Bros. for the piano. But as I mentioned, I can't read music! Fortunately, my wife can. She even taught violin for a while. I had her look at the sheet music and tell me what the notes were. That's how I was able to make this:

//create one of Tone's built-in synthesizers
var synth = new Tone.MonoSynth();

//connect the synth to the master output channel

synth.setVolume(-15); // i have no idea

var index = 0;
var notes = ["E5", "G5", "E6", "C5", "D6", "G6"];

//create a callback which is invoked every sixteenth note
    if (index < notes.length) {
        synth.triggerAttackRelease(notes[index++], "16n", time);
    } else {
}, "16n");

//start the transport

This code uses Tone.js to reproduce the 1-UP mushroom sound from Super Mario Bros. There are 6 notes, which I put in an array there on line 10. On line 13, I set up a callback on the Transport that will be called every 16th note and will play the next note in the array for the duration of a 16th note. Once the Transport is started, on the last line, it will play through the notes and you should hear the familiar music. Here is a link to the demo page.

The one thing that I could not figure out is why I had to set the synth volume to a negative number in order for it not to blast my speakers (line 7). By default, it is really loud. According to the documentation, setVolume should take a number representing the decibels. My understanding was that anything below 0 could not be heard. I thought perhaps using a negative number meant turning down the volume by that many decibels, but setting the volume to -5 three times did not have the same effect as setting it to -15 once. So I don't know. If I use Tone.js more, I'll have to perform a more thorough investigation.

For now, enjoy some frog comics about streaming music. comic for 14 January 2015

No comments:

Post a Comment