SamuKata
colugomusic
colugomusic

patreon


Dev Log #66 - Dependency graphs

When blocks are moved around a bunch of stuff is happening behind the scenes. One of the most complex things going on is the dependency graphs.

I have been working on implementing macros and the existing implementation of this stuff was giving me a huge headache so I ended up rewriting the entire thing. So while it is fresh in my head here is a post about it.

Blockhead has to keep track of which blocks are logically connected to other blocks on the workspace. There are two different contexts where this has to happen:

1. Manipulators

Every time any block is moved, even if it's only a pixel or two, it has to update its relationships with the other blocks on the workspace.

If a manipulator block is moved, it has to check if it is now intersecting with any plugin blocks.

If it detects an intersection, it further checks if:

The connection is always performed symmetrically, so the manipulator block is added to the plugin block's list of dependencies, while the plugin block is added to the manipulator block's list of dependants.

If the plugin block was the one that moved into range of the manipulator, but the manipulator stayed still, the same logic has to sort of be performed in reverse.

Here the plugin block checks if it is intersecting with any matching manipulator and creates any appropriate connections.

Any time a block is moved (and certain other operations), it also has to iterate through all its existing connections, and check if any of them are expired.

In the case of manipulators, connections are considered expired if the two blocks no longer intersect, or if the destination setting no longer matches.

This is part of why it's necessary to maintain the connections symmetrically. When the plugin block moves we don't want to have to go through every manipulator on the workspace to check if it was connected to the block we just moved (though this is in fact what is happening in v0.25.2, so perhaps it is fine to do that. Regardless we won't be doing that any more.)


2. Baking

To save CPU during baking, a block will always wait for its dependencies to be baked first, so that it can just use the baked audio in its own baking process rather than relying on the real-time processor. Send/receive blocks also rely on the baking system to do their stuff, as long as they are not involved in a feedback loop.

Therefore the baking system needs to maintain a similar graph to the manipulation system. Here it's a bit more complicated though.

The baking graph consists of audio sources and audio sinks. An audio source is anything that emits audio whereas an audio sink is anything which receives audio.


In this example, assuming the send and receive blocks are connected together, a feedback loop exists. In this case baking is turned off for everything involved in the feedback loop, which here includes the send block, the receive block, and the effect block. Baking doesn't need to be turned off for the sampler block because it only injects audio into the feedback path, but doesn't rely on the output of the feedback signal.


More Creators