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
|
|
@ -192,6 +192,8 @@ function p_postfix() -> Node {
|
|||
# the engine auto-advance to do the ticking.
|
||||
if e.a.kind == E_ID and e.a.s == "Anim" and (e.s == "play" or e.s == "clip" or e.s == "on_frame" or e.s == "fired") { g_uses_anim_rt = true }
|
||||
if e.a.kind == E_ID and e.a.s == "Motion" and e.s == "to" { g_uses_anim_rt = true }
|
||||
# Audio.* (#22) — any Audio method splices the audio runtime.
|
||||
if e.a.kind == E_ID and e.a.s == "Audio" { g_uses_audio = true }
|
||||
# Tween.to/chain/delay/value/stop/parallel (#48): the fluent stateful handles
|
||||
# live in tween.ludic, advanced by an engine-owned system each Update tick.
|
||||
if e.a.kind == E_ID and e.a.s == "Tween" and (e.s == "to" or e.s == "chain" or e.s == "delay" or e.s == "value" or e.s == "stop" or e.s == "parallel") { g_uses_tween_rt = true }
|
||||
|
|
@ -426,6 +428,7 @@ var g_uses_esys: bool = false # an engine-owned system component (SpriteAnim
|
|||
var g_uses_input: bool = false # a program used Input.bind/down/poll/… (action maps + record/replay) -> splice input.ludic
|
||||
var g_uses_anim_rt: bool = false # Anim.play/clip/on_frame/fired or Motion.to (#48) -> splice systems.ludic + force the reflection ABI
|
||||
var g_uses_tween_rt: bool = false # Tween.to/chain/delay/… (#48) -> splice tween.ludic + run esys_tween each Update
|
||||
var g_uses_audio: bool = false # Audio.* (#22) -> splice audio.ludic; a windowed build also links audio.ll + AVFoundation
|
||||
|
||||
function already_loaded(full: pointer) -> bool {
|
||||
var i = 0
|
||||
|
|
@ -646,6 +649,14 @@ function maybe_splice_runtime() -> void {
|
|||
do_import("runtime/native/tween.ludic")
|
||||
cur_dir = saved
|
||||
}
|
||||
# Audio.* (#22): splice the Audio runtime. It is self-contained — its native
|
||||
# calls (snd_*) are is_windowed()-guarded, so a headless build carries the API
|
||||
# as no-ops and needs no audio backend — so it works in a plain program too.
|
||||
if g_uses_audio {
|
||||
cur_dir = ""
|
||||
do_import("runtime/native/audio.ludic")
|
||||
cur_dir = saved
|
||||
}
|
||||
# Anim.play/Motion.to sugar (#48): the writes live in systems.ludic and use the
|
||||
# reflection ABI, so splice it and force the world table even when the game does
|
||||
# not otherwise trip uses_engine_systems.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue