simvx.core.audio_backend._miniaudio¶
The native (ma_engine) production audio backend.
MiniaudioBackend mixes, spatializes, and runs DSP entirely in native C via
the simvx.core._native.miniaudio_engine CFFI wrapper. Python only pushes
parameter updates. Falls back to the legacy / null backends via
make_backend when the compiled extension is unavailable.
Module Contents¶
Classes¶
Native-mixed audio backend on top of |
API¶
- class simvx.core.audio_backend._miniaudio.MiniaudioBackend(sample_rate: int = _DEFAULT_SAMPLE_RATE, nchannels: int = _DEFAULT_CHANNELS)[source]¶
Native-mixed audio backend on top of
ma_engine.All mixing, spatialization, and DSP runs in native C: Python only pushes parameter updates. Target latency:
buffersize_msec=20(vs the legacy backend’s 100 ms).The native extension must be built once after install:
simvx build-audio
If the extension is missing, the constructor raises
MiniaudioEngineUnavailable. Usemake_backend()for an automatic fallback to_LegacyMiniaudioBackendwith a one-time warning.Initialization
- play_audio(stream: simvx.core.audio.AudioClip, *, mode: str = 'non_positional', position: Any = None, volume_db: float = 0.0, pitch: float = 1.0, loop: bool = False, bus: str = 'Master', max_distance: float = 100.0, from_position: float = 0.0, pan: float = 0.0, gain_db: float = 0.0) int | None[source]¶
- end_of_stream(channel_id: int) None[source]¶
Mark a chunk-fed channel as having had its last chunk fed.
This backend’s half of
- Class:
~simvx.core.audio_protocol.AudioStreamDraining. The sound keeps playing out the ring; :meth:is_channel_activereports it finished once the ring is empty. A no-op for a channel whose audio the native decoder owns, which has no ring and ends by itself.
- open_stream(*, volume_db: float = 0.0, bus: str = 'Master', buffer_seconds: float = 0.5, loop: bool = False, stream: simvx.core.audio.AudioClip | None = None) int[source]¶
Open a streaming channel.
buffer_seconds(default 0.5 s) trades latency for underrun tolerance. At 48 kHz stereo s16 = ~96 KB. Drop to 0.1 s for low-latency interactive synthesis; bump to 1-2 s for music streams under heavy CPU load. Producer pushes int16 stereo bytes viafeed_audio_chunk; underrun produces silence (no glitch). Only applies to the chunk-fed PCM path: whenstreamis a compressed container, miniaudio’sma_sound_init_from_file(..., MA_SOUND_FLAG_STREAM)owns the buffering internally.Container routing (when
streamis provided):"wav"/"pcm"(synthetic) /None: open anma_pcm_rb-backed sound. Caller feeds raw int16 stereo bytes."ogg"/"mp3"/"flac": open the file directly viaSound.from_file(stream=True). Subsequent- meth:
feed_audio_chunkcalls are no-ops for this channel.
"unknown": raise :class:InvalidStreamError.
loopapplies to the compressed path only, where this backend owns the decoder and so knows where the file starts. A ring-backed channel holds nothing but the frames its producer has just written, so its producer is the one that rewinds.Pitch is intentionally not a parameter: streaming sounds are built with
pitch_enabled=Falseso the resampler can’t read past what the producer has written.set_pitchagainst the returned channel raises :class:AudioCapabilityError.
- stream_format() tuple[int, int][source]¶
(sample_rate, channels)every ring opened by this backend accepts.
- frames_available(channel_id: int) int[source]¶
Frames the channel’s ring can take right now.
0for an unknown channel and for a compressed channel the native decoder owns, neither of which has a ring to write into.
- feed_audio_chunk(channel_id: int, chunk: bytes) None[source]¶
Push raw int16 stereo bytes into the channel’s ring buffer.
Misaligned chunks (length not a multiple of bytes-per-frame) have the trailing partial frame trimmed. When the ring is full the excess is silently dropped: call :meth:
frames_availablefirst to write only what fits.
- set_listener_position(x: float, y: float, z: float) None[source]¶
Forward listener position to the native ma_engine spatializer.
- set_listener_velocity(x: float, y: float, z: float) None[source]¶
Forward listener velocity (m/s) to the native engine for Doppler.
- set_listener_direction(x: float, y: float, z: float) None[source]¶
Forward listener forward vector to the native engine.
- set_listener_world_up(x: float, y: float, z: float) None[source]¶
Forward listener world-up vector to the native engine.
- list_capabilities() frozenset[simvx.core.audio_protocol.Capability][source]¶
- sync_bus_layout(layout: simvx.core.audio_bus.AudioBusLayout) None[source]¶
Reconcile native
ma_sound_groupgains, routing and effect chains againstlayout.The native groups mirror the layout’s
send_tograph: each group attaches, through its own effect chain when it has one, to the group of the bus it routes into. miniaudio therefore multiplies the routing chain itself and a bus effect processes every bus routed into it, which is whatsend_tomeans and what the browser lane does. Each group carries only its bus’s own gain.Cheap and idempotent: pushes only on change. Called by the audio server every frame; safe to call manually.