The Audio Matrix is a configurable router and signal processor for Audio-Setups with lots of channels/speakers that offers built in remote control via OSC.
A configurable amount of input tracks is routed in parallel through user configurable tracks, that can process the audio on them using a variety of modules.
An example Procesing setup could look like this:
cmake -DCMAKE_BUILD_TYPE=Debug -B ./build -G Ninja
cmake --build ./build --config Debug --target all
Configuration is done using a config file in YAML format. Some example configs can be found in examples/configs
.
Option Name | Description | Default Value |
---|---|---|
port |
Port the OSC-Server listens on | 12345 |
n_input_channels |
Amount of Input channels to be created. these are sent to every track | 64 |
tracks |
Array of tracks , see "Configuration of Tracks" |
[] |
Option Name | Description | Default Value |
---|---|---|
name |
Name of this track | "" |
modules |
Array of modules , see "Configuration of Modules" |
[] |
Modules always start with the name of the module as the outermost key. The module names can be found in the following "Module: ..." sections.
For simple modules that don't take any parameters like sum
only the name is needed, you could specify it as
tracks:
- name: example track
modules:
- sum
for more complex modules additional options are needed. If the module should be OSC-controllable, the option osc_path
is required. it expects as a string the osc path this module should listen to. complex modules might use this path as a base path to listen on different subpaths, see their individual configs for that.
Some options called "primary options" can be directly set as the value of a module type, for example
tracks:
- name: example track
modules:
- gain: 0.5
This only works if this is the only option that is manually set.
In all other cases modules are defined using more key-value-pairs:
tracks:
- name: example track
modules:
- hoa_encoder:
order: 3
osc_path: /positions
name | gain |
OSC controllable | yes |
OSC format | [osc_path] if <channel_index> <channel_gain> |
Module used to apply individual gains per channel. OSC control enables enables the setting of these gains.
Option Name | Description | Default Value |
---|---|---|
factor (primary option) |
Initial gain for all channels | 1 |
name | hoa_encoder |
OSC controllable | yes |
OSC format | [osc_path] ifff <channel_index> <azimuth_in_rad> <elevation_in_rad> <distance_in_m> |
This module encodes the incoming audio into the ambisonics domain. channels are ordered following the AmbiX format. The individual positions for each channel are settable using OSC.
Option Name | Description | Default Value |
---|---|---|
order |
Ambisonics order to encode to, currently orders up to 4 are supported | 3 |
name | sum |
OSC controllable | no |
This module sums up all incoming channels into one channel
name | filter |
OSC controllable | no |
This module implements Linkwitz-Riley lowpass- or highpass filters.
Option Name | Description | Default Value |
---|---|---|
order |
Order of the Linkwitz-Riley Filter, order of the individual butterworth filters will be order/2 |
4 |
freq |
Cutoff Frequency of the Filter (in Hz) | 150 |
type |
Filter type, options are LP and HP |
HP |
name | distance_gain |
OSC controllable | yes |
OSC format | [osc_path] if <channel_index> <distance_in_m> |
This Module adjusts the gain of a channel depending on its distance from the listener
name | delay |
OSC controllable | no |
Simple non-interpolating delay line to adjust for latencies between different playback systems
Option Name | Description | Default Value |
---|---|---|
time (primary option) |
Delay time in ms | 5 |
To add a module, several files have to be edited, until a more streamlined approach is found.
In ModuleConfig.h
add an identifier of your module to the Modules
enum
Modules must inherit from Module
.
Modules need a config struct inheriting from ModuleConfig
.
Modules should be created in a subfolder of Module following this directory structure:
source
└── Module
├── MyModule
│ ├── MyModuleConfig.h
│ ├── MyModule.cpp
│ └── MyModule.h
├── ModuleConfig.h
├── Module.cpp
└── Module.h
- Create your classes
TODO explain classes
Parsing of the config happens in the ConfigParser
, so we need to add the module-specific config parser there.
Add an include directive for your config in ConfigParser.h
:
#include <MyModuleConfig.h>
Add a function signature for the parser function to ConfigParser.h
, next to the other similar functions:
ModuleConfigPtr parse_module_mymodule(YAML::Node module);
Implement this function in ConfigParser.cpp
. Parsing is done using the yaml-cpp library. For simple key/value pairs in the config you can use the get_config_option()
function for convenience. If your Module is controlled using OSC, call parse_module_osc_params()
for your module.
The call to parse_module_mymodule
should happen in ConfigParser::parse_module()
, so you have to add an if-case for your module to the case structure there:
} else if (name == "mymodule") {
return parse_module_mymodule(module);
}
Modules are initialized by the Track
they belong to.
First you have to include your module header in the file Track/Track.h
like this: #include <MyModule.h>
, then you have to add a case for your module in the Constructor of Track
in the file Track/Track.cpp
:
switch (module_config->module_type())
{
// MODULE SWITCH CASES GO HERE
...
CASE_MODULE(Modules::MY_MODULE, MyModuleConfig, MyModule);
...
// END MODULE SWITCH CASES
default:
break;
}
This Project is licensed under the GPLv3.
Calculation of the IIR filter coefficients is performed using the IIR library by Exstrom Laboratories, also licensed under the GPLv3
AudioBuffer and RingBuffer implementations are taken from the anira library, licensed under Apache License 2.0