ludic/packages/ludic.audio/tests/audio_test.ludic
Orkuncakilkaya 19fcf60599 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>
2026-09-25 15:31:34 +03:00

102 lines
4.1 KiB
Text

# audio_test.ludic - a listener at the origin facing -z: the roll-off, the pan on each side, the pitch
# of a far sound, emit's numbers (Audio.* itself is a no-op headless), a packed file lifted into the
# cache once, and a WAV's length from its header
import "ludic.audio"
program AudioTest {
numbers float
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(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" (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)
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" (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)
audio_test_st.lyaw = 3.14159265
expect(aud_pan_at(-60.0, 0.0) > 0.5)
}
test "a far sound is a little lower" {
expect(aud_pitch_at(1.0) > aud_pitch_at(0.1))
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" (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 {
let n = 44 + frames * 2
let b = buffer(n)
b[22] = 1
b[24] = rate & 255
b[25] = (rate >> 8) & 255
b[26] = (rate >> 16) & 255
b[34] = 16
let data = frames * 2
b[40] = data & 255
b[41] = (data >> 8) & 255
b[42] = (data >> 16) & 255
Fs.write_bytes(path, b, n)
}
test "a WAV's length from its header, and a fallback for anything else" {
Fs.mkdir(dir())
wav(dir() + "/two.wav", 22050, 44100)
expect_near(aud_wav_seconds(dir() + "/two.wav", 9.0), 2.0, 0.001)
wav(dir() + "/blip.wav", 22050, 100)
expect_near(aud_wav_seconds(dir() + "/blip.wav", 9.0), 9.0, 0.001)
expect_near(aud_wav_seconds(dir() + "/none.wav", 5.4), 5.4, 0.001)
}
test "a packed clip is lifted into the cache once, and a short copy is replaced" {
Fs.mkdir(dir())
wav(dir() + "/click.wav", 22050, 300)
Fs.remove(cache() + "/click.wav")
let out = aud_unpack(dir() + "/click.wav", "click.wav")
expect(out == cache() + "/click.wav")
expect_eq(Fs.size(out), 44 + 600)
Fs.write_text(out, "cut")
let again = aud_unpack(dir() + "/click.wav", "click.wav")
expect_eq(Fs.size(again), 44 + 600)
expect(aud_unpack(dir() + "/nothing.wav", "nothing.wav") == null)
}
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)
}
}