Mixing effects

Contents

Mixing effects#

Mix-grade effect modules for building channel strips and effect returns. Each is a YSE::DSP::dspObject subclass, so it chains with dspObject::link and mixes wet/dry through the inherited impact.

Routing them is a channel-level concern:

Both are documented on the Channels page. The Mixing with inserts and sends tutorial walks through a complete console mixer built from these pieces.

Parametric EQ#

namespace YSE

Public API of libYSE — sound playback, mixing, and 3D positional audio.

Entry points: YSE::System (lifecycle and audio device), YSE::Listener (3D origin), YSE::sound (a playable source), YSE::channel (mixing tree), YSE::reverb (positioned reverb zone), YSE::patcher (modular DSP graph), YSE::player (note sequencer). Sub-namespaces group domain-specific types: YSE::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

namespace DSP

Audio buffers, oscillators, filters, envelopes, and effect modules.

Build chains of DSP::dspObject to process a sound, DSP::dspSourceObject to feed one. Single-channel audio data lives in DSP::buffer and its drawing / file / wavetable subclasses.

namespace MODULES

Enums

enum eqBand#

The four fixed bands of the parametricEQ.

Values:

enumerator EQ_LOW_SHELF#

Low shelf — boosts/cuts everything below its corner.

enumerator EQ_PEAK_1#

Lower peaking (bell) band.

enumerator EQ_PEAK_2#

Upper peaking (bell) band.

enumerator EQ_HIGH_SHELF#

High shelf — boosts/cuts everything above its corner.

enumerator EQ_BAND_COUNT#

Number of bands (sentinel).

class parametricEQ : public YSE::DSP::dspObject#

Channel-strip parametric EQ packaged as a chainable dspObject.

Four cascaded biquad bands — a low shelf, two peaking (bell) bands, and a high shelf — each with its own frequency, gain (dB), and Q. This is the corrective/tone-shaping half of the channel strip (the compressor is the dynamics half); together they make the channel insert chain a usable mixing tool.

The bands are the standard RBJ “Audio EQ Cookbook” biquads (the same transfer functions the engine’s biQuad primitive implements). Each band’s five coefficients are shared across all channels and recomputed only when one of its parameters changes (or the sample rate changes), gated by a dirty flag — never per sample and never allocating. The recompute is a bounded handful of transcendentals for at most four bands and runs at the very top of process only on a block where a parameter actually moved, which keeps the audio path allocation- and lock-free.

Public Functions

parametricEQ()#
inline virtual ~parametricEQ()#
parametricEQ &frequency(eqBand band, Flt hz)#

Set a band’s centre/corner frequency in Hz (clamped to a sane audio range).

Flt frequency(eqBand band)#

Current centre/corner frequency of a band, in Hz.

parametricEQ &gain(eqBand band, Flt db)#

Set a band’s gain in dB (clamped to +/-24 dB).

Positive boosts, negative cuts; 0 leaves the band flat (a bypass for that band).

Flt gain(eqBand band)#

Current gain of a band, in dB.

parametricEQ &q(eqBand band, Flt value)#

Set a band’s Q (clamped to a sane range).

For the peaking bands Q is the bell width; for the shelves it shapes the corner steepness.

Flt q(eqBand band)#

Current Q of a band.

virtual void create()#

dspObject lifecycle hook.

virtual void process(std::vector<YSE::DSP::buffer> &buffer)#

dspObject audio-thread entry point.

Private Functions

void computeBand(eqBand band)#

Private Members

aFlt parmFreq[EQ_BAND_COUNT]#
aFlt parmGain[EQ_BAND_COUNT]#
aFlt parmQ[EQ_BAND_COUNT]#
std::atomic<bool> dirty#
Coeffs coeffs[EQ_BAND_COUNT]#
perChannel<ChannelState> channels#
UInt builtRate#
DSP::buffer wet#
std::size_t blockLength#
struct BandState#

One channel’s per-band biquad delay memory (Direct Form I).

Public Functions

BandState()#

Public Members

