P8.1: minimal match/clear SFX via iOS System Sound Services (sx FFI)

Feasibility spike outcome: iOS audio from sx is feasible with no sx-library
change. System Sound Services is plain C, reached with the same `#foreign`
FFI uikit.sx already uses (UIApplicationMain / dlsym / CACurrentMediaTime);
AudioToolbox + CoreFoundation are linked per-target in build.sx.

Smallest viable SFX: one short CC0 clip (Kenney Interface Sounds, CC0 1.0)
played when a swap clears a match. Purely additive — audio.sx reads/writes
no score/board/move state; the wiring in board_view only adds a call.

- audio.sx: load clear.wav once, AudioServicesPlaySystemSound on clear
- board_view.sx: trigger sfx_clear() on a legal swap that clears (>=1 round)
- main.sx: allocate + init g_audio at boot
- build.sx: link AudioToolbox + CoreFoundation on iOS
- assets/audio/clear.wav (+ one-line CC0 credit in LICENSE.txt)

Verified: ios-sim build links; 18/18 tests pass; sim boot log shows
"[sx] audio: clear cue loaded" (AudioServicesCreateSystemSoundID succeeded,
asset shipped in the bundle and decoded).
This commit is contained in:
swipelab
2026-06-05 18:19:33 +03:00
parent 0a3cd1561b
commit f0a13293bb
6 changed files with 115 additions and 0 deletions

View File

@@ -18,6 +18,7 @@
#import "board_anim.sx";
#import "board_fx.sx";
#import "gem_anim.sx";
#import "audio.sx";
#run configure_build();
@@ -284,6 +285,14 @@ main :: () -> void {
g_motion = xx context.allocator.alloc(size_of(GemMotion));
g_motion.init();
// SFX (P8.1). Loads the one System Sound Services cue once; board_view
// plays it when a swap clears a match. Purely additive — never touches
// score/board/move state. On iOS the platform has already chdir'd to the
// bundle, so the cue's relative path resolves. No-op off iOS.
g_audio = xx context.allocator.alloc(size_of(GameAudio));
memset(xx g_audio, 0, size_of(GameAudio));
g_audio.init();
// Deterministic-capture hooks: pin the animation clock and/or preselect a
// cell so the always-on idle (and the select reaction) screenshot the same
// way every time. No env set → fully live.