pixi-effects 0.27.1 → 0.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +6 -6
  3. package/ai/chat-template.html +3 -3
  4. package/dist/Composition-DRCTWIYH.cjs +16 -0
  5. package/dist/{Composition-YW2G6EXL.cjs.map → Composition-DRCTWIYH.cjs.map} +1 -1
  6. package/dist/Composition-TZHVYIGH.js +7 -0
  7. package/dist/{Composition-BJVY5HP7.js.map → Composition-TZHVYIGH.js.map} +1 -1
  8. package/dist/{chunk-X42BGR6P.js → chunk-BQNNZEAX.js} +4 -4
  9. package/dist/{chunk-X42BGR6P.js.map → chunk-BQNNZEAX.js.map} +1 -1
  10. package/dist/{chunk-VH5R473P.cjs → chunk-C7HJJ7MZ.cjs} +61 -61
  11. package/dist/{chunk-VH5R473P.cjs.map → chunk-C7HJJ7MZ.cjs.map} +1 -1
  12. package/dist/{chunk-AR3HVHKF.js → chunk-K3F2YKL4.js} +3 -3
  13. package/dist/{chunk-AR3HVHKF.js.map → chunk-K3F2YKL4.js.map} +1 -1
  14. package/dist/{chunk-ETKTRQL2.cjs → chunk-YOV6MEXH.cjs} +4 -4
  15. package/dist/{chunk-ETKTRQL2.cjs.map → chunk-YOV6MEXH.cjs.map} +1 -1
  16. package/dist/index.cjs +32 -32
  17. package/dist/index.js +5 -5
  18. package/dist/loader.css +1 -1
  19. package/dist/{music-YPUZTWTN.cjs → music-6MTHQEXS.cjs} +269 -99
  20. package/dist/music-6MTHQEXS.cjs.map +1 -0
  21. package/dist/{music-JVRNXHQ5.js → music-72YJAMXH.js} +269 -99
  22. package/dist/music-72YJAMXH.js.map +1 -0
  23. package/dist/three.cjs +5 -5
  24. package/dist/three.js +1 -1
  25. package/llms-full.txt +1 -1
  26. package/package.json +1 -1
  27. package/skills/pixi-effects/SKILL.md +1 -1
  28. package/skills/pixi-effects/template.html +6 -6
  29. package/dist/Composition-BJVY5HP7.js +0 -7
  30. package/dist/Composition-YW2G6EXL.cjs +0 -16
  31. package/dist/music-JVRNXHQ5.js.map +0 -1
  32. package/dist/music-YPUZTWTN.cjs.map +0 -1
@@ -6,8 +6,50 @@ var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
6
6
  var TAU = Math.PI * 2;
7
7
  var SR = 44100;
8
8
  function setSampleRate(sr) {
9
+ if (sr !== SR) clearVoiceTables();
9
10
  SR = sr;
10
11
  }
12
+ function clearVoiceTables() {
13
+ KEYS_TABLES.clear();
14
+ BELL_TABLES.clear();
15
+ }
16
+ var BELL_TABLES = /* @__PURE__ */ new Map();
17
+ function bellTables(f, k, n) {
18
+ const key = f + "/" + k;
19
+ let tb = BELL_TABLES.get(key);
20
+ if (tb && tb.n >= n) return tb;
21
+ const len = Math.max(n, tb ? tb.n * 2 : 0);
22
+ const s35 = new Float64Array(len), e5 = new Float64Array(len), ek = new Float64Array(len), s276 = new Float64Array(len), e4 = new Float64Array(len);
23
+ for (let i = 0; i < len; i++) {
24
+ const t = i / SR;
25
+ s35[i] = Math.sin(TAU * f * 3.5 * t);
26
+ e5[i] = Math.exp(-t * 5);
27
+ ek[i] = Math.exp(-t * k);
28
+ s276[i] = Math.sin(TAU * f * 2.76 * t);
29
+ e4[i] = Math.exp(-t * 4);
30
+ }
31
+ tb = { s35, e5, ek, s276, e4, n: len };
32
+ BELL_TABLES.set(key, tb);
33
+ return tb;
34
+ }
35
+ var KEYS_TABLES = /* @__PURE__ */ new Map();
36
+ function keysTables(f, n) {
37
+ let tb = KEYS_TABLES.get(f);
38
+ if (tb && tb.n >= n) return tb;
39
+ const len = Math.max(n, tb ? tb.n * 2 : 0);
40
+ const decay = new Float64Array(len), s14 = new Float64Array(len), s1 = new Float64Array(len), e18 = new Float64Array(len), e32 = new Float64Array(len);
41
+ for (let i = 0; i < len; i++) {
42
+ const t = i / SR;
43
+ decay[i] = Math.exp(-t * (1.1 + f / 1500));
44
+ s14[i] = Math.sin(TAU * f * 14 * t);
45
+ s1[i] = Math.sin(TAU * f * t);
46
+ e18[i] = Math.exp(-t * 18);
47
+ e32[i] = Math.exp(-t * 3.2);
48
+ }
49
+ tb = { decay, s14, s1, e18, e32, n: len };
50
+ KEYS_TABLES.set(f, tb);
51
+ return tb;
52
+ }
11
53
  var mtof = (m) => 440 * 2 ** ((m - 69) / 12);