Flt x1#
Flt x2#
Flt y1#
Flt y2#
struct ChannelState#

Public Members

BandState bands[EQ_BAND_COUNT]#
struct Coeffs#

One band’s shared biquad coefficients (Direct Form I, normalised so a0 == 1).

Public Functions

Coeffs()#

Public Members

Flt b0#
Flt b1#
Flt b2#
Flt a1#
Flt a2#

Compressor#

namespace YSE

Public API of libYSE — sound playback, mixing, and 3D positional audio.

Entry points: YSE::System (lifecycle and audio device), YSE::Listener (3D origin), YSE::sound (a playable source), YSE::channel (mixing tree), YSE::reverb (positioned reverb zone), YSE::patcher (modular DSP graph), YSE::player (note sequencer). Sub-namespaces group domain-specific types: YSE::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

namespace DSP

Audio buffers, oscillators, filters, envelopes, and effect modules.

Build chains of DSP::dspObject to process a sound, DSP::dspSourceObject to feed one. Single-channel audio data lives in DSP::buffer and its drawing / file / wavetable subclasses.

namespace MODULES

Enums

enum compressorDetector#

Level-detector mode for the compressor.

Values:

enumerator DETECT_PEAK#

Track the instantaneous linked peak — fast, hits transients.

enumerator DETECT_RMS#

Track a short mean-square window — smoother, loudness-like.

class compressor : public YSE::DSP::dspObject#

Feed-forward dynamics compressor packaged as a chainable dspObject.

The dynamics half of the channel strip (the parametric EQ is the tone half). A classic feed-forward design: a switchable peak/RMS detector drives a static compression curve (threshold + ratio), the resulting gain is smoothed by separate attack and release time constants, and a makeup gain restores level.

Public Functions

compressor()#
inline virtual ~compressor()#
compressor &detector(compressorDetector value)#

Select the peak or RMS level detector.

compressorDetector detector()#

Current detector mode.

compressor &threshold(Flt db)#

Set the threshold in dBFS (clamped to [-60, 0]).

Signal above the threshold is compressed; below it passes unchanged.

Flt threshold()#

Current threshold in dBFS.

compressor &ratio(Flt value)#

Set the compression ratio (clamped to [1, 20]).

1 is no compression; 4 means 4 dB in above threshold yields 1 dB out.

Flt ratio()#

Current compression ratio.

compressor &attack(Flt ms)#

Set the attack time in milliseconds (clamped to a sane range) — how fast gain reduction engages when the signal rises.

Flt attack()#

Current attack time in milliseconds.

compressor &release(Flt ms)#

Set the release time in milliseconds (clamped to a sane range) — how fast gain recovers when the signal falls.

Flt release()#

Current release time in milliseconds.

compressor &makeup(Flt db)#

Set the makeup gain in dB (clamped to [-24, 24]) — a fixed gain applied after compression to restore level.

Flt makeup()#

Current makeup gain in dB.

Flt gainReductionDb()#

Current gain reduction being applied, in dB (<= 0).

Useful for metering; reflects the last processed sample.

virtual void create()#

dspObject lifecycle hook.

virtual void process(std::vector<YSE::DSP::buffer> &buffer)#

dspObject audio-thread entry point.

Private Members

aFlt parmThreshold#
aFlt parmRatio#
aFlt parmAttack#
aFlt parmRelease#
aFlt parmMakeup#
aInt parmDetector#
Flt gain#
Flt msEnv#
Flt rmsCoef#
aFlt reductionDb#
DSP::buffer gainBuf#
DSP::buffer wet#
std::size_t blockLength#

Chorus / flanger#

namespace YSE

Public API of libYSE — sound playback, mixing, and 3D positional audio.

Entry points: YSE::System (lifecycle and audio device), YSE::Listener (3D origin), YSE::sound (a playable source), YSE::channel (mixing tree), YSE::reverb (positioned reverb zone), YSE::patcher (modular DSP graph), YSE::player (note sequencer). Sub-namespaces group domain-specific types: YSE::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

