February 2011: Aplay clarification.
March 2011: Sox and OSX are anagrams.

Original Finally got around to cross posting this. Aplay clarification. Sox and OSX are anagrams. Editable
version 2 & 3 of 3

Originally posted on reddit

"I've enjoyed this sound so much that I just left the script running for the last 3 hours." -- eleven357

Listen to a sample MP3

Copy and paste into your terminal and run. It can keep playing pseudo random tones forever, though it might loop after a few decades.

awk 'function wl() <span class="del">{\</span> <span class="add">{</span>
        rate=64000; <span class="del">\</span>
        return (rate/160)*(0.87055^(int(rand()*10)))}; <span class="del">\</span>
    BEGIN <span class="del">{\</span> <span class="add">{</span>
        srand(); <span class="del">\</span>
        wla=wl(); <span class="del">\</span>
        while(1) <span class="del">{\</span> <span class="add">{</span>
            wlb=wla; <span class="del">\</span>
            wla=wl(); <span class="del">\</span>
            if (wla==wlb) <span class="del">\</span>
                <span class="del">wla*=2; \</span> <span class="add">{wla*=2;};</span>
            d=(rand()*10+5)*rate/4; <span class="del">\</span>
            a=b=0; c=128; <span class="del">\</span>
            ca=40/wla; cb=20/wlb; <span class="del">\</span>
            de=rate/10; di=0; <span class="del">\</span>
            for (i=0;i<d;i++) <span class="del">{\</span> <span class="add">{</span>
                a++; b++; di++; c+=ca+cb; <span class="del">\</span>
                if (a>wla) <span class="del">\</span>
                    {a=0; ca*=-1}; <span class="del">\</span>
                if (b>wlb) <span class="del">\</span>
                    {b=0; cb*=-1}; <span class="del">\</span>
                if (di>de) <span class="del">\</span>
                    {di=0; ca*=0.9; cb*=0.9}; <span class="del">\</span>
                printf("%c",c)}; <span class="del">\</span>
            c=int(c); <span class="del">\</span>
            while(c!=128) <span class="del">{\</span> <span class="add">{</span>
                c<128?c++:c--; <span class="del">\</span>
                printf("%c",c)};};}' > /dev/dsp

 

Some notes on running it: It should chime about once a second. If the speed is messed up, vary the `ratevariable on the second line. No clue what it depends on, but values seem to range between 4000 and 256000. Forcing a bitrate withpvmight help. If it spits out an error about/dev/dsp, edit the last line. Replace> /dev/dspwith| aplay</span> <span class="add">aplay -r 64000 and try it again. If you are on OSX, use | sox -t raw -r 64k -c 1 -e unsigned -b 8 - -d after installing Sox. If it stutters (though you'll need to be on a low end pentium 2), use mawk instead of gawk. It'll run about four times faster. You can also get rid of all the trailing slashes to make a scary looking one-liner.

itsnotvalid (2011-03-06-17-23-31-264)

For aplay, use -r 64000 (same as rate defined in the script) to get the sample rate to 64000Hz. The default used on my Ubuntu box is 8000Hz which makes the pitch 3 (2^3 = 8) times lower. (hope my math here's right)



 

One day I sat down to learn Awk, a language with a reputation for terse but powerful one liners. It was a pleasant surprise to find an entire "conventional" language underneath with loops, branching and math operations common to all imperative languages. Kernighan was a primary author, so it is no surprise it has a very C like syntax. Unlike C it is completely untyped. There are positional variables ($1) similar to what is found in shells. Regex can be used anywhere. Everything fits together perfectly for maximum convenience while processing text.

But this is sound, not text. So what is going on here? Sound is just a stream of numbers. Text is also a stream of numbers. If you've ever tried making white noise with cat /dev/urandom > /dev/dsp this is just an extension of the idea. In fact, the very first version of this script was

cat /dev/urandom | fold -b1 | awk '{for (i=0;i<100;i++) printf($1);}' > /dev/dsp

which has the effect of holding each random value for 100 cycles, lowering the pitch of the noise. While Awk was made for processing text, it has a general purpose language backing it up. To illustrate a few of these features, here is the oldest version of the script:

awk 'BEGIN {srand(); <span class="del">\</span>
    while(1) { <span class="del">\</span>
        wl=400*(0.87055^(int(rand()*10)+1)); <span class="del">\</span>
        d=(rand()*80000+8000)/wl; <span class="del">\</span>
        for (i=0;i<d;i++) { <span class="del">\</span>
            for (j=0;j<wl;j++) <span class="del">\</span>
               {printf("a")}; <span class="del">\</span>
            for (j=0;j<wl;j++) <span class="del">\</span>
                 {printf("z")}; };};};' <span class="del">\</span> <span class="add">> /dev/dsp</span>

> /dev/dsp

This makes sound, but it pretty crude. It plays random square waves for random durations. The BEGIN{} stanza gives a chunk of code for Awk to execute before processing standard input. Sadly I don't have the previous iteration of the script, which used yes | awk '{...}' instead of BEGIN. The third line, wl=400*(0.87055^(int(rand()*10)+1)), takes a bit of explaining. A musical scale is exponential in nature, and this function generates the wavelengths of notes on a musical scale. There is one magic number, 0.87055. This is not a random number or even something found by trial and error. It is the fifth root of 0.5. One half is important because every octave the wavelength doubles or halves. Using the fifth root gives five intervals for each octave, a pentatonic scale. Why five notes? It is really easy to make something that sounds good in the pentatonic scale and really hard for a pair of notes to be dissonant. With a wavelength and a duration for the note, a pair of for loops pumps out a square wave. A and Z make up the low and high values.

The more complicated version grew out of this. The added features are triangle waves, two note chords, amplitude decay, and anti-pop. The last item needs a little explaining. Towards the end of the script there is a short loop, while(c!=128), which returns the interrupted triangle wave to center. Without it there will be a sharp popping sound between note transitions. An explanation of the scoping rules regarding the rate variable is left as an exercise for the reader.

Since the output is a single byte, this is real 8 bit music. But this particular sound is a bit more mellow than other chip tunes around. It is a wide open area for more experimentation.