runtime/audio (macOS): voices on one AVAudioEngine instead of an AVAudioPlayer per sound
AVFAudio keeps a 64-byte AudioQueueOwner for good on every AVAudioPlayer play after the clip has finished - measured one a play, whatever is called around it (prepareToPlay, pause, no rewind) and still there after the player is released; a stop before the play made one every time. The windowed walk showed it as AudioQueueOwner 73 -> 87 in a minute. A sound is now decoded once into a PCM buffer and played by a voice of its own on one shared engine: a player node (the buffer scheduled again on each play, looping for -1), a varispeed (the rate) and a small mixer (volume and pan). snd_playing compares the uptime clock with the end worked out when the clip was played (asking the node where it is made two AVAudioTime objects a call), and snd_play drains an autorelease pool of its own. The C interface is unchanged. A harness driving snd_* directly (400 plays past the end, the playing flag checked during and after each, a loop and a stop, the setters) holds the heap flat to a block and gets the flag right 400 of 400; the windowed valley compiles and links against it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
34421b8015
commit
a5f6c02a09
3 changed files with 295 additions and 96 deletions
|
|
@ -2,7 +2,7 @@
|
|||
# audio.ludic — the Audio.* standard library (#22).
|
||||
#
|
||||
# A small, game-shaped sound API over the platform audio backend (audio.ll,
|
||||
# AVAudioPlayer on macOS). A sound is loaded once into a handle; sfx are fired
|
||||
# AVAudioEngine on macOS). A sound is loaded once into a handle; sfx are fired
|
||||
# one-shot, music plays on a single looping channel. Playback itself is not part
|
||||
# of the deterministic simulation — the device output is real-time and
|
||||
# non-deterministic — but every *trigger* here is an ordinary function call the
|
||||
|
|
@ -21,7 +21,7 @@ const AUDIO_CAP: int = 64 # max simultaneously-loaded sounds
|
|||
|
||||
export state RtAudioState {
|
||||
snd_ready: bool = false
|
||||
snd_tab: pointers = null # handle (1-based) -> AVAudioPlayer pointer
|
||||
snd_tab: pointers = null # handle (1-based) -> the backend's voice pointer
|
||||
snd_master: fixed = 1.0 # master volume, applied to every play (and to audio_play_at's gain)
|
||||
snd_rate: fixed = 1.0 # playback rate / pitch (1.0 = normal)
|
||||
snd_music: int = 0 # the handle currently playing as music (0 = none)
|
||||
|
|
@ -124,7 +124,7 @@ function audio_play_at(rt_audio_st: mut RtAudioState, id: int, gain: fixed, pitc
|
|||
if pn < -1.0 { pn = -1.0 }
|
||||
if pn > 1.0 { pn = 1.0 }
|
||||
var pt = pitch
|
||||
if pt < 0.25 { pt = 0.25 } # AVAudioPlayer's own rate range
|
||||
if pt < 0.25 { pt = 0.25 } # the varispeed's own rate range
|
||||
if pt > 4.0 { pt = 4.0 }
|
||||
snd_set_pan(p, pn)
|
||||
snd_play(p, 0, pt, g * rt_audio_st.snd_master)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue