simvx.core.audio_effect

Audio effects: typed DSP nodes that attach to AudioBus.

Effects are pure-Python descriptors; backends synchronise them into their native graph during :meth:AudioBackend.sync_bus_layout. The Python layer owns the what (parameters, ordering, capability requirements); the backend owns the how (ma_engine ma_node_graph for desktop, Web Audio AudioNode chains for the browser).

Authoring::

from simvx.core import AudioBusLayout
from simvx.core.audio_effect import ReverbEffect, ParametricEQ, EQBand

layout = AudioBusLayout.get_default()
music = layout.get_bus("Music")
music.add_effect(ReverbEffect(room_size=0.7, wet=0.3))
music.add_effect(ParametricEQ(bands=[
    EQBand(type="lowshelf", freq=120.0, gain_db=-3.0),
    EQBand(type="peaking", freq=2500.0, q=1.4, gain_db=+2.0),
]))

Effects chain in declaration order (source bus_effects[0] ... bus_effects[N-1] send_to).

Capability gating: each effect declares required_capability. Backends skip effects whose capability they don’t advertise; the audio server logs a single warning per unsupported effect / backend pair.

Effects are applied on both backends. The native desktop backend builds a per-bus ma_*_node chain (built-in ma_lpf/hpf/bpf/notch/delay plus custom DSP nodes for freeverb reverb, parametric EQ, soft-clip distortion, and the compressor); the web backend maps each effect to a Web Audio node. Effect Properties also round-trip through the inspector and scene serializer.

Module Contents

Classes

AudioEffect

Base class for all audio effects.

GainEffect

Static gain stage. Useful for trim before downstream effects.

LowPassFilter

2nd-order low-pass biquad. Attenuates frequencies above cutoff_hz.

HighPassFilter

2nd-order high-pass biquad. Attenuates frequencies below cutoff_hz.

BandPassFilter

2nd-order band-pass biquad. Centered on cutoff_hz with bandwidth from q.

NotchFilter

2nd-order notch biquad. Removes a narrow band around cutoff_hz.

DelayEffect

Single-tap delay with feedback. Wet/dry mix exposed for parallel sends.

ReverbEffect

Schroeder-style algorithmic reverb (Jezar’s freeverb model).

EQBand

One band of a ParametricEQ.

ParametricEQ

Multi-band parametric equalizer. Each band is an EQBand.

SoftClipEffect

Symmetric tanh-curve soft clipping.

CompressorEffect

Feed-forward compressor with soft knee.

Data

API

simvx.core.audio_effect.__all__

