I built an ANSI C library for building audio graphs called patchwerk: https://pbat.ch/proj/patchwerk/

It’s really nothing too fancy (no feedback), and I don’t claim to have the best possible solution, but it gets the job done. I use it everyday in my own musical software ecosystem.

You can find the code here.

It’s also a literate program, written using CWEB. For convenience, here is the woven PDF (375.2 KB) . Fair warning: this document is in dire need of an editor! But my hope is that it should be coherent enough.

1 Like

I just stumbled on this as I was searching ! I’ve been spying on soundpipe as well, I am a fan :+1:

feedback routing would be nice, but this seems like a good starter resource and I appreciate the documentation efforts

At some point I’ll manage to get feedback into patchwerk, but I’ve found myself not needing it at that level. Usually most of my feedback needs can be done inside of a single node (feedback delay, for example). For more crazy signal chains utilizing feedback, I’ve found myself writing stuff using FAUST (the recursion operator is a very powerful thing), and then exporting that to a patchwerk node where it can talk to other pre-baked nodes.

The challenge with getting feedback in patchwerk has to do with the fact that patchwerk does block-processing (internally 64 sample block size usually), and for feedback to work you need single-sample processing (a block size of 1). My previous system Sporth is capable of feedback because it does sample-accurate processing. Of course, you could also set the block size in patchwerk to be 1, but you get reduced performance that way.

So, the solution is to figure a clean (well, clean enough) way to get single-sample processing chunks working inside of the normal block processing. From there, inside of the single-sample processing environment, it would be a pretty trivial matter to introduce recursive feedback into systems with an implicit 1-sample delay.

1 Like

I think I might be mixed up about something, is the feedback limitation related to this ?

When a new node is created, it is appended to the end of a linked list of nodes and given an incremental unique ID based the position in the list (0, 1, 2, 3, etc.). A node can only connect to inputs of other nodes with higher IDs. This ensures that the node is guaranteed to be rendered beforehand.

does this prevent connecting nodes in a circular fashion, like
A -> B, B -> C, C -> output, C -> A ?

my use case is building a real time GUI, so I can’t reasonably restrict users from making a chain like that, but if I need to construct some logic in the host to say, insert a 64 sample delay node between C & A that’s perfectly fine

Yup. Nothing circular like that without some kind of specialized delay that works at the block-level.

It’s helpful to imagine patchwerk patches as being chunks of Sporth-like code. That is to say, a stack based system using post fix / reverse-polish notation. Without the circular logic, your system could be expressed as A B C out. Using some kind of delay logic, you may end up with something that looks like delay_get A B C bdup delay_set out, where delay_get and delay_set are coordinated nodes with shared data, and bdup makes a copy of the output of signal C, sending one copy to delay_set and one copy to out.

1 Like

gotcha, I think this could work for me ! I doubt it would be too tricky for my host application to handle that logic and shuffle connections when needed. keeping the C side of things simple feels like a plus for me anyway

Checkout my project Oscen: https://github.com/reedrosenbluth/oscen

It’s a DSP graph / sound synthesis library written in Rust. The DSP graph code is here. We call the graph a rack.

2 Likes

I found this too ! it looks nice, but my hope was to mostly integrate with existing DSP written in C/C++ to avoid the overhead of porting to another language like rust. the codebase is very clean though

1 Like

If there was a way to wrap existing C/C++ code into a rust component in the oscen system, you could get the best of both worlds. Soundpipe + STK would provide a solid foundation to be musical. From there, the underlying DSP code could then be gradually refactored and ported to work in Rust.

it does seem like the rust folks are pretty on top of things here. reading the comments on this has been helpful

short of adopting rust, I am using lua elsewhere in my system. I’m curious if it would make any sense to handle graphs and memory allocation in lua(jit) and define the opcodes in C ? (its a real-time audio thing)

in that case it looks like this would be helpful -> https://github.com/chen0040/lua-graph
that rust code above uses a topological sort (of a DAG) to compute the order that nodes are evaluated in

3 Likes

@andrew You might find some insights from the (now dormant) LuaAV project, which used luajit to compile audio graphs on-the-fly. It was super freaking cool 10 years ago. The authors went on to create the Gen language for Max/MSP/Jitter.

1 Like

whoa !!

earlier in the day I was trying to search around for the code in this paper but all the links were broken https://www.mat.ucsb.edu/Publications/07_WakefieldSmith_ICMC_Lua.pdf

that sounds way closer to what I’m looking for, will dig

2 Likes

@andrew Yeah, it’s frustrating that some of those links are broken…

This is probably the best reference for the audio graph: https://www.mat.ucsb.edu/Publications/smith_wakefield_ICMC2009.pdf

Here’s a paper with a broader overview of the system that I ever-so-slightly contributed to:

And if you’ve got a researchgate account you might want to request a copy of Graham’s dissertation which goes into more detial… and is also the only scholarly reference on the architecture of gen~ that I’m aware of: https://www.researchgate.net/publication/320736494_Real-Time_Meta-Programming_for_Interactive_Computational_Arts

And after all of that… on re-reading some of these a bit I remembered that luaav doesn’t actually use luajit, instead their c/c++ bindings call LLVM on-the-fly to compile the audio graph.

Been a professional developer for 8 years, college for CS for 7 years before that. Never once have I tried to use my powers on my hobbies. Hell I even do dawless most of the time.

I think I need to change that.

5 Likes

I’ve been working on a new project recently called sndkit. It’s a statically generated wiki of DSP algorithms (implemented in ANSI C) written using a literate style. You can find the current list of algorithms here. It’s a work in progress. It’s only got a handful of things so far, but the list will grow.

13 Likes

This is fantastic! Not sure if I missed the location of the actual C files or how you’re generating the literate code… assuming they exist in a raw form, it’d be great to have the C files linked at the top of each algorithm page to make copy/pasting easier.

But great job on the primary intent (explaining/detailing the algorithms).

1 Like

Thank you for the kind words!

The source code for sndkit can be found on sourcehut. The tangling happens with a tool called worgle. The weaving happens using a tool called weewiki. This process will be documented better in the future.

I’ve intentionally left out the ability to view the tangled C code. In true literate programming environments (like Knuth’s WEB/CWEB), the tangled code output is not supposed to be read by human eyes. My fear is that once code is readily available, people will want to immediately look to that instead of the words. Because that’s what is comfortable for programmers. The problem is, by the time DSP code reaches C, all the meaningful stuff has been boiled off.

Many of these algorithms can be found pre-tangled in soundpipe or monolith, which should satisfy most of the grab-and-go types.

3 Likes

Great, I’ll look in those other repos as needed!

In regards to Knuth’s intentions, he was also of course thinking about people writing and editing the tangled code (and compiling it after processing by TANGLE), whereas in a pure wiki not having a link to the untangled code—or a way to easily untangle—makes that more difficult than in Knuth’s systems.

But I definitely get the intention of the project and the fear/hesitation for providing that option. Thanks for sharing, looking forward to both studying some of the algorithms I don’t know and pointing students to this.

1 Like

This looks great, thank-you!

1 Like

This tangling, does it refer to presenting the code in a different order than the compiler needs it? Because that has always struck me as a limitation of literate programming, that you might want to tell the reader about some basic functions before introducing the full type definitions, for instance.