Thanks for the MMMAudio talk at the NOTAM SC meet-up last night.
I asked briefly if MMMAudio had something similar to the SuperCollider node tree allowing runtime topology changes. For example inserting a synth with addToHead, or reordering with moveBefore and moveAfter, etc. Might be slightly niche but sometimes I insert, remove and swap synth order while audio is running in SuperCollider.
From the MMMAudio docs it looks like the graph topology is defined at the Mojo level and compiled, which suggests this kind of runtime restructuring isn’t directly available. But having had a very superficial dig around the source code, maybe the Messenger system could be used for this? Or maybe I’m just used to thinking in SC signals instead of functions.
Many thanks for the work on this, looking forward to digging in!
Jim
Hi Jim,
The Messenger system will definitely be the thing that you’ll use to inform the Mojo side of things what order the “nodes” should be processed in. I’m sure there are a few different ways that this could be achieved. This how I’ve been “reordering” things is like this:
- I have different “modules” that are their own Mojo struct. Each has a next function that returns audio samples as output (an
MFloat[2] for stereo to be exact). It might also take an input (also an MFloat[2]).
- I have a matrix mixer that can receive samples from the hardware and also from the output of all of my modules. The outputs of the matrix mixer are routed to the input of all the modules as well as the output of MMMAudio.
- Changing the coefficients in the matrix mixer and send audio from any module to any other module, regardless of where is in the overall graph.
- If a module’s out is routed through the matrix mixer to the input of a module earlier in the graph, that connection is delayed by only 1 sample, instead of a whole block, which doesn’t bother me. It’s a tradeoff I’m willing to make.
//==============================================
For example, if my modules are A, B, and C (in that order on the graph. Coefficients like this would route the input → A → B → C → output. Nothing is delayed a sample because it’s all “in order.”
//==============================================
Changing the coefficients to this would be the following: input → A → C → (delayed 1 sample) B → output. So B & C have been “reordered.” The nice thing about this is that it also means I can route any of the sources directly to the output or do parallel processing or send FX in addition to insert FX.
It’s a different way of thinking about things, but for me it is actually kind of liberating to do all my routing through one powerful data structure instead of having to keep track of lots of busses.
1 Like
This is great! But it all has to run on the sameMMMAudio instance/World, correct? There’s no way to route between different processes, as I understand it? I’m asking because I’ve been thinking a bit about resource management for bigger systems, and I see it’s possible to pause audio streaming for instances, but I don’t imagine it’s possible to remove parts of the graph from the processing? I’m thinking of something similar to pauseing a Node in SC…obviously I haven’t built a massive system in MMMAudio yet, so perhaps it’s not even necessary…
Yes for 0 or 1 sample latency between modules that are attached to the matrix mixer.
By different processes, if you mean different MMMAudio Worlds running in parallel, then no, there’s no native way to route audio between different processes. Once could use Blackhole or a different virtual audio device.
It’s not implemented, but is theoretically possible to route audio between MMM Worlds, so it’s something we should look at in the future.
It depends on what you mean by “remove”. I’ve been imagining a system that defines just one main graph (using the routing described above) that I can launch as n different Worlds, it’s just that for one world only A and C are turned on (so B is taking 0 CPU) and for a different world only B is turned on, etc. That way I don’t need to manage multiple graphs, but can still easily disperse my computation across multiple CPUs.
Pausing is very easy to do! It’s likely that will look like sending a boolean to the MMM World that will indicate whether a certain struct’s .next() function should be run or not.
That would be amazing - when I learned it was possible to run several Worlds in parallel I was picturing: World for input processing → World(s) for fx processing/synthesis → World for output processing (“master fx”)…
…but then this completely blew my brain open *chef’s kiss*
Also: of course! This is pretty slick, and I guess it’s the way you could approach “turning off” structs in the main graph distributed across different worlds (as mentioned above).
Looks like my summer project will be rebuilding my SC setup… #MMMigration
For sure. In any case it will be one block size of latency between graphs, plus a block delay in and out, so just good to keep that accumulation of latency in mind.
Exactly.
1 Like