
Sporth: A small stack-based audio programming language - erikschoster
https://paulbatchelor.github.io/proj/sporth
======
pierrec
If you're thinking of compiling this to webassembly and making an online
Sporth playground... It's already done! Works pretty well, too. You can browse
a bunch of Sporth scripts and play with them here:
[https://audiomasher.org/browse](https://audiomasher.org/browse)

I found the stack-based, Forth style syntax works like _magic_ for a lot of
creative audio DSP tasks. Blocks of code are like little signal chains, but
denser and easier to manage than traditional spaghetti graphs (like in
Reaktor, PureData, etc.) though I believe spaghetti graphs are more
approachable at first.

~~~
mntmoss
The thing I think the spaghetti visual graphs get wrong is a lack of adherence
to the structured programming theorem. Most data flows are mostly
linear/sequential, and DAGs - which also can be expressed linearly as
"send/jump ahead but not backwards", cover most of the rest. Complex
structures that need to describe comprehensive fanin/fanout/feedback
functionality are exceptions, and as with uses of "goto" in structured
languages, most can be reduced to an implementation detail of a single node.
So presenting all the options on every node, every time, with cable wiring
going everywhere, is a very cluttered syntax for something that could usually
be a "Lego brick" expression.

The beauty of stack languages is shared with that of RPN calculators and their
postfix syntax model: You can enter the expression very quickly and precisely.
Parens and operators don't "get in the way". Names aren't needed for temporary
values. Small modifications don't require a rewrite. They feel mostly linear
when "in their wheelhouse."

Where postfix starts losing to prefix and infix is when you have something
that requires a lot of scanning back and forth to determine state. Function
arguments lack clarity. Stack underflow becomes a real failure case. The
scaling in terms of readability isn't very good, so "don't write large words"
becomes an imperative when it's a generalized programming syntax as in Forth.

The language I'm working on right now currently uses RPN for single-line,
compile-time expressions, while runtime evaluation is vertical and operates
entirely in terms of keyword prefix + memory addresses, like an assembler.
This constrains the scope in two ways, giving it a "twice linear" feel since
left-to-right is the compile-time world while top-to-bottom is the runtime
world, and those worlds only really cross over in terms of referring to
compile-time identifiers(e.g. label and variable declarations, type
evaluation). Since left to right is all compile-time, the meaning of an
expression can vary a lot depending on the keyword, and so to expand the
language I also have the option of adding original single-line DSLs to target
specific runtime expressions, if I can't get what I want by adding more types
to the RPN system. This is intended for small inline programs called from
scripting so the at-scale view of composability isn't the main priority. It's
still early, though, so I'll see how that all works out in practice.

~~~
zebproj
Very well said. I've come to similar conclusions working with the postfix
syntax of Sporth.

Is your language public at all?

------
genedelisa
The AudioKit project uses Soundpipe/Sporth under the hood if you'd like to see
another example of it in use.
[https://audiokitpro.com](https://audiokitpro.com)

~~~
BenGosub
oh that's a nice fact, The Audio Kit project is great!

------
nexuist
In a similar vein I present:
[http://nexuist.github.io/brainsynth/](http://nexuist.github.io/brainsynth/)

I want to eventually rewrite it in Svelte because React is too slow (probably
my own fault) to play notes and update the UI at the same time. I think I
should have used PureComponents or something.

~~~
zebproj
Thank you for sharing. It's always fun to see the ways BF can be used to make
music.

I really like the way the editor visually shows where you are. It would be
really neat to get it controlling some synths in an Ableton live session or
something. You'll get the notation system AND the great sounds that way.

At one point, I wrote a little BF sequencer for Sporth [0]. The nice thing
about it was it just output a signal which could be mapped to literally any
sound parameter at audio-rate. If the project still builds [1], there's a
single demo that I made of it.

0:
[http://paulbatchelor.github.io/proj/spigot/](http://paulbatchelor.github.io/proj/spigot/)

1:
[https://github.com/paulbatchelor/spigot](https://github.com/paulbatchelor/spigot)

------
anonytrary
This sounds really cool, but could use better docs. As someone who is new to
programmatic music composition, I'm not sure what the value proposition here
is.

~~~
zebproj
> This sounds really cool, but could use better docs.

I agree. As the creator, it can be hard to write documentation for a beginner
audience, so I'd be great to hear your input. What would you suggest?

Right now, here is the documentation that exists now:

Some intro tutorials can be found in the Sporth cookbook:

[http://paulbatchelor.github.io/proj/cook/](http://paulbatchelor.github.io/proj/cook/)

An interactive version can be found here:

[https://audiomasher.org/learn](https://audiomasher.org/learn)

Once you understand the syntax fundamentals, most of the work is just
remembering all the ugens and the argument order. For this, there's a
reference guide:

[https://github.com/PaulBatchelor/Sporth/blob/master/ugen_ref...](https://github.com/PaulBatchelor/Sporth/blob/master/ugen_reference.txt)

There's a command line utility that comes with sporth called "ugen_lookup"
that allows you to look up entries from here:

[https://github.com/PaulBatchelor/Sporth/blob/master/util/uge...](https://github.com/PaulBatchelor/Sporth/blob/master/util/ugen_lookup)

Most Sporth ugens are wrappers around Soundpipe modules, for which there is
also a reference manual for. It also provides more information about what the
module does:

[http://paulbatchelor.github.io/res/soundpipe/docs/](http://paulbatchelor.github.io/res/soundpipe/docs/)

~~~
pathsjs
I am a programmer and a musician. For me, syntax is easy, and understanding
stack languages is easy as well (for instance I authored a long tutorial on
Factor).

What is difficult for me is to understand:

* what kind of objects are available (generators and filters, I guess, anything more?)

* what generators are available, and what is their effect

* what are triggers. This sentence doesn't explain much "A single trigger is exactly one sample that is a non-zero value (typically, this value is just a '1')"

and so on. In general, much of the terminology is alien, for instance "Since
everything in Sporth is sample accurate, triggers are sample accurate and can
work at audio rate." or "This creates a gate signal which is then fed into the
portamento filter, whose half time value is 10ms. The portamento filter (a
simple one pole smoothing filter), creates the ideal exponential curves for
envelope, with a convex exponential slope on the attack, and a concave
exponential slope on the release."

I just do not understand half of the words in the above sentences.

What does it mean to be sample accurate? What is a portamento filter? Why
there should be a convex exponential slope on the attack? By the way, what is
the attack? (I know, but it wasn't explained earlier).

This looks like a tutorial written for people who are already programming
audio, and only need to understand Sporth. What is missing is a tutorial for
people that are programmers and musicians, but are using Sporth to start
programming audio.

~~~
zebproj
Good questions. My background is in computer music, and I do use a lot of
jargon. A good chunk of them probably come from Csound (and, by extension, the
MUSIC N family of languages). Sporth also expects a certain familiarity with
modular synthesis, and digital audio systems in general.

> what kind of objects are available (generators and filters, I guess,
> anything more?)

In Sporth, "objects" are referred to as things called unit generators, or
"ugens" for short. These things can take in signals as input, and write
signals as output. Signals are all audio rate. Even a constant value is an
audio-rate signal.

As you mentioned, effects and signal generators are two kinds of unit
generators. Another important one are control-signal generator, which include
things like low-frequency oscillators (LFOs), or envelopes. These things
generally produce signals designed control parameters of other things.

There are also things called ftables (short for function tables I think?
another term borrowed from Csound). These things are more or less floating
point arrays. Usually they are used for sequences, storing wavetables for
table-lookup oscillators, or loading audio samples. An ftable is an ftable, so
if you wanted to take a loaded snare sample and use it inside of a sequencer
ugen, there's nothing stopping you from that.

> what generators are available, and what is their effect

Hard to answer that one concisely. There are approximately 220 unit generators
in Sporth right now. Some of them do simple things like addition and
multiplication. Others do more complicated things. Some make sound, some
process sound, some do a combination of both. The art of composing with Sporth
is building an intuition for how these modules will sound together.
Admittedly, there is no documentation for all of this in Sporth. There's no
time for that. BUT! If you study highly modular computer music systems like
Csound or Supercollider, you'll learn concepts that can be applicable to
Sporth.

> what are triggers. This sentence doesn't explain much "A single trigger is
> exactly one sample that is a non-zero value (typically, this value is just a
> '1')"

Ah yes, this is modular synthesizer terminology. If you don't have experience
using modular synthesizers, I can see why this is confusing.

Triggers can be thought of as a sort of message, encoded in an audio signal.
When a trigger signal occurs, it means "do something". What that something is,
depends entirely on the ugen reading the signal. When a trigger signal gets
fed into an envelope generator, it tells the envelope to re-trigger the
envelope. When a trigger signal gets fed into a sequencer, it means go to the
next note in the sequence. Stuff like that.

> I just do not understand half of the words in the above sentences.

That was me and the music-dsp mailing list for many years. I've now got around
70-80 percent comprehension there, but yeah jargon. The thing is, once you
start memorizing jargon words, you forget what is jargon and what isn't.

I can totally see where you are coming from. It would be great to see Sporth
tutorials written for different audiences than mine own. But I need help for
that. It's just not a responsibility I am ready to take on. If you do not have
the prerequisite background in computer music, there is just _so_ much you
need to unpack, and it will never be enough. I'm only one person, and I need
what little time I have to try to compose my own music with the tools I write
for myself. Selfish by definition, but true.

~~~
pathsjs
Thank you for the explanations, I will try to read more and figure out stuff.

By the way, I was not complaining, I was just responding to

> I agree. As the creator, it can be hard to write documentation for a
> beginner audience, so I'd be great to hear your input. What would you
> suggest?

~~~
zebproj
They were great suggestions! Thank you for putting in the time. I hope my
responses were helpful.

------
Aleeshakhan786
Good

------
BubRoss
Things like this seem to me to be putting a language where what you really
want is just a focused IDE.

~~~
zebproj
Could you elaborate on what you mean by that?

~~~
BubRoss
I don't think there is anything that a different language here is going to
accomplish that a library couldn't, it seems like the main advantage is the
environment that allows for more fluid iteration combined with functions for
audio.

~~~
zebproj
> it seems like the main advantage is the environment that allows for more
> fluid iteration combined with functions for audio.

That's _exactly_ right. Having a terse and precise notation for expressing a
patch was originally why it was created. There are indeed lower level
libraries in other languages powering this (Soundpipe), but I found working in
C to be too slow for creative working. Sporth eliminated some of those
keystrokes and enabled me to compose music I wouldn't have been able to do
otherwise [0].

0:
[http://paulbatchelor.github.io/sporthlings/](http://paulbatchelor.github.io/sporthlings/)

~~~
TylerE
What about a dedicated high-performance audio/DSP langauge like csound?

~~~
zebproj
They are great as well! Especially Csound. That's where I started. In fact,
Sporth uses a lot of DSP code adapted from Csound.

From a language/syntax standpoint, you're still typing way more keystrokes
with Csound to do equivalent things in Sporth.

Design philosophies are different as well. Csound is divided up into an
orchestra/score, where the orchestra defines the signal flow of an instrument,
and the score has instructions on when to play the instruments. Sporth can
only define a sound in terms of signal flow, similar to a modular synthesizer.
In Csound terms, it's the equivalent of having a single Csound instrument that
is always on.

Performance-wise, I've found that Csound has a slight edge, but not by that
much. Both are mainly written in C, and both perform quite well in real-time.
I use low-end hardware, and I almost never have any issues myself. I wouldn't
recommend sporth for embedded systems at all, and to be careful when using it
on a raspberry pi 1 (but I said the same thing about Csound too).

From a code infrastructure standpoint, Sporth is way cleaner. And this is
mainly what motivated me to write Soundpipe + Sporth, and the biggest
strength. It's a very tiny language... the core of it is only a few thousand
lines of reasonably C code, with the rest of it being ugen code, which is
repetitive and easy to understand. It is built inside of a POSIX environment
using simple Makefiles. Sporth code is pretty portable, and can be trivially
dropped into a project (AudioKit does exactly this). Csound, on the other
hand, is a massive code base full of very cryptic legacy C code, with bits of
C++ thrown in for good measure, and uses CMake.

Csound has an amazing collection of opcodes for creating sounds. It's an
amazing ecosystem to explore. Sporth has 224 unit generators, which is a lot,
but a fraction of what Csound has to offer.