namespace DSP

Audio buffers, oscillators, filters, envelopes, and effect modules.

Build chains of DSP::dspObject to process a sound, DSP::dspSourceObject to feed one. Single-channel audio data lives in DSP::buffer and its drawing / file / wavetable subclasses.

namespace MODULES

Enums

enum chorusMode#

Operating mode for the chorus module.

Values:

enumerator MODE_CHORUS#

Longer base delay, wide slow sweep — thickening/detune.

enumerator MODE_FLANGER#

Short base delay, feedback — sweeping comb notches.

class chorus : public YSE::DSP::dspObject#

Chorus / flanger effect packaged as a chainable dspObject.

Chorus and flanger are the same modulated-delay topology with different ranges and feedback, so this is one module with a mode() switch. An internal LFO sweeps a per-channel fractional (linearly interpolated) delay line; the sweep is click-free by construction — the delay time is a continuous signal and the read is fractional, so no read-pointer step is ever taken. MODE_CHORUS uses a longer base delay and no feedback by default (thickening / detune); MODE_FLANGER uses a short base delay and a feedback path (the classic resonant jet-sweep comb).

Processes every channel of the multichannel buffer independently, each with its own delay line and LFO phase (see the N-channel contract on dspObject::process). The LFO is shared — one sweep for the whole buffer — but spread() fans a per-channel phase offset across the channels for stereo width; at spread(0) every channel is in phase (and a mono buffer is the degenerate single-channel case).

The wet/dry balance is the inherited impact(): impact(0.5) is a natural insert mix, impact(1) is fully wet. All buffers are sized on the create() / channel-count-change path; process allocates nothing in steady state.

Public Functions

chorus()#
inline virtual ~chorus()#
chorus &mode(chorusMode value)#

Select chorus or flanger topology.

chorusMode mode()#

Current mode.

chorus &rate(Flt hz)#

Set the LFO sweep rate in Hz (clamped to a sane range).

Flt rate()#

Current LFO rate in Hz.

chorus &depth(Flt value)#

Set the modulation depth in [0, 1] — scales the sweep width.

Flt depth()#

Current modulation depth.

chorus &feedback(Flt value)#

Set the feedback amount in [-0.95, 0.95].

Drives the resonant flanger comb; negative values invert the fed-back signal (the hollow “through-zero”-style comb). Typically 0 for chorus.

Flt feedback()#

Current feedback amount.

chorus &spread(Flt value)#

Set the stereo spread in [0, 1] — the per-channel LFO phase offset.

0 keeps every channel in phase; 1 spreads the channels evenly around a full LFO cycle for maximum width.

Flt spread()#

Current stereo spread.

virtual void create()#

dspObject lifecycle hook.

virtual void process(std::vector<YSE::DSP::buffer> &buffer)#

dspObject audio-thread entry point.

Private Functions

Flt baseDelayMs() const#
Flt sweepDelayMs() const#

Private Members

aFlt parmRate#
aFlt parmDepth#
aFlt parmFeedback#
aFlt parmSpread#
aInt parmMode#
Flt lfoCursor#
Flt delaySmoothCoef#
std::size_t lineSize#
std::size_t blockLength#
perChannel<voice> voices#
DSP::buffer phaseBuf#
DSP::buffer wet#
struct voice#

One channel’s modulated-delay state.

Public Functions

voice()#

Public Members

std::vector<Flt> line#
std::size_t writePos#
Flt smoothedDelay#
Bool primed#

Feedback delay#

namespace YSE

Public API of libYSE — sound playback, mixing, and 3D positional audio.

Entry points: YSE::System (lifecycle and audio device), YSE::Listener (3D origin), YSE::sound (a playable source), YSE::channel (mixing tree), YSE::reverb (positioned reverb zone), YSE::patcher (modular DSP graph), YSE::player (note sequencer). Sub-namespaces group domain-specific types: YSE::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

namespace DSP

Audio buffers, oscillators, filters, envelopes, and effect modules.

