MUSIC SOFTSYNTH

577 words ~ 3-5 minsMathieu 'p01' Henri on October 13th, 2011

Music SoftSynth

This is the brain child of 140byt.es and Experimental music from very short C programs.

Indeed, this year has kept people busy crafting little things. It started on May 23, 2011 when Jed Schmidt created the 140byt.es master GIST thus throwing the ball for a tweet-sized, fork-to-play, community-curated collection of JavaScript. Later, on September 26, 2011 Viznut released the Experimental music from very short C programs video on Youtube. Soon people started playing with various formulas and building tools to test them. The time has come for these two projects to make tiny babies. Behold the 140bytes music softSynth in JavaScript!

The code synthesizing the sound

function(f){for(var t=0,S='RIFF_oO_WAVEfmt '+atob('EAAAAAEAAQBAHwAAQB8AAAEACAA')+'data';++t<3e5;)S+=String.fromCharCode(eval(f));return S}

That's it, a 138 bytes function of JavaScript that doesn't leak in the global scope, takes a formula f as argument and process a ~37 seconds sound in mono, 8bits at 8Khz.

The formula f takes a single argument t which is an integer between 0 and 300 000 representing the time.

Bonus point: Here is a much faster but also much bigger version. How much bigger you may ask ? 4 bytes bigger!!!

function(f){return eval("for(var t=0,S='RIFF_oO_WAVEfmt "+atob('EAAAAAEAAQBAHwAAQB8AAAEACAA')+"data';++t<3e5;)S+=String.fromCharCode("+f+")")}

Usage and warnings

var softSynth = function(f){for(var t=0,S='RIFF_oO_WAVEfmt '+atob('EAAAAAEAAQBAHwAAQB8AAAEACAA')+'data';++t<3e5;)S+=String.fromCharCode(eval(f));return S};
new Audio( 'data:audio/wav;base64,'+btoa( softSynth( 'HERE_BE_CRAZY_FORMULA' ) ) ).play();

The sound is generated in 8bits and built as a String, therefore it is wise to properly wrap your crazy formula in parenthesis and end it with &255 to cast the value to an 8bits integer and avoid any DOMException: INVALID_CHARACTER_ERR error.

A peculiar WAVE header

Whether you are familiar or not with the WAVE PCM file format, you might have noticed the interesting WAVE header used here.

After a bit of experimentation with valid WAVE PCM headers, failing to reach the 140bytes mark, things got dirty. I tried to tweak and remove things and see how various browsers react to them. Turns out, they all ignore the size of the chunks and happily load the sound. Be gone size of the main chunk with your non printable characters and Hello Cookie Monster! _oO_ Oh and we also got rid of the size of the data chunk and let the formula overwrite the 4 bytes making it.

So we got the WAVE header down to 40 bytes, most of which are printable characters. The other bytes are encoded into a base64 string. Another thing browsers happily do is decode base64 strings deprived of the their padding. Another byte bites the dust!

All in all, getting the WAVE header costs us 61 bytes instead of 68 bytes if we encoded a valid header in base64. A mere 7 bytes, but a whooping 5% if you think about the maximum size of a 140bytes snippet.

Examples of advanced formula

80s synth type thing by Gasman:

(t<<3)*[8/9,1,9/8,6/5,4/3,3/2,0][[0xd2d2c8,0xce4088,0xca32c8,0x8e4009][t>>14&3]>>(0x3dbe4688>>((t>>10&15)>9?18:t>>10&15)*3&7)*3&7]&255

(broken) cover of Chaos Theory by Mu6k, Ryg, p01 et al.:

w=t>>9,k=32,m=2048,a=1-t/m%1,s=((y=[3,3,4.7,2][p=w/k&3]/5*t)*.96%64+y%64)*1.2+((5*t%m*a&k*4)*(0x53232323>>w/4&1)+((d=(14*t*t^t)%m*a)&127)*(0xa444c444>>w/4&1)*1.5+(d*w&1)/7.5+((h="IQNNNN!!]]!Q!IW]WQNN??!!W]WQNNN?".charCodeAt(w/2&15|p/3<<4)/33*t-t)%k+h*1.99%k+h*.49%k+h*.97%k-64)*(2-a)*2)*(w>>7&&a),s*s>>14?127:s&255

Related links