makepad/libs/game/audio/examples/vorbis_exact.rs
andodeki 435135ed50 feat(arcade): port makepad game libs + arcade from makepad-internal onto the fork
Reintroduce apps/arcade and the ten game crates that were ripped out of
the fork (assets, audio, blocks, coedit, gen, net, pkg, render, script,
session), from makepad-internal/dev. Reconcile against the fork's layout:

- makepad-game-sim/math come from the fork's existing libs/sim + sim/math;
  the copied internal libs/game/{sim,math} are dropped and the kept game
  crates plus arcade point at ../../sim and ../../sim/math.
- arcade AI is rewritten off the removed makepad-ai agent API onto the
  makepad-ai-hub headless ChatProvider worker (example/cad pattern):
  AiWorker{send,cancel,poll}, ai_worker_loop driving
  ClaudeApiChatProvider(ClaudeCli), AiWorkerEvent availability/delta/
  done/error mapped into the chat feed and authoring land_edit, with
  main.rs send_message/cancel_request/event-drain rebuilt around it.
- the game-script sandbox is adapted to the fork's jailed splash storage:
  every isolate gets mod.fs = splash_storage jail; splaes splash_storage
  set_root_for_heap is now public so the ScriptHost (or Splash) can grant
  a per-game jail root. ENT font: build.rs/dispatch.rs entities fill new
  fork sim Entity/Part fields via ..Default::default(); world_raycast now
  borrows mutably and returns the material id.

cargo check -p makepad-arcade and the game + arcade test suites pass.
2026-09-05 05:49:12 +03:00

77 lines
2.8 KiB
Rust

//! Is a mismatch a TRIM bug or a CONTENT bug?
//!
//! Correlation cannot tell those apart: a transient decoded correctly but
//! offset scores badly, and a transient decoded wrongly can still score 0.8 on
//! envelope shape alone. This sweeps whole-frame lags looking for an EXACT
//! sample match instead. A lag where the overlap matches bit-for-bit means the
//! decode is right and only the trim is wrong; no such lag anywhere means the
//! samples themselves differ and the trim is a red herring.
//!
//! Usage: vorbis_exact <file.ogg> <reference.wav> [max_lag_frames]
use makepad_game_audio as audio;
fn main() {
let args: Vec<String> = std::env::args().collect();
let ogg = std::fs::read(&args[1]).expect("read ogg");
let refwav = std::fs::read(&args[2]).expect("read ref");
let max_lag: i64 = args.get(3).and_then(|s| s.parse().ok()).unwrap_or(2048);
let got = audio::decode(&ogg).expect("decode ogg");
let want = audio::wav::decode(&refwav).expect("decode ref");
let ch = got.channels as usize;
println!(
"got {}ch {} frames | ref {}ch {} frames | delta {}",
got.channels,
got.frames(),
want.channels,
want.frames(),
got.frames() as i64 - want.frames() as i64
);
let g = &got.samples;
let w = &want.samples;
// afconvert writes 16-bit, so our f32 can only ever match to quantisation.
// Compare at that resolution or every sample "differs" for no useful reason.
let q = |x: f32| (x * 32768.0).round() as i32;
let mut best = (0i64, 0usize, 0usize, f64::INFINITY);
for lag in -max_lag..=max_lag {
let off = lag * ch as i64;
let (gs, ws) = if off >= 0 {
(off as usize, 0usize)
} else {
(0usize, (-off) as usize)
};
if gs >= g.len() || ws >= w.len() {
continue;
}
let n = (g.len() - gs).min(w.len() - ws);
if n < ch * 256 {
continue;
}
let mut exact = 0usize;
let mut sad = 0f64;
for i in 0..n {
let (a, b) = (g[gs + i], w[ws + i]);
if q(a) == q(b) {
exact += 1;
}
sad += (a - b).abs() as f64;
}
let mean_abs = sad / n as f64;
if exact > best.1 || (exact == best.1 && mean_abs < best.3) {
best = (lag, exact, n, mean_abs);
}
}
let (lag, exact, n, mean_abs) = best;
println!(
"best lag {lag} frames: {exact}/{n} samples exact ({:.2}%), mean|diff| {mean_abs:.6}",
100.0 * exact as f64 / n as f64
);
if exact * 100 / n.max(1) >= 99 {
println!("VERDICT: decode is correct, trim is off by {lag} frames");
} else {
println!("VERDICT: content differs — trim alone cannot explain this");
}
}