AtlatestRepositorysigil-audio
sigil-audio / treeREADME.md
1
# sigil-audio3
Audio playback and streaming for [Sigil](https://codeberg.org/sigil/sigil).5
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.7
## Modules9
| Module | Purpose |10
|--------|---------|11
| `(sigil audio)` | Audio playback, sound effects, and music streaming |13
## System Prerequisites15
None to build. The playback device is [miniaudio](https://miniaud.io)16
0.11.25, vendored under `vendor/miniaudio` (device layer only; see17
`vendor/miniaudio/VENDOR.md`), and it loads its backend at run time:19
- Linux: `libpulse.so` / `libpulse.so.0` first (PulseAudio, which on a PipeWire desktop is20
pipewire-pulse; `PULSE_SINK` picks the sink), then `libasound.so.2`21
(ALSA), then `libjack.so.0`. Nothing is linked: `-lasound` is gone.22
- Windows: WASAPI through `ole32` / `mmdevapi` / `avrt`, loaded at run23
time; no link flags.24
- macOS: the CoreAudio frameworks, loaded by absolute path; no link flags.26
On Guix and Nix a profile's `lib/` is on no default loader path, so the27
loads go through the resolver in `src/c/dl-search.c` (a copy of28
sigil-desktop's, see `src/c/DL-SEARCH-ORIGIN.md`): `SIGIL_LIBRARY_PATH`,29
the active `guix shell` / `nix-shell`, then the user and system profiles30
of both. With no backend library anywhere miniaudio's Null backend runs31
the mixer in silence and one line on stderr says so.32
`(audio-backend)` answers the backend as a symbol and `(audio-device?)`33
whether it is a real one; `SIGIL_AUDIO_BACKEND=null` (or a backend name)34
pins the choice. `SIGIL_AUDIO_VERBOSE=1` prints the backend, the device and every35
resolver hit. A run manifest for a game on Guix adds `pulseaudio` (the36
client library, 187 MiB of closure) or `alsa-lib` (3 MiB; untested here, and the ALSA37
route would go through the machine's ALSA configuration to reach PipeWire) to38
sigil-desktop's `guix/run-wayland.scm`.40
OGG Vorbis encoding/decoding is **vendored** (libogg 1.3.6 +41
libvorbis 1.3.7, see `vendor/ogg/README.md` and42
`vendor/vorbis/README.md`). No system `libogg` / `libvorbis` /43
`libvorbisenc` packages need to be installed — sigil-audio links44
them statically into `libsigil-audio.a`. This adds ~1 MB to the45
final binary and unblocks Windows cross-compile.47
## Dependencies49
- sigil-stdlib51
## Build53
```sh54
sigil deps install55
sigil build # native (host platform)56
sigil build --config windows-amd64 # cross-compile for Windows (zig)57
```59
## Streaming audio sink61
Three playback paths coexist in `(sigil audio)`:63
1. **`load-sound` + `play-sound`** — short SFX, decoded once into64
memory, fired from Sigil, mixed on the audio thread.65
2. **`play-music`** — long OGG, streamed from disk on the audio66
thread via stb_vorbis.67
3. **`open-audio-stream` + `push-audio-samples`** — caller-driven68
streaming sink. The caller produces interleaved float32 PCM69
(any thread) and pushes it into a lockless SPSC ring; the audio70
thread drains the ring into its output buffer. Use this for71
live / generative audio (motif streaming render, live-coded72
synths, etc.).74
Example — play a 440 Hz sine wave for 1 second:76
```scheme77
(import (sigil audio) (sigil math))79
(audio-setup)81
(define stream (open-audio-stream channels: 282
buffer-frames: 8192))84
(define sr 44100)85
(define pi 3.14159265358979)86
(define frames (* sr 1))88
;; Build a stereo float32 bytevector with a 440 Hz sine.89
(define samples90
(let ((v (make-vector (* frames 2) 0.0)))91
(let loop ((i 0))92
(when (< i frames)93
(let ((s (sin (* 2.0 pi 440.0 (/ i sr)))))94
(vector-set! v (* i 2) s)95
(vector-set! v (+ (* i 2) 1) s))96
(loop (+ i 1))))97
v))99
(define pcm (make-float-buffer samples))101
;; Push in chunks of whatever the ring has room for.102
(let loop ((remaining frames) (offset-frames 0))103
(when (> remaining 0)104
(let ((room (audio-stream-room stream)))105
(if (= room 0)106
(begin (sleep 0.005) (loop remaining offset-frames))107
(let ((n (min remaining room)))108
(push-audio-samples stream pcm n)109
(loop (- remaining n) (+ offset-frames n)))))))111
(close-audio-stream! stream)112
```114
Notes:115
- Sample rate is fixed at 44100 to match the device's116
configured rate; callers MUST match (no resampling).117
- `push-audio-samples` is non-blocking and returns the frame118
count actually accepted — the caller decides whether to119
retry or drop.120
- Streaming sinks coexist with `play-sound` / `play-music` —121
they're an additional mix source, not a replacement.122
- Up to 4 streams may be open simultaneously.123
- Default `buffer-frames: 16384` (~370 ms at 44.1 kHz) is124
safety margin against under-run, not added latency. Live125
producers who want tighter reactivity can open with126
`buffer-frames: 2048` or smaller.128
See `folio topics/sigil-audio-streaming-sink-architecture`129
for the SPSC ring design, threading model, and under-run /130
over-run semantics.132
## License134
BSD-3-Clause.136
Vendored libogg (1.3.6) and libvorbis (1.3.7) are also distributed137
under BSD-3-Clause (see `vendor/ogg/COPYING` and138
`vendor/vorbis/COPYING`). Both licenses are compatible with this139
package's BSD-3-Clause terms.