simvx.core.audio

Audio system — background music, UI sounds, and 3D spatial audio.

This module provides:

  • AudioStream: Audio resource (WAV/OGG data)

  • AudioStreamPlayer: Background music/UI sounds

  • AudioStreamPlayer2D: 2D positional audio with panning

  • AudioStreamPlayer3D: 3D spatial audio with attenuation

Public API::

from simvx.core import AudioStream, AudioStreamPlayer, Resource

# Filesystem audio file
player = AudioStreamPlayer(stream="music/theme.ogg", autoplay=True)

# Asset shipped inside a Python package
sfx = AudioStreamPlayer2D(stream=Resource("game.assets", "explosion.wav"))
sfx.play()

# Synthetic procedural tone
beep = AudioStreamPlayer(stream=AudioStream.tone(440))
beep.play()

Module Contents

Classes

AudioStream

Audio resource (WAV/OGG file or synthetic PCM).

AudioListener

Singleton that tracks the active camera for 3D audio spatialization.

AudioStreamPlayer

Non-positional audio player for background music and UI sounds.

AudioStreamPlayer2D

2D positional audio player with stereo panning.

AudioStreamPlayer3D

3D spatial audio player with distance attenuation and directional panning.

Data

API

simvx.core.audio.AudioSource

None

simvx.core.audio.log

‘getLogger(…)’

simvx.core.audio.__all__

[‘AudioStream’, ‘AudioStreamPlayer’, ‘AudioStreamPlayer2D’, ‘AudioStreamPlayer3D’, ‘AudioListener’]

class simvx.core.audio.AudioStream(source: simvx.core.audio.AudioSource)[source]

Audio resource (WAV/OGG file or synthetic PCM).

This is a lightweight handle to audio data. Actual decoding is deferred to the backend (miniaudio, SDL3, web audio).

Accepts any of:

  • class:

    str / :class:os.PathLike – a filesystem audio file.

  • class:

    ~simvx.core.Resource – audio inside a Python package.

  • class:

    importlib.resources.abc.Traversable – the raw return of importlib.resources.files(pkg) / name.

Use :meth:tone for procedural sine-wave tones and :meth:from_pcm to wrap pre-rendered PCM data.

Attributes: source: Original spec the stream was constructed from – a string, :class:pathlib.Path, :class:Resource, or :class:Traversable. Preserved verbatim so scene serialisation can round-trip it. path: Resolved filesystem path string used by the backend (empty string for synthetic streams that have no file). backend_data: Backend-specific audio data (PCM ndarray, channel id, etc). Set automatically when decoded; may also be set by :meth:from_pcm / :meth:tone.

Initialization

__slots__

(‘source’, ‘path’, ‘backend_data’)

__repr__()[source]
classmethod tone(freq_hz: float, *, duration: float = 1.0, volume: float = 0.3, sample_rate: int = _SAMPLE_RATE) simvx.core.audio.AudioStream[source]

Generate a sine-wave tone at freq_hz with a short fade-in/out.

The resulting stream has its PCM data baked into backend_data so the audio backend skips file decoding entirely.

classmethod from_pcm(samples: numpy.ndarray, *, name: str = 'pcm') simvx.core.audio.AudioStream[source]

Wrap a pre-rendered PCM buffer as an AudioStream.

samples is a float32 ndarray, interleaved stereo (channels first within each frame). name is a descriptive label used only in

Meth:

__repr__ and as the stream’s path (which the backend ignores when backend_data is set).

class simvx.core.audio.AudioListener[source]

Singleton that tracks the active camera for 3D audio spatialization.

The listener position is automatically updated from Camera3D or Camera2D by the backend’s audio system.

Initialization

classmethod get() simvx.core.audio.AudioListener[source]
classmethod reset()[source]

Reset singleton (useful for tests).

class simvx.core.audio.AudioStreamPlayer(stream: simvx.core.audio.AudioSource | simvx.core.audio.AudioStream | None = None, **kwargs)[source]

Bases: simvx.core.audio._AudioPlaybackMixin, simvx.core.node.Node

Non-positional audio player for background music and UI sounds.

This player does not use 3D positioning — volume is constant regardless of camera position. Use AudioStreamPlayer2D or AudioStreamPlayer3D for spatial audio.

Settings: volume_db: Volume in decibels (-80 to 24). 0 = full volume. pitch_scale: Playback speed multiplier (0.5 to 2.0). bus: Audio bus name (“master”, “music”, “sfx”, etc). autoplay: Start playing when added to scene tree. loop: Loop playback when finished. stream_mode: “memory” loads entire file; “streaming” reads in chunks. buffer_size: Chunk size in bytes for streaming mode (default 64KB).

Initialization

volume_db

‘Property(…)’

pitch_scale

‘Property(…)’

bus

‘Property(…)’

autoplay

‘Property(…)’

loop

‘Property(…)’

stream_mode

‘Property(…)’

