Getting started
Here is a minimal, self-contained definition of an Avendish processor:
import std;
[[name: "Hello World"]]
export struct MyProcessor
{
void operator()() {
std::print("Henlo\n");
}
};
... at least, in an alternative universe where C++ has gotten custom attributes and reflection on those,
and where modules and std::print
work consistently across all compilers ; in our universe, this is still a few years away. Keep hope, dear reader, keep hope !
Getting started, for good
Here is a minimal, self-contained definition of an Avendish processor, which works on 2022 compilers:
#pragma once
#include <cstdio>
struct MyProcessor
{
static consteval auto name() { return "Hello World"; }
void operator()() {
printf("Henlo\n");
}
};
Yes, it's not much. You may even already have some in your codebase without even being aware of it !
Now, you may be used to the usual APIs for making audio plug-ins and start wondering about all the things you are used too and that are missing here:
- Inheritance or shelving function pointers in a C struct.
- Libraries: defining an Avendish processor does not in itself require including anything. A central point of the system is that everything can be defined through bare C++ constructs, without requiring the user to import types from a library. A library of helpers is nonetheless provided, to simplify some repetitive cases, but is in no way mandatory ; if anything, I encourage anyone to try to make different helper APIs that fit different coding styles.
- Functions to process audio such as
void process(double** inputs, double** outpus, int frames);
We'll see how all the usual amenities can be built on top of this and simple C++ constructs such as variables, methods and structures.
Line by line
// This line is used to instruct the compiler to not include a header file multiple times.
#pragma once
// This line is used to allow our program to use `printf`:
#include <cstdio>
// This line declares a struct named MyProcessor. A struct can contain functions, variables, etc.
// It could also be a class - in C++, there is no strong semantic difference between either.
struct MyProcessor
{
// This line declares a function that will return a visible name to show to our
// users.
// - static is used so that an instance of MyProcessor is not needed:
// we can just refer to the function as MyProcessor::name();
// - consteval is used to enforce that the function can be called at compile-time,
// which may enable optimizations in the backends that will generate plug-ins.
// - auto because it does not matter much here, we know that this is a string :-)
static consteval auto name() { return "Hello World"; }
// This line declares a special function that will allow our processor to be executed as follows:
//
// MyProcessor the_processor;
// the_processor();
//
// ^ the second line will call the "operator()" function.
void operator()()
{
// This one should hopefully be obvious :-)
printf("Henlo\n");
}
};