simvx.core._native.miniaudio_engine

Python wrapper around the compiled _simvx_miniaudio_engine cffi extension.

Exposes a small, Pythonic surface over miniaudio’s ma_engine / ma_sound / ma_sound_group API. Used by simvx.core.audio_backend.MiniaudioBackend to run audio mixing in native C.

Build the extension once after install:

uv run python -m simvx.core._native.miniaudio_engine_build

If the .so is missing, importing this module raises MiniaudioEngineUnavailable with build instructions. Callers (the backend) catch this and fall back to the pure-Python mixer with a one-time warning.

Module Contents

Classes

Engine

Owns a single ma_engine instance.

SoundGroup

An ma_sound_group: a named bus that sounds attach to.

AudioBuffer

An ma_audio_buffer backed by a numpy ndarray.

StreamSource

An ma_pcm_rb (PCM ring buffer) wrapped as a ma_data_source.

Sound

An ma_sound: a single playable voice.

EffectNode

Base wrapper for ma_*_node effect nodes attached to the engine’s node graph.

LPFEffectNode

2nd-order low-pass biquad with configurable Q (resonance).

HPFEffectNode

2nd-order high-pass biquad with configurable Q (resonance).

BPFEffectNode

2nd-order band-pass biquad with configurable Q (bandwidth).

NotchEffectNode

2nd-order notch biquad (band-stop).

PeakEffectNode

Peaking-EQ band biquad.

LowShelfEffectNode

Low-shelf parametric-EQ band biquad.

HighShelfEffectNode

High-shelf parametric-EQ band biquad.

DelayEffectNode

Single-tap delay with feedback decay and wet/dry mix.

SoftClipEffectNode

Tanh-curve symmetric soft clipping (custom DSP node).

CompressorEffectNode

Feed-forward compressor with soft knee (custom DSP node).

FreeverbEffectNode

Schroeder-style algorithmic reverb (custom DSP node).

Functions

is_available

True if the native extension was built and loaded successfully.

refresh

Re-run the extension import.

ma_result_message

Human-readable miniaudio result code.

engine_endpoint

Return the engine’s audio destination node (chain tail attaches here).

sound_group_as_node

Cast a SoundGroup to a generic node pointer for chain wiring.

attach_node

Route src output bus to dst input bus. Either may be NULL.

detach_node

Detach the output bus on src (no-op if not attached).

set_node_output_volume

Scale the gain on an output bus (used for GainEffect: FadeEffect was removed in the audio refactor; per-player fades live on AudioStreamPlayer .fade_in/.fade_out).

Data

API

simvx.core._native.miniaudio_engine.log

‘getLogger(…)’

simvx.core._native.miniaudio_engine.__all__