buffer_size

‘Property(…)’

ready()[source]
process(delta: float)[source]

Feed audio chunks to backend in streaming mode.

play(from_position: float = 0.0)[source]

Start or resume playback.

Args: from_position: Start position in seconds (0.0 = beginning).

stop()[source]

Stop playback and reset position to beginning.

get_playback_position() float[source]

Get current playback position in seconds.

pause()
is_playing() bool
is_paused() bool
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
property visible: bool
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
enter_tree() None
exit_tree() None
physics_process(dt: float) None
draw(renderer) None
picked(event: simvx.core.events.InputEvent) None
handle_input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.audio.AudioStreamPlayer2D(stream: simvx.core.audio.AudioSource | simvx.core.audio.AudioStream | None = None, **kwargs)[source]

Bases: simvx.core.audio._AudioPlaybackMixin, simvx.core.nodes_2d.node2d.Node2D

2D positional audio player with stereo panning.

Audio volume and pan are calculated based on distance from the 2D listener (typically Camera2D position). Left/right panning simulates direction.

Settings: volume_db: Base volume in decibels (-80 to 24). pitch_scale: Playback speed multiplier (0.5 to 2.0). bus: Audio bus name. autoplay: Start playing when added to scene tree. loop: Loop playback when finished. max_distance: Distance at which audio is inaudible (pixels). attenuation: Distance attenuation exponent (1.0 = linear, 2.0 = inverse square).

Initialization

volume_db

‘Property(…)’

pitch_scale

‘Property(…)’

bus

‘Property(…)’

autoplay

‘Property(…)’

loop

‘Property(…)’

max_distance

‘Property(…)’

attenuation

‘Property(…)’

pan_override

‘Property(…)’

gizmo_colour

‘Colour(…)’

get_gizmo_lines() list[tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2]][source]

Return circle showing the audio range.

ready()[source]
set_pan_and_gain(pan: float, gain_db: float) None[source]

Atomically set pan_override + volume_db with one backend call.

Use when both must land on the same audio frame (e.g. fade-out before retrigger). Setting them as separate Property assignments would fire two backend updates.

process(delta: float)[source]

Update 2D spatialization each frame.

play(from_position: float = 0.0)[source]

Start or resume playback.

stop()
pause()
is_playing() bool
is_paused() bool
z_index

‘Property(…)’

z_as_relative

‘Property(…)’

render_layer

‘Property(…)’

set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
property absolute_z_index: int
property position: simvx.core.math.types.Vec2
property rotation: float
property rotation_degrees: float
property scale: simvx.core.math.types.Vec2
property world_position: simvx.core.math.types.Vec2
property world_rotation: float
property world_scale: simvx.core.math.types.Vec2
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)
wrap_screen(margin: float = 20)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
property visible: bool
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
enter_tree() None
exit_tree() None
physics_process(dt: float) None
draw(renderer) None
picked(event: simvx.core.events.InputEvent) None
handle_input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.audio.AudioStreamPlayer3D(stream: simvx.core.audio.AudioSource | simvx.core.audio.AudioStream | None = None, **kwargs)[source]

Bases: simvx.core.audio._AudioPlaybackMixin, simvx.core.nodes_3d.node3d.Node3D

3D spatial audio player with distance attenuation and directional panning.

Audio volume and stereo panning are calculated based on distance and direction from the 3D listener (typically Camera3D position/orientation).

Settings: volume_db: Base volume in decibels (-80 to 24). pitch_scale: Playback speed multiplier (0.5 to 2.0). bus: Audio bus name. autoplay: Start playing when added to scene tree. loop: Loop playback when finished. max_distance: Distance at which audio is inaudible (world units). attenuation: Distance attenuation exponent (1.0 = linear, 2.0 = inverse square). doppler_scale: Doppler effect strength (0.0 = off, 1.0 = realistic).

Initialization

volume_db

‘Property(…)’

pitch_scale

‘Property(…)’

bus

‘Property(…)’

autoplay

‘Property(…)’

loop

‘Property(…)’

max_distance

‘Property(…)’

attenuation

‘Property(…)’

doppler_scale

‘Property(…)’

pan_override

‘Property(…)’

gizmo_colour

‘Colour(…)’

get_gizmo_lines() list[tuple[simvx.core.math.types.Vec3, simvx.core.math.types.Vec3]][source]

Return 3 circles showing the audio range sphere.

ready()[source]
set_pan_and_gain(pan: float, gain_db: float) None[source]

Atomically set pan_override + volume_db with one backend call.

process(delta: float)[source]

Update 3D spatialization each frame.

play(from_position: float = 0.0)[source]

Start or resume playback.

stop()
pause()
is_playing() bool
is_paused() bool
render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
property visible: bool
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
enter_tree() None
exit_tree() None
physics_process(dt: float) None
draw(renderer) None
picked(event: simvx.core.events.InputEvent) None
handle_input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()