// Web Worker entry: the two computations that are too slow for the main thread. "modes" is the // full eigensolve (seconds to minutes); "gains" is the listener sum, redone whenever the // listener or the material changes (a fraction of a second). import type { Listener, SimulationConfig } from "../config/schema.ts"; import { parseModes, serializeModes, type ModesData } from "../modes.ts"; import { computeGains } from "./gains.ts"; import { computeModes, type PrecomputeOptions } from "./pipeline.ts"; export type PrecomputeRequest = | { type: "modes"; token: number; config: SimulationConfig; options: PrecomputeOptions } | { type: "gains"; token: number; buffer?: ArrayBuffer; listener: Listener; freq: Float64Array; phiFactor: number }; export type PrecomputeReply = | { type: "progress"; token: number; phase: string; frac: number } | { type: "modes"; token: number; buffer: ArrayBuffer; seconds: number } | { type: "gains"; token: number; front: Float32Array; back: Float32Array; seconds: number } | { type: "error"; token: number; message: string }; /** The worker keeps the modes it was last given so that repeated gain requests, which happen * while a listener slider is being dragged, do not re-send a 12 MB buffer every time. */ let held: { buffer: ArrayBuffer; data: ModesData } | null = null; self.onmessage = (ev: MessageEvent) => { const msg = ev.data; const post = (reply: PrecomputeReply, transfer?: Transferable[]) => (self as unknown as Worker).postMessage(reply, transfer ?? []); const t0 = performance.now(); try { if (msg.type === "modes") { let lastPost = 0; const data = computeModes(msg.config, msg.options, (phase, frac) => { const t = performance.now(); if (t - lastPost > 100 || frac >= 1) { lastPost = t; post({ type: "progress", token: msg.token, phase, frac }); } }); const buffer = serializeModes(data); held = { buffer, data }; post({ type: "modes", token: msg.token, buffer: buffer.slice(0), seconds: (performance.now() - t0) / 1000 }); } else { if (msg.buffer) held = { buffer: msg.buffer, data: parseModes(msg.buffer) }; if (!held) throw new Error("no modes in the worker: send the buffer with the first gains request"); const g = computeGains(held.data, msg.listener, msg.freq, msg.phiFactor); post({ type: "gains", token: msg.token, front: g.front, back: g.back, seconds: (performance.now() - t0) / 1000 }, [g.front.buffer, g.back.buffer]); } } catch (err) { post({ type: "error", token: msg.token, message: (err as Error).message ?? String(err) }); } };