makepad/libs/game/audio/examples/vorbis_ch.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

51 lines
1.9 KiB
Rust

//! Per-channel comparison: tells swapped channels apart from a broken
//! coupling (one channel right, the other wrong).
//! Usage: vorbis_ch <file.ogg> <ref.wav>
use makepad_game_audio as audio;
fn corr(a: &[f32], b: &[f32]) -> f64 {
let n = a.len().min(b.len());
let (mut num, mut da, mut db) = (0f64, 0f64, 0f64);
for i in 0..n {
let (x, y) = (a[i] as f64, b[i] as f64);
num += x * y;
da += x * x;
db += y * y;
}
if da <= 0.0 || db <= 0.0 {
return 0.0;
}
num / (da.sqrt() * db.sqrt())
}
fn chan(p: &audio::Pcm, c: usize) -> Vec<f32> {
p.samples.iter().skip(c).step_by(p.channels).cloned().collect()
}
fn main() {
let a: Vec<String> = std::env::args().collect();
let got = audio::decode(&std::fs::read(&a[1]).expect("ogg")).expect("decode");
let want = audio::wav::decode(&std::fs::read(&a[2]).expect("ref")).expect("ref");
println!("ch={} got={} ref={}", got.channels, got.frames(), want.frames());
if got.channels < 2 {
println!("mono: corr {:.4}", corr(&got.samples, &want.samples));
return;
}
let (gl, gr) = (chan(&got, 0), chan(&got, 1));
let (wl, wr) = (chan(&want, 0), chan(&want, 1));
println!(" L->L {:.4} L->R {:.4}", corr(&gl, &wl), corr(&gl, &wr));
println!(" R->R {:.4} R->L {:.4}", corr(&gr, &wr), corr(&gr, &wl));
// Mid/side view: coupling errors usually leave the mid intact and wreck
// the side, which is invisible in a plain per-channel correlation.
let mid = |l: &[f32], r: &[f32]| -> Vec<f32> {
l.iter().zip(r).map(|(a, b)| (a + b) * 0.5).collect()
};
let side = |l: &[f32], r: &[f32]| -> Vec<f32> {
l.iter().zip(r).map(|(a, b)| (a - b) * 0.5).collect()
};
println!(
" mid {:.4} side {:.4}",
corr(&mid(&gl, &gr), &mid(&wl, &wr)),
corr(&side(&gl, &gr), &side(&wl, &wr))
);
}