simvx.core._audio_decoder

The one place the engine reaches the miniaudio package.

miniaudio decodes audio files and, on the legacy mixer, owns the playback device. It is a dependency of simvx-core on the desktop and absent in the browser, where the web runtime installs a name-compatible stand-in that decodes nothing.

Reaching it by a bare import miniaudio from inside a call path leaves no seam. Substituting a decoder then means mutating :data:sys.modules, which is process-global and outlives whatever installed the stand-in, and a stand-in that omits a name the engine references fails at the point of reference rather than at a checked boundary. That is how a decode failure under the browser stand-in surfaced as AttributeError raised while handling the original exception: the except clause named miniaudio.DecodeError, which the stand-in does not carry.

Every name the engine needs is wrapped here. A stand-in therefore has one surface to be compatible with, an absent or partial install is reported once as

class:

DecoderUnavailableError rather than as whatever attribute happened to be touched first, and a test substitutes a function on this module with monkeypatch.setattr instead of reaching for a global.

This is a leaf module: it imports nothing else from simvx, so the modules below audio.py and the modules in audio_backend can both use it without inverting the import graph between them.

Module Contents

Functions

decoder_available

Whether a decoder able to turn an audio file into PCM is installed.

decode_file

Decode the whole of path to interleaved signed 16-bit PCM.

stream_file

Open path as a generator of interleaved signed 16-bit PCM.

has_duration_probe

Whether :func:file_duration can read a length for this container tag.

file_duration

Seconds of audio in path, read from the container’s own metadata.

playback_device

Open a signed-16-bit playback device the legacy mixer can push frames to.

Data

API

simvx.core._audio_decoder.__all__

[‘DecodeFailedError’, ‘DecoderUnavailableError’, ‘decode_file’, ‘decoder_available’, ‘file_duration’…

exception simvx.core._audio_decoder.DecoderUnavailableError[source]

Bases: RuntimeError

No usable miniaudio is installed, so nothing here can decode.

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()
exception simvx.core._audio_decoder.DecodeFailedError[source]

Bases: RuntimeError

A usable decoder refused this particular file.

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._audio_decoder.decoder_available() bool[source]

Whether a decoder able to turn an audio file into PCM is installed.

False both when miniaudio is absent and when what is installed is a stand-in that carries only some of the names decoding needs.

Answered from the names alone, so it opens no file and costs one import. A stand-in that carries every name and decodes nothing still answers True here; it is caught at the first decode, which raises

Class:

DecoderUnavailableError rather than reporting the file as unplayable.

simvx.core._audio_decoder.decode_file(path: str, *, sample_rate: int, channels: int) Any[source]

Decode the whole of path to interleaved signed 16-bit PCM.

Args: path: Filesystem path to the audio file. sample_rate: Output rate in Hz. The decoder resamples to it. channels: Output channel count. The decoder mixes down or up to it.

Returns: The samples, as an object supporting the buffer protocol whose items are interleaved int16 in the requested format.

Raises: DecoderUnavailableError: no usable decoder is installed, or what is installed decodes nothing. DecodeFailedError: the file is missing, unreadable, or in a codec this build has no decoder for.

simvx.core._audio_decoder.stream_file(path: str, *, sample_rate: int, channels: int, frames_to_read: int, seek_frame: int = 0) Any[source]

Open path as a generator of interleaved signed 16-bit PCM.

Args: path: Filesystem path to the audio file. sample_rate: Output rate in Hz. channels: Output channel count. frames_to_read: Frames the generator allocates for, and the ceiling on one send. seek_frame: Frame to start at.

Returns: A generator whose send(frames) yields up to frames frames as an array.array of interleaved int16, and which raises StopIteration at the end of the file.

Raises: DecoderUnavailableError: no usable decoder is installed, or what is installed decodes nothing. DecodeFailedError: the file cannot be opened or decoded.

simvx.core._audio_decoder.has_duration_probe(container: str) bool[source]

Whether :func:file_duration can read a length for this container tag.

simvx.core._audio_decoder.file_duration(path: str, container: str) float | None[source]

Seconds of audio in path, read from the container’s own metadata.

Opens no audio device and decodes no samples. None is “unknown”: the container has no metadata reader here, no decoder is installed, or the file is missing, truncated, mislabelled or in a codec this build lacks.

simvx.core._audio_decoder.playback_device(*, sample_rate: int, channels: int, buffersize_msec: int) Any[source]

Open a signed-16-bit playback device the legacy mixer can push frames to.

Raises: DecoderUnavailableError: miniaudio is absent, or carries no PlaybackDevice. Anything the device itself objects to (no sound server, no card, the device held by another process) is raised by miniaudio and passes through: make_backend reads that as “this backend cannot start” and falls through to the next one.