Mixing & Routing¶
The Mixing Hierarchy¶
The mixing hierarchy exists to allow for dynamic runtime mixing. The hierarchy consists of buses. A bus is an audio node that takes multiple input audio streams, mixes them together, and outputs a single combined stream. Buses are managed via string identifiers.
The Master Bus¶
The Engine creates a permanently allocated Master bus on initialization. This bus serves as the root node for the
mixing hierarchy and all audio in the engine ultimately flows through it before reaching the speakers. The Master bus
cannot be destroyed or routed.
Bus Management¶
To create a new bus, use Engine::CreateBus(). Every new bus must be given a unique identifier string. Optionally, you
can also provide the identifier string of the bus you wish the new bus to be routed to
(it can be re-routed at any point). If no parent identifier is passed, the new bus will be routed to Master.
engine->CreateBus("SFX"); // New bus with identifier "SFX" routed to "Master"
engine->CreateBus("UI", "SFX"); // New bus with identifier "UI" routed to "SFX"
Engine::CreateBus() using an identifier that already exists, the engine will
increment the internal reference count of that bus.
A bus can be re-routed at any point using Engine::RouteBus():
Result::InvalidRouting.
To destroy a bus, simply call Engine::DestroyBus(). As buses are reference counted, this function must be called the
same number of times as Engine::CreateBus() for the same identifier in order to be destroyed.
PlaybackExitCondition::ExplicitStop.
Bus Parameter Control¶
The volume of a bus can be changed at any point. Doing this will identically scale the volume of every playback and child bus routed into it.
Routing Playback Instances¶
By default, all new playback instances are routed directly to the Master bus. To route it to the desired bus in your
mixing hierarchy, use Engine::RoutePlayback(). This can be done at any point before or during playback.
Effects¶
A bus does not just combine audio. It can also process it. Every bus has an Effects Rack with 4 effect slots
(indices 0 through 3). Before the output of a bus is mixed into its parent, it is processed sequentially through the
effect slots in the order 0, 1, 2, 3.
Creating an Effect¶
Effects must first be created and configured before being attached to a bus. Let's create a Lowpass filter.
// Filter that cuts off frequencies above 800Hz
dalia::BiquadParams lowpassParams;
lowpassParams.type = dalia::BiquadParams::Type::LowPass;
lowpassParams.frequency = 800.0f;
lowpassParams.resonance = 1.0f;
dalia::EffectHandle lowpassFilter;
engine->CreateEffect(lowpassFilter, lowpassParams);
Attaching and Hot-Swapping¶
Once you have a valid EffectHandle, you can attach it to a specific slot on a bus.
Note: Hot-swapping an effect on a bus that is actively playing audio bypasses internal fade-outs and may cause "pops". It is advised to only attach and detach effects when a bus is not actively playing audio.
Modifying and Detaching¶
The parameters of an effect can be altered at any time, even when it is actively processing audio.
lowpassParams.frequency = 400.0f;
engine->SetEffectParams(lowpassFilter, lowpassParams); // Lower the frequency cutoff to 400Hz
BiquadParams::Type of an active filter alters its DSP topology instantly. Unlike frequency and
resonance, discrete topology changes are not smoothed and will cause an audible pop if audio is currently passing
through. It is highly recommended to only change filter types during silence or when the filter is not attached.
When the effect is no longer needed, it can be detached from the Effect Rack. Detaching an effect does not destroy it. It simply unplugs it from the bus and can be re-attached to the same, or any other bus later.
To destroy the effect and free its memory, simply callEngine::DestroyEffect().
Note: It is completely safe to destroy an effect that is attached to a bus. This will have the same effect as first
detaching the effect and then destroying it.