[‘MiniaudioEngineUnavailable’, ‘Engine’, ‘Sound’, ‘SoundGroup’, ‘AudioBuffer’, ‘StreamSource’, ‘Effe…

exception simvx.core._native.miniaudio_engine.MiniaudioEngineUnavailable[source]

Bases: RuntimeError

The compiled _simvx_miniaudio_engine extension was not found.

Run uv run python -m simvx.core._native.miniaudio_engine_build (or simvx build-audio) to compile it once.

Initialization

Initialize self. See help(type(self)) for accurate signature.

class __cause__
class __context__
__delattr__()
__dir__()
__eq__()
__format__()
__ge__()
__getattribute__()
__getstate__()
__gt__()
__hash__()
__le__()
__lt__()
__ne__()
__new__()
__reduce__()
__reduce_ex__()
__repr__()
__setattr__()
__setstate__()
__sizeof__()
__str__()
__subclasshook__()
class __suppress_context__
class __traceback__
add_note()
class args
with_traceback()
simvx.core._native.miniaudio_engine.is_available() bool[source]

True if the native extension was built and loaded successfully.

simvx.core._native.miniaudio_engine.refresh() bool[source]

Re-run the extension import.

Called by audio_backend.make_backend() after a successful auto-build, so module-level _FFI / _LIB pick up the freshly-compiled .so without a Python restart.

simvx.core._native.miniaudio_engine.ma_result_message(code: int) str[source]

Human-readable miniaudio result code.

class simvx.core._native.miniaudio_engine.Engine(*, sample_rate: int = 48000, channels: int = 2, device: bool = True)[source]

Owns a single ma_engine instance.

Pass device=True (default) for a real playback device, or device=False to use the no-device path (read mixed PCM manually via read_pcm_frames: useful for headless testing).

Initialization

property handle: Any[source]
property sample_rate: int[source]
property channels: int[source]
set_volume(linear_gain: float) None[source]
set_listener_position(x: float, y: float, z: float, *, idx: int = 0) None[source]
set_listener_velocity(x: float, y: float, z: float, *, idx: int = 0) None[source]
set_listener_direction(x: float, y: float, z: float, *, idx: int = 0) None[source]
set_listener_world_up(x: float, y: float, z: float, *, idx: int = 0) None[source]
read_pcm_frames(frame_count: int) numpy.ndarray[source]

Pull frame_count mixed PCM frames (float32, interleaved).

Only valid for engines built with device=False. Returns an ndarray of shape (frame_count, channels). The number of frames actually read may be less than requested if the engine is idle.

shutdown() None[source]
__del__() None[source]
class simvx.core._native.miniaudio_engine.SoundGroup(engine: simvx.core._native.miniaudio_engine.Engine, *, parent: simvx.core._native.miniaudio_engine.SoundGroup | None = None)[source]

An ma_sound_group: a named bus that sounds attach to.

parent=None parents to the engine root. Volumes multiply through the parent chain in native code.

Initialization

property handle: Any[source]
set_volume(linear_gain: float) None[source]
shutdown() None[source]
__del__() None[source]
class simvx.core._native.miniaudio_engine.AudioBuffer(samples: numpy.ndarray, *, sample_rate: int, channels: int)[source]

An ma_audio_buffer backed by a numpy ndarray.

The ndarray must remain alive for the lifetime of the buffer: we do not copy. Stored as a Python attribute so the GC keeps it pinned.

Initialization

property data_source: Any[source]

The ma_data_source* view of this buffer (usable by Sound).

shutdown() None[source]
__del__() None[source]
class simvx.core._native.miniaudio_engine.StreamSource(engine: simvx.core._native.miniaudio_engine.Engine, *, sample_rate: int, channels: int, buffer_seconds: float = 0.5, format: str = 's16')[source]

An ma_pcm_rb (PCM ring buffer) wrapped as a ma_data_source.

Used by MiniaudioBackend.open_stream for live procedural audio (AudioSynth.attach_to) and disk-streamed playback. Producer writes chunks via write(bytes) from the main thread; miniaudio’s audio thread pulls from the ring buffer’s built-in data source view. SPSC thread-safe: no locks needed.

Underrun semantics: when the ring is empty, miniaudio’s read callback pads with silence and continues. The audio thread never stalls or glitches; consumers just hear silence until more chunks arrive.

Default format is s16 to match the legacy backend’s feed_audio_chunk(bytes) convention.

Initialization

property data_source: Any[source]

The ma_data_source* view of this ring buffer (for Sound.from_data_source).

property format: str[source]
property sample_rate: int[source]
property channels: int[source]
property buffer_size_frames: int[source]
property available_write_frames: int[source]
property available_read_frames: int[source]
write(chunk: bytes) int[source]

Push chunk into the ring buffer. Returns frames actually written.

Frame-misaligned chunks have the trailing partial frame trimmed rather than rejected (most consumers feed in fixed-size sample-count buffers, so misalignment usually means the caller produced more samples than channels divide cleanly).

Returns the number of frames written. If the ring is full, returns less than requested: the caller can retry on the next tick.

reset() None[source]

Drop all queued frames (useful when restarting a stream).

shutdown() None[source]
__del__() None[source]
class simvx.core._native.miniaudio_engine.Sound(engine: simvx.core._native.miniaudio_engine.Engine, *, spatial: bool = False, pitch_enabled: bool = True)[source]

An ma_sound: a single playable voice.

Construct via Sound.from_file(engine, path) or Sound.from_buffer(engine, buffer). Optional group attaches the sound to a SoundGroup (bus).

Initialization

classmethod from_file(engine: simvx.core._native.miniaudio_engine.Engine, path: str | os.PathLike, *, group: simvx.core._native.miniaudio_engine.SoundGroup | None = None, spatial: bool = False, pitch_enabled: bool = True, decode_now: bool = True, stream: bool = False) simvx.core._native.miniaudio_engine.Sound[source]
classmethod from_buffer(engine: simvx.core._native.miniaudio_engine.Engine, buffer: simvx.core._native.miniaudio_engine.AudioBuffer, *, group: simvx.core._native.miniaudio_engine.SoundGroup | None = None, spatial: bool = False, pitch_enabled: bool = True) simvx.core._native.miniaudio_engine.Sound[source]
classmethod from_stream(engine: simvx.core._native.miniaudio_engine.Engine, stream: simvx.core._native.miniaudio_engine.StreamSource, *, group: simvx.core._native.miniaudio_engine.SoundGroup | None = None, spatial: bool = False) simvx.core._native.miniaudio_engine.Sound[source]

Bind a ma_sound to a live StreamSource (ma_pcm_rb backed).

Pitch is disabled by default because the engine’s pitch resampler would try to read ahead into the ring buffer past what’s been written, producing audible artifacts. Stream consumers wanting pitch control should resample Python-side before feed_audio_chunk.

classmethod from_data_source(engine: simvx.core._native.miniaudio_engine.Engine, data_source: Any, *, keeper: Any = None, group: simvx.core._native.miniaudio_engine.SoundGroup | None = None, spatial: bool = False, pitch_enabled: bool = True) simvx.core._native.miniaudio_engine.Sound[source]

Bind a ma_sound to any ma_data_source-compatible source.

keeper is pinned via the sound’s _source_keeper so the source’s GC can’t reap it while the sound is alive (essential for ndarray-backed AudioBuffer + ring-buffer-backed StreamSource).

start() None[source]
stop() None[source]
set_volume(linear_gain: float) None[source]
set_pan(pan: float) None[source]
set_pitch(pitch: float) None[source]
set_looping(looping: bool) None[source]
set_position(x: float, y: float, z: float) None[source]
set_velocity(x: float, y: float, z: float) None[source]
set_spatialization_enabled(enabled: bool) None[source]
set_min_distance(d: float) None[source]
set_max_distance(d: float) None[source]
set_rolloff(r: float) None[source]
set_doppler_factor(f: float) None[source]
is_playing() bool[source]
at_end() bool[source]
seek_to_frame(frame: int) None[source]
cursor_frames() int[source]
length_frames() int[source]
shutdown() None[source]
__del__() None[source]
class simvx.core._native.miniaudio_engine.EffectNode(engine: simvx.core._native.miniaudio_engine.Engine)[source]

Bases: abc.ABC

Base wrapper for ma_*_node effect nodes attached to the engine’s node graph.

Abstract: instantiating it directly (or a subclass that omits _init) raises TypeError. Subclasses set _alloc_fn / _free_fn / _uninit_fn (callable cffi function handles) and implement _init(engine). The base class manages allocation, init, output-bus connections, and cleanup.

handle returns the raw ma_node* (the first field is ma_node_base, so the alloc’d struct pointer doubles as a node). Engine, source, and destination nodes plug into each other via attach_node / detach_node module-level helpers.

Initialization

property handle: Any[source]

Pointer-typed handle suitable for attach_node / detach_node.

shutdown() None[source]
__del__() None[source]
__slots__

()

class simvx.core._native.miniaudio_engine.LPFEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, cutoff_hz: float = 1000.0, q: float = 0.707)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

