From 2fcbcc30029df5370e5a3f1e5ce1ed9713ba57db Mon Sep 17 00:00:00 2001 From: Hails <26784904+Hailey-Ross@users.noreply.github.com> Date: Fri, 2 Oct 2026 22:58:17 -0600 Subject: [PATCH] New audio who dis Native port of Skate 3's retail audio runtime (AEMS) behind SKATE_AEMS=1, plus the world audio work: - skate-audio crate: formats, patch-program evaluator, voice graph, mixer, MixMap, granular rolling bed and its bus chain, Splice contacts, the buses (env reverb network, eEQChain, flanger returns, collision and footstep submixes, reverb zones) and the player components (rolling layers, seams on the console's 30 fps cadence, grinds, skids, squeaks, tricks, treatment, footsteps, clothing, hands on deck, wind, wheels, collision manager). - game_audio host: native runtime, player audio state from our physics, world emitters from the maps' .ems files, random distant one-shots, zone ambience, state log, e2e render harness, timing. - Setup: audio export of the banks, grains, MixMap, vault tuning and reverb zones. - skate-core: per-wheel / per-part audio surfaces and seam patterns (additive). - Docs: docs/hails-additions/11-audio.md and the PR plan. --- Cargo.lock | 5 + Cargo.toml | 2 +- crates/skate-audio/Cargo.toml | 6 + crates/skate-audio/examples/aems_list.rs | 51 + crates/skate-audio/examples/aems_render.rs | 129 ++ crates/skate-audio/examples/aems_replay.rs | 183 ++ .../skate-audio/examples/board_mix_probe.rs | 63 + crates/skate-audio/examples/effect_routes.rs | 82 + .../examples/emitter_scene_probe.rs | 199 ++ .../skate-audio/examples/grain_bed_render.rs | 161 ++ .../skate-audio/examples/grain_chain_probe.rs | 255 +++ crates/skate-audio/examples/mixmap_golden.rs | 87 + crates/skate-audio/examples/pan_fold_probe.rs | 18 + crates/skate-audio/examples/post_probe.rs | 68 + crates/skate-audio/examples/program_trace.rs | 110 + .../skate-audio/examples/seam_pulse_probe.rs | 135 ++ .../skate-audio/examples/voice_load_probe.rs | 61 + crates/skate-audio/src/be.rs | 82 + crates/skate-audio/src/bus/env.rs | 587 ++++++ crates/skate-audio/src/bus/eqchain.rs | 152 ++ crates/skate-audio/src/bus/flange.rs | 239 +++ crates/skate-audio/src/bus/mod.rs | 111 + crates/skate-audio/src/bus/submix.rs | 191 ++ crates/skate-audio/src/bus/zones.rs | 442 ++++ crates/skate-audio/src/dsp/biquad.rs | 217 ++ crates/skate-audio/src/dsp/delay.rs | 126 ++ crates/skate-audio/src/dsp/fss.rs | 278 +++ crates/skate-audio/src/dsp/gain.rs | 79 + crates/skate-audio/src/dsp/mod.rs | 20 + crates/skate-audio/src/dsp/pan.rs | 290 +++ crates/skate-audio/src/dsp/peaking.rs | 117 ++ crates/skate-audio/src/dsp/resample.rs | 138 ++ crates/skate-audio/src/dsp/reverb.rs | 230 ++ crates/skate-audio/src/dsp/routes.rs | 79 + crates/skate-audio/src/dsp/send.rs | 116 + crates/skate-audio/src/dsp/shelf.rs | 135 ++ crates/skate-audio/src/eval/fmath.rs | 61 + crates/skate-audio/src/eval/mod.rs | 770 +++++++ crates/skate-audio/src/eval/ops.rs | 449 ++++ crates/skate-audio/src/eval/ops/tests.rs | 383 ++++ crates/skate-audio/src/eval/rng.rs | 59 + crates/skate-audio/src/eval/symbols.rs | 97 + crates/skate-audio/src/eval/tests.rs | 477 +++++ crates/skate-audio/src/formats/abk.rs | 256 +++ crates/skate-audio/src/formats/csi.rs | 52 + crates/skate-audio/src/formats/mod.rs | 24 + crates/skate-audio/src/formats/snr.rs | 63 + crates/skate-audio/src/grain/bed.rs | 491 +++++ crates/skate-audio/src/grain/board.rs | 512 +++++ crates/skate-audio/src/grain/chain.rs | 539 +++++ crates/skate-audio/src/grain/format.rs | 229 ++ crates/skate-audio/src/grain/mod.rs | 17 + crates/skate-audio/src/grain/player.rs | 610 ++++++ crates/skate-audio/src/lib.rs | 47 + crates/skate-audio/src/mixer.rs | 805 +++++++ crates/skate-audio/src/mixmap/format.rs | 255 +++ crates/skate-audio/src/mixmap/keys.rs | 116 + crates/skate-audio/src/mixmap/mod.rs | 750 +++++++ crates/skate-audio/src/mixmap/tables.rs | 254 +++ crates/skate-audio/src/mixmap/tests.rs | 151 ++ crates/skate-audio/src/player/clothing.rs | 363 ++++ crates/skate-audio/src/player/collision.rs | 693 ++++++ crates/skate-audio/src/player/components.rs | 676 ++++++ crates/skate-audio/src/player/contacts.rs | 972 +++++++++ crates/skate-audio/src/player/footsteps.rs | 1153 ++++++++++ crates/skate-audio/src/player/globals.rs | 35 + crates/skate-audio/src/player/inputs.rs | 189 ++ crates/skate-audio/src/player/jitter.rs | 126 ++ crates/skate-audio/src/player/mod.rs | 77 + crates/skate-audio/src/player/objpos.rs | 159 ++ crates/skate-audio/src/player/rolling.rs | 859 ++++++++ crates/skate-audio/src/player/seams.rs | 660 ++++++ crates/skate-audio/src/player/state.rs | 277 +++ crates/skate-audio/src/player/step_on.rs | 184 ++ crates/skate-audio/src/player/treatment.rs | 243 +++ crates/skate-audio/src/player/tricks.rs | 338 +++ crates/skate-audio/src/player/tricks/tests.rs | 192 ++ crates/skate-audio/src/player/tuning.rs | 227 ++ crates/skate-audio/src/player/wheels.rs | 203 ++ crates/skate-audio/src/runtime.rs | 242 +++ crates/skate-audio/src/splice/format.rs | 155 ++ crates/skate-audio/src/splice/mod.rs | 432 ++++ crates/skate-audio/src/splice/tests.rs | 139 ++ crates/skate-audio/tests/disc_banks.rs | 236 +++ crates/skate-audio/tests/disc_grains.rs | 39 + crates/skate-audio/tests/dsp_oracle.rs | 248 +++ crates/skate-audio/tests/offboard_levels.rs | 405 ++++ crates/skate-audio/tests/player_tricks.rs | 651 ++++++ crates/skate-audio/tests/rolling_banks.rs | 604 ++++++ crates/skate-core/src/physics/board_ground.rs | 13 + .../src/physics/tests/board_ground.rs | 9 + crates/skate-game/Cargo.toml | 1 + crates/skate-game/src/game_audio/ambience.rs | 249 ++- .../src/game_audio/crossfade_groups.rs | 29 + crates/skate-game/src/game_audio/cues.rs | 73 +- crates/skate-game/src/game_audio/e2e.rs | 499 +++++ crates/skate-game/src/game_audio/emitters.rs | 269 ++- crates/skate-game/src/game_audio/grain_bed.rs | 447 ++++ crates/skate-game/src/game_audio/library.rs | 779 ++++++- crates/skate-game/src/game_audio/mod.rs | 27 +- crates/skate-game/src/game_audio/native.rs | 774 +++++++ .../skate-game/src/game_audio/player_audio.rs | 961 +++++++++ .../src/game_audio/random_programs.rs | 121 ++ .../skate-game/src/game_audio/random_sets.rs | 329 +++ .../skate-game/src/game_audio/skate_events.rs | 523 ++++- crates/skate-game/src/game_audio/state_log.rs | 176 ++ crates/skate-game/src/game_audio/timing.rs | 97 + crates/skate-game/src/game_audio/voices.rs | 122 +- .../src/physics/foot_physical_output.rs | 5 + docs/hails-additions/02-long-paths.md | 4 +- docs/hails-additions/05-collision-volumes.md | 54 +- docs/hails-additions/11-audio.md | 1863 ++++++++++++++++- docs/hails-additions/PULL-REQUESTS.md | 4 +- docs/hails-additions/README.md | 4 +- tools/asset_pipeline/audio_export.py | 845 +++++++- tools/asset_pipeline/audio_formats.py | 62 + tools/asset_pipeline/test_audio_formats.py | 248 +++ 117 files changed, 31947 insertions(+), 128 deletions(-) create mode 100644 crates/skate-audio/Cargo.toml create mode 100644 crates/skate-audio/examples/aems_list.rs create mode 100644 crates/skate-audio/examples/aems_render.rs create mode 100644 crates/skate-audio/examples/aems_replay.rs create mode 100644 crates/skate-audio/examples/board_mix_probe.rs create mode 100644 crates/skate-audio/examples/effect_routes.rs create mode 100644 crates/skate-audio/examples/emitter_scene_probe.rs create mode 100644 crates/skate-audio/examples/grain_bed_render.rs create mode 100644 crates/skate-audio/examples/grain_chain_probe.rs create mode 100644 crates/skate-audio/examples/mixmap_golden.rs create mode 100644 crates/skate-audio/examples/pan_fold_probe.rs create mode 100644 crates/skate-audio/examples/post_probe.rs create mode 100644 crates/skate-audio/examples/program_trace.rs create mode 100644 crates/skate-audio/examples/seam_pulse_probe.rs create mode 100644 crates/skate-audio/examples/voice_load_probe.rs create mode 100644 crates/skate-audio/src/be.rs create mode 100644 crates/skate-audio/src/bus/env.rs create mode 100644 crates/skate-audio/src/bus/eqchain.rs create mode 100644 crates/skate-audio/src/bus/flange.rs create mode 100644 crates/skate-audio/src/bus/mod.rs create mode 100644 crates/skate-audio/src/bus/submix.rs create mode 100644 crates/skate-audio/src/bus/zones.rs create mode 100644 crates/skate-audio/src/dsp/biquad.rs create mode 100644 crates/skate-audio/src/dsp/delay.rs create mode 100644 crates/skate-audio/src/dsp/fss.rs create mode 100644 crates/skate-audio/src/dsp/gain.rs create mode 100644 crates/skate-audio/src/dsp/mod.rs create mode 100644 crates/skate-audio/src/dsp/pan.rs create mode 100644 crates/skate-audio/src/dsp/peaking.rs create mode 100644 crates/skate-audio/src/dsp/resample.rs create mode 100644 crates/skate-audio/src/dsp/reverb.rs create mode 100644 crates/skate-audio/src/dsp/routes.rs create mode 100644 crates/skate-audio/src/dsp/send.rs create mode 100644 crates/skate-audio/src/dsp/shelf.rs create mode 100644 crates/skate-audio/src/eval/fmath.rs create mode 100644 crates/skate-audio/src/eval/mod.rs create mode 100644 crates/skate-audio/src/eval/ops.rs create mode 100644 crates/skate-audio/src/eval/ops/tests.rs create mode 100644 crates/skate-audio/src/eval/rng.rs create mode 100644 crates/skate-audio/src/eval/symbols.rs create mode 100644 crates/skate-audio/src/eval/tests.rs create mode 100644 crates/skate-audio/src/formats/abk.rs create mode 100644 crates/skate-audio/src/formats/csi.rs create mode 100644 crates/skate-audio/src/formats/mod.rs create mode 100644 crates/skate-audio/src/formats/snr.rs create mode 100644 crates/skate-audio/src/grain/bed.rs create mode 100644 crates/skate-audio/src/grain/board.rs create mode 100644 crates/skate-audio/src/grain/chain.rs create mode 100644 crates/skate-audio/src/grain/format.rs create mode 100644 crates/skate-audio/src/grain/mod.rs create mode 100644 crates/skate-audio/src/grain/player.rs create mode 100644 crates/skate-audio/src/lib.rs create mode 100644 crates/skate-audio/src/mixer.rs create mode 100644 crates/skate-audio/src/mixmap/format.rs create mode 100644 crates/skate-audio/src/mixmap/keys.rs create mode 100644 crates/skate-audio/src/mixmap/mod.rs create mode 100644 crates/skate-audio/src/mixmap/tables.rs create mode 100644 crates/skate-audio/src/mixmap/tests.rs create mode 100644 crates/skate-audio/src/player/clothing.rs create mode 100644 crates/skate-audio/src/player/collision.rs create mode 100644 crates/skate-audio/src/player/components.rs create mode 100644 crates/skate-audio/src/player/contacts.rs create mode 100644 crates/skate-audio/src/player/footsteps.rs create mode 100644 crates/skate-audio/src/player/globals.rs create mode 100644 crates/skate-audio/src/player/inputs.rs create mode 100644 crates/skate-audio/src/player/jitter.rs create mode 100644 crates/skate-audio/src/player/mod.rs create mode 100644 crates/skate-audio/src/player/objpos.rs create mode 100644 crates/skate-audio/src/player/rolling.rs create mode 100644 crates/skate-audio/src/player/seams.rs create mode 100644 crates/skate-audio/src/player/state.rs create mode 100644 crates/skate-audio/src/player/step_on.rs create mode 100644 crates/skate-audio/src/player/treatment.rs create mode 100644 crates/skate-audio/src/player/tricks.rs create mode 100644 crates/skate-audio/src/player/tricks/tests.rs create mode 100644 crates/skate-audio/src/player/tuning.rs create mode 100644 crates/skate-audio/src/player/wheels.rs create mode 100644 crates/skate-audio/src/runtime.rs create mode 100644 crates/skate-audio/src/splice/format.rs create mode 100644 crates/skate-audio/src/splice/mod.rs create mode 100644 crates/skate-audio/src/splice/tests.rs create mode 100644 crates/skate-audio/tests/disc_banks.rs create mode 100644 crates/skate-audio/tests/disc_grains.rs create mode 100644 crates/skate-audio/tests/dsp_oracle.rs create mode 100644 crates/skate-audio/tests/offboard_levels.rs create mode 100644 crates/skate-audio/tests/player_tricks.rs create mode 100644 crates/skate-audio/tests/rolling_banks.rs create mode 100644 crates/skate-game/src/game_audio/crossfade_groups.rs create mode 100644 crates/skate-game/src/game_audio/e2e.rs create mode 100644 crates/skate-game/src/game_audio/grain_bed.rs create mode 100644 crates/skate-game/src/game_audio/native.rs create mode 100644 crates/skate-game/src/game_audio/player_audio.rs create mode 100644 crates/skate-game/src/game_audio/random_programs.rs create mode 100644 crates/skate-game/src/game_audio/random_sets.rs create mode 100644 crates/skate-game/src/game_audio/state_log.rs create mode 100644 crates/skate-game/src/game_audio/timing.rs diff --git a/Cargo.lock b/Cargo.lock index 52561c16..20c3d1dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4840,6 +4840,10 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "skate-audio" +version = "0.1.0" + [[package]] name = "skate-core" version = "0.1.0" @@ -4878,6 +4882,7 @@ dependencies = [ "sdl3", "serde", "serde_json", + "skate-audio", "skate-core", "skate-data", "skate-dynamics", diff --git a/Cargo.toml b/Cargo.toml index e8a82a77..c9b8ba01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/skate-core", "crates/skate-data", "crates/skate-game", "crates/skate-dynamics", "crates/skate-mods", "crates/skate-net", "crates/skate-steam-relay"] +members = ["crates/skate-core", "crates/skate-audio", "crates/skate-data", "crates/skate-game", "crates/skate-dynamics", "crates/skate-mods", "crates/skate-net", "crates/skate-steam-relay"] resolver = "3" [workspace.package] diff --git a/crates/skate-audio/Cargo.toml b/crates/skate-audio/Cargo.toml new file mode 100644 index 00000000..347e24ab --- /dev/null +++ b/crates/skate-audio/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "skate-audio" +version.workspace = true +edition.workspace = true +publish.workspace = true +description = "Native port of Skate 3's AEMS patch-program evaluator and RenderWare-style voice graph (engine independent)" diff --git a/crates/skate-audio/examples/aems_list.rs b/crates/skate-audio/examples/aems_list.rs new file mode 100644 index 00000000..47bca200 --- /dev/null +++ b/crates/skate-audio/examples/aems_list.rs @@ -0,0 +1,51 @@ +//! List every bank's modules: bound class, capacity, payload words, players and the opcodes used. +//! +//! usage: aems_list [name filter] +use std::path::Path; + +use skate_audio::be::u8_at; +use skate_audio::eval::Evaluator; +use skate_audio::eval::symbols::SymRef; +use skate_audio::formats::{Bank, Project}; + +fn main() { + let args: Vec = std::env::args().collect(); + let dir = Path::new(&args[1]); + let filter = args.get(2).map(|s| s.to_ascii_lowercase()); + let mut eval = Evaluator::new(); + for name in std::fs::read_to_string(dir.join("csi_order.txt")).expect("csi_order.txt").lines() { + eval.install_project(&Project::parse(name, &std::fs::read(dir.join(name)).unwrap()).unwrap()); + } + let mut names: Vec = std::fs::read_dir(dir) + .unwrap() + .filter_map(|e| e.ok()?.file_name().into_string().ok()) + .filter(|n| n.to_ascii_lowercase().ends_with(".abk")) + .filter(|n| filter.as_ref().is_none_or(|f| n.to_ascii_lowercase().contains(f))) + .collect(); + names.sort(); + for name in names { + let bank = Bank::parse(&name, std::fs::read(dir.join(&name)).unwrap()).unwrap(); + for (i, m) in bank.modules.iter().enumerate() { + let class = bank + .exports + .iter() + .find(|e| e.handle_offset == m.offset + 4) + .and_then(|e| eval.registry.lookup(e.kind, e.project, e.name_id, &e.name)) + .and_then(|s| match s { + SymRef::Class(c) => Some(eval.registry.classes[c].name.clone()), + _ => None, + }) + .unwrap_or_else(|| "?".into()); + let payload = m.class_data_state.map(|o| u8_at(m.template(&bank.data), o as usize + 16)); + let mut ops: Vec = m.program.iter().map(|o| o.opcode).collect(); + ops.sort_unstable(); + ops.dedup(); + println!( + "{name} #{i} class={class} capacity={} payload={payload:?} players={} samples={} ops={ops:?}", + m.max_instances, + m.num_players, + bank.samples.len() + ); + } + } +} diff --git a/crates/skate-audio/examples/aems_render.rs b/crates/skate-audio/examples/aems_render.rs new file mode 100644 index 00000000..c8700c9a --- /dev/null +++ b/crates/skate-audio/examples/aems_render.rs @@ -0,0 +1,129 @@ +//! Headless end-to-end render: install the projects, load banks with their decoded WAVs, post one +//! message and render through the voice graph and the output stage's stereo fold. Prints per-second +//! RMS / peak (dBFS), voice counts and the render speed; optionally writes a 48 kHz stereo WAV. +//! +//! usage: aems_render