Skip to content
magmacrunchmedia edited this page Aug 24, 2026 · 1 revision

Audio

Added in 0.5.0.

Sound with the same degradation contract as everything else in the engine: a game on a machine with no audio device runs identically to one with speakers, the way a game with no I2C bus runs identically on a laptop. No method ever raises into your frame.

from texastoast import Mixer

mixer = Mixer()                    # picks the best backend available
game.on_close(mixer.close)         # you wire the teardown

mixer.load("jump", "assets/jump.wav")
mixer.load("theme", "assets/theme.wav", volume=0.6)

mixer.play_music("theme")          # one music slot; loops; replaces previous
mixer.play("jump")                 # fire-and-forget SFX
mixer.play("jump", volume=0.4)     # per-play override
mixer.set_master_volume(0.8)       # effective = master × load × override
mixer.stop_music()
mixer.stop_all()

WAV is the guaranteed format on every tier. A missing file logs a warning at load and plays as silence — a Pi image missing one asset must not kill the game. An unknown name is a logged no-op, never an error.

The backend tiers

Mixer() detects the best backend; mixer.backend_name tells you which won.

Tier Gets you Install
pygame Real mixing, seamless loops, per-channel volume; also decodes OGG (allowed, not promised) pip install "texastoast[audio]"
winsound Windows built-in. One sound at a time — a new play cancels the previous. SFX-grade. nothing
aplay / afplay Linux/Pi / macOS command players. Process per sound (the system mixer mixes); loops respawn with an audible seam. nothing
null Every call is a silent no-op. nothing

The pygame tier is the one to use for real games — pygame-ce ships SDL2 wheels that are first-class on the Raspberry Pi, which is the target hardware. The basic tiers exist so a fresh clone makes some sound with zero pip installs; their set_volume is a documented no-op where the player has no volume control, so code written against them upgrades cleanly (the same role present() plays on the tkinter renderer).

pygame is imported only when its backend is constructed — import texastoast never pulls it in.

Testing your game's audio

Inject a backend, exactly like I2CBus(backend=...):

class RecordingBackend:
    name = "test"
    def __init__(self): self.calls = []
    def play(self, path, *, loop=False, volume=1.0):
        self.calls.append(("play", path, loop, volume)); return len(self.calls)
    def stop(self, handle): self.calls.append(("stop", handle))
    def stop_all(self): self.calls.append(("stop_all",))
    def set_volume(self, handle, volume): pass
    def close(self): pass

backend = RecordingBackend()
mixer = Mixer(backend=backend)

NullBackend (importable from texastoast.audio) is the ready-made silent one. AudioBackend is a typing.Protocol — a backend is anything with the surface, nothing to inherit.

From magmascript

m = tt.mixer()
g.on_close(m.close)

Clone this wiki locally