Patchwerk is a portable ANSI C library for constructing audio graphs and DSP signal chains. It designed to be portable, fast, and memory efficient. The core Patchwerk is written using the CWEB literate programming system.
In addition to the core patchwerk API, there is also an actively maintained Runt interface with a built-in collection of DSP nodes from Soundpipe. The goal this Runt interface is to provide a similar feeling and faster alternative to Sporth, a stack-based audio language previously created by the author.
Patchwerk was built to be a better engine than Sporth. In most ways, this is true. It is much easier to introduce new modules than in Sporth. Patchwerk can often perform much better than the Sporth system as well.
Patchwerk is privately maintained as a Fossil repo, but a read-only git export can be found on github
Before compiling patchwerk, one must be to sure to have CWEB installed. This is usually comes standard with any standard tex/latex distribution. If you don't have tex already installed, one can compile CWEB as a separate program.
The core patchwerk library can be compiled with "make". This will generate, amongst other things, a C file called "patchwerk.c", and a header file called "patchwerk.h". These can be dropped into any C project and used as-is without any CWEB dependencies.
The patchwerk distribution also contains some additional goodies to work with.
The "dsp" folder contains a bunch of pre-made patchwerk nodes which wrap around Soundpipe DSP modules. The soundpipe library will be required to use them.
The "runt" folder contains a Runt interface for patchwerk. It is designed to create something that resembles the syntax found in Sporth.
The "examples" folder shows examples of how to use the patchwerk C API directly.
The "janet" folder has some experimental bindings to the Janet programming language. It is very much a work in progress.
Patchwerk produces audio graphs known as patches.
Patches are made up of interconnected things called nodes.
Nodes connect to one another with the use of cables.
All of this is managed using the patchwerk API.
Patchwerk computes audio one block at a time. A block size of 64 samples is used by default.
Nodes are executed sequentially in the order they are created. Since nodes can modulate other nodes, nodes can only make a connection if the node they are connecting to was created after itself. This simple rule solves the issue of dependency.
But wait! Things get a bit trickier. Patchwerk is a block-based system, meaning any cable that wants to be audio-rate needs to store things in a buffer. Every audio-rate cable could have allocated their own buffer, but this does not scale well from a memory standpoint, especially on constrained computing environments.
To make memory more manageable, Patchwerk implements a combination of a memory pool manage buffers. The memory pool has a pre-allocated set of buffers to choose from, which removes the scaling issue.
Buffers in the pool are consantly being overwritten and re-used. This is managed using a stack interface. Buffers pushed to the stack are immutable, and when they are popped, they return back to the pool to be reclaimed.
Highly serial patches (A to B to C), such as the ones you find in Sporth, lend themselves quite well to this approach. In fact, thinking in Patchwerk feels a lot like thinking in Sporth. Many Runt + Patchwerk patches look identical to Sporth code.
The buffer stack stops being useful when you have signals that need to modulate many different components. Stack operation would make this incredibly difficult to read.
The workaround for this is to explicitly hold the buffer to ensure it doesn't get reclaimed for the duration that it is needed, then explicitely un-hold the buffer so that it can be re-used.
For those reading this far down: buffer holding is the big gotcha in Patchwerk. If you do not unhold a previously held buffer, it will cause a certain kind of resource leak, which is troublesome if you want to recompile + hotswap patchwerk patches.
By itself, none to speak of. One must define these externally. The runt patchwerk interface provides a default set of DSP routines via the soundpipe DSP library. With some work, one could easily write new DSP modules. With even more work, one could use FAUST to generate DSP code for patchwerk (this is doable).