Commit8e069cf9Recorded9 Jul 2026Repositorysigil-audio

Add (sigil audio sink) cond-expand facade

Message

Extract a portable streaming audio-sink facade that selects its backend with cond-expand: the native (else) arm re-exports the sokol SPSC ring from (sigil audio); the wasm arm imports (sigil browser audio-sink) from the sigil-wasm-audio bridge package.

The facade adds WebAudio-shaped context lifecycle ops (audio-context-open /-resume/-sample-rate/-state) plus a portable make-float-buffer re-export so the same source compiles and runs on native and wasm. On native the context ops are thin shims over sokol's single always-running device.

(sigil audio) and the native sink C are untouched; test-streaming-sink.sgl stays green.

Changed
 src/sigil/audio/sink.sgl | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)
Diff
src/sigil/audio/sink.sgladded
@@ -0,0 +1,93 @@
+1
;;; (sigil audio sink) - Portable streaming audio-sink facade
+2
;;;
+3
;;; This is the target-independent audio sink API. It presents ONE surface
+4
;;; for pushing interleaved float32 PCM into a device, and selects its
+5
;;; backend with `cond-expand`:
+6
;;;
+7
;;; - native (else): the sokol_audio SPSC ring living in (sigil audio)
+8
;;; - wasm: the WebAudio bridge in (sigil browser audio-sink),
+9
;;; provided by the sigil-wasm-audio package
+10
;;;
+11
;;; This mirrors how (sigil websocket) selects a native socket stack vs the
+12
;;; (sigil browser websocket) bridge. Application code imports THIS module
+13
;;; and the same source compiles for both targets.
+14
;;;
+15
;;; ## Context lifecycle
+16
;;;
+17
;;; WebAudio requires an AudioContext that starts suspended and must be
+18
;;; resumed from a user gesture (autoplay policy). The context ops model
+19
;;; that. On native, sokol owns a single always-running device, so the
+20
;;; context ops are thin shims (open initializes sokol; resume is a no-op;
+21
;;; the sample rate is sokol's fixed 44100). They exist so the SAME source
+22
;;; compiles and runs on both targets behind this facade.
+23
;;;
+24
;;; ```scheme
+25
;;; (import (sigil audio sink))
+26
;;; (define ctx (audio-context-open))
+27
;;; (audio-context-resume ctx) ; from a user gesture on wasm
+28
;;; (let ((rate (audio-context-sample-rate ctx))
+29
;;; (s (open-audio-stream channels: 2 buffer-frames: 8192)))
+30
;;; (push-audio-samples s pcm n-frames) ; interleaved f32
+31
;;; (audio-stream-depth s)) ; backpressure accounting
+32
;;; ```
+33
+34
(define-library (sigil audio sink)
+35
(import (sigil core))
+36
+37
(cond-expand
+38
(wasm
+39
(import (sigil browser audio-sink)))
+40
(else
+41
(import (sigil audio))))
+42
+43
(export
+44
;; Context lifecycle (WebAudio-shaped; native shims)
+45
audio-context-open
+46
audio-context-resume
+47
audio-context-sample-rate
+48
audio-context-state
+49
;; Streaming sink
+50
open-audio-stream
+51
push-audio-samples
+52
audio-stream-depth
+53
audio-stream-room
+54
audio-stream-capacity
+55
audio-stream-channels
+56
audio-stream-underruns
+57
audio-stream?
+58
audio-stream-closed?
+59
close-audio-stream!
+60
set-audio-stream-volume!
+61
;; Portable f32 PCM buffer builder (available on both backends)
+62
make-float-buffer)
+63
+64
;; The wasm arm gets every export from (sigil browser audio-sink). The
+65
;; native arm re-exports the sink ops from (sigil audio) and supplies the
+66
;; context shims below.
+67
(cond-expand
+68
(wasm)
+69
(else
+70
(begin
+71
+72
;;; Open the audio context. On native this initializes sokol_audio
+73
;;; and returns an opaque non-#f handle (sokol owns a single device,
+74
;;; so the handle is a sentinel). On wasm this creates a suspended
+75
;;; AudioContext and returns its id.
+76
(define (audio-context-open)
+77
(audio-setup)
+78
'native-audio-context)
+79
+80
;;; Resume a suspended context from a user gesture. Native contexts
+81
;;; are never suspended, so this is a no-op that reports success.
+82
(define (audio-context-resume ctx)
+83
#t)
+84
+85
;;; The context sample rate in Hz. Sokol runs at a fixed 44100; the
+86
;;; sink ring assumes the same rate (no resampling).
+87
(define (audio-context-sample-rate ctx)
+88
44100)
+89
+90
;;; The context run state: one of 'suspended, 'running, 'closed.
+91
;;; Native contexts are always 'running once opened.
+92
(define (audio-context-state ctx)
+93
'running)))))