// Validation: re-render the listener at the file's stored position from the acceleration record // and compare with the stored signals. The front (baffled) path uses the same model and taps as // cymsimweb's built-in listener, so it should agree to ~1e-3 relative rms; the back path takes // the rim from the boundary nodes instead of a 720-point analytic ring, so the unbaffled signal // differs a little more. Usage: node tools/render-check.ts file.cymsim import { readCymsim, type H5Module } from "../src/io/cymsimFile.ts"; import { parseConfigYaml } from "../src/config/yaml.ts"; import { renderListener } from "../src/listener.ts"; // With --gpu, run the WGSL kernel under Dawn (cymsimweb's `webgpu` package, sibling checkout). if (process.argv.includes("--gpu")) { const { createRequire } = await import("node:module"); const req = createRequire(new URL("../../cymsimweb/tests/", import.meta.url)); const { create, globals } = req("webgpu"); Object.assign(globalThis, globals); Object.defineProperty(globalThis, "navigator", { value: { gpu: create([]) }, configurable: true }); } const h5: H5Module = (await import("h5wasm/node")).default as unknown as H5Module; await h5.ready; const run = readCymsim(h5, process.argv[2], (y) => { const r = parseConfigYaml(y); if (!r.config) throw new Error("bad yaml"); return r.config; }); if (!run.acceleration) throw new Error("no acceleration record"); const out = await renderListener(run, [...run.config.listener.position]); if (!out) throw new Error("render stopped"); console.log("backend:", out.backend); const rel = (a: Float32Array, b: Float32Array) => { let d = 0, r = 0; const n = Math.min(a.length, b.length); for (let i = 0; i < n; i++) { d += (a[i] - b[i]) ** 2; r += b[i] ** 2; } return Math.sqrt(d / (r || 1)); }; const unbaffled = Float32Array.from(out.front, (f, i) => f - out.back[i]); console.log("baffled relative rms vs stored:", rel(out.front, run.audioBaffled).toExponential(2)); console.log("unbaffled relative rms vs stored:", rel(unbaffled, run.audioUnbaffled).toExponential(2)); process.exit(0); // Dawn's threads keep the event loop alive