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