2nd-order low-pass biquad with configurable Q (resonance).

Wraps the C simvx_biquad_lpf_node (an ma_node subclass that runs ma_lpf2_process_pcm_frames in its process callback). Unlike the stock ma_lpf_node, which hardcodes Q to 0.707, this honours the Q passed by LowPassFilter(q=...) so the engine’s filter behaviour matches the web backend’s BiquadFilterNode.

Initialization

set_params(cutoff_hz: float, q: float) None[source]
property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.HPFEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, cutoff_hz: float = 1000.0, q: float = 0.707)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

2nd-order high-pass biquad with configurable Q (resonance).

Initialization

set_params(cutoff_hz: float, q: float) None[source]
property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.BPFEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, cutoff_hz: float = 1000.0, q: float = 0.707)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

2nd-order band-pass biquad with configurable Q (bandwidth).

Initialization

set_params(cutoff_hz: float, q: float) None[source]
property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.NotchEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, cutoff_hz: float = 1000.0, q: float = 1.0)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

2nd-order notch biquad (band-stop).

Initialization

set_params(cutoff_hz: float, q: float) None[source]
property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.PeakEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, freq: float = 1000.0, q: float = 1.0, gain_db: float = 0.0)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

Peaking-EQ band biquad.

Initialization

property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.LowShelfEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, freq: float = 200.0, q: float = 1.0, gain_db: float = 0.0)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

