Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
1–10 of 36 posts
Re: Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
#2Re: Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
#3What are some things this can be used for? From a short skim this seems like a way to make sharable, tweakable effects and patches for any OS. Stuff that historically soundflower could be use for?
Here the idea is to write the algorithms in a way that is more future-proof, by not having them to depend on any run-time API, just a generic specification (given as a set of C++20 concepts). This way the algorithms will still be useful in 10 years when everyone has moved to API N+1, unlike a metric ton of existing audio software which depends on a specific audio / media-object API for no good reason (today ! When they were written C++ wasn't advanced enough to allow this at all)
- all the objects in https://github.com/pcastine-lp/LitterPower for instance
- all the algorithms in https://github.com/MTG/essentia/tree/master/src/algorithms
- ditto for https://github.com/cycfi/q/tree/master/q_lib/include/q
- ditto for all the VCV algorithms: https://github.com/VCVRack/Fundamental/tree/v1/src
- ditto for all the Bespoke objects: https://github.com/BespokeSynth/BespokeSynth/tree/main/Sourc...
- ditto for all the LMMS plugins: https://github.com/LMMS/lmms/tree/master/plugins
etc etc, there's a couple hundred of those, which always depend on some API and are thus not easily portable across environments: if tomorrow you want to make an audio software and want to use one of the, say, VCVRack plug-ins you're going to have to bring the whole VCV run-time API along.
Related projects are Faust (https://faust.grame.fr/) and SOUL (https://juce.com/discover/stories/soul-first-public-beta-rel...) but they both are domain-specific languages with their own compilers. I wanted a pure-C++ thing instead, which allows to call directly native code, and enables more than just generic audio processing: unlike Faust and SOUL (last time I checked) it's possible to make a message-based object for Pd or Max, not just an audio filter or synthesizer.
My end goal for this is that when I make an object for the main software I'm developing, ossia score (https://ossia.io) then the whole media arts community can benefit :-)
Re: Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
#4What are some things this can be used for? From a short skim this seems like a way to make sharable, tweakable effects and patches for any OS. Stuff that historically soundflower could be use for?
If you wrote a software that worked with soundflower it means that at some point you used to call either the CoreAudio API directly or any abstraction on top of it (RtAudio, PortAudio, ...). Thus harder to port to another OS :-) Here the idea is to write the algorithms in a way that is more future-proof, by not having them to depend on any run-time API, just a generic specification (given as a set of C++20 concepts).…
More baroque, for sure. More future-proof? Only if this is the one true form of reflected code that gets adopted.
> This way the algorithms will still be useful in 10 years when everyone has moved to API N+1
Actually, this way algorithms are not useful today since nobody writes them this way, and in 10 years, whoever writes this way right now will probably have moved on.
Now, if some small community (like C++-reflection-and-audio-buffs) commits to this, then maybe it makes sense for them. But not generally. General reflection (not to mention runtime reflection) is probably the way to go.
Re: Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
#5Re: Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
#6Earlier quoted context omitted.
If you wrote a software that worked with soundflower it means that at some point you used to call either the CoreAudio API directly or any abstraction on top of it (RtAudio, PortAudio, ...). Thus harder to port to another OS :-) Here the idea is to write the algorithms in a way that is more future-proof, by not having them to depend on any run-time API, just a generic specification (given as a set of C++20 concepts).…
> Here the idea is to write the algorithms in a way that is more future-proof More baroque, for sure. More future-proof? Only if this is the one true form of reflected code that gets adopted. > This way the algorithms will still be useful in 10 years when everyone has moved to API N+1 Actually, this way algorithms are not useful today since nobody writes them this way, and in 10 years, whoever writes this way right n…
ah, actually not :) the library tries to do things in two steps in many places:
1/ map the user's code to a proxy depending on which concepts it conforms to: for instance whether your audio processing function is written per-sample (that's still in progress tho):
float operator()(float input) { return input * 0.5; }
or per-buffer: void operator()(float** inputs, float** outputs, int frames) { for(each channel, each sample) { ... } }
2/ the back-end accesses the class through these proxies to do whatever it needs to do.this means than if tomorrow we get reflection and metaclasses (I hint to this in the readme), and that we can write the "ideal" struct which (to me) would look like
struct my_effect {
[[name: "Main inputs"]]
audio_input main_input;
[[name: "Main outputs"]]
audio_output main_input;
[[range: {20, 20000}]]
[[unit: hertz]]
slider frequency;
// will create an appropriate message in max / pd with the name some_function
void some_function(int, std::ranges::view> list_of_stuff);
auto operator()(std::floating_point auto in) { return in * gain; }
};
the only thing that one will have to do is map the new "shape" in these proxies (which are as far as possible doing their stuff at compile-time) and all the existing backends will keep working with the algos defined in the better way.of course the existing algorithms will stay uglier, but they'll also keep working with new environments (and, as far as possible, without a runtime perf. hit)
> General reflection (not to mention runtime reflection) is probably the way to go.
yes, we all want that !
Re: Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
#7Re: Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
#8This is mindblowing work. I'm having some trouble wrapping my head around all of it. Will lead with the context that:
A) I am not very familiar with audio programming specifics, or DSP at all. I have done some things with VST2 + VST3, and looked into LV2.
B) I have very little background with C++
I am curious: 1. Is this targeted more towards the DSP and algorithm side of audio plugin development, or is there interest/use for the plugin side too (IE, generating VST/LV2 compatible interfaces)?
2. Would a byproduct of this be that you would be able to implement functionality + interact with C++ classes from other languages? Or generate C ABI's? I see the note: "Automatically generate Python bindings for a C++ class"
I ask about the second one because I have been trying to work towards getting a C ABI for VST3 so that language bindings can be codegen'ed. I know VST3 works through COM so you can use that, but the alternative is to emulate the VTable layouts in C structs. This can make it easier than implementing COM in a language.-----
Edit: I also wanted to say that when reading your blogpost, the way you write C++ is very easy to understand. Not sure if it's a stylistic thing or a purposeful choice. I usually have to spend a second reading C++ code to comprehend it, but in your snippets, I was able to (mostly) grok it first pass.
Same for the example code in the repo.
Re: Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
#9Re: Declarative, non-intrusive, compile-time C++ reflection for audio plug-ins
#10Earlier quoted context omitted.
> Here the idea is to write the algorithms in a way that is more future-proof More baroque, for sure. More future-proof? Only if this is the one true form of reflected code that gets adopted. > This way the algorithms will still be useful in 10 years when everyone has moved to API N+1 Actually, this way algorithms are not useful today since nobody writes them this way, and in 10 years, whoever writes this way right n…
> More baroque, for sure. More future-proof? Only if this is the one true form of reflected code that gets adopted. ah, actually not :) the library tries to do things in two steps in many places: 1/ map the user's code to a proxy depending on which concepts it conforms to: for instance whether your audio processing function is written per-sample (that's still in progress tho): float operator()(float input) { return i…