2 "instrawave": 1,
3 "name": "chime",
4 "description": "A stiff bar in open air, struck. No tension, so the stiffness sets the partials at n²·f1, which is the bell-like sound.",
5 "params": [
6 {
7 "key": "nx",
8 "label": "air cells along x",
9 "unit": "1",
10 "value": 96,
11 "min": 32,
12 "max": 256,
13 "class": "accuracy",
14 "hint": "The air grid; ny = nz = nx/2. The simulation is honest to about c/(8h). Must be even.",
15 "step": 2
16 },
17 {
18 "key": "Lx",
19 "label": "domain length",
20 "unit": "m",
21 "value": 1,
22 "min": 0.6,
23 "max": 2,
24 "class": "geometry",
25 "hint": "The air box around the instrument, twice as long in x as in y and z."
26 },
27 {
28 "key": "ir_seconds",
29 "label": "room response length",
30 "unit": "s",
31 "value": 0.5,
32 "min": 0.1,
33 "max": 2,
34 "class": "accuracy",
35 "hint": "How long the recorded room responses run; a room still ringing past this is truncated."
36 },
37 {
38 "key": "L1",
39 "label": "length",
40 "unit": "m",
41 "value": 0.3,
42 "min": 0.15,
43 "max": 0.5,
44 "class": "geometry"
45 },
46 {
47 "key": "oy",
48 "label": "across",
49 "unit": "m",
50 "value": 0,
51 "min": -0.15,
52 "max": 0.15,
53 "class": "geometry"
54 },
55 {
56 "key": "oz",
57 "label": "height",
58 "unit": "m",
59 "value": 0.05,
60 "min": 0.005,
61 "max": 0.15,
62 "class": "geometry"
63 },
64 {
65 "key": "mic_x",
66 "label": "microphone x",
67 "unit": "m",
68 "value": 0.12,
69 "min": -0.45,
70 "max": 0.45,
71 "class": "geometry"
72 },
73 {
74 "key": "mic_y",
75 "label": "microphone y",
76 "unit": "m",
77 "value": 0.08,
78 "min": -0.2,
79 "max": 0.2,
80 "class": "geometry"
81 },
82 {
83 "key": "mic_z",
84 "label": "microphone z",
85 "unit": "m",
86 "value": 0.1,
87 "min": -0.2,
88 "max": 0.2,
89 "class": "geometry"
90 },
91 {
92 "key": "f1",
93 "label": "pitch",
94 "unit": "Hz",
95 "value": 780,
96 "min": 200,
97 "max": 2000,
98 "class": "tuning",
99 "hint": "The first partial; higher ones land at n^2 f1."
100 },
101 {
102 "key": "t60",
103 "label": "decay t60",
104 "unit": "s",
105 "value": 6,
106 "min": 0.5,
107 "max": 12,
108 "class": "tuning"
109 },
110 {
111 "key": "sig1",
112 "label": "brightness decay",
113 "unit": "m^2/s",
114 "value": 0.001,
115 "min": 0,
116 "max": 0.02,
117 "class": "tuning"
118 },
119 {
120 "key": "strikepos",
121 "label": "strike position",
122 "unit": "1",
123 "value": 0.3,
124 "min": 0.1,
125 "max": 0.9,
126 "class": "tuning"
127 },
128 {
129 "key": "striker",
130 "label": "mallet width",
131 "unit": "m",
132 "value": 0.025,
133 "min": 0.01,
134 "max": 0.08,
135 "class": "tuning"
136 },
137 {
138 "key": "vel",
139 "label": "strike speed",
140 "unit": "m/s",
141 "value": 1,
142 "min": 0.1,
143 "max": 5,
144 "class": "tuning"
145 },
146 {
147 "key": "gair",
148 "label": "radiation gain",
149 "unit": "1",
150 "value": 1,
151 "min": 0,
152 "max": 2,
153 "class": "tuning"
154 }
155 ],
156 "derived": {
157 "c0": "343",
158 "cfl": "0.5",
159 "ny": "nx / 2",
160 "nz": "nx / 2",
161 "npts": "nx * ny * nz",
162 "h": "Lx / nx",
163 "Ly": "ny * h",
164 "Lz": "nz * h",
165 "dt": "cfl * h / (sqrt(3) * c0)",
166 "ir_steps": "max(1, round(ir_seconds / dt))",
167 "mic_index": "clamp(round((mic_x + Lx/2) / h - 0.5), 0, nx - 1) + nx * (clamp(round((mic_y + Ly/2) / h - 0.5), 0, ny - 1) + ny * clamp(round((mic_z + Lz/2) / h - 0.5), 0, nz - 1))",
168 "kap_worst": "2 * f1__max * (L1 * L1) / pi",
169 "kap2_worst": "kap_worst * kap_worst",
170 "cs_worst": "0",
171 "a_worst": "cs_worst*cs_worst*dt*dt + 4*sig1__max*dt",
172 "h_min": "sqrt((a_worst + sqrt(a_worst*a_worst + 16*kap2_worst*dt*dt)) / 2) / 0.95",
173 "n1": "max(5, min(1024, floor(L1 / h_min) + 1))",
174 "h1": "L1 / (n1 - 1)",
175 "mx": "n1 - 2",
176 "n_rows": "mx"
177 },
178 "shaders": {
179 "medium": {
180 "code": "// The medium: the body's rigid mask, the absorption, and where the object\n// couples into the air. One thread per air cell.\nconst PI = 3.14159265358979;\noverride npts: u32;\noverride nx: u32;\noverride ny: u32;\noverride h: f32;\noverride Lx: f32;\noverride Ly: f32;\noverride Lz: f32;\noverride oy: f32;\noverride oz: f32;\noverride L1: f32;\n@group(0) @binding(0) var<storage, read_write> wall: array<f32>;\n@group(0) @binding(1) var<storage, read_write> sig: array<f32>;\n@group(0) @binding(2) var<storage, read_write> prof: array<f32>;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n if (i >= npts) { return; }\n let ix = i % nx;\n let iy = (i / nx) % ny;\n let iz = i / (nx * ny);\n let x = -Lx/2.0 + (f32(ix) + 0.5) * h;\n let y = -Ly/2.0 + (f32(iy) + 0.5) * h;\n let z = -Lz/2.0 + (f32(iz) + 0.5) * h;\n let sw = 0.45 * h;\n let shell = 0.0;\n let w = 1.0 - shell;\n wall[i] = w;\n // Open boundary at the domain edge (a quadratic absorbing ramp over the\n // outer 0.15 Ly of each face), plus the body's lossy surface skin.\n let spw = 0.15 * Ly;\n let dx = max(0.0, spw - (Lx/2.0 - abs(x))) / spw;\n let dy = max(0.0, spw - (Ly/2.0 - abs(y))) / spw;\n let dz = max(0.0, spw - (Lz/2.0 - abs(z))) / spw;\n let d = max(max(dx, dy), dz);\n sig[i] = 9000.0 * d * d;\n // The object radiates from its line: a tube two cells wide around\n // (y = oy, z = oz), fading at its ends, cross-section normalized to one.\n let aw = 2.0 * h;\n let tube = exp(-((y - oy)*(y - oy) + (z - oz)*(z - oz)) / (aw*aw)) / (PI * aw * aw);\n let span = 0.5 * (1.0 + tanh((L1/2.0 - abs(x)) / (1.5*h)));\n prof[i] = tube * span * w;\n\n}\n",
181 "constants": [
182 "npts",
183 "nx",
184 "ny",
185 "h",
186 "Lx",
187 "Ly",
188 "Lz",
189 "oy",
190 "oz",
191 "L1"
192 ]
193 },
194 "sines_x": {
195 "code": "// Sine mode m of the object grid, interpolated to air\n// x centers: sx[(mode-1) * nx + ix]. One thread per table entry.\nconst PI = 3.14159265358979;\noverride n1: u32;\noverride mx: u32;\noverride nx: u32;\noverride L1: f32;\noverride h1: f32;\noverride Lx: f32;\noverride h: f32;\n@group(0) @binding(0) var<storage, read_write> table: array<f32>;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n if (i >= mx * nx) { return; }\n let mi = i % mx;\n let ia = i / mx;\n let xw = -Lx/2.0 + (f32(ia) + 0.5) * h;\n let u = (xw + L1/2.0) / h1;\n let j = i32(floor(u));\n var v = 0.0;\n if (j >= 0 && j < i32(n1) - 1) {\n let f = u - f32(j);\n let mm = f32(mi + 1u);\n let s0 = sin(mm * PI * f32(j) / f32(n1 - 1u));\n let s1 = sin(mm * PI * f32(j + 1) / f32(n1 - 1u));\n v = (1.0 - f) * s0 + f * s1;\n }\n table[mi * nx + ia] = v;\n}\n",
196 "constants": [
197 "n1",
198 "mx",
199 "nx",
200 "L1",
201 "h1",
202 "Lx",
203 "h"
204 ]
205 },
206 "impulse": {
207 "code": "// The reciprocal kick: one unit impulse at the microphone cell, entering\n// the way a source enters the first step. One thread.\noverride mic_index: u32;\noverride dt: f32;\n@group(0) @binding(0) var<storage, read_write> p: array<f32>;\n@group(0) @binding(1) var<storage, read> wall: array<f32>;\n@group(0) @binding(2) var<storage, read> sig: array<f32>;\n@compute @workgroup_size(1)\nfn main() {\n p[mic_index] = dt * dt * wall[mic_index] / (1.0 + sig[mic_index] * dt);\n}\n",
208 "constants": [
209 "mic_index",
210 "dt"
211 ]
212 },
213 "step": {
214 "code": "// One leapfrog step of the air: the wall-masked Laplacian (each stencil\n// face scaled by the mask at the neighbour it reads, the rigid condition in\n// divergence form) fused with the update. One thread per air cell.\noverride npts: u32;\noverride nx: u32;\noverride ny: u32;\noverride nz: u32;\noverride h: f32;\noverride dt: f32;\noverride c0: f32;\n@group(0) @binding(0) var<storage, read> p: array<f32>;\n@group(0) @binding(1) var<storage, read> pm: array<f32>;\n@group(0) @binding(2) var<storage, read_write> pn: array<f32>;\n@group(0) @binding(3) var<storage, read> wall: array<f32>;\n@group(0) @binding(4) var<storage, read> sig: array<f32>;\nfn face(ix: i32, iy: i32, iz: i32, pc: f32) -> f32 {\n if (ix < 0 || ix >= i32(nx) || iy < 0 || iy >= i32(ny) || iz < 0 || iz >= i32(nz)) {\n return -pc; // outside the domain: p = 0, open\n }\n let k = u32(ix) + nx * (u32(iy) + ny * u32(iz));\n return wall[k] * (p[k] - pc);\n}\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n if (i >= npts) { return; }\n let ix = i32(i % nx);\n let iy = i32((i / nx) % ny);\n let iz = i32(i / (nx * ny));\n let pc = p[i];\n let s = face(ix - 1, iy, iz, pc) + face(ix + 1, iy, iz, pc)\n + face(ix, iy - 1, iz, pc) + face(ix, iy + 1, iz, pc)\n + face(ix, iy, iz - 1, pc) + face(ix, iy, iz + 1, pc);\n let lp = wall[i] * s / (h * h);\n let sd = sig[i] * dt;\n pn[i] = wall[i] * ((2.0 * pc - (1.0 - sd) * pm[i] + (c0 * dt) * (c0 * dt) * lp) / (1.0 + sd));\n}\n",
215 "constants": [
216 "npts",
217 "nx",
218 "ny",
219 "nz",
220 "h",
221 "dt",
222 "c0"
223 ]
224 },
225 "plane": {
226 "code": "// Contract the field against the radiation profile over z:\n// one thread per air column.\noverride nx: u32;\noverride ny: u32;\noverride nz: u32;\n@group(0) @binding(0) var<storage, read_write> plane: array<f32>;\n@group(0) @binding(1) var<storage, read> p: array<f32>;\n@group(0) @binding(2) var<storage, read> prof: array<f32>;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n if (i >= nx * ny) { return; }\n var s = 0.0;\n for (var iz = 0u; iz < nz; iz++) {\n let k = i + nx * ny * iz;\n s += prof[k] * p[k];\n }\n plane[i] = s;\n}\n",
227 "constants": [
228 "nx",
229 "ny",
230 "nz"
231 ]
232 },
233 "xc": {
234 "code": "// Contract the plane against every x sine row: tmp[m + mx * iy].\noverride nx: u32;\noverride ny: u32;\noverride mx: u32;\n@group(0) @binding(0) var<storage, read_write> tmp: array<f32>;\n@group(0) @binding(1) var<storage, read> plane: array<f32>;\n@group(0) @binding(2) var<storage, read> sx: array<f32>;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n if (i >= mx * ny) { return; }\n let m = i % mx;\n let iy = i / mx;\n var s = 0.0;\n for (var ix = 0u; ix < nx; ix++) {\n s += sx[m * nx + ix] * plane[ix + nx * iy];\n }\n tmp[i] = s;\n}\n",
235 "constants": [
236 "nx",
237 "ny",
238 "mx"
239 ]
240 },
241 "proj": {
242 "code": "// A line object's y factor is one everywhere: finish by summing tmp over\n// iy and write this step's sample of every kernel row.\noverride ny: u32;\noverride mx: u32;\noverride ir_steps: u32;\n@group(0) @binding(0) var<storage, read_write> kernels: array<f32>;\n@group(0) @binding(1) var<storage, read> tmp: array<f32>;\n@group(0) @binding(2) var<storage, read> tick: array<u32>;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n if (i >= mx) { return; }\n var s = 0.0;\n for (var iy = 0u; iy < ny; iy++) {\n s += tmp[i + mx * iy];\n }\n let t = tick[0];\n if (t < ir_steps) {\n kernels[i * ir_steps + t] = s;\n }\n}\n",
243 "constants": [
244 "ny",
245 "mx",
246 "ir_steps"
247 ]
248 },
249 "excite_x": {
250 "code": "// Project this axis's excitation profile onto each recorded sine mode:\n// bx[e * mx + (mode-1)]. One thread per (event, mode).\nconst PI = 3.14159265358979;\noverride n1: u32;\noverride mx: u32;\noverride ev_stride: u32;\noverride h1: f32;\noverride L1: f32;\n@group(0) @binding(0) var<storage, read_write> bout: array<f32>;\n@group(0) @binding(1) var<storage, read> ev: array<f32>;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n if (i >= mx * arrayLength(&ev) / ev_stride) { return; }\n let mi = i % mx;\n let e = i / mx;\n let base = e * ev_stride;\n let strikepos = ev[base + 4u];\n let striker = ev[base + 5u];\n let mm = f32(mi + 1u);\n var s = 0.0;\n for (var k = 1u; k < n1 - 1u; k++) {\n let xi = f32(k) * h1;\n let dx = (xi - strikepos * L1) / striker;\n let u0 = exp(-dx * dx);\n s += sin(mm * PI * f32(k) / f32(n1 - 1u)) * u0;\n }\n bout[i] = s;\n}\n",
251 "constants": [
252 "n1",
253 "mx",
254 "ev_stride",
255 "h1",
256 "L1"
257 ]
258 },
259 "slots": {
260 "code": "// One thread per (event, mode) of the bar: the tension-free stiff scheme,\n// partials at n^2 f1 up to the grid's own dispersion.\nconst PI = 3.14159265358979;\noverride mx: u32;\noverride n1: u32;\noverride ev_stride: u32;\noverride h1: f32;\noverride L1: f32;\noverride dt: f32;\n@group(0) @binding(0) var<storage, read_write> slots: array<f32>;\n@group(0) @binding(1) var<storage, read> ev: array<f32>;\n@group(0) @binding(2) var<storage, read> bx: array<f32>;\n\nfn cmul(ar: f32, ai: f32, br: f32, bi: f32) -> vec2f {\n return vec2f(ar * br - ai * bi, ar * bi + ai * br);\n}\n// c = a * gain * (z - 2 + 1/z) = a * gain * (z-1)^2 / z, the acceleration\n// series, with z-1 supplied stably (the naive z - 1 cancels).\nfn acc_scale(p: vec4f, zm1Re: f32, gain: f32) -> vec4f {\n let m2 = p.z * p.z + p.w * p.w;\n if (m2 == 0.0) { return vec4f(0.0); }\n let sq = cmul(zm1Re, p.w, zm1Re, p.w);\n let num = cmul(sq.x, sq.y, p.z, -p.w);\n let c = cmul(p.x, p.y, gain * num.x / m2, gain * num.y / m2);\n return vec4f(c.x, c.y, p.z, p.w);\n}\n// c = a * gain * z, the next-displacement series (what a string's bridge\n// slope is read from).\nfn next_scale(p: vec4f, gain: f32) -> vec4f {\n let c = cmul(p.x, p.y, gain * p.z, gain * p.w);\n return vec4f(c.x, c.y, p.z, p.w);\n}\n\n\nfn write_slot(idx: u32, p: vec4f, row: f32, start: f32) {\n slots[idx * 6u] = p.x;\n slots[idx * 6u + 1u] = p.y;\n slots[idx * 6u + 2u] = p.z;\n slots[idx * 6u + 3u] = p.w;\n slots[idx * 6u + 4u] = row;\n slots[idx * 6u + 5u] = start;\n}\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n let n_events = arrayLength(&ev) / ev_stride;\n if (i >= n_events * mx) { return; }\n let e = i / mx;\n let mi = i % mx;\n let base = e * ev_stride;\n let start = ev[base];\n let f1 = ev[base + 1u];\n let t60 = ev[base + 2u];\n let sig1 = ev[base + 3u];\n let vel = ev[base + 6u];\n let gair = ev[base + 7u];\n let m = f32(mi + 1u);\n let b = bx[i] * (-(dt * vel)) * 2.0 / f32(n1 - 1u);\n let s1 = sin(m * PI / (2.0 * f32(n1 - 1u)));\n let lam = (4.0 / (h1 * h1)) * s1 * s1;\n let kap = 2.0 * f1 * (L1 * L1) / PI;\n let w2 = kap * kap * lam * lam;\n\n let d = (6.91 / t60) * dt;\n let opd = 1.0 + d;\n let g = d + 2.0 * sig1 * dt * lam;\n let x = dt * dt * w2 + 2.0 * sig1 * dt * lam;\n let zRe = (1.0 - 0.5 * x) / opd;\n let zm1Re = -(0.5 * x + d) / opd;\n let nn = dt * dt * w2 - g * d - 0.25 * x * x;\n var p0 = vec4f(0.0);\n var p1 = vec4f(0.0);\n var zm10 = 0.0;\n var zm11 = 0.0;\n if (nn > 0.0) {\n let zIm = sqrt(nn) / opd;\n let mag2 = (1.0 - g) / opd;\n let aRe = 0.0;\n let aIm = b * mag2 / zIm;\n p0 = vec4f(aRe, aIm, zRe, zIm);\n zm10 = zm1Re;\n } else {\n let sq = sqrt(-nn) / opd;\n let r1 = zRe + sq;\n let r2 = zRe - sq;\n if (r1 != r2 && r1 != 0.0 && r2 != 0.0) {\n let det = 1.0 / r1 - 1.0 / r2;\n let a1 = b / det;\n let a2 = -a1;\n p0 = vec4f(a1, 0.0, r1, 0.0);\n p1 = vec4f(a2, 0.0, r2, 0.0);\n zm10 = zm1Re + sq;\n zm11 = zm1Re - sq;\n }\n }\n let ga = gair / (dt * dt);\n write_slot(i * 2u, acc_scale(p0, zm10, ga), f32(mi), start);\n write_slot(i * 2u + 1u, acc_scale(p1, zm11, ga), f32(mi), start);\n}\n",
261 "constants": [
262 "mx",
263 "n1",
264 "ev_stride",
265 "h1",
266 "L1",
267 "dt"
268 ]
269 },
270 "synth": {
271 "code": "// One thread per slot: a complex one-pole over this tile of samples, the\n// kernel row as input while it lasts and silence after (the closed-form\n// tail). State persists across tiles.\noverride tile: u32;\noverride total_steps: u32;\noverride ir_steps: u32;\noverride n_slots: u32;\n@group(0) @binding(0) var<storage, read> slots: array<f32>;\n@group(0) @binding(1) var<storage, read_write> state: array<f32>;\n@group(0) @binding(2) var<storage, read> kernels: array<f32>;\n@group(0) @binding(3) var<storage, read_write> partial: array<f32>;\n@group(0) @binding(4) var<storage, read> tick: array<u32>;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n if (i >= n_slots) { return; }\n let cRe = slots[i * 6u];\n let cIm = slots[i * 6u + 1u];\n let zRe = slots[i * 6u + 2u];\n let zIm = slots[i * 6u + 3u];\n let row = u32(slots[i * 6u + 4u]);\n let start = u32(slots[i * 6u + 5u]);\n var wRe = state[i * 2u];\n var wIm = state[i * 2u + 1u];\n let t0 = tick[0] * tile;\n for (var s = 0u; s < tile; s++) {\n let t = t0 + s;\n if (t >= total_steps) { break; }\n var hv = 0.0;\n if (t >= start && t - start < ir_steps) {\n hv = kernels[row * ir_steps + (t - start)];\n }\n let nRe = zRe * wRe - zIm * wIm + hv;\n let nIm = zRe * wIm + zIm * wRe;\n wRe = nRe;\n wIm = nIm;\n partial[i * tile + s] = cRe * wRe - cIm * wIm;\n }\n state[i * 2u] = wRe;\n state[i * 2u + 1u] = wIm;\n}\n",
272 "constants": [
273 "tile",
274 "total_steps",
275 "ir_steps",
276 "n_slots"
277 ]
278 },
279 "reduce": {
280 "code": "// One thread per sample of the tile: sum every slot's contribution.\noverride tile: u32;\noverride total_steps: u32;\noverride n_slots: u32;\n@group(0) @binding(0) var<storage, read> partial: array<f32>;\n@group(0) @binding(1) var<storage, read_write> trace: array<f32>;\n@group(0) @binding(2) var<storage, read> tick: array<u32>;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x + gid.y * 4194240u;\n if (i >= tile) { return; }\n let t = tick[0] * tile + i;\n if (t >= total_steps) { return; }\n var s = 0.0;\n for (var q = 0u; q < n_slots; q++) {\n s += partial[q * tile + i];\n }\n trace[t] = s;\n}\n",
281 "constants": [
282 "tile",
283 "total_steps",
284 "n_slots"
285 ]
286 }
287 },
288 "precompute": {
289 "buffers": [
290 {
291 "name": "wall",
292 "count": "npts"
293 },
294 {
295 "name": "sig",
296 "count": "npts"
297 },
298 {
299 "name": "prof",
300 "count": "npts"
301 },
302 {
303 "name": "sx",
304 "count": "mx * nx"
305 },
306 {
307 "name": "p",
308 "count": "npts"
309 },
310 {
311 "name": "pm",
312 "count": "npts"
313 },
314 {
315 "name": "pn",
316 "count": "npts"
317 },
318 {
319 "name": "plane",
320 "count": "nx * ny"
321 },
322 {
323 "name": "tmp",
324 "count": "mx * ny"
325 },
326 {
327 "name": "kernels",
328 "count": "n_rows * ir_steps"
329 }
330 ],
331 "setup": [
332 {
333 "shader": "medium",
334 "buffers": [
335 "wall",
336 "sig",
337 "prof"
338 ],
339 "dispatch": "npts"
340 },
341 {
342 "shader": "sines_x",
343 "buffers": [
344 "sx"
345 ],
346 "dispatch": "mx * nx"
347 },
348 {
349 "shader": "impulse",
350 "buffers": [
351 "p",
352 "wall",
353 "sig"
354 ],
355 "dispatch": "1"
356 }
357 ],
358 "loop": {
359 "count": "ir_steps",
360 "batch": 512,
361 "rotate": [
362 [
363 "p",
364 "pm",
365 "pn"
366 ]
367 ],
368 "body": [
369 {
370 "shader": "plane",
371 "buffers": [
372 "plane",
373 "p",
374 "prof"
375 ],
376 "dispatch": "nx * ny"
377 },
378 {
379 "shader": "xc",
380 "buffers": [
381 "tmp",
382 "plane",
383 "sx"
384 ],
385 "dispatch": "mx * ny"
386 },
387 {
388 "shader": "proj",
389 "buffers": [
390 "kernels",
391 "tmp",
392 "__tick"
393 ],
394 "dispatch": "mx"
395 },
396 {
397 "shader": "step",
398 "buffers": [
399 "p",
400 "pm",
401 "pn",
402 "wall",
403 "sig"
404 ],
405 "dispatch": "npts"
406 }
407 ]
408 },
409 "outputs": [
410 "kernels"
411 ]
412 },
413 "render": {
414 "buffers": [
415 {
416 "name": "kernels",
417 "count": "n_rows * ir_steps"
418 },
419 {
420 "name": "bx",
421 "count": "n_events * mx"
422 },
423 {
424 "name": "slots",
425 "count": "n_slots * 6"
426 },
427 {
428 "name": "state",
429 "count": "n_slots * 2"
430 },
431 {
432 "name": "partial",
433 "count": "n_slots * tile"
434 },
435 {
436 "name": "trace",
437 "count": "total_steps"
438 }
439 ],
440 "inputs": [
441 "kernels"
442 ],
443 "eventParams": [
444 "f1",
445 "t60",
446 "sig1",
447 "strikepos",
448 "striker",
449 "vel",
450 "gair"
451 ],
452 "derived": {
453 "ev_stride": "8",
454 "n_slots": "n_events * mx * 2"
455 },
456 "setup": [
457 {
458 "shader": "excite_x",
459 "buffers": [
460 "bx",
461 "__events"
462 ],
463 "dispatch": "n_events * mx"
464 },
465 {
466 "shader": "slots",
467 "buffers": [
468 "slots",
469 "__events",
470 "bx"
471 ],
472 "dispatch": "n_events * mx"
473 }
474 ],
475 "loop": {
476 "count": "n_tiles",
477 "batch": 8,
478 "body": [
479 {
480 "shader": "synth",
481 "buffers": [
482 "slots",
483 "state",
484 "kernels",
485 "partial",
486 "__tick"
487 ],
488 "dispatch": "n_slots"
489 },
490 {
491 "shader": "reduce",
492 "buffers": [
493 "partial",
494 "trace",
495 "__tick"
496 ],
497 "dispatch": "tile"
498 }
499 ]
500 },
501 "trace": "trace"
502 },
503 "visual": {
504 "dims": [
505 "nx",
506 "ny",
507 "nz"
508 ],
509 "extents": [
510 "Lx",
511 "Ly",
512 "Lz"
513 ],
514 "buffers": [
515 {
516 "name": "wall",
517 "count": "npts"
518 },
519 {
520 "name": "sig",
521 "count": "npts"
522 },
523 {
524 "name": "prof",
525 "count": "npts"
526 }
527 ],
528 "setup": [
529 {
530 "shader": "medium",
531 "buffers": [
532 "wall",
533 "sig",
534 "prof"
535 ],
536 "dispatch": "npts"
537 }
538 ],
539 "volumes": [
540 {
541 "buffer": "wall",
542 "style": "solid",
543 "invert": true,
544 "color": [
545 0.62,
546 0.48,
547 0.34
548 ]
549 },
550 {
551 "buffer": "prof",
552 "style": "cloud",
553 "color": [
554 1,
555 0.45,
556 0.2
557 ]
558 }
559 ],
560 "markers": [
561 {
562 "kind": "line",
563 "from": [
564 "-L1/2",
565 "oy",
566 "oz"
567 ],
568 "to": [
569 "L1/2",
570 "oy",
571 "oz"
572 ],
573 "color": [
574 1,
575 0.85,
576 0.3
577 ],
578 "label": "bar"
579 },
580 {
581 "kind": "point",
582 "at": [
583 "mic_x",
584 "mic_y",
585 "mic_z"
586 ],
587 "color": [
588 0.3,
589 0.85,
590 1
591 ],
592 "label": "mic"
593 }
594 ],
595 "partials": {
596 "buffer": "slots",
597 "stride": 6,
598 "zRe": 2,
599 "zIm": 3
600 }
601 }
602}