[‘AudioEffect’, ‘GainEffect’, ‘LowPassFilter’, ‘HighPassFilter’, ‘BandPassFilter’, ‘NotchFilter’, ‘D…

class simvx.core.audio_effect.AudioEffect(*, enabled: bool = True)[source]

Base class for all audio effects.

Concrete subclasses declare typed Property descriptors for their parameters; backends read parameter values via attribute access (effect.cutoff_hz, effect.wet, …).

required_capability is the :class:Capability a backend must advertise to materialise this effect; backends that don’t advertise it skip the effect with a single warning per (effect, backend) pair. None means “every backend supports this”. Compares equal to the underlying str for existing call sites that pattern-match on the wire-format tag.

Initialization

required_capability: ClassVar[simvx.core.audio_protocol.Capability | None]

None

classmethod __init_subclass__(**kwargs: Any) None[source]
__repr__() str[source]
get_property_values() dict[str, Any][source]

Return a dict of (Property name → current value) for serialization.

property effect_type: str[source]

Lowercased class name with the Effect / Filter suffix stripped.

Used as the canonical type tag in backend wire protocols (reverb, lowpass, compressor, …). Matches the switch-cases on the JS bridge in audio_bridge.js.

to_dict() dict[str, Any][source]

Serialize for cross-backend transport (JS bridge, IPC, save files).

The format is deliberately flat: backends pattern-match on type and read params directly. Subclasses with nested types (e.g. ParametricEQ with EQBand) override this to flatten the nesting.

classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect[source]

Reconstruct an effect from its :meth:to_dict payload.

Dispatches on data["type"] via the module-level

Data:

_EFFECT_REGISTRY, then forwards params plus the enabled flag to the target class’s constructor. Subclasses with nested types (e.g. :class:ParametricEQ) override this to rebuild their nested structure from the flattened payload.

Raises: InvalidStreamError: data["type"] isn’t a known effect tag. The message lists the registered tags so the caller can spot the typo or register the missing effect class.

class simvx.core.audio_effect.GainEffect(*, volume_db: float = 0.0, enabled: bool = True)[source]

Bases: simvx.core.audio_effect.AudioEffect

Static gain stage. Useful for trim before downstream effects.

Initialization

required_capability

None

volume_db

‘Property(…)’

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
property effect_type: str
to_dict() dict[str, Any]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect
class simvx.core.audio_effect.LowPassFilter(*, cutoff_hz: float = 1000.0, q: float = 0.707, enabled: bool = True)[source]

Bases: simvx.core.audio_effect._BiquadEffect

2nd-order low-pass biquad. Attenuates frequencies above cutoff_hz.

Initialization

required_capability

None

cutoff_hz

‘Property(…)’

q

‘Property(…)’

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
property effect_type: str
to_dict() dict[str, Any]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect
class simvx.core.audio_effect.HighPassFilter(*, cutoff_hz: float = 1000.0, q: float = 0.707, enabled: bool = True)[source]

Bases: simvx.core.audio_effect._BiquadEffect

2nd-order high-pass biquad. Attenuates frequencies below cutoff_hz.

Initialization

required_capability

None

cutoff_hz

‘Property(…)’

q

‘Property(…)’

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
property effect_type: str
to_dict() dict[str, Any]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect
class simvx.core.audio_effect.BandPassFilter(*, cutoff_hz: float = 1000.0, q: float = 0.707, enabled: bool = True)[source]

Bases: simvx.core.audio_effect._BiquadEffect

2nd-order band-pass biquad. Centered on cutoff_hz with bandwidth from q.

Initialization

required_capability

None

cutoff_hz

‘Property(…)’

q

‘Property(…)’

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
property effect_type: str
to_dict() dict[str, Any]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect
class simvx.core.audio_effect.NotchFilter(*, cutoff_hz: float = 1000.0, q: float = 0.707, enabled: bool = True)[source]

Bases: simvx.core.audio_effect._BiquadEffect

2nd-order notch biquad. Removes a narrow band around cutoff_hz.

Initialization

required_capability

None

cutoff_hz

‘Property(…)’

q

‘Property(…)’

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
property effect_type: str
to_dict() dict[str, Any]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect
class simvx.core.audio_effect.DelayEffect(*, time_seconds: float = 0.25, feedback: float = 0.4, wet: float = 0.5, dry: float = 0.5, enabled: bool = True)[source]

Bases: simvx.core.audio_effect.AudioEffect

Single-tap delay with feedback. Wet/dry mix exposed for parallel sends.

Initialization

required_capability

None

time_seconds

‘Property(…)’

feedback

‘Property(…)’

wet

‘Property(…)’

dry

‘Property(…)’

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
property effect_type: str
to_dict() dict[str, Any]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect
class simvx.core.audio_effect.ReverbEffect(*, room_size: float = 0.5, damping: float = 0.5, wet: float = 0.3, dry: float = 0.7, width: float = 1.0, freeze: bool = False, enabled: bool = True)[source]

Bases: simvx.core.audio_effect.AudioEffect

Schroeder-style algorithmic reverb (Jezar’s freeverb model).

Same parameters across desktop and web. Desktop runs the freeverb algorithm in a custom ma_node; web runs a ConvolverNode with a baked impulse response tuned to match.

Initialization

required_capability

None

room_size

‘Property(…)’

damping

‘Property(…)’

wet

‘Property(…)’

dry

‘Property(…)’

width

‘Property(…)’

freeze

‘Property(…)’

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
property effect_type: str
to_dict() dict[str, Any]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect
class simvx.core.audio_effect.EQBand[source]

One band of a ParametricEQ.

type selects the shape: "peaking" (centered bell), "lowshelf" (low-frequency cut/boost), "highshelf" (high-frequency cut/boost).

gain_db is the boost (positive) or cut (negative) at the band’s centre. Peaking bands also use q for bandwidth; shelves don’t.

type: str

‘peaking’

freq: float

1000.0

q: float

1.0

gain_db: float

0.0

class simvx.core.audio_effect.ParametricEQ(*, bands: list[simvx.core.audio_effect.EQBand] | None = None, enabled: bool = True)[source]

Bases: simvx.core.audio_effect.AudioEffect

Multi-band parametric equalizer. Each band is an EQBand.

Default is a 3-band EQ (low shelf, mid peaking, high shelf) at flat settings: add or modify bands to taste.

Initialization

required_capability

None

property effect_type: str[source]
to_dict() dict[str, Any][source]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.ParametricEQ[source]

Rebuild bands from the flattened :meth:to_dict payload.

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
class simvx.core.audio_effect.SoftClipEffect(*, drive: float = 2.0, output_gain: float = 1.0, enabled: bool = True)[source]

Bases: simvx.core.audio_effect.AudioEffect

Symmetric tanh-curve soft clipping.

drive increases input gain before the curve (more saturation); output_gain trims the output. tanh(x*drive) / tanh(drive) normalises so the curve passes through (1, 1) regardless of drive.

Initialization

required_capability

None

drive

‘Property(…)’

output_gain

‘Property(…)’

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
property effect_type: str
to_dict() dict[str, Any]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect
class simvx.core.audio_effect.CompressorEffect(*, threshold_db: float = -24.0, ratio: float = 4.0, attack_ms: float = 5.0, release_ms: float = 100.0, knee_db: float = 6.0, makeup_db: float = 0.0, enabled: bool = True)[source]

Bases: simvx.core.audio_effect.AudioEffect

Feed-forward compressor with soft knee.

Web maps this to DynamicsCompressorNode (browser-native). Desktop runs a custom envelope-follower + gain-reduction node.

Initialization

required_capability

None

threshold_db

‘Property(…)’

ratio

‘Property(…)’

attack_ms

‘Property(…)’

release_ms

‘Property(…)’

knee_db

‘Property(…)’

makeup_db

‘Property(…)’

classmethod __init_subclass__(**kwargs: Any) None
__repr__() str
get_property_values() dict[str, Any]
property effect_type: str
to_dict() dict[str, Any]
classmethod from_dict(data: dict[str, Any]) simvx.core.audio_effect.AudioEffect