12
54
  var Biquad = class {
13
55
  b0 = 1;
@@ -46,6 +88,10 @@ var Biquad = class {
46
88
  this.a1 = -2 * c / a0;
47
89
  this.a2 = (1 - al) / a0;
48
90
  }
91
+ /** All four memories are so small that anything this filter outputs from silence on rounds to 0 in a Float32Array (below half its smallest number). */
92
+ settled() {
93
+ return Math.abs(this.x1) < 1e-47 && Math.abs(this.x2) < 1e-47 && Math.abs(this.y1) < 1e-47 && Math.abs(this.y2) < 1e-47;
94
+ }
49
95
  run(x) {
50
96
  const y = this.b0 * x + this.b1 * this.x1 + this.b2 * this.x2 - this.a1 * this.y1 - this.a2 * this.y2;
51
97
  this.x2 = this.x1;
@@ -62,28 +108,27 @@ var adsr = (t, dur, a, d, s, r) => {
62
108
  const base = dur < a ? dur / a : dur < a + d ? 1 - (1 - s) * ((dur - a) / d) : s;
63
109
  return Math.max(0, base * (1 - (t - dur) / r));
64
110
  };
65
- function addTo(out, start, i, l, r) {
66
- const k = start + i;
67
- if (k >= 0 && k < out.L.length) {
68
- out.L[k] += l;
69
- out.R[k] += r;
70
- }
71
- }
72
111
  var INSTRUMENTS = ["keys", "pluck", "pad", "bass", "sub", "lead", "bell", "musicbox"];
73
112
  var VOICES = {
74
113
  // electric piano: two FM operators (tine + bell), a little stereo chorus
75
114
  keys(m, dur, vel, out, start, rnd, o) {
76
115
  const f = mtof(m), rel = o.release ?? 0.5, total = dur + rel + 1.2;
77
- const n = Math.floor(Math.min(total, 6) * SR), idx = 1.1 + 2.2 * vel, det = 1 + (rnd() - 0.5) * 2e-3;
78
- for (let i = 0; i < n; i++) {
116
+ const n = Math.min(Math.floor(Math.min(total, 6) * SR), Math.ceil((dur + rel) * SR) + 2), idx = 1.1 + 2.2 * vel, det = 1 + (rnd() - 0.5) * 2e-3;
117
+ const lim = Math.min(n, out.L.length - start);
118
+ const tb = keysTables(f, lim), dcy = tb.decay, s14 = tb.s14, s1 = tb.s1, e18 = tb.e18, e32 = tb.e32;
119
+ for (let i = 0; i < lim; i++) {
79
120
  const t = i / SR;
80
- const decay = Math.exp(-t * (1.1 + f / 1500));
81
- const mod = Math.sin(TAU * f * 14 * t) * idx * 0.12 * Math.exp(-t * 18) + Math.sin(TAU * f * t) * idx * Math.exp(-t * 3.2);
121
+ const decay = dcy[i];
122
+ const mod = s14[i] * idx * 0.12 * e18[i] + s1[i] * idx * e32[i];
82
123
  const body = Math.sin(TAU * f * t + mod) * decay;
83
124
  const body2 = Math.sin(TAU * f * det * t + mod * 0.9) * decay;
84
125
  const env = t < dur ? 1 : Math.max(0, 1 - (t - dur) / rel);
85
126
  const a = Math.min(1, t / 4e-3) * env * 0.5 * vel;
86
- addTo(out, start, i, a * (body * 0.7 + body2 * 0.3), a * (body * 0.3 + body2 * 0.7));
127
+ {
128
+ const wl = a * (body * 0.7 + body2 * 0.3), wr = a * (body * 0.3 + body2 * 0.7);
129
+ out.L[start + i] += wl;
130
+ out.R[start + i] += wr;
131
+ }
87
132
  }
88
133
  },
89
134
  // plucked string (Karplus–Strong with a darker loss filter): guitar / harp / koto
@@ -94,36 +139,65 @@ var VOICES = {
94
139
  const rel = o.release ?? 0.25, total = Math.min(dur + rel + 0.4, 5), n = Math.floor(total * SR), loss = 0.9965 - 0.5 / (f + 100);
95
140
  let p = 0, prev = 0;
96
141
  const pan = (rnd() - 0.5) * 0.3;
97
- for (let i = 0; i < n; i++) {
142
+ for (let i = 0, lim = Math.min(n, out.L.length - start); i < lim; i++) {
98
143
  const cur = buf[p], nxt = buf[(p + 1) % len];
99
144
  buf[p] = loss * 0.5 * (cur + nxt);
100
145
  p = (p + 1) % len;
101
146
  const t = i / SR, env = t < dur ? 1 : Math.max(0, 1 - (t - dur) / rel);
102
147
  const y = (cur - prev * 0.2) * env * 0.55 * vel;
103
148
  prev = cur;
104
- addTo(out, start, i, y * (1 - pan), y * (1 + pan));
149
+ {
150
+ const wl = y * (1 - pan), wr = y * (1 + pan);
151
+ out.L[start + i] += wl;
152
+ out.R[start + i] += wr;
153
+ }
105
154
  }
106
155
  },
107
156
  // warm pad: five detuned band-limited saws through a slow low-pass
108
157
  pad(m, dur, vel, out, start, rnd, o) {
109
158
  const f = mtof(m), A = o.attack ?? 0.7, R = o.release ?? 1.4, n = Math.floor((dur + R) * SR);
110
- const voices = [-0.14, -0.07, 0, 0.07, 0.14].map((c, k) => ({ ph: rnd(), f: f * 2 ** (c / 12 * 0.6), pan: (k - 2) / 2.5 }));
159
+ const cs = [-0.14, -0.07, 0, 0.07, 0.14];
160
+ const st = (k) => f * 2 ** (cs[k] / 12 * 0.6) / SR, gL = (k) => 1 - (k - 2) / 2.5, gR = (k) => 1 + (k - 2) / 2.5;
161
+ let p0 = rnd(), p1 = rnd(), p2 = rnd(), p3 = rnd(), p4 = rnd();
162
+ const d0 = st(0), d1 = st(1), d2 = st(2), d3 = st(3), d4 = st(4);
163
+ const gl0 = gL(0), gl1 = gL(1), gl2 = gL(2), gl3 = gL(3), gl4 = gL(4), gr0 = gR(0), gr1 = gR(1), gr2 = gR(2), gr3 = gR(3), gr4 = gR(4);
111
164
  const cut = Math.min(1800, 500 + f * 1.5);
112
165
  const lp = new Biquad("lp", cut, 0.6), lp2 = new Biquad("lp", cut, 0.6);
113
- for (let i = 0; i < n; i++) {
166
+ for (let i = 0, lim = Math.min(n, out.L.length - start); i < lim; i++) {
114
167
  const t = i / SR;
115
- let l = 0, r = 0;
116
- for (const v of voices) {
117
- const dt = v.f / SR;
118
- v.ph += dt;
119
- if (v.ph >= 1) v.ph -= 1;
120
- const s = saw(v.ph, dt);
121
- l += s * (1 - v.pan) * 0.5;
122
- r += s * (1 + v.pan) * 0.5;
123
- }
168
+ let l = 0, r = 0, sv;
169
+ p0 += d0;
170
+ if (p0 >= 1) p0 -= 1;
171
+ sv = saw(p0, d0);
172
+ l += sv * gl0 * 0.5;
173
+ r += sv * gr0 * 0.5;
174
+ p1 += d1;
175
+ if (p1 >= 1) p1 -= 1;
176
+ sv = saw(p1, d1);
177
+ l += sv * gl1 * 0.5;
178
+ r += sv * gr1 * 0.5;
179
+ p2 += d2;
180
+ if (p2 >= 1) p2 -= 1;
181
+ sv = saw(p2, d2);
182
+ l += sv * gl2 * 0.5;
183
+ r += sv * gr2 * 0.5;
184
+ p3 += d3;
185
+ if (p3 >= 1) p3 -= 1;
186
+ sv = saw(p3, d3);
187
+ l += sv * gl3 * 0.5;
188
+ r += sv * gr3 * 0.5;
189
+ p4 += d4;
190
+ if (p4 >= 1) p4 -= 1;
191
+ sv = saw(p4, d4);
192
+ l += sv * gl4 * 0.5;
193
+ r += sv * gr4 * 0.5;
124
194
  const env = t < dur ? Math.min(1, t / A) : Math.max(0, Math.min(1, dur / A) * (1 - (t - dur) / R));
125
195
  const g = env * 0.11 * vel;
126
- addTo(out, start, i, lp.run(l) * g, lp2.run(r) * g);
196
+ {
197
+ const wl = lp.run(l) * g, wr = lp2.run(r) * g;
198
+ out.L[start + i] += wl;
199
+ out.R[start + i] += wr;
200
+ }
127
201
  }
128
202
  },
129
203
  // synth bass: saw + sine sub, filter envelope
@@ -131,7 +205,7 @@ var VOICES = {
131
205
  const f = mtof(m), rel = o.release ?? 0.12, n = Math.floor((dur + rel) * SR);
132
206
  let ph = 0, ph2 = 0;
133
207
  const lp = new Biquad("lp", 400, 1.1);
134
- for (let i = 0; i < n; i++) {
208
+ for (let i = 0, lim = Math.min(n, out.L.length - start); i < lim; i++) {
135
209
  const t = i / SR, dt = f / SR;
136
210
  ph = (ph + dt) % 1;
137
211
  ph2 = (ph2 + dt) % 1;
@@ -139,19 +213,27 @@ var VOICES = {
139
213
  const s = lp.run(saw(ph, dt)) * 0.55 + Math.sin(TAU * ph2) * 0.55;
140
214
  const env = t < dur ? Math.min(1, t / 6e-3) * (0.75 + 0.25 * Math.exp(-t * 4)) : Math.max(0, 1 - (t - dur) / rel) * 0.75;
141
215
  const y = s * env * 0.5 * vel;
142
- addTo(out, start, i, y, y);
216
+ {
217
+ const wl = y, wr = y;
218
+ out.L[start + i] += wl;
219
+ out.R[start + i] += wr;
220
+ }
143
221
  }
144
222
  },
145
223
  // a pure sine bass: a soft heartbeat or sub (no filter sweep, hardly any upper harmonics)
146
224
  sub(m, dur, vel, out, start, _rnd, o) {
147
225
  const f = mtof(m), rel = o.release ?? 0.2, n = Math.floor((dur + rel) * SR);
148
226
  let ph = 0;
149
- for (let i = 0; i < n; i++) {
227
+ for (let i = 0, lim = Math.min(n, out.L.length - start); i < lim; i++) {
150
228
  const t = i / SR;
151
229
  ph += TAU * f / SR;
152
230
  const env = t < dur ? Math.min(1, t / (o.attack ?? 0.012)) * (0.8 + 0.2 * Math.exp(-t * 5)) : Math.max(0, 1 - (t - dur) / rel) * 0.8;
153
231
  const y = (Math.sin(ph) + 0.12 * Math.sin(2 * ph)) * env * 0.6 * vel;
154
- addTo(out, start, i, y, y);
232
+ {
233
+ const wl = y, wr = y;
234
+ out.L[start + i] += wl;
235
+ out.R[start + i] += wr;
236
+ }
155
237
  }
156
238
  },
157
239
  // lead: soft saw / triangle with a delayed vibrato
@@ -159,7 +241,7 @@ var VOICES = {
159
241
  const f = mtof(m), rel = o.release ?? 0.18, A = o.attack ?? 0.02, n = Math.floor((dur + rel) * SR);
160
242
  let ph = rnd(), ph2 = rnd();
161
243
  const lp = new Biquad("lp", 2600 + 1800 * vel, 0.8);
162
- for (let i = 0; i < n; i++) {
244
+ for (let i = 0, lim = Math.min(n, out.L.length - start); i < lim; i++) {
163
245
  const t = i / SR, vib = 1 + 35e-4 * Math.sin(TAU * 5.3 * t) * Math.min(1, Math.max(0, (t - 0.18) / 0.3));
164
246
  const dt = f * vib / SR;
165
247
  ph = (ph + dt) % 1;
@@ -167,38 +249,56 @@ var VOICES = {
167
249
  const tri = 4 * Math.abs(ph - 0.5) - 1;
168
250
  const s = lp.run(saw(ph2, dt) * 0.35 + tri * 0.65);
169
251
  const y = s * adsr(t, dur, A, 0.15, 0.7, rel) * 0.28 * vel;
170
- addTo(out, start, i, y * 0.9, y * 1.1);
252
+ {
253
+ const wl = y * 0.9, wr = y * 1.1;
254
+ out.L[start + i] += wl;
255
+ out.R[start + i] += wr;
256
+ }
171
257
  }
172
258
  },
173
259
  // bell / glockenspiel: inharmonic FM; `ring` is how many seconds it keeps sounding
174
260
  bell(m, dur, vel, out, start, rnd, o) {
175
261
  const f = mtof(m), ring = o.ring ?? 2.5, n = Math.floor(Math.min(dur + ring, 6) * SR), pan = (rnd() - 0.5) * 0.6;
176
262
  const k = 1.5 * (2.5 / ring);
177
- for (let i = 0; i < n; i++) {
263
+ const lim = Math.min(n, out.L.length - start);
264
+ const tb = bellTables(f, k, lim), s35 = tb.s35, e5 = tb.e5, ek = tb.ek, s276 = tb.s276, e4 = tb.e4;
265
+ for (let i = 0; i < lim; i++) {
178
266
  const t = i / SR;
179
- const mod = Math.sin(TAU * f * 3.5 * t) * 1.6 * Math.exp(-t * 5);
180
- const s = Math.sin(TAU * f * t + mod) * Math.exp(-t * k) + Math.sin(TAU * f * 2.76 * t) * 0.25 * Math.exp(-t * 4);
267
+ const mod = s35[i] * 1.6 * e5[i];
268
+ const s = Math.sin(TAU * f * t + mod) * ek[i] + s276[i] * 0.25 * e4[i];
181
269
  const y = s * Math.min(1, t / 2e-3) * 0.3 * vel;
182
- addTo(out, start, i, y * (1 - pan), y * (1 + pan));
270
+ {
271
+ const wl = y * (1 - pan), wr = y * (1 + pan);
272
+ out.L[start + i] += wl;
273
+ out.R[start + i] += wr;
274
+ }
183
275
  }
184
276
  },
185
277
  // music box: a bright tine with a strong upper partial and a short, dry ring
186
278
  musicbox(m, dur, vel, out, start, rnd, o) {
187
279
  const f = mtof(m), ring = o.ring ?? 1.3, n = Math.floor(Math.min(dur + ring, 4) * SR), pan = (rnd() - 0.5) * 0.4;
188
- for (let i = 0; i < n; i++) {
280
+ for (let i = 0, lim = Math.min(n, out.L.length - start); i < lim; i++) {
189
281
  const t = i / SR;
190
282
  const s = Math.sin(TAU * f * t) * Math.exp(-t * (3.2 / ring * 1.3)) + Math.sin(TAU * f * 4 * t) * 0.22 * Math.exp(-t * (9 / ring)) + Math.sin(TAU * f * 6.8 * t) * 0.08 * Math.exp(-t * 18);
191
283
  const y = s * Math.min(1, t / 15e-4) * 0.34 * vel;
192
- addTo(out, start, i, y * (1 - pan), y * (1 + pan));
284
+ {
285
+ const wl = y * (1 - pan), wr = y * (1 + pan);
286
+ out.L[start + i] += wl;
287
+ out.R[start + i] += wr;
288
+ }
193
289
  }
194
290
  }
195
291
  };
196
292
  var DRUMS = ["kick", "snare", "hat", "openhat", "clap", "rim", "tom", "crash", "shaker", "sleigh"];
197
293
  function hitDrum(kind, vel, out, start, rnd) {
198
- const mono = (n, f) => {
294
+ const mono = (n, f, gl = 1, gr = 1) => {
295
+ const lim = Math.min(n, out.L.length - start), L = out.L, R = out.R;
199
296
  for (let i = 0; i < n; i++) {
200
- const [l, r] = f(i / SR);
201
- addTo(out, start, i, l, r);
297
+ const o = f(i / SR);
298
+ if (i < lim) {
299
+ L[start + i] += o * gl;
300
+ R[start + i] += o * gr;
301
+ }
202
302
  }
203
303
  };
204
304
  const len = (s) => Math.floor(s * SR);
@@ -207,7 +307,7 @@ function hitDrum(kind, vel, out, start, rnd) {
207
307
  mono(len(0.5), (t) => {
208
308
  ph += TAU * (45 + 110 * Math.exp(-t * 28)) / SR;
209
309
  const o = (Math.sin(ph) * Math.exp(-t * 7.5) + (t < 4e-3 ? (rnd() - 0.5) * 0.5 : 0)) * 0.95 * vel;
210
- return [o, o];
310
+ return o;
211
311
  });
212
312
  } else if (kind === "snare") {
213
313
  const bp = new Biquad("bp", 1900, 0.9), hp = new Biquad("hp", 700, 0.7);
@@ -216,50 +316,50 @@ function hitDrum(kind, vel, out, start, rnd) {
216
316
  ph += TAU * (185 - 40 * t) / SR;
217
317
  const nz = hp.run(bp.run(rnd() * 2 - 1)) * 2.2;
218
318
  const o = (nz * Math.exp(-t * 17) + Math.sin(ph) * Math.exp(-t * 26) * 0.6) * 0.6 * vel;
219
- return [o * 0.95, o * 1.05];
220
- });
319
+ return o;
320
+ }, 0.95, 1.05);
221
321
  } else if (kind === "clap") {
222
322
  const bp = new Biquad("bp", 1500, 1.2);
223
323
  mono(len(0.35), (t) => {
224
324
  const burst = t < 0.033 ? Math.exp(-(t * 1e3 % 11) * 0.5) : Math.exp(-(t - 0.033) * 22);
225
325
  const o = bp.run(rnd() * 2 - 1) * 2.4 * burst * 0.5 * vel;
226
- return [o, o * 0.9];
227
- });
326
+ return o;
327
+ }, 1, 0.9);
228
328
  } else if (kind === "hat" || kind === "openhat") {
229
329
  const hp = new Biquad("hp", 7500, 0.8), dec = kind === "hat" ? 55 : 9;
230
330
  mono(len(kind === "hat" ? 0.12 : 0.6), (t) => {
231
331
  const o = hp.run(rnd() * 2 - 1) * Math.exp(-t * dec) * 0.35 * vel;
232
- return [o * 0.8, o * 1.2];
233
- });
332
+ return o;
333
+ }, 0.8, 1.2);
234
334
  } else if (kind === "rim") {
235
335
  let ph = 0;
236
336
  const bp = new Biquad("bp", 3200, 4);
237
337
  mono(len(0.1), (t) => {
238
338
  ph += TAU * 1750 / SR;
239
339
  const o = (Math.sin(ph) * 0.5 + bp.run(rnd() * 2 - 1)) * Math.exp(-t * 70) * 0.5 * vel;
240
- return [o, o];
340
+ return o;
241
341
  });
242
342
  } else if (kind === "tom") {
243
343
  let ph = 0;
244
344
  mono(len(0.5), (t) => {
245
345
  ph += TAU * (95 + 70 * Math.exp(-t * 15)) / SR;
246
346
  const o = Math.sin(ph) * Math.exp(-t * 7) * 0.7 * vel;
247
- return [o, o];
347
+ return o;
248
348
  });
249
349
  } else if (kind === "crash") {
250
350
  const hp = new Biquad("hp", 5200, 0.7), bp = new Biquad("bp", 9e3, 0.6);
251
351
  mono(len(1.8), (t) => {
252
352
  const nz = rnd() * 2 - 1;
253
353
  const o = (hp.run(nz) * 0.7 + bp.run(nz) * 0.5) * (0.55 * Math.exp(-t * 2.6) + 0.45 * Math.exp(-t * 11)) * 0.5 * vel;
254
- return [o * 0.9, o * 1.1];
255
- });
354
+ return o;
355
+ }, 0.9, 1.1);
256
356
  } else if (kind === "shaker") {
257
357
  const bp = new Biquad("bp", 6500, 1.4);
258
358
  mono(len(0.16), (t) => {
259
359
  const env = (t < 0.018 ? t / 0.018 : 1) * Math.exp(-t * 28);
260
360
  const o = bp.run(rnd() * 2 - 1) * env * 1.4 * 0.3 * vel;
261
- return [o * 0.85, o * 1.15];
262
- });
361
+ return o;
362
+ }, 0.85, 1.15);
263
363
  } else {
264
364
  const bp = new Biquad("bp", 7800, 1);
265
365
  const partials = [4200, 5630, 6900, 8350];
@@ -267,8 +367,8 @@ function hitDrum(kind, vel, out, start, rnd) {
267
367
  let s = 0;
268
368
  for (let k = 0; k < partials.length; k++) s += Math.sin(TAU * partials[k] * t * (1 + k * 7e-4)) * (0.5 + 0.5 * Math.sin(TAU * (19 + k * 3.1) * t + k)) * Math.exp(-t * (7 + k * 2));
269
369
  const o = (s * 0.11 + bp.run(rnd() * 2 - 1) * Math.exp(-t * 16) * 0.5) * Math.min(1, t / 3e-3) * vel * 0.8;
270
- return [o * 0.85, o * 1.15];
271
- });
370
+ return o;
371
+ }, 0.85, 1.15);
272
372
  }
