olofson / audiality2

A realtime scripted modular audio engine for video games and musical applications.
http://audiality.org/
zlib License
80 stars 5 forks source link

Patch objects - canned program coefficients #54

Open olofson opened 10 years ago

olofson commented 10 years ago

Any non-trivial instrument or sound effect tends to have a number of "tuning coefficients" that are used for tweaking. If it's desired to make these available for run-time tweaking, we currently have to resort to passing them as additional arguments to the program entry point. We do have a nice mechanism for providing defaults for unspecified arguments, but it would be nice to have a mechanism more suitable for this task.

The logic I have in mind here is a bit like adding an extra level of "patch" programming, much like you would find on traditional non-modular synthesizers, where you chose a hardwired voice structure and then tweak the available parameters on it.

In our case, the hardwired voice structures are replaced by modular voice structures, along with A2S programs to drive them. Roughly, you could say our voice structure corresponds to the audio DSP part of a traditional synth voice, whereas the script code corresponds to the LFOs, EGs etc.

What we don't have is a strict "parameter block." Instead, it's like all tweakable parameters have to be sent via MIDI or similar, which isn't very user friendly at all.

One design that would be nice and easy to use in pure A2S projects as well as when working with an external sequencer, would be to insert a new "level": Patch objects. These would be used almost exactly like programs; you start one, get a voice handle back, send messages to it, then tell it to quit and release the handle. Except they're not actually programs; only references to programs, along with patch parameters. They should be all constant data (that is, immutable objects - though it should probably be possible for A2S to create new ones at run time!), and they should have named fields, probably declared inside the respective programs.

olofson commented 9 years ago

One doesn't really need a special mechanism for this. It's entirely possible to just pass the "tuning coefficients" as extra arguments, and then implement the "patches" as wrapper programs that add these arguments.

Unfortunately, we currently have a max limit at 8 arguments, which might limit the usefulness of this a bit. That can be changed, of course, but there's a fixed cost to it that impacts all programs and (as of now, since they use the same interface) message handlers.

A more annoying problem is that wrapping programs like this actually runs the "real" program in a subvoice, which adds overhead, and is a waste of resources. Further, it has the side effect of messages being sent to the wrapper program, which would then have to receive and forward them. (That's actually a very useful feature, although undesired in this special case.)

In the past, there was a solution for this: A program could physically hand its voice over to another program. (This is how "function calls" were originally implemented, and was primarily intended for songs and other high level programs.) Unfortunately, this would be expensive and complicated to do now, and one would have to rebuild the voice structure; that's why this feature was removed.

Maybe we could avoid this problem by making a "patch" a special sort of program, that is not allowed to specify a voice structure, isn't allowed to use timing instructions etc? Maybe we could even make that a generic feature; "programs with no units are allowed to hand over their voices to other programs"...? Then a patch would basically run like a message handler on an "uninitialized" voice, do some argument/coefficient juggling, and end by tail-calling into the actual program, which is when proper voice initialization happens.