Mixing with inserts and sends#
Goal: build a console-style mixer — a channel with an insert effect chain, and a shared reverb fed by aux sends.
Channels introduced the channel tree for grouping sounds under shared volume. Channels also carry the two routing primitives every mixer needs:
an insert chain — a pre-fader
YSE::DSP::dspObjectchain that processes everything on the channel (YSE::channel::setDSP());aux sends to a return bus — a scaled copy of the channel routed to a shared effect (
YSE::channel::makeReturn(),YSE::channel::send()).
This tutorial walks through Demo21_Mixer: a music channel running
EQ → compressor → chorus, plus a plate reverb both channels send into.
Source: Demo21_Mixer.cpp.
Two source channels#
musicCh_.create("music", YSE::ChannelMaster());
drumCh_.create("drums", YSE::ChannelMaster());
Building an insert chain#
Configure the effect modules, chain them with dspObject::link, then
attach the head of the chain to the channel. The whole chain runs pre-fader
on the summed channel signal:
// Build the music channel's insert chain: EQ -> compressor -> chorus.
eq_.gain(YSE::DSP::MODULES::EQ_LOW_SHELF, 4.0f);
eq_.frequency(YSE::DSP::MODULES::EQ_PEAK_1, 900.f).gain(YSE::DSP::MODULES::EQ_PEAK_1, -3.0f);
eq_.gain(YSE::DSP::MODULES::EQ_HIGH_SHELF, 3.0f);
comp_.threshold(-18.f).ratio(4.f).attack(10.f).release(120.f).makeup(4.f);
chorus_.mode(YSE::DSP::MODULES::MODE_CHORUS).rate(0.6f).depth(0.4f).impact(0.4f);
eq_.link(comp_);
comp_.link(chorus_);
musicCh_.setDSP(&eq_);
Each module is an ordinary YSE::DSP::dspObject, so it has the
inherited bypass / impact (wet/dry) controls on top of its own
parameters. The mix-grade modules — parametric EQ,
compressor,
chorus — are documented on the
Mixing effects page.
Driving it live#
Toggling an insert chain and riding the send levels is glitch-free — send levels are ramped internally, so they are safe to set every control tick:
void DemoMixer::ToggleInserts() {
insertsOn_ = !insertsOn_;
musicCh_.setDSP(insertsOn_ ? &eq_ : nullptr);
}
void DemoMixer::SendUp() {
sendLevel_ = sendLevel_ + 0.1f > 1.0f ? 1.0f : sendLevel_ + 0.1f;
musicCh_.setSendLevel(0, sendLevel_);
drumCh_.setSendLevel(0, sendLevel_);
}
void DemoMixer::SendDown() {
sendLevel_ = sendLevel_ - 0.1f < 0.0f ? 0.0f : sendLevel_ - 0.1f;
musicCh_.setSendLevel(0, sendLevel_);
drumCh_.setSendLevel(0, sendLevel_);
}
Passing nullptr to setDSP detaches the insert chain; the engine takes
no ownership of the modules, so they must outlive the channel.
Tearing down routing#
Because the audio thread walks the insert chain and sends every block, tear
the routing down — clearSend and setDSP(nullptr) — before the
channels and effect objects destruct:
// Tear down the routing before the channels/effects destruct so the audio
// thread stops touching the insert chain and sends first.
musicCh_.clearSend(0);
drumCh_.clearSend(0);
musicCh_.setDSP(nullptr);
reverbReturn_.setDSP(nullptr);
for (int i = 0; i < 5; i++) {
YSE::System().update();
YSE::System().sleep(20);
}
What you learned#
channel::setDSP(head)installs a pre-fader insert chain; chain modules withdspObject::link.channel::makeReturnturns a channel into a return bus;channel::send/setSendLevelfeed it from other channels.The engine never owns insert or return effects — they must outlive the channel, and routing should be torn down before they destruct.
Next#
Mixing effects — the mixing-effect module reference.
Channels — channel routing, sends and metering.
Per-note 3D: position handlers and swarms — spatial positioning per note.