Low-shelf parametric-EQ band biquad.

Initialization

property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.HighShelfEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, freq: float = 8000.0, q: float = 1.0, gain_db: float = 0.0)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

High-shelf parametric-EQ band biquad.

Initialization

property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.DelayEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, delay_seconds: float = 0.25, decay: float = 0.4, wet: float = 0.5, dry: float = 0.5)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

Single-tap delay with feedback decay and wet/dry mix.

Initialization

set_wet(wet: float) None[source]
set_dry(dry: float) None[source]
set_decay(decay: float) None[source]
property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.SoftClipEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, drive: float = 2.0, output_gain: float = 1.0)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

Tanh-curve symmetric soft clipping (custom DSP node).

Initialization

set_params(drive: float, output_gain: float) None[source]
property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.CompressorEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, 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)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

Feed-forward compressor with soft knee (custom DSP node).

Initialization

set_params(*, threshold_db: float | None = None, ratio: float | None = None, attack_ms: float | None = None, release_ms: float | None = None, knee_db: float | None = None, makeup_db: float | None = None) None[source]
property handle: Any
shutdown() None
__del__() None
__slots__

()

class simvx.core._native.miniaudio_engine.FreeverbEffectNode(engine: simvx.core._native.miniaudio_engine.Engine, *, room_size: float = 0.5, damping: float = 0.5, wet: float = 0.3, dry: float = 0.7, width: float = 1.0, freeze: bool = False)[source]

Bases: simvx.core._native.miniaudio_engine.EffectNode

Schroeder-style algorithmic reverb (custom DSP node).

8 parallel comb filters → 4 series allpass filters per channel. Same parameters as WebAudioBackend’s ConvolverNode-based reverb so the cross-backend sound is consistent within “same vibe” tolerance.

Initialization

set_params(*, room_size: float | None = None, damping: float | None = None, wet: float | None = None, dry: float | None = None, width: float | None = None, freeze: bool | None = None) None[source]
property handle: Any
shutdown() None
__del__() None
__slots__

()

simvx.core._native.miniaudio_engine.engine_endpoint(engine: simvx.core._native.miniaudio_engine.Engine) Any[source]

Return the engine’s audio destination node (chain tail attaches here).

simvx.core._native.miniaudio_engine.sound_group_as_node(group: simvx.core._native.miniaudio_engine.SoundGroup) Any[source]

Cast a SoundGroup to a generic node pointer for chain wiring.

simvx.core._native.miniaudio_engine.attach_node(src: Any, src_bus: int, dst: Any, dst_bus: int) None[source]

Route src output bus to dst input bus. Either may be NULL.

simvx.core._native.miniaudio_engine.detach_node(src: Any, src_bus: int) None[source]

Detach the output bus on src (no-op if not attached).

simvx.core._native.miniaudio_engine.set_node_output_volume(node: Any, bus: int, volume: float) None[source]

Scale the gain on an output bus (used for GainEffect: FadeEffect was removed in the audio refactor; per-player fades live on AudioStreamPlayer .fade_in/.fade_out).