/ proof-of-concept / bead-percussion
Sign in
proof-of-concept / bead-percussion
bead-percussion / tests / nonlin.ts
21 lines · 1.9 KBBlameHistoryRaw
1// How nonlinear is a run? Compare the listener signal with the same strike
2// scaled down by 1000 (then scaled back up). Usage: tsx tests/nonlin.ts script.m [duration] [v1 v2 ...]
3import { readFileSync, readdirSync } from "fs";
4import { runScript } from "../src/sim/runScript";
5import { simulate } from "../src/sim/solver";
6import { relDiff, spectrum } from "../src/sim/analysis";
7const helpers = readdirSync("matlab/helpers").map(name => ({ name, source: readFileSync("matlab/helpers/" + name, "utf8") }));
8const { sys } = runScript(readFileSync(process.argv[2], "utf8"), helpers, () => {});
9sys.duration = Number(process.argv[3] ?? 0.3);
10const scales = process.argv.slice(4).map(Number);
11for (const sc of scales.length ? scales : [1]) {
12 const hits = sys.hits.map(h => ({ ...h, velocity: h.velocity.map(v => v * sc) as [number, number, number], force: h.force.map(v => v * sc) as [number, number, number] }));
13 const big = simulate({ ...sys, hits });
14 const small = simulate({ ...sys, hits: hits.map(h => ({ ...h, velocity: h.velocity.map(v => v * 1e-3) as [number, number, number], force: h.force.map(v => v * 1e-3) as [number, number, number] })) });
15 const lin = small.audio.map(v => v * 1e3);
16 const fs = big.sampleRate;
17 const win = [0.01, 0.03, 0.1, 0.3].filter(t => t <= sys.duration).map(t => `${t}s:${relDiff(lin, big.audio, Math.round(t * fs)).toFixed(3)}`);
18 // spectral centroid, first 50 ms
19 const cen = (x: Float32Array) => { const s = spectrum(x, 0, Math.round(0.05 * fs)); let a = 0, b = 0; s.db.forEach((d, i) => { const p = 10 ** (d / 10); a += p * i * s.df * fs; b += p; }); return a / b; };
20 console.log(`scale ${sc}: dt ${big.dt.toExponential(2)} drift ${big.energyDrift.toExponential(1)} ${big.aborted ?? ""} | nonlin diff ${win.join(" ")} | centroid lin ${cen(lin).toFixed(0)} Hz, actual ${cen(big.audio).toFixed(0)} Hz | ${(big.elapsedMs/1000).toFixed(1)}s`);
↑↓ move↵openescclose