273
373
  }
274
374
  function* reverbSteps(send, out, wet, size = 0.8) {
@@ -279,30 +379,40 @@ function* reverbSteps(send, out, wet, size = 0.8) {
279
379
  c: [1116, 1188, 1277, 1356, 1422, 1491].map((l) => comb(l + off)),
280
380
  a: [556, 441, 341].map((l) => ({ b: new Float32Array(Math.max(8, Math.round((l + off) * scale))), i: 0 }))
281
381
  });
282
- const run = (x, ch) => {
283
- let y = 0;
284
- for (const c of ch.c) {
285
- const o = c.b[c.i];
286
- c.d = o * (1 - c.damp) + c.d * c.damp;
287
- c.b[c.i] = x * 0.03 + c.d * c.fb;
288
- c.i = (c.i + 1) % c.b.length;
289
- y += o;
290
- }
291
- for (const a of ch.a) {
292
- const o = a.b[a.i], inp = y;
293
- a.b[a.i] = inp + o * 0.5;
294
- a.i = (a.i + 1) % a.b.length;
295
- y = o - inp;
382
+ const chL = mk(0), chR = mk(23);
383
+ const block = (i, stop) => {
384
+ for (; i < stop; i++) {
385
+ const m = (sL[i] + sR[i]) * 0.5;
386
+ L[i] += runFlat(m, chL) * wet;
387
+ R[i] += runFlat(m, chR) * wet;
296
388
  }
297
- return y;
298
389
  };
299
- const chL = mk(0), chR = mk(23);
300
- for (let i = 0; i < L.length; i++) {
301
- if ((i & 16383) === 0) yield i / L.length;
302
- const m = (sL[i] + sR[i]) * 0.5;
303
- L[i] += run(m, chL) * wet;
304
- R[i] += run(m, chR) * wet;
390
+ for (let i = 0; i < L.length; ) {
391
+ yield i / L.length;
392
+ const stop = Math.min(L.length, (i | 16383) + 1);
393
+ block(i, stop);
394
+ i = stop;
395
+ }
396
+ }
397
+ function runFlat(x, ch) {
398
+ let y = 0;
399
+ const cs = ch.c, as = ch.a;
400
+ for (let k = 0; k < 6; k++) {
401
+ const c = cs[k], b = c.b;
402
+ const o = b[c.i];
403
+ c.d = o * (1 - c.damp) + c.d * c.damp;
404
+ b[c.i] = x * 0.03 + c.d * c.fb;
405
+ c.i = c.i + 1 === b.length ? 0 : c.i + 1;
406
+ y += o;
407
+ }
408
+ for (let k = 0; k < 3; k++) {
409
+ const a = as[k], b = a.b;
410
+ const o = b[a.i], inp = y;
411
+ b[a.i] = inp + o * 0.5;
412
+ a.i = a.i + 1 === b.length ? 0 : a.i + 1;
413
+ y = o - inp;
305
414
  }
415
+ return y;
306
416
  }
307
417
 
308
418
  // src/audio/music/notation.ts
@@ -724,6 +834,18 @@ function rng(seed) {
724
834
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
725
835
  };
726
836
  }
837
+ function extent(b) {
838
+ let lo = b.L.length, hi = -1;
839
+ for (let i = 0; i < b.L.length; i++) if (b.L[i] !== 0 || b.R[i] !== 0) {
840
+ lo = i;
841
+ break;
842
+ }
843
+ for (let i = b.L.length - 1; i >= lo; i--) if (b.L[i] !== 0 || b.R[i] !== 0) {
844
+ hi = i;
845
+ break;
846
+ }
847
+ return [lo, hi];
848
+ }
727
849
  function musicLength(m) {
728
850
  return m.seconds + m.tail;
729
851
  }
@@ -746,6 +868,37 @@ function musicBeatTimes(m, until = m.seconds) {
746
868
  }
747
869
  return times.filter((t) => t < until);
748
870
  }
