feat(audio): Audio.* standard library over a native AVAudioPlayer backend (#22)
Adds the Audio.* namespace and its platform backend, the audio subsystem #22 was blocked on. - runtime/native/audio.ll: the macOS backend, AVAudioPlayer driven through the objc runtime C ABI (no ObjC/C source), same style as cocoa.ll — snd_load / play / stop / playing / set_volume / set_rate. Spliced and linked with AVFoundation only when a windowed build actually uses Audio.* (needed_framework, since AVAudioPlayer is reached by name). - runtime/native/audio.ludic: the Audio.* runtime — a handle table, master volume/pitch, a single music channel. load/play/play_sound/play_music/stop/ stop_music/stop_all/volume/pitch/is_playing. Every native call is is_windowed()-guarded, so a headless build carries the API as no-ops (load returns 0, is_playing false) and needs no audio device. - compiler: Audio.* namespace dispatch, g_uses_audio splice, snd_* intrinsics + declarations, and the conditional AVFoundation link in both the canonical (main.ludic) and dev-runner (x app) paths. - docs: a full docs/language/audio section (10 method pages); check-impl green. - test: examples/library/audio.ludic self-asserts the headless no-op path. Playback is out-of-band and never feeds the deterministic sim, but triggers are frame-driven so replays fire the same sounds. Reseeded; suites green (80 + 29). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d6ca320269
commit
4eef5ebbce
23 changed files with 22294 additions and 21004 deletions
|
|
@ -216,6 +216,21 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
if (meth == "touch_y") { bare = "input_touch_y"; push(labels, "index") }
|
||||
if (meth == "set_touch") { bare = "input_set_touch"; push(labels, "index"); push(labels, "x"); push(labels, "y"); push(labels, "active") }
|
||||
}
|
||||
# Audio.* (#22) — sfx/music playback over the platform audio backend
|
||||
# (runtime/native/audio.ludic + audio.ll). Playback is out-of-band; the
|
||||
# triggers are ordinary frame-driven calls, so a replay fires the same sounds.
|
||||
if (ns == "Audio") {
|
||||
if (meth == "load") { bare = "audio_load"; push(labels, "path") }
|
||||
if (meth == "play") { bare = "audio_play"; push(labels, "id") }
|
||||
if (meth == "play_sound") { bare = "audio_play"; push(labels, "id") }
|
||||
if (meth == "play_music") { bare = "audio_play_music"; push(labels, "id") }
|
||||
if (meth == "stop") { bare = "audio_stop"; push(labels, "id") }
|
||||
if (meth == "stop_music") { bare = "audio_stop_music" }
|
||||
if (meth == "stop_all") { bare = "audio_stop_all" }
|
||||
if (meth == "volume") { bare = "audio_volume"; push(labels, "v") }
|
||||
if (meth == "pitch") { bare = "audio_pitch"; push(labels, "v") }
|
||||
if (meth == "is_playing") { bare = "audio_is_playing"; push(labels, "id") }
|
||||
}
|
||||
# Phase 3: the bare reflection / networking / process builtins, namespaced.
|
||||
# Each is a pure alias — the callee is rewritten to the bare name below.
|
||||
if (ns == "World") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue