Floatbeat is a sound and music generator for fast live coding in a Forth-like language I've called Diminished Forth, because it's missing a lot of the standard Forth words. And because it's a pun!
Like the bytebeat programs it is based on a single time counter, which increments for every sample it needs to output, about 20,000 times a second. Floatbeat runs your program in full for each sample. This need for decent performance is why I chose Forth (and golang) for this project. Any value that gets 'printed' during execution (with the Forth word
.) is sent to the sound library. Floatbeat expects the stack to be empty at the end, and rejects the program if it isn't.
When you run Floatbeat, you can supply a program as an input file, or you can connect to its web server. This uses web sockets to allow you to edit the program while the sound is being output.
Here's a sample d4 program which outputs a looping tune with a vibrato effect:
:: scale ticks; (This line loads some supplied libraries: ) ( scale defines the standard note names, ) ( and ticks divides time up into bars. ) 8 280bpm ticks swap (Sets up 8-beat bars of 280bpm ticks. ) from G low, B', G, D, D', C, E, G choose (Sets the pitch based on the beat in the bar. ) lfo. (Runs the subroutine below and outputs the result. ) :lfo (age freq -- signal) (This subroutine provides the synth sound. ) over * swap 6Hz * sin +tr ;
You can change the tune, sound or modulation with a few keystrokes and immediately hear the result.
Floatbeat uses Gorilla for websockets and portaudio for sound. I've only tested it on my Linux machine. You need to build it yourself to run it, which involves setting up golang.
In 2015 my computer was just about fast enough to produce simple sounds using Floatbeat. Although current hardware is of course faster, the program could still do with quite a bit of optimization.
Live coding in Forth is very easy to mess up. There's only one program buffer and it's unforgiving if you make a mistake. Too many or too few items on the stack will cause the program to be rejected, so floatbeat will continue running the last program that worked. At best this means you don't hear your changes; at worst you've left it in some weird but valid state that sounds bad. Maybe there should be multiple program buffers, that you can switch between during a performance.