5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 1// The Web Worker of the linear model. A worker holds the linear system for
2// the eigenvalue jobs of modes.ts (inertia counts and slices) and, later, a
2a1a6daDerive the losses from physical constants instead of a prescribed decay curveJeremy Magland 3// slice of the modes for the per-mode losses of damping.ts and the synthesis
4// parts of synth.ts. pool.ts drives a set of these.
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 5
6import { type LinearSystem } from "../model/assemble.ts";
7import { runJob, type Job } from "./modes.ts";
2a1a6daDerive the losses from physical constants instead of a prescribed decay curveJeremy Magland 8import { type DampingPlate, radiationRates, thermoelasticRates } from "./damping.ts";
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 9import { type ContactResult, type ModalModel, modalTail, movieFrames, nodalAccelerationTable, radiationGains } from "./synth.ts";
11export type WorkerRequest =
12 | { type: "system"; n: number; bw: number; band: Float64Array; mass: Float64Array }
13 | { type: "job"; id: number; job: Job }
2a1a6daDerive the losses from physical constants instead of a prescribed decay curveJeremy Magland 14 | { type: "model"; model: ModalModel; plate: DampingPlate | null }
15 | { type: "rates"; id: number }
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 16 | {
17 type: "synth";
18 id: number;
19 sigma: Float64Array;
20 contact: ContactResult;
21 weight: Float64Array;
22 delay: Float64Array;
23 tStart: number;
24 len: number;
25 fs: number;
26 nSamples: number;
27 audio: boolean;
28 movie: { fps: number; nFrames: number } | null;
29 };
31export type WorkerResponse =
32 | { type: "ready" }
33 | { type: "job"; id: number; result: unknown }
2a1a6daDerive the losses from physical constants instead of a prescribed decay curveJeremy Magland 34 | { type: "rates"; id: number; rad: Float64Array; te: Float64Array }
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 35 | { type: "synth"; id: number; table: Float32Array | null; audio: Float32Array | null; frames: Float32Array | null }
36 | { type: "error"; id: number; message: string };
38let sys: LinearSystem | null = null;
39let model: ModalModel | null = null;
2a1a6daDerive the losses from physical constants instead of a prescribed decay curveJeremy Magland 40let plate: DampingPlate | null = null;
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 41
42self.onmessage = (ev: MessageEvent<WorkerRequest>) => {
43 const msg = ev.data;
44 try {
45 if (msg.type === "system") {
2481a63Remove the standJeremy Magland 46 sys = { n: msg.n, bw: msg.bw, band: msg.band, mass: msg.mass, fields: 1, dofNode: new Int32Array(0), nodeDof: new Int32Array(0) };
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 47 post({ type: "ready" });
48 } else if (msg.type === "job") {
49 if (!sys) throw new Error("no system");
50 const result = runJob(sys, msg.job);
51 const transfer: ArrayBuffer[] = result.kind === "slice" ? result.shapes.map((s) => s.buffer as ArrayBuffer) : [];
52 post({ type: "job", id: msg.id, result }, transfer);
53 } else if (msg.type === "model") {
54 model = msg.model;
2a1a6daDerive the losses from physical constants instead of a prescribed decay curveJeremy Magland 55 plate = msg.plate;
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 56 post({ type: "ready" });
2a1a6daDerive the losses from physical constants instead of a prescribed decay curveJeremy Magland 57 } else if (msg.type === "rates") {
58 if (!model || !plate) throw new Error("no model or plate");
59 const rad = radiationRates(model, plate, 0, model.count);
60 const te = thermoelasticRates(model, plate, 0, model.count);
61 post({ type: "rates", id: msg.id, rad, te }, [rad.buffer as ArrayBuffer, te.buffer as ArrayBuffer]);
5778036cymbalsim: scripted plate and cymbal designer with a modal linear model and a von Karman GPU modelJeremy Magland 62 } else if (msg.type === "synth") {
63 if (!model) throw new Error("no model");
64 const c = msg.contact;
65 let table: Float32Array | null = null, audio: Float32Array | null = null, frames: Float32Array | null = null;
66 if (msg.audio) {
67 table = nodalAccelerationTable(model, c, msg.len);
68 const gains = radiationGains(model, msg.sigma, msg.weight, msg.delay);
69 audio = modalTail(model, msg.sigma, c, gains, msg.fs, msg.tStart, msg.nSamples);
70 }
71 if (msg.movie) frames = movieFrames(model, msg.sigma, c, msg.movie.fps, msg.movie.nFrames);
72 const transfer: ArrayBuffer[] = [];
73 for (const a of [table, audio, frames]) if (a) transfer.push(a.buffer as ArrayBuffer);
74 post({ type: "synth", id: msg.id, table, audio, frames }, transfer);
75 }
76 } catch (e) {
77 post({ type: "error", id: (msg as { id?: number }).id ?? -1, message: e instanceof Error ? e.message : String(e) });
78 }
79};
81function post(r: WorkerResponse, transfer: ArrayBuffer[] = []): void {
82 (self as unknown as Worker).postMessage(r, transfer);
83}