871
+ function toneRun(scratch, a, b, i, stop, end) {
872
+ const L = scratch.L, R = scratch.R;
873
+ const a0 = a[0], a1 = a[1], b0 = b[0], b1 = b[1];
874
+ for (; i < stop && (i < end || !(a0.settled() && a1.settled() && b0.settled() && b1.settled())); i++) {
875
+ L[i] = a1.run(a0.run(L[i]));
876
+ R[i] = b1.run(b0.run(R[i]));
877
+ }
878
+ return i;
879
+ }
880
+ function mixRun(scratch, out, send, i, stop, level, constant, g, gl, gr, rv, sampleRate) {
881
+ const sL = scratch.L, sR = scratch.R, oL = out.L, oR = out.R, eL = send.L, eR = send.R;
882
+ for (; i < stop; i++) {
883
+ if (!constant) g = level(i / sampleRate);
884
+ const l = sL[i] * g * gl, r = sR[i] * g * gr;
885
+ oL[i] += l;
886
+ oR[i] += r;
887
+ eL[i] += l * rv;
888
+ eR[i] += r * rv;
889
+ }
890
+ }
891
+ function tanhRun(out, i, stop, acc) {
892
+ const L = out.L, R = out.R;
893
+ let peak = acc[0], sum = acc[1];
894
+ for (; i < stop; i++) {
895
+ const l = L[i] = Math.tanh(L[i] * 0.9), r = R[i] = Math.tanh(R[i] * 0.9);
896
+ peak = Math.max(peak, Math.abs(l), Math.abs(r));
897
+ sum += l * l + r * r;
898
+ }
899
+ acc[0] = peak;
900
+ acc[1] = sum;
901
+ }
749
902
  function* renderMusicSteps(m, sampleRate, length, loop) {
750
903
  setSampleRate(sampleRate);
751
904
  const units = m.tracks.length + (m.drums.length ? 1 : 0) + 2;
@@ -764,6 +917,15 @@ function* renderMusicSteps(m, sampleRate, length, loop) {
764
917
  const levelAt = (vol) => {
765
918
  if (typeof vol === "number") return () => vol;
766
919
  const pts = vol.map(([b, v]) => [map.seconds(b), v]);
920
+ if (!loop && pts.every((p, i) => i === 0 || p[0] >= pts[i - 1][0])) {
921
+ let j = 1;
922
+ return (sec) => {
923
+ if (sec <= pts[0][0]) return pts[0][1];
924
+ while (j < pts.length && !(sec < pts[j][0])) j++;
925
+ if (j < pts.length) return pts[j - 1][1] + (pts[j][1] - pts[j - 1][1]) * (sec - pts[j - 1][0]) / (pts[j][0] - pts[j - 1][0]);
926
+ return pts[pts.length - 1][1];
927
+ };
928
+ }
767
929
  return (sec) => {
768
930
  const t = loop && loopSec > 0 ? sec % loopSec : sec;
769
931
  if (t <= pts[0][0]) return pts[0][1];
@@ -775,8 +937,6 @@ function* renderMusicSteps(m, sampleRate, length, loop) {
775
937
  };
776
938
  for (const tr of m.tracks) {
777
939
  const inst = VOICES[tr.inst];
778
- scratch.L.fill(0);
779
- scratch.R.fill(0);
780
940
  for (let r = 0; r < repeats; r++) {
781
941
  const offset = r * loopSec;
782
942
  for (let ei = 0; ei < tr.events.length; ei++) {
@@ -793,28 +953,36 @@ function* renderMusicSteps(m, sampleRate, length, loop) {
793
953
  });
794
954
  }
795
955
  }
956
+ const [lo, hi] = extent(scratch);
957
+ let end = hi + 1;
796
958
  if (tr.tone < 1) {
797
959
  const fc = 400 * 50 ** tr.tone;
798
960
  const a = [new Biquad("lp", fc, 0.7), new Biquad("lp", fc, 0.7)], b = [new Biquad("lp", fc, 0.7), new Biquad("lp", fc, 0.7)];
799
- for (let i = 0; i < total; i++) {
800
- if ((i & 65535) === 0) yield (unit + 0.9) / units;
801
- scratch.L[i] = a[1].run(a[0].run(scratch.L[i]));
802
- scratch.R[i] = b[1].run(b[0].run(scratch.R[i]));
961
+ let i = lo;
962
+ for (; ; ) {
963
+ const stop = Math.min(total, (i | 65535) + 1);
964
+ const j = toneRun(scratch, a, b, i, stop, end);
965
+ if (j < stop || j >= total) {
966
+ i = j;
967
+ break;
968
+ }
969
+ i = j;
970
+ yield (unit + 0.9) / units;
803
971
  }
972
+ end = Math.max(end, i);
804
973
  }
805
974
  const level = levelAt(tr.vol), pan = Math.max(-1, Math.min(1, tr.pan));
806
975
  const gl = Math.cos((pan + 1) * Math.PI / 4) * 1.3, gr = Math.sin((pan + 1) * Math.PI / 4) * 1.3;
807
976
  const constant = typeof tr.vol === "number";
808
977
  let g = constant ? level(0) : 0;
809
- for (let i = 0; i < total; i++) {
810
- if ((i & 65535) === 0) yield (unit + 0.95) / units;
811
- if (!constant) g = level(i / sampleRate);
812
- const l = scratch.L[i] * g * gl, r = scratch.R[i] * g * gr;
813
- out.L[i] += l;
814
- out.R[i] += r;
815
- send.L[i] += l * tr.reverb;
816
- send.R[i] += r * tr.reverb;
978
+ for (let i = lo; i < end; ) {
979
+ const stop = Math.min(end, (i | 65535) + 1);
980
+ mixRun(scratch, out, send, i, stop, level, constant, g, gl, gr, tr.reverb, sampleRate);
981
+ i = stop;
982
+ yield (unit + 0.95) / units;
817
983
  }
984
+ scratch.L.fill(0, lo, end);
985
+ scratch.R.fill(0, lo, end);
818
986
  unit++;
819
987
  }
820
988
  if (m.drums.length) {
@@ -846,13 +1014,14 @@ function* renderMusicSteps(m, sampleRate, length, loop) {
846
1014
  }
847
1015
  for (const f of reverbSteps(send, out, m.reverb, 0.8)) yield (unit + f) / units;
848
1016
  unit++;
849
- let peak = 0, sum = 0;
850
- for (let i = 0; i < total; i++) {
851
- if ((i & 65535) === 0) yield (unit + 0.5 * i / total) / units;
852
- const l = out.L[i] = Math.tanh(out.L[i] * 0.9), r = out.R[i] = Math.tanh(out.R[i] * 0.9);
853
- peak = Math.max(peak, Math.abs(l), Math.abs(r));
854
- sum += l * l + r * r;
1017
+ const acc = new Float64Array(2);
1018
+ for (let i = 0; i < total; ) {
1019
+ const stop = Math.min(total, (i | 65535) + 1);
1020
+ tanhRun(out, i, stop, acc);
1021
+ i = stop;
1022
+ yield (unit + 0.5 * i / total) / units;
855
1023
  }
1024
+ const peak = acc[0], sum = acc[1];
856
1025
  const rms = Math.sqrt(sum / (2 * total));
857
1026
  const gain = peak > 0 ? Math.min(MUSIC_PEAK / peak, RMS_CAP / Math.max(rms, 1e-9)) : 0;
858
1027
  const fade = Math.min(Math.floor(0.4 * sampleRate), total >> 2);
@@ -861,6 +1030,7 @@ function* renderMusicSteps(m, sampleRate, length, loop) {
861
1030
  out.L[i] *= gain * f;
862
1031
  out.R[i] *= gain * f;
863
1032
  }
1033
+ clearVoiceTables();
864
1034
  return [out.L, out.R];
865
1035
  }
866
1036
  function renderMusic(m, sampleRate, length, loop = false) {
@@ -896,5 +1066,5 @@ exports.parseNotes = parseNotes;
896
1066
  exports.renderMusic = renderMusic;
897
1067
  exports.renderMusicAsync = renderMusicAsync;
898
1068
  exports.resolveMusic = resolveMusic;
899
- //# sourceMappingURL=music-YPUZTWTN.cjs.map
900
- //# sourceMappingURL=music-YPUZTWTN.cjs.map
1069
+ //# sourceMappingURL=music-6MTHQEXS.cjs.map
1070
+ //# sourceMappingURL=music-6MTHQEXS.cjs.map