Build chains of DSP::dspObject to process a sound, DSP::dspSourceObject to feed one. Single-channel audio data lives in DSP::buffer and its drawing / file / wavetable subclasses.

namespace MODULES
class feedbackDelay : public YSE::DSP::dspObject#

Mix-grade feedback delay packaged as a chainable dspObject.

Where basicDelay is a three-tap feed-forward building block, this is a recirculating delay meant to sit on a channel insert or a send return: a per-channel delay line with a feedback path, a damping low-pass filter in that path (successive echoes darken, the classic tape character), and cross-feed between channel pairs for ping-pong. The wet/dry balance is the inherited impact()impact(1) is echoes only (send use), a lower value keeps the dry signal (insert use).

Processes every channel of the multichannel buffer independently, each with its own delay line and damping filter (see the N-channel contract on dspObject::process). Cross-feed pairs adjacent channels (0<->1, 2<->3, …); an unpaired trailing channel feeds back only into itself.

Delay-time changes are smoothed per-sample so retuning the delay does not click. All buffers are sized on the create() / channel-count change path; process allocates nothing in steady state.

Public Functions

feedbackDelay()#
inline virtual ~feedbackDelay()#
feedbackDelay &time(Flt ms)#

Set the delay time in milliseconds (clamped to the module’s maximum).

Changes are ramped to avoid zipper noise.

Flt time()#

Current delay time in milliseconds.

feedbackDelay &feedback(Flt amount)#

Set the feedback amount in [0, 0.99].

Higher sustains echoes longer; the upper bound keeps the loop stable.

Flt feedback()#

Current feedback amount.

feedbackDelay &damping(Flt hz)#

Set the damping cut-off in Hz — a low-pass in the feedback path.

Lower values darken successive echoes faster.

Flt damping()#

Current damping cut-off in Hz.

feedbackDelay &crossfeed(Flt amount)#

Set the cross-feed amount in [0, 1] between channel pairs.

0 keeps channels independent; 1 is full ping-pong (each channel’s feedback comes entirely from its partner).

Flt crossfeed()#

Current cross-feed amount.

virtual void create()#

dspObject lifecycle hook.

virtual void process(std::vector<YSE::DSP::buffer> &buffer)#

dspObject audio-thread entry point.

Private Members

aFlt parmTime#
aFlt parmFeedback#
aFlt parmDamping#
aFlt parmCrossfeed#
Flt currentTime#
Flt timeSmoothCoef#
Bool primed#
std::size_t blockLength#
perChannel<delayChannel> channels#
DSP::buffer toWrite#
DSP::buffer delayed#
DSP::buffer timeBuffer#
DSP::buffer crossScratch#
struct delayChannel#

One channel’s recirculating delay state.

Public Functions

delayChannel()#

Public Members

DSP::delay line#
DSP::lowPass damper#
DSP::buffer damped#
DSP::buffer fbSaved#

Plate reverb#

A Dattorro-style plate reverb, well suited to a send/return bus.

namespace YSE

Public API of libYSE — sound playback, mixing, and 3D positional audio.

Entry points: YSE::System (lifecycle and audio device), YSE::Listener (3D origin), YSE::sound (a playable source), YSE::channel (mixing tree), YSE::reverb (positioned reverb zone), YSE::patcher (modular DSP graph), YSE::player (note sequencer). Sub-namespaces group domain-specific types: YSE::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

namespace DSP

Audio buffers, oscillators, filters, envelopes, and effect modules.

Build chains of DSP::dspObject to process a sound, DSP::dspSourceObject to feed one. Single-channel audio data lives in DSP::buffer and its drawing / file / wavetable subclasses.

namespace MODULES
class plateReverb : public YSE::DSP::dspObject#

Insert/return-grade plate reverb packaged as a chainable dspObject.

Implements the classic Dattorro plate topology (“Effect Design, Part 1”, JAES 1997): a four-allpass input diffuser feeding a cross-coupled figure-eight tank of two symmetric halves, each a modulated allpass, a long delay, a damping low-pass, a second allpass, and a final delay. The stereo output is read from seven fixed taps across the tank nodes, giving the smooth, dense, slightly shimmering plate character.

