/ proof-of-concept / cymbalsim
Sign in
proof-of-concept / cymbalsim
cymbalsim / src / modal / worker.ts
73 lines · 3.1 KBCodeBlameHistory
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
3// slice of the modes for the synthesis parts of synth.ts. pool.ts drives a
4// set of these.
6import { type LinearSystem } from "../model/assemble.ts";
7import { runJob, type Job } from "./modes.ts";
8import { type ContactResult, type ModalModel, modalTail, movieFrames, nodalAccelerationTable, radiationGains } from "./synth.ts";
10export type WorkerRequest =
11 | { type: "system"; n: number; bw: number; band: Float64Array; mass: Float64Array }
12 | { type: "job"; id: number; job: Job }
13 | { type: "model"; model: ModalModel }
14 | {
15 type: "synth";
16 id: number;
17 sigma: Float64Array;
18 contact: ContactResult;
19 weight: Float64Array;
20 delay: Float64Array;
21 tStart: number;
22 len: number;
23 fs: number;
24 nSamples: number;
25 audio: boolean;
26 movie: { fps: number; nFrames: number } | null;
27 };
29export type WorkerResponse =
30 | { type: "ready" }
31 | { type: "job"; id: number; result: unknown }
32 | { type: "synth"; id: number; table: Float32Array | null; audio: Float32Array | null; frames: Float32Array | null }
33 | { type: "error"; id: number; message: string };
35let sys: LinearSystem | null = null;
36let model: ModalModel | null = null;
38self.onmessage = (ev: MessageEvent<WorkerRequest>) => {
39 const msg = ev.data;
40 try {
41 if (msg.type === "system") {
42 sys = { n: msg.n, bw: msg.bw, band: msg.band, mass: msg.mass, fields: 1, dofNode: new Int32Array(0), nodeDof: new Int32Array(0), onStand: false };
43 post({ type: "ready" });
44 } else if (msg.type === "job") {
45 if (!sys) throw new Error("no system");
46 const result = runJob(sys, msg.job);
47 const transfer: ArrayBuffer[] = result.kind === "slice" ? result.shapes.map((s) => s.buffer as ArrayBuffer) : [];
48 post({ type: "job", id: msg.id, result }, transfer);
49 } else if (msg.type === "model") {
50 model = msg.model;
51 post({ type: "ready" });
52 } else if (msg.type === "synth") {
53 if (!model) throw new Error("no model");
54 const c = msg.contact;
55 let table: Float32Array | null = null, audio: Float32Array | null = null, frames: Float32Array | null = null;
56 if (msg.audio) {
57 table = nodalAccelerationTable(model, c, msg.len);
58 const gains = radiationGains(model, msg.sigma, msg.weight, msg.delay);
59 audio = modalTail(model, msg.sigma, c, gains, msg.fs, msg.tStart, msg.nSamples);
60 }
61 if (msg.movie) frames = movieFrames(model, msg.sigma, c, msg.movie.fps, msg.movie.nFrames);
62 const transfer: ArrayBuffer[] = [];
63 for (const a of [table, audio, frames]) if (a) transfer.push(a.buffer as ArrayBuffer);
64 post({ type: "synth", id: msg.id, table, audio, frames }, transfer);
65 }
66 } catch (e) {
67 post({ type: "error", id: (msg as { id?: number }).id ?? -1, message: e instanceof Error ? e.message : String(e) });
68 }
69};
71function post(r: WorkerResponse, transfer: ArrayBuffer[] = []): void {
72 (self as unknown as Worker).postMessage(r, transfer);
↑↓ move↵openescclose