mathr / blog / #

Barry (postfix bytebeat)

Bytebeat is a genre of music using small programs to generate digital audio data. Often the resolution is low (8bit) as well as the sample rate (8kHz), so comparisons can be made to chiptune aesthetics, though the method of sound generation is different (CPU controlling a DAC vs specialized sound chips). The thread that began the modern bytebeat phenomenon is already over 8 years old:

Algorithmic symphonies from one line of code -- how and why?

Lately, there has been a lot of experimentation with very short programs that synthesize something that sounds like music. I now want to share some information and thoughts about these experiments.

Most computer languages use infix operators with varying precedence levels (so * for multiplication binds more tightly than + for addition). Brackets are used to override the precedence, so for example "a * b + c" means "(a * b) + c" and you need brackets if you want "a * (b + c)". When coding bytebeat, one often uses integer bitwise operators like & | ^, as well as the more usual arithmetic. I find it impossible to keep track of all the precedence levels in my head, so I have to add brackets everywhere, and then I have to match up brackets with each other, which is a non-local editing process, which is slow when live-coding.

Enter postfix notation: instead of "a * b + c", write "a b * c +", and for "a * (b + c)" write "a b c + *" or "b c + a swap *" or (using the commutativity of "*") "b c + a *". No brackets, but there is still some counting. I wrote a postfix language aimed at bytebeat, inspired by Forth though it isn't a Forth. I called it Barry, because I already have projects named Clive, Raymond, Trudy, Harry. The file extensions is .baz, the interpreter library called Barrington, the compiler is (boringly) named bazc (actually there are two variants, bazc-simple does forward tracking of types from arguments to outputs via stack and locals, bazc-unified does multi-way unification which is more powerful but my implementation is much too slow to be useful). There's a helper program called Barrence which I needed to topsort the dictionary for bazc when experimenting with the collaborative editor Troop.

I wrote a compiler (which compiles to C) because Barrington the interpreter was much too slow. But then I had the idea of porting Barry to run in a web browser, so I made improvements to Barrington to make it much faster. It now has a DSP chain, similar to Pd, where the main loop just pops function pointers and calls them, with all the type checking done once at DSP chain compilation time. It also uses block-based processing, so the overhead of dispatch is much smaller. There is no state in the language (nor branching, loops, recursion) - the only thing that changes is the input, which is a time counter and the audio channel number. Maybe this will change but I'll need to find a way to make it safe and guaranteed crash-free - I already have protection against integer division by 0 etc.

You can try Barry in the browser via the Barry home page, which also describes how to use it with Clive, my thing for live-coding audio in the C programming language:

I plan to add document permalinks (so you can save your work without having to copy/paste into a text editor) in the next update, not sure when that will be though. It will probably be via base64 encoding after the # anchor, so that it doesn't get sent to the server (and also meaning that Barry web app cab be cached by the browser).

There were a few things needed to get the web version working nicely: Emscripten/SDL2 seems to do audio on the main thread, but chain recompilation can take too long and interrupt it - luckily the --proxy-to-worker link time flag seems to solve this. And allowing memory growth was another huge slowdown, so I set total memory to 64MB, which should be enough: typically the audio block size is 4096 samples each of which take 8 bytes, and 1 block is allocated per operation (no recycling yet, I couldn't get it working safely but it's on my list to retry soon). And I wrote a new shell HTML/JS which is simpler than the default Emscripten one, with additional features like an audio context resume button needed to make sound at all in some browsers.