This is distinct from the engine’s existing global spatial reverb (INTERNAL::reverbDSP, welded to the listener/zone system): the plate is a plain effect you drop on a channel insert or a send return.

Public Functions

plateReverb()#
inline virtual ~plateReverb()#
plateReverb &decay(Flt value)#

Set the tank decay in [0, 0.98].

Higher values recirculate the tank longer, lengthening the reverb tail (RT60).

Flt decay()#

Current tank decay.

plateReverb &damping(Flt hz)#

Set the damping cut-off in Hz — a low-pass inside each tank half.

Lower values shed high-frequency energy faster, so the tail darkens as it decays.

Flt damping()#

Current damping cut-off in Hz.

plateReverb &preDelay(Flt ms)#

Set the pre-delay in milliseconds (clamped to the module’s maximum).

Offsets the onset of the reverb from the dry signal.

Flt preDelay()#

Current pre-delay in milliseconds.

virtual void create()#

dspObject lifecycle hook.

virtual void process(std::vector<YSE::DSP::buffer> &buffer)#

dspObject audio-thread entry point.

Private Functions

void build()#

Private Members

aFlt parmDecay#
aFlt parmDamping#
aFlt parmPreDelay#
Allpass diff1#
Allpass diff2#
Allpass diff3#
Allpass diff4#
DelayLine preLine#
ModAllpass apL1#
DelayLine delL1#
Allpass apL2#
DelayLine delL2#
Flt dampL#
ModAllpass apR1#
DelayLine delR1#
Allpass apR2#
DelayLine delR2#
Flt dampR#
Flt fbL#
Flt fbR#
Flt lfoPhase#
Flt modExcursion#
Flt modInc#
std::size_t tapL[7]#
std::size_t tapR[7]#
UInt builtRate#
std::size_t blockLength#
DSP::buffer wetL#
DSP::buffer wetR#
struct Allpass#

Schroeder allpass with node tapping.

Public Functions

void init(std::size_t len)#
Flt process(Flt x, Flt g)#
Flt tap(std::size_t k) const#
inline std::size_t length() const#

Public Members

std::vector<Flt> buf#
std::size_t pos#
struct DelayLine#

Fixed-length delay line with node tapping for output reads.

Public Functions

void init(std::size_t len)#
Flt process(Flt x)#
Flt tap(std::size_t k) const#
inline std::size_t length() const#

Public Members

std::vector<Flt> buf#
std::size_t pos#
struct ModAllpass#

Allpass whose delay is modulated by a fractional excursion — the plate’s tank chorusing.

Read is linearly interpolated.

Public Functions

void init(std::size_t len, Flt base)#
Flt process(Flt x, Flt g, Flt delaySamps)#

Public Members

std::vector<Flt> buf#
std::size_t pos#
Flt baseDelay#

Morphing reverb#

The engine’s zone/global reverb core as a chainable insert, with preset interpolation exposed as a morph control input.

namespace YSE

Public API of libYSE — sound playback, mixing, and 3D positional audio.

Entry points: YSE::System (lifecycle and audio device), YSE::Listener (3D origin), YSE::sound (a playable source), YSE::channel (mixing tree), YSE::reverb (positioned reverb zone), YSE::patcher (modular DSP graph), YSE::player (note sequencer). Sub-namespaces group domain-specific types: YSE::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

namespace DSP

Audio buffers, oscillators, filters, envelopes, and effect modules.

Build chains of DSP::dspObject to process a sound, DSP::dspSourceObject to feed one. Single-channel audio data lives in DSP::buffer and its drawing / file / wavetable subclasses.

namespace MODULES
class morphingReverb : public YSE::DSP::dspObject#

A reverb whose preset interpolation is a control input.

This is the engine’s zone/global reverb core (INTERNAL::reverbDSP) packaged as an ordinary chainable dspObject, with the parameter blend generalized into a morph control (issue #326). Two endpoint parameter sets — slot A and slot B, each a named REVERB_PRESET or a custom REVERB::presetValues — are linearly interpolated by morph(t): 0 is pure A (“cathedral”), 1 is pure B (“closet”), anything in between is a genuine hybrid space.

Public Functions

morphingReverb()#

Slots default to A = REVERB_GENERIC, B = REVERB_HALL, morph() == 0 (pure A).

inline virtual ~morphingReverb()#
morphingReverb &presetA(REVERB_PRESET value)#

Set morph endpoint A from a named preset.

morphingReverb &presetA(const REVERB::presetValues &value)#

Set morph endpoint A from a custom parameter set.

Fields are stored individually-atomically; the faders smooth the (rare) block that observes a partly-updated slot.

REVERB::presetValues presetA() const#

Current endpoint A parameter set.

morphingReverb &presetB(REVERB_PRESET value)#

Set morph endpoint B from a named preset.

morphingReverb &presetB(const REVERB::presetValues &value)#

Set morph endpoint B from a custom parameter set.

REVERB::presetValues presetB() const#

Current endpoint B parameter set.

morphingReverb &morph(Flt value)#

The morph control input: 0 = pure A, 1 = pure B, clamped to [0, 1].

Callable from any control thread at control rate — allocation-free, click-free (see class docs).

Flt morph() const#

Current morph position.

virtual void create()#

dspObject lifecycle hook.

Per-channel state is sized in process (the channel count is only known there).

virtual void process(std::vector<YSE::DSP::buffer> &buffer)#

dspObject audio-thread entry point.

Private Members

slot slotA#
slot slotB#
aFlt parmMorph#
INTERNAL::reverbDSP verb#
struct slot#

Public Functions

void store(const REVERB::presetValues &v)#
REVERB::presetValues load() const#

Public Members

aFlt roomsize#
aFlt damp#
aFlt dry#
aFlt wet#
aFlt modFrequency#
aFlt modWidth#
aFlt earlyTime[4]#
aFlt earlyGain[4]#

Underwater#

The classic underwater treatment — a depth-driven low-pass blended toward a position-neutral mixdown — re-expressed as an ordinary insert.

namespace YSE

Public API of libYSE — sound playback, mixing, and 3D positional audio.

Entry points: YSE::System (lifecycle and audio device), YSE::Listener (3D origin), YSE::sound (a playable source), YSE::channel (mixing tree), YSE::reverb (positioned reverb zone), YSE::patcher (modular DSP graph), YSE::player (note sequencer). Sub-namespaces group domain-specific types: YSE::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

namespace DSP

Audio buffers, oscillators, filters, envelopes, and effect modules.

Build chains of DSP::dspObject to process a sound, DSP::dspSourceObject to feed one. Single-channel audio data lives in DSP::buffer and its drawing / file / wavetable subclasses.

namespace MODULES
class underWater : public YSE::DSP::dspObject#

The underwater treatment as an ordinary chainable dspObject.

This is the DSP half of the engine’s classic underwater effect (issue #327), extracted from the hard-wired slot it used to occupy in the channel path. Sound underwater is more position neutral — the speed of sound is much higher, so the ear cannot tell what direction it comes from. The module therefore mixes all channels down to a position-neutral average, darkens it with a depth-driven low-pass, and crossfades the original image toward that neutral version as the listener sinks.

Public Functions

underWater()#

Defaults to depth() == 0 — fully transparent.

inline virtual ~underWater()#
underWater &depth(Flt value)#

The depth control input, in distance units below the water surface.

Negative values clamp to 0 (above water). Callable from any control thread at control rate — allocation-free, wait-free (see class docs for the response curve).

Flt depth() const#

Current depth.

virtual void create()#

dspObject lifecycle hook.

Nothing to allocate up front: the mixdown scratch is sized in process (the block length is only known there).

virtual void process(std::vector<YSE::DSP::buffer> &buffer)#

dspObject audio-thread entry point.

Private Members

aFlt parmDepth#
buffer mono#
lowPass filter#