Why Sporth?
2015-11-25
This week is Thanksgiving break, which means I have some free time to blog, and also some time to work on some my final projects for school. As it turns out, I've managed to weasel in my quirky language Sporth into all my final projects. This means at some point, I will have to explain what Sporth is to people, and answer this inevitable question: "Why would I ever want to use Sporth?".
I can't directly answer that question, and I don't really want to. What I can do is explain why I built Sporth in the first place. To understand this, we must look at one of my all-time favorite music languages: Csound. Consider this an "origin" story.
Csound has a pretty awesome reverb opcode called reverbsc. I use it in all my ambient pieces, as it provides a beautiful and large reverb.
Here's one of many ways I'd use ReverbSC in Csound:
sr = 44100
ksmps = 1
nchnls = 1
0dbfs = 1
instr 1
ainput diskin2 "piano.wav", 1
aoutL, aoutR reverbsc ainput, ainput, 0.97, 10000
out aoutL
endin
In the example above, I'm using ReverbSC to process an audio file called "piano.wav".
I loved ReverbSC so much, that I wanted to build a plugin out of it. While the Csound API could do this task (and things like Cabbage and the like exist for this), I found it to be too much overhead for the task. I didn't need all of Csound to run ReverbSC, just the code for this particular opcode. So I looked into the Csound source code, extracted the code I needed, and wrote a simple C interface around it. I did this for a few other Opcodes I liked, and built Soundpipe.
With Soundpipe, I was able to port the Csound code above to a page of C code:
#include <stdlib.h>
#include <stdio.h>
#include "soundpipe.h"
typedef struct {
sp_diskin *disk;
sp_revsc *rev;
} UserData;
void process(sp_data sp, void udata) {
UserData *ud= udata;
SPFLOAT in = 0;
SPFLOAT out = 0;
SPFLOAT foo = 0;
sp_diskin_compute(sp, ud->disk, NULL, &in);
sp_revsc_compute(sp, ud->rev, &in, &in, &sp->out[0], &foo);
}int main() {
UserData ud;
sp_data *sp;
sp_create(&sp);
sp_diskin_create(&ud.disk);
sp_revsc_create(&ud.rev);
sp_diskin_init(sp, ud.disk, "piano.wav");
sp_revsc_init(sp, ud.rev);
ud.rev->feedback = 0.97;
ud.rev->lpfreq = 10000;
sp->len = 44100 * 10;
sp_process(sp, &ud, process);
sp_diskin_destroy(&ud.disk);
sp_revsc_destroy(&ud.rev);
sp_destroy(&sp);
return 0;
}
I now had the means to build a ReverbSC plugin without needing the Csound API. From a programming standpoint, things were way less complicated now. With minimal dependencies and painless build system, Soundpipe became a lightweight alternative to the Csound API. However, it meant I needed to write more code. It took more time to make tweaks to patches, and hindering my creative productivity.
I soon realized that I needed an abstraction for Soundpipe so I didn't have to worry about the low-level details while composing. I decided to build a domain-specific language on top of Soundpipe that could easily be embedded into larger applications the same way Soundpipe could. I had read up on Forths and stack based languages how trivial they were to implement, so I went with that. A few weeks later, I built my Soundpipe Forth and, Sporth was born!
Now my many lines of Soundpipe C code could be written as one line of Sporth code:
"piano.wav" diskin dup 0.97 10000 revsc drop
And this the essence of why I use Sporth. It is a terse language that allows me to quickly come up with interesting sounds. And because it is using Csound DNA, things can sound great in the right hands. I can easily add experimental ugens to the language, and it is easy to embed inside other applications.
I hope someday I won't be the only Sporth user in the world, but for now, I'm plenty happy hacking Sporth on my own :)