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>
33 lines
1.8 KiB
Text
33 lines
1.8 KiB
Text
# audio.ludic — the Audio.* standard library (#22). Sound effects and music over
|
|
# the platform audio backend (AVAudioPlayer on macOS). A sound is loaded once
|
|
# into a handle; sfx fire one-shot, music loops on a single channel; master
|
|
# volume and pitch apply across everything.
|
|
#
|
|
# Playback is out-of-band — the audio device is real-time, not part of the
|
|
# deterministic simulation — but every trigger is an ordinary frame-driven call,
|
|
# so a recorded run replays the same sounds at the same frames. Headless (no
|
|
# window) every call is a safe no-op: load returns 0 and is_playing returns
|
|
# false, so the same code runs under the test harness. A full headless run:
|
|
# bin/ludic examples/library/audio.ludic -> 0 0 0 0 1
|
|
program AudioDemo {
|
|
function bi(b: bool) -> int { if b { return 1 }; return 0 }
|
|
|
|
entry {
|
|
let sfx = Audio.load("assets/sfx/blip.wav") # 0 headless (no audio device)
|
|
let mus = Audio.load("assets/music/theme.ogg") # 0 headless
|
|
print(sfx) # 0
|
|
print(mus) # 0
|
|
|
|
Audio.volume(0.8) # master volume, 0.0..1.0
|
|
Audio.pitch(1.25) # playback rate / pitch
|
|
Audio.play(sfx) # one-shot sound effect
|
|
Audio.play_music(mus) # looping background music
|
|
print(bi(Audio.is_playing(sfx))) # 0 headless
|
|
|
|
Audio.stop(sfx) # stop one sound
|
|
Audio.stop_music() # stop the music channel
|
|
Audio.stop_all() # stop everything
|
|
print(bi(Audio.is_playing(mus))) # 0
|
|
print(1) # 1 — every call returned cleanly
|
|
}
|
|
}
|