Paul Batchelormore siren words87789ea5 years agoHistory
632 lines · 14.2 KB · Text
RawDownload
#+TITLE: The "Siren" Patch* OverviewThis document provides a rosetta stone for a =siren= patch.It's not exactly the most interesting patch, but itsuccinctly shows how to work with a stack-basedmodular environment. I originally wrote this patch as a bridge to show therelationship between runt code and C + patchwerk code(included amongst the examples).Now, I have adapated a version of it that works in sndkit!* Patch DescriptionThe siren patch can be generally described in the followingway: Generate an LFO signal, and use it to simultaneouslymodulate the frequency and timbre of another oscillator. Take that oscillator, make a copy of it, and route it toa reverb. Sum the oscillator and the reverb. Write it to disk.* Implementation 1: LIL and sndkitHere is the first implementation, written in LIL, thescripting language embedded in the sndkit repository. #+NAME: siren.lil#+BEGIN_SRC lil :tangle siren.lilsine 0.1 1holdregset zz 0 fmpair \    [gensine [tabnew 8192]] \    [biscale [regget 0] 100 600] \    [param 1] \    [param 1] \    [biscale [regget 0] 0.1 2] \    [param 0] mul zz 0.5 dup; dup bigverb zz zz 0.97 10000dropmul zz 0.1dcblocker zz add zz zz wavout zz "siren.wav" unhold [regget 0] computes 10#+END_SRC* Implementation 2: C and sndkitThis implementation shows the siren written in C usingthe sndkit @!(ref "core" "core API")!@. The corepatch construction can be seen in the =patch= function.What's really cool is how closely it matches the LIL code.If you take away the syntactic sugar in LIL, the underlyingsndkit calls are essentially 1:1. This patch was written byhand looking at the LIL code above. It was a surprisinglytrivial process! #+NAME: siren.c#+BEGIN_SRC c :tangle siren.c#include <math.h>#include <stdio.h>#include "patchwerk.h"#include "core.h"#include "wavout/wavout.h"#include "sine/sine.h"#include "scale/scale.h"#include "arith/arith.h"#include "bigverb/bigverb.h"#include "fmpair/fmpair.h"#include "gensine/gensine.h"#include "dcblocker/dcblocker.h" int patch(sk_core *core){    /* warning: no error checking */     sk_core_constant(core, 0.1);    sk_core_constant(core, 1);    sk_node_sine(core);    sk_core_hold(core);    sk_core_regset(core, 0);     sk_core_table_new(core, 8192);    sk_node_gensine(core);     sk_core_regget(core, 0);    sk_core_constant(core, 100);    sk_core_constant(core, 600);    sk_node_biscale(core);     sk_core_constant(core, 1);    sk_core_constant(core, 1);     sk_core_regget(core, 0);    sk_core_constant(core, 0.1);    sk_core_constant(core, 2);    sk_node_biscale(core);     sk_core_constant(core, 0);     sk_node_fmpair(core);     sk_core_constant(core, 0.5);    sk_node_mul(core);     sk_core_dup(core);    sk_core_dup(core);     sk_core_constant(core, 0.97);    sk_core_constant(core, 10000);    sk_node_bigverb(core);    sk_core_drop(core);    sk_core_constant(core, 0.1);    sk_node_mul(core);    sk_node_dcblocker(core);     sk_node_add(core);     sk_node_wavout(core, "siren.wav");     sk_core_regget(core, 0);    sk_core_unhold(core);     return 0;} int main(int argc, char *argv[]){    sk_core *core;    unsigned int n;    unsigned int nblocks;    int rc;     core = sk_core_new(44100);     rc = patch(core);     if (rc) {        fprintf(stderr, "Error code %d\n", rc);        goto clean;    }     nblocks = sk_core_seconds_to_blocks(core, 10);     for (n = 0; n < nblocks; n++) {        sk_core_compute(core);    }     clean:    sk_core_del(core);    return rc;}#+END_SRC* Implementation 3: Monolith and Runt=Monolith= is a realtime computer music environment I wrotefor myself as a precursor to sndkit. =runt= is a quirkyhomemade stack based language used to notate patches. Even though sndkit uses TCL and C syntax, the thoughtprocess and structure I use is still in stacks. #+NAME: siren.rnt#+BEGIN_SRC runt :tangle siren.rntpatchwerk nodes 0.1 1 sine bhold 0 cabset 0 cabget 100 600 biscale1 10 cabget 0.1 2 biscale0 8192 ftnew gen_sine fmpair0.5 mul bdup bdup0.97 10000 revscbdrop 0.1 mul dcblockadd "siren.wav" wavout 0 cabget bunhold sr 10 * _compute rep#+END_SRC* Implementation 4: Monolith and Scheme=monolith= is usually controlled via a Scheme REPL spawnedinside of Emacs, the flavor of scheme being a fork of s9scheme. Runt code is then executed in scheme as inline code.The scheme language is used as a kind of macro language,making it possible to build more complex structures andabstractions. The scheme =ugens= are wrappers around runt code, so it isstructurally identical to the runt and monolith code. #+NAME: siren.scm#+BEGIN_SRC scheme :tangle siren.scm(monolith:start-offline)(monolith:load "ugens.scm")(sine 0.1 0.5)(bhold zz)(cabset zz 0) (fmpair (biscale (cabget 0) 100 600) (param 1) (param 1) (biscale (cabget 0) 0.1 2) (param 0) (lambda () (gen_sine (ftnew 8192)))) (mul zz 0.5) (bdup)(bdup) (revsc zz zz 0.97 10000)(bdrop)(mul zz 0.1)(dcblock zz)(add zz zz)(wavout zz "siren.wav")(bunhold (cabget 0))(out zz) (monolith:compute (* 44100 10))#+END_SRC* Implementation 5: Sporth=sporth= was my first ever stack-based audio system.Runt syntax and Sporth syntax are very similar to oneanother, but the underlying architecture is verydifferent (and slower too). #+NAME: siren.sp#+BEGIN_SRC sporth :tangle siren.sp_ft 8192 gen_sine0.1 1 sine 0 pset 0 p 100 600 biscale0.51 10 p 0.1 2 biscale_ft fosc dup dup0.97 10000 revscdrop 0.1 mul dcblkadd#+END_SRC* Implementation 6: C and PatchwerkThis comes from my original[[https://github.com/paulbatchelor/patchwerk-siren][siren patch]].It's not identical to the previous patches, butit is in the same spirit. The corresponding runtcode (which runs on the =rntpatchwerk= utilitythat comes with patchwerk) is included inthe comments. As one can see, patchwerk has a lot of repetitive low-leveloperations which makes for tedious to read code. Most ofthe sndkit core abstraction aims to be an abstraction on topof this code. For contrast, you compare this to the C andsndkit code from above. #+NAME: siren.c :tangle siren.c#+BEGIN_SRC c/* * Siren * * This code will generate a simple siren patch using * some of the pre-made patchwerk-wrapped soundpipe dsp * nodes found included in the patchwerk codebase. * * The patch is equivalent to the following runt code: * * == BEGIN RUNT CODE == * * patchwerk nodes * * 0.1 1 sine 0 1 biscale bhold 0 cabset * * 0 cabget 100 600 scale 0.5 blsaw * * 0 cabget 100 2000 scale butlp * * bdup * bdup 0.97 10000 revsc bdrop 0.1 mul dcblock * * 0 cabget bunhold * * add * * "siren.wav" wavout bdrop * * sr 10 * _compute rep * * == END RUNT CODE == * * It is advisable to understand how the runt code above * works before attempting to parse out the C code below. * That way, the runt code can be used as a sort of Rosetta * Stone. Comments in the C program will break up the * program by Runt statements. With any luck, a reader * should begin to understand connection between the * Patchwerk C library and Runt abstraction. * * Even with the DSP wrapper code, one can see from * this small program that the Patchwerk C API at this level * is quite repetive and redundant. In practice, it is best * to write abstractions on top of this and not mess with * these C operations directly. Otherwise, it is slow and * tedious work. * * When building up a patchwerk patch, the programmer must * be able to keep track of what is on the buffer stack at * all times. Missing a push or pop operation can cause the * entire patch to break. At the C level, these can be very * tedious to debug! It is highly recommended to express the * Patch using some sort of postfix notation like Sporth or * Runt. Presenting the patch in this way will naturally * align the stack operations. * */ #include <stdlib.h>#include <soundpipe.h>#include "patchwerk.h" #include "dsp/sine.h"#include "dsp/wavout.h"#include "dsp/biscale.h"#include "dsp/blsaw.h"#include "dsp/scale.h"#include "dsp/butlp.h"#include "dsp/revsc.h"#include "dsp/mul.h"#include "dsp/dcblock.h"#include "dsp/add.h" #define NBUFS 8#define STACKSIZE 10#define SR 44100#define BLKSIZE 64 static add_d * mk_add(pw_patch *patch,                      sp_data *sp){    pw_node *node;    add_d *add;    pw_patch_new_node(patch, &node);    node_add(node, sp);    pw_node_setup(node);    add = pw_node_get_data(node);    return add;} static dcblock_d * mk_dcblock(pw_patch *patch,                              sp_data *sp){    pw_node *node;    dcblock_d *dcblock;    pw_patch_new_node(patch, &node);    node_dcblock(node, sp);    pw_node_setup(node);    dcblock = pw_node_get_data(node);    return dcblock;} static mul_d * mk_mul(pw_patch *patch,                      sp_data *sp){    pw_node *node;    mul_d *mul;    pw_patch_new_node(patch, &node);    node_mul(node, sp);    pw_node_setup(node);    mul = pw_node_get_data(node);    return mul;} static revsc_d * mk_revsc(pw_patch *patch,                          sp_data *sp){    pw_node *node;    revsc_d *revsc;    pw_patch_new_node(patch, &node);    node_revsc(node, sp);    pw_node_setup(node);    revsc = pw_node_get_data(node);    return revsc;} static butlp_d * mk_butlp(pw_patch *patch,                          sp_data *sp){    pw_node *node;    butlp_d *butlp;    pw_patch_new_node(patch, &node);    node_butlp(node, sp);    pw_node_setup(node);    butlp = pw_node_get_data(node);    return butlp;} static blsaw_d * mk_blsaw(pw_patch *patch,                          sp_data *sp){    pw_node *node;    blsaw_d *blsaw;    pw_patch_new_node(patch, &node);    node_blsaw(node, sp);    pw_node_setup(node);    blsaw = pw_node_get_data(node);    return blsaw;} static scale_d * mk_scale(pw_patch *patch,                          sp_data *sp){    pw_node *node;    scale_d *scale;    pw_patch_new_node(patch, &node);    node_scale(node, sp);    pw_node_setup(node);    scale = pw_node_get_data(node);    return scale;} static biscale_d * mk_biscale(pw_patch *patch,                              sp_data *sp){    pw_node *node;    biscale_d *biscale;    pw_patch_new_node(patch, &node);    node_biscale(node, sp);    pw_node_setup(node);    biscale = pw_node_get_data(node);    return biscale;} static sine_d * mk_sine(pw_patch *patch,                        sp_data *sp){    pw_node *node;    sine_d *sine;    pw_patch_new_node(patch, &node);    node_sine(node, sp);    pw_node_setup(node);    sine = pw_node_get_data(node);    return sine;} static wavout_d * mk_wavout(pw_patch *patch,                            sp_data *sp,                            const char *filename){    pw_node *node;    wavout_d *wavout;     pw_patch_new_node(patch, &node);    node_wavout(sp, node, filename);    pw_node_setup(node);    wavout = pw_node_get_data(node);    return wavout;} int main(int argc, char *argv[]){    sp_data *sp;    pw_patch *patch;    sine_d *sine;    pw_stack *stack;    wavout_d *wavout;    unsigned int n;    pw_buffer *buf;    pw_cable *lfo;    biscale_d *biscale;    blsaw_d *blsaw;    scale_d *scale;    butlp_d *butlp;    revsc_d *revsc;    mul_d *mul;    add_d *add;    dcblock_d *dcblock;      /* initialize + allocate */ 

Lines 1–500 of 632.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t