wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run
ludic migrate state packages <every example program> packages/ludic.lab/example/plate.ludic 1804 vars into 126 states, 64 into lets; 23498 edits in 460 files Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5ffe50ed02
commit
19fcf60599
459 changed files with 17862 additions and 17603 deletions
|
|
@ -1,33 +1,37 @@
|
|||
# emit.ludic - the only calls into the runtime's playback. aud_emit is the ONE place a float
|
||||
# crosses into Audio.play_at's Q16.16: handed over unconverted, 0.94 as a pitch clamped to 4x and
|
||||
# -0.85 as a pan to hard left, and every sound in a valley played fast out of one speaker
|
||||
var last_gain: float = 0.0
|
||||
var last_pitch: float = 0.0
|
||||
var last_pan: float = 0.0
|
||||
var emitted: int = 0
|
||||
export state AudioState {
|
||||
last_gain: float = 0.0
|
||||
last_pitch: float = 0.0
|
||||
last_pan: float = 0.0
|
||||
emitted: int = 0
|
||||
tried: int = 0
|
||||
live: int = 0
|
||||
}
|
||||
|
||||
export function aud_emit(h: int, gain: float, pitch: float, pan: float) -> void {
|
||||
export function aud_emit(audio_st: mut AudioState, h: int, gain: float, pitch: float, pan: float) -> void {
|
||||
if h == 0 { return }
|
||||
last_gain = gain
|
||||
last_pitch = pitch
|
||||
last_pan = pan
|
||||
emitted += 1
|
||||
audio_st.last_gain = gain
|
||||
audio_st.last_pitch = pitch
|
||||
audio_st.last_pan = pan
|
||||
audio_st.emitted += 1
|
||||
Audio.play_at(h, fixed(gain), fixed(pitch), fixed(pan))
|
||||
}
|
||||
|
||||
# a sound at a place in the world, heard within `range` metres: false (and nothing played) when the
|
||||
# listener is out of earshot
|
||||
export function aud_emit_at(h: int, x: float, z: float, range: float) -> bool {
|
||||
export function aud_emit_at(audio_st: mut AudioState, h: int, x: float, z: float, range: float) -> bool {
|
||||
let g = aud_gain_at(x, z, range)
|
||||
if g == 0.0 { return false }
|
||||
aud_emit(h, g, aud_pitch_at(g), aud_pan_at(x, z))
|
||||
aud_emit(audio_st, h, g, aud_pitch_at(g), aud_pan_at(x, z))
|
||||
return true
|
||||
}
|
||||
|
||||
# a click, a chime, a shutter, a warning: in the player's head, flat
|
||||
export function aud_ui(h: int) -> void {
|
||||
export function aud_ui(audio_st: mut AudioState, h: int) -> void {
|
||||
if h == 0 { return }
|
||||
emitted += 1
|
||||
audio_st.emitted += 1
|
||||
Audio.play(h)
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +41,7 @@ export function aud_master(v: float) -> void { Audio.volume(fixed(Math.clamp(v,
|
|||
|
||||
# what the last emit handed the runtime, and how many shots so far: Audio.* is a no-op headless, so
|
||||
# the numbers are the only thing a test can hear
|
||||
export function aud_last_gain() -> float { return last_gain }
|
||||
export function aud_last_pitch() -> float { return last_pitch }
|
||||
export function aud_last_pan() -> float { return last_pan }
|
||||
export function aud_emitted() -> int { return emitted }
|
||||
export function aud_last_gain(audio_st: AudioState) -> float { return audio_st.last_gain }
|
||||
export function aud_last_pitch(audio_st: AudioState) -> float { return audio_st.last_pitch }
|
||||
export function aud_last_pan(audio_st: AudioState) -> float { return audio_st.last_pan }
|
||||
export function aud_emitted(audio_st: AudioState) -> int { return audio_st.emitted }
|
||||
|
|
|
|||
|
|
@ -2,25 +2,23 @@
|
|||
# bottoms out in AVAudioPlayer), not the pack-aware file_open every other asset arrives through: in a
|
||||
# bundle every clip loads as 0 while Fs.exists, which IS pack-aware, says it is there. So try the
|
||||
# runtime's path, and on 0 lift the bytes out of the pack into the cache directory and load that.
|
||||
var tried: int = 0
|
||||
var live: int = 0
|
||||
|
||||
export function aud_load(path: string) -> int {
|
||||
tried += 1
|
||||
export function aud_load(audio_st: mut AudioState, path: string) -> int {
|
||||
audio_st.tried += 1
|
||||
if not is_windowed() { return 0 }
|
||||
var h = Audio.load(path)
|
||||
if h == 0 {
|
||||
let out = aud_unpack(path, base_name(path))
|
||||
if out != null { h = Audio.load(out) }
|
||||
}
|
||||
if h != 0 { live += 1 }
|
||||
if h != 0 { audio_st.live += 1 }
|
||||
return h
|
||||
}
|
||||
|
||||
# how many clips were asked for and how many loaded: a silent game looks exactly like a working one
|
||||
# unless something says so
|
||||
export function aud_tried() -> int { return tried }
|
||||
export function aud_live() -> int { return live }
|
||||
export function aud_tried(audio_st: AudioState) -> int { return audio_st.tried }
|
||||
export function aud_live(audio_st: AudioState) -> int { return audio_st.live }
|
||||
|
||||
# One packed file out to the cache, or null. A cached copy is trusted only if it is as long as the
|
||||
# packed one: a write interrupted halfway would otherwise play truncated for ever.
|
||||
|
|
|
|||
|
|
@ -4,35 +4,37 @@
|
|||
import "ludic.audio"
|
||||
program AudioTest {
|
||||
numbers float
|
||||
var lx: float = 0.0
|
||||
var lz: float = 0.0
|
||||
var lyaw: float = 0.0
|
||||
function t_x() -> float { return lx }
|
||||
state AudioTestState {
|
||||
lx: float = 0.0
|
||||
lyaw: float = 0.0
|
||||
}
|
||||
let lz: float = 0.0
|
||||
function t_x(audio_test_st: AudioTestState) -> float { return audio_test_st.lx }
|
||||
function t_z() -> float { return lz }
|
||||
function t_yaw() -> float { return lyaw }
|
||||
function t_yaw(audio_test_st: AudioTestState) -> float { return audio_test_st.lyaw }
|
||||
function dir() -> string { return Os.temp_dir() + "/ludic-audio-test" }
|
||||
function cache() -> string { return dir() + "/cache" }
|
||||
bind AudioListener { x: fn t_x, z: fn t_z, yaw: fn t_yaw }
|
||||
bind AudioFiles { cache_dir: fn cache }
|
||||
|
||||
test "the gain falls off with distance and is gone past the range" {
|
||||
test "the gain falls off with distance and is gone past the range" (audio_test_st: mut AudioTestState) {
|
||||
let near = aud_gain_at(0.0, -10.0, 200.0)
|
||||
let mid = aud_gain_at(0.0, -100.0, 200.0)
|
||||
let far = aud_gain_at(0.0, -150.0, 200.0)
|
||||
expect(near > mid and mid > far and far > 0.0)
|
||||
expect_eq(aud_gain_at(0.0, -400.0, 200.0), 0.0)
|
||||
expect_near(aud_gain_at(0.0, 0.0, 50.0), 1.0, 0.0001)
|
||||
lx = 1000.0
|
||||
audio_test_st.lx = 1000.0
|
||||
expect_near(aud_gain_at(1000.0, 0.0, 50.0), 1.0, 0.0001)
|
||||
}
|
||||
|
||||
test "the pan says which side, ahead and behind are centred, and it turns with the listener" {
|
||||
test "the pan says which side, ahead and behind are centred, and it turns with the listener" (audio_test_st: mut AudioTestState) {
|
||||
expect(aud_pan_at(-60.0, 0.0) < -0.5)
|
||||
expect(aud_pan_at(60.0, 0.0) > 0.5)
|
||||
expect_near(aud_pan_at(0.0, -60.0), 0.0, 0.01)
|
||||
expect_near(aud_pan_at(0.0, 60.0), 0.0, 0.01)
|
||||
expect_near(aud_pan_at(0.5, 0.0), 0.0, 0.0001)
|
||||
lyaw = 3.14159265
|
||||
audio_test_st.lyaw = 3.14159265
|
||||
expect(aud_pan_at(-60.0, 0.0) > 0.5)
|
||||
}
|
||||
|
||||
|
|
@ -41,18 +43,18 @@ program AudioTest {
|
|||
expect(aud_pitch_at(0.0) > 0.9 and aud_pitch_at(1.0) < 1.1)
|
||||
}
|
||||
|
||||
test "emit hands the runtime what it was told, and a sound out of earshot is not played" {
|
||||
aud_emit(7, 0.5, 0.97, -0.25)
|
||||
expect_near(aud_last_gain(), 0.5, 0.0001)
|
||||
expect_near(aud_last_pan(), -0.25, 0.0001)
|
||||
expect_eq(aud_emitted(), 1)
|
||||
aud_emit(0, 1.0, 1.0, 0.0)
|
||||
expect_eq(aud_emitted(), 1)
|
||||
expect(not aud_emit_at(7, 0.0, -500.0, 100.0))
|
||||
expect(aud_emit_at(7, 30.0, 0.0, 100.0))
|
||||
expect(aud_last_pan() > 0.5)
|
||||
expect(aud_last_gain() > 0.4 and aud_last_gain() < 0.5)
|
||||
expect(aud_last_pitch() < 1.0)
|
||||
test "emit hands the runtime what it was told, and a sound out of earshot is not played" (audio_st: mut AudioState) {
|
||||
aud_emit(audio_st, 7, 0.5, 0.97, -0.25)
|
||||
expect_near(aud_last_gain(audio_st), 0.5, 0.0001)
|
||||
expect_near(aud_last_pan(audio_st), -0.25, 0.0001)
|
||||
expect_eq(aud_emitted(audio_st), 1)
|
||||
aud_emit(audio_st, 0, 1.0, 1.0, 0.0)
|
||||
expect_eq(aud_emitted(audio_st), 1)
|
||||
expect(not aud_emit_at(audio_st, 7, 0.0, -500.0, 100.0))
|
||||
expect(aud_emit_at(audio_st, 7, 30.0, 0.0, 100.0))
|
||||
expect(aud_last_pan(audio_st) > 0.5)
|
||||
expect(aud_last_gain(audio_st) > 0.4 and aud_last_gain(audio_st) < 0.5)
|
||||
expect(aud_last_pitch(audio_st) < 1.0)
|
||||
}
|
||||
|
||||
function wav(path: string, rate: int, frames: int) -> void {
|
||||
|
|
@ -92,9 +94,9 @@ program AudioTest {
|
|||
expect(aud_unpack(dir() + "/nothing.wav", "nothing.wav") == null)
|
||||
}
|
||||
|
||||
test "nothing loads headless, and the count says so" {
|
||||
expect_eq(aud_load(dir() + "/click.wav"), 0)
|
||||
expect_eq(aud_tried(), 1)
|
||||
expect_eq(aud_live(), 0)
|
||||
test "nothing loads headless, and the count says so" (audio_st: mut AudioState) {
|
||||
expect_eq(aud_load(audio_st, dir() + "/click.wav"), 0)
|
||||
expect_eq(aud_tried(audio_st), 1)
|
||||
expect_eq(aud_live(audio_st), 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue