sigil-audio / treeREADME.md
sigil-audio
Audio playback and streaming for Sigil.
Provides audio playback through miniaudio (vendored, device layer only, every backend loaded at run time). Sound effects are loaded into memory, music is streamed via stb_vorbis.
Modules
| Module | Purpose |
|---|---|
(sigil audio) | Audio playback, sound effects, and music streaming |
System Prerequisites
None to build. The playback device is miniaudio 0.11.25, vendored under vendor/miniaudio (device layer only; see vendor/miniaudio/VENDOR.md), and it loads its backend at run time:
- Linux:
libpulse.so/libpulse.so.0first (PulseAudio, which on a PipeWire desktop is pipewire-pulse;PULSE_SINKpicks the sink), thenlibasound.so.2(ALSA), thenlibjack.so.0. Nothing is linked:-lasoundis gone. - Windows: WASAPI through
ole32/mmdevapi/avrt, loaded at run time; no link flags. - macOS: the CoreAudio frameworks, loaded by absolute path; no link flags.
On Guix and Nix a profile's lib/ is on no default loader path, so the loads go through the resolver in src/c/dl-search.c (a copy of sigil-desktop's, see src/c/DL-SEARCH-ORIGIN.md): SIGIL_LIBRARY_PATH, the active guix shell / nix-shell, then the user and system profiles of both. With no backend library anywhere miniaudio's Null backend runs the mixer in silence and one line on stderr says so. (audio-backend) answers the backend as a symbol and (audio-device?) whether it is a real one; SIGIL_AUDIO_BACKEND=null (or a backend name) pins the choice. SIGIL_AUDIO_VERBOSE=1 prints the backend, the device and every resolver hit. A run manifest for a game on Guix adds pulseaudio (the client library, 187 MiB of closure) or alsa-lib (3 MiB; untested here, and the ALSA route would go through the machine's ALSA configuration to reach PipeWire) to sigil-desktop's guix/run-wayland.scm.
OGG Vorbis encoding/decoding is vendored (libogg 1.3.6 + libvorbis 1.3.7, see vendor/ogg/README.md and vendor/vorbis/README.md). No system libogg / libvorbis / libvorbisenc packages need to be installed — sigil-audio links them statically into libsigil-audio.a. This adds ~1 MB to the final binary and unblocks Windows cross-compile.
Dependencies
- sigil-stdlib
Build
sigil deps install
sigil build # native (host platform)
sigil build --config windows-amd64 # cross-compile for Windows (zig)Streaming audio sink
Three playback paths coexist in (sigil audio):
load-sound+play-sound— short SFX, decoded once into memory, fired from Sigil, mixed on the audio thread.play-music— long OGG, streamed from disk on the audio thread via stb_vorbis.open-audio-stream+push-audio-samples— caller-driven streaming sink. The caller produces interleaved float32 PCM (any thread) and pushes it into a lockless SPSC ring; the audio thread drains the ring into its output buffer. Use this for live / generative audio (motif streaming render, live-coded synths, etc.).
Example — play a 440 Hz sine wave for 1 second:
(import (sigil audio) (sigil math))
(audio-setup)
(define stream (open-audio-stream channels: 2
buffer-frames: 8192))
(define sr 44100)
(define pi 3.14159265358979)
(define frames (* sr 1))
;; Build a stereo float32 bytevector with a 440 Hz sine.
(define samples
(let ((v (make-vector (* frames 2) 0.0)))
(let loop ((i 0))
(when (< i frames)
(let ((s (sin (* 2.0 pi 440.0 (/ i sr)))))
(vector-set! v (* i 2) s)
(vector-set! v (+ (* i 2) 1) s))
(loop (+ i 1))))
v))
(define pcm (make-float-buffer samples))
;; Push in chunks of whatever the ring has room for.
(let loop ((remaining frames) (offset-frames 0))
(when (> remaining 0)
(let ((room (audio-stream-room stream)))
(if (= room 0)
(begin (sleep 0.005) (loop remaining offset-frames))
(let ((n (min remaining room)))
(push-audio-samples stream pcm n)
(loop (- remaining n) (+ offset-frames n)))))))
(close-audio-stream! stream)Notes:
- Sample rate is fixed at 44100 to match the device's configured rate; callers MUST match (no resampling).
push-audio-samplesis non-blocking and returns the frame count actually accepted — the caller decides whether to retry or drop.- Streaming sinks coexist with
play-sound/play-music— they're an additional mix source, not a replacement. - Up to 4 streams may be open simultaneously.
- Default
buffer-frames: 16384(~370 ms at 44.1 kHz) is safety margin against under-run, not added latency. Live producers who want tighter reactivity can open withbuffer-frames: 2048or smaller.
See folio topics/sigil-audio-streaming-sink-architecture for the SPSC ring design, threading model, and under-run / over-run semantics.
License
BSD-3-Clause.
Vendored libogg (1.3.6) and libvorbis (1.3.7) are also distributed under BSD-3-Clause (see vendor/ogg/COPYING and vendor/vorbis/COPYING). Both licenses are compatible with this package's BSD-3-Clause terms.