pixi-effects 0.46.0 → 0.47.0

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  var chunkDKCMJILW_cjs = require('./chunk-DKCMJILW.cjs');
4
4
  var chunkLALEBV63_cjs = require('./chunk-LALEBV63.cjs');
5
- var chunkO66JGPVV_cjs = require('./chunk-O66JGPVV.cjs');
5
+ var chunk23SXKSUI_cjs = require('./chunk-23SXKSUI.cjs');
6
6
  var chunkJN7JHNZR_cjs = require('./chunk-JN7JHNZR.cjs');
7
7
  var chunk3SAS3ZE5_cjs = require('./chunk-3SAS3ZE5.cjs');
8
8
  var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
@@ -109,7 +109,7 @@ async function mixdown(audios, totalDuration, sampleRate = 44100, pace) {
109
109
  if (pace) await pace(made / all);
110
110
  if (!buffer) continue;
111
111
  if (a.warp || a.sourceMap) {
112
- const r = chunkO66JGPVV_cjs.resampleThrough(a, buffer.getChannelData(0), buffer.numberOfChannels > 1 ? buffer.getChannelData(1) : buffer.getChannelData(0), buffer.sampleRate, sampleRate, totalDuration);
112
+ const r = chunk23SXKSUI_cjs.resampleThrough(a, buffer.getChannelData(0), buffer.numberOfChannels > 1 ? buffer.getChannelData(1) : buffer.getChannelData(0), buffer.sampleRate, sampleRate, totalDuration);
113
113
  if (!r) continue;
114
114
  const out = ctx.createBuffer(2, r.L.length, sampleRate);
115
115
  out.getChannelData(0).set(r.L);
@@ -143,18 +143,86 @@ async function mixdown(audios, totalDuration, sampleRate = 44100, pace) {
143
143
  }
144
144
  var KNEE = 0.9;
145
145
  var LOUDNESS_CEILING_DB = -1;
146
+ var MAX_LIMIT_DB = 10;
147
+ function limitInto(source, out, gain, ceil, sampleRate) {
148
+ const n = source[0]?.length ?? 0;
149
+ const req = new Float32Array(n);
150
+ for (let i = 0; i < n; i++) {
151
+ let p = 0;
152
+ for (const c of source) {
153
+ const a = Math.abs(c[i]) * gain;
154
+ if (a > p) p = a;
155
+ }
156
+ req[i] = p > ceil ? ceil / p : 1;
157
+ }
158
+ const L = Math.max(1, Math.round(3e-3 * sampleRate));
159
+ const mn = new Float32Array(n), dq = new Int32Array(n);
160
+ let head = 0, tail = 0;
161
+ for (let i = n - 1; i >= 0; i--) {
162
+ while (tail > head && req[dq[tail - 1]] >= req[i]) tail--;
163
+ dq[tail++] = i;
164
+ while (dq[head] > i + L) head++;
165
+ mn[i] = req[dq[head]];
166
+ }
167
+ const release = 1 - Math.exp(-1 / (0.08 * sampleRate));
168
+ let run = 0, prev = 1, lowest = 1, lowestAt = 0;
169
+ const gains = new Float32Array(n);
170
+ for (let i = 0; i < n; i++) {
171
+ run += mn[i];
172
+ if (i > L) run -= mn[i - L - 1];
173
+ const avg = run / Math.min(i + 1, L + 1);
174
+ const g = Math.min(avg, prev + (1 - prev) * release);
175
+ gains[i] = g;
176
+ prev = g;
177
+ if (g < lowest) {
178
+ lowest = g;
179
+ lowestAt = i;
180
+ }
181
+ }
182
+ for (let ch = 0; ch < source.length; ch++) {
183
+ const s = source[ch], o = out[ch];
184
+ for (let i = 0; i < n; i++) {
185
+ const v = s[i] * gain * gains[i];
186
+ o[i] = v > ceil ? ceil : v < -ceil ? -ceil : v;
187
+ }
188
+ }
189
+ return { reductionDb: lowest < 1 ? -20 * Math.log10(lowest) : 0, at: lowestAt / sampleRate };
190
+ }
146
191
  function normalizeMix(buffer, target) {
147
192
  const chans = [];
148
193
  for (let ch = 0; ch < buffer.numberOfChannels; ch++) chans.push(buffer.getChannelData(ch));
149
- const m = chunkO66JGPVV_cjs.measureLoudness(chans, buffer.sampleRate, { truePeak: false });
194
+ const sr = buffer.sampleRate;
195
+ const m = chunk23SXKSUI_cjs.measureLoudness(chans, sr, { truePeak: false });
150
196
  if (m.integratedLufs === null) return { target, gainDb: 0, reachedLufs: null, limited: false };
197
+ const original = chans.map((c) => Float32Array.from(c));
151
198
  let gainDb = target - m.integratedLufs;
152
- const room = LOUDNESS_CEILING_DB - m.samplePeakDb;
153
- const limited = gainDb > room;
154
- if (limited) gainDb = room;
155
- const gain = 10 ** (gainDb / 20);
156
- for (const d of chans) for (let i = 0; i < d.length; i++) d[i] = d[i] * gain;
157
- return { target, gainDb: Math.round(gainDb * 10) / 10, reachedLufs: Math.round((m.integratedLufs + gainDb) * 10) / 10, limited };
199
+ let ceilDb = LOUDNESS_CEILING_DB;
200
+ let applied = { reductionDb: 0, at: 0 }, measured = m;
201
+ for (let round2 = 0; round2 < 8; round2++) {
202
+ const cap = MAX_LIMIT_DB + ceilDb - m.samplePeakDb;
203
+ if (gainDb > cap) gainDb = cap;
204
+ applied = limitInto(original, chans, 10 ** (gainDb / 20), 10 ** (ceilDb / 20), sr);
205
+ measured = chunk23SXKSUI_cjs.measureLoudness(chans, sr);
206
+ const overTp = measured.truePeakDb - LOUDNESS_CEILING_DB;
207
+ if (overTp > 0.02) {
208
+ ceilDb -= overTp + 0.02;
209
+ continue;
210
+ }
211
+ const deficit = target - measured.integratedLufs;
212
+ if (Math.abs(deficit) <= 0.15) break;
213
+ const next = Math.min(gainDb + deficit, cap);
214
+ if (Math.abs(next - gainDb) < 0.05) break;
215
+ gainDb = next;
216
+ }
217
+ const limited = measured.integratedLufs < target - 0.15;
218
+ const reduction = Math.round(applied.reductionDb * 10) / 10;
219
+ return {
220
+ target,
221
+ gainDb: Math.round(gainDb * 10) / 10,
222
+ reachedLufs: measured.integratedLufs,
223
+ limited,
224
+ ...reduction >= 0.1 ? { limiterDb: reduction, limiterAt: Math.round(applied.at * 100) / 100 } : {}
225
+ };
158
226
  }
159
227
  function limitMix(buffer, audios) {
160
228
  let peak = 0;
@@ -172,7 +240,7 @@ function limitMix(buffer, audios) {
172
240
  }
173
241
  const peakAt = peakIndex / buffer.sampleRate;
174
242
  if (peak > 1) {
175
- const playing = audios.map((a) => ({ a, ...chunkO66JGPVV_cjs.playSpan(a, buffer.duration) })).filter((s) => s.start <= peakAt && peakAt < s.end).map((s) => s.a.layer ?? "an audio layer");
243
+ const playing = audios.map((a) => ({ a, ...chunk23SXKSUI_cjs.playSpan(a, buffer.duration) })).filter((s) => s.start <= peakAt && peakAt < s.end).map((s) => s.a.layer ?? "an audio layer");
176
244
  console.warn(
177
245
  `pixi-effects: the audio mix peaks at ${peak.toFixed(2)} (${(20 * Math.log10(peak)).toFixed(1)} dBFS) at ${peakAt.toFixed(2)}s, so it was limited (it would distort). Playing there: ${[...new Set(playing)].join(", ")}. Lower their volume.`
178
246
  );
@@ -3423,7 +3491,7 @@ var Movie = class {
3423
3491
  chunkTWZNOUM7_cjs.warnUnknownOptions("movie.inspectAudio()", opts, ["window", "scenes"]);
3424
3492
  this._requireReady("inspectAudio");
3425
3493
  const scenes = opts.scenes ?? namedScenes(this.timelineData().rows);
3426
- return chunkO66JGPVV_cjs.analyzeAudio(this.audioBuffer, this._audioSources, this._mixStats, this.duration, { ...opts, scenes });
3494
+ return chunk23SXKSUI_cjs.analyzeAudio(this.audioBuffer, this._audioSources, this._mixStats, this.duration, { ...opts, scenes });
3427
3495
  }
3428
3496
  /**
3429
3497
  * Draw the stage now. PixiJS's CullerPlugin only culls inside `app.render()` (the ticker), and we render
@@ -4448,20 +4516,25 @@ function animateText(text, style, options) {
4448
4516
  const pieces = splitText(text, style, { by, x, y, align, ...vertical ? { vertical } : {} });
4449
4517
  if (pieces.length === 0) return [];
4450
4518
  const lineHeight = measureText("M", style).height;
4451
- const inTween = options.in === false ? null : resolveTween(options.in ?? "rise", "in");
4452
- const outTween = options.out === void 0 || options.out === false ? null : resolveTween(options.out, "out");
4519
+ const given = (v) => v === false || v === "none" ? void 0 : v;
4520
+ const inSpec = options.in === void 0 ? "rise" : given(options.in), outSpec = given(options.out);
4521
+ const inTween = inSpec === void 0 ? null : resolveTween(inSpec, "in");
4522
+ const outTween = outSpec === void 0 ? null : resolveTween(outSpec, "out");
4453
4523
  const delays = stagger(pieces.length, options.stagger ?? { each: 0.04 });
4454
4524
  const wave = Math.max(...delays);
4455
- const need = (inTween?.duration ?? 0) + (outTween?.duration ?? 0) + wave;
4525
+ const outDelays = outTween?.stagger ? stagger(pieces.length, outTween.stagger) : delays;
4526
+ const outWave = Math.max(...outDelays);
4527
+ const lead = Math.max(...delays.map((d, i) => d - outDelays[i]));
4528
+ const need = (inTween?.duration ?? 0) + (outTween ? outTween.duration + outWave + lead : wave);
4456
4529
  if (duration < need - 1e-9) {
4457
4530
  throw new Error(`animateText(): duration ${duration} is too short for this wave: the entrance${outTween ? ", the exit" : ""} and the stagger need at least ${+need.toFixed(3)} s`);
4458
4531
  }
4459
4532
  const end = at + duration;
4460
4533
  if (outTween?.at !== void 0) {
4461
4534
  if (outTween.at < at - 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} is before the layer starts (at ${at}): the exit starts with the piece; use out.at \u2265 ${at}`);
4462
- const latest = end - outTween.duration - wave;
4463
- if (outTween.at > latest + 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} leaves after the layer ends (the exit and the wave take ${+(outTween.duration + wave).toFixed(3)} s and the layer ends at ${+end.toFixed(3)} s): use out.at \u2264 ${+latest.toFixed(3)}, or a longer duration`);
4464
- if (inTween && outTween.at - at < inTween.duration - 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} is before the entrance has finished (it takes ${inTween.duration} s from at ${at}): use out.at \u2265 ${+(at + inTween.duration).toFixed(3)}`);
4535
+ const latest = end - outTween.duration - outWave;
4536
+ if (outTween.at > latest + 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} leaves after the layer ends (the exit and the wave take ${+(outTween.duration + outWave).toFixed(3)} s and the layer ends at ${+end.toFixed(3)} s): use out.at \u2264 ${+latest.toFixed(3)}, or a longer duration`);
4537
+ if (inTween && outTween.at - at < inTween.duration + lead - 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} is before the entrance has finished (it takes ${inTween.duration} s from at ${at}): use out.at \u2265 ${+(at + inTween.duration + lead).toFixed(3)}`);
4465
4538
  }
4466
4539
  return pieces.map((p, i) => {
4467
4540
  const cx = p.cx ?? p.x + p.width / 2, cy = p.cy ?? p.y + lineHeight / 2;
@@ -4481,8 +4554,8 @@ function animateText(text, style, options) {
4481
4554
  }
4482
4555
  let idleEnd = len;
4483
4556
  if (outTween) {
4484
- const begin = outTween.at !== void 0 ? void 0 : outTween.duration + (wave - delays[i]);
4485
- const local = outTween.at !== void 0 ? Math.max(0, outTween.at - at) : -begin;
4557
+ const begin = outTween.at !== void 0 ? void 0 : outTween.duration + (outWave - outDelays[i]);
4558
+ const local = outTween.at !== void 0 ? Math.max(0, outTween.at + outDelays[i] - start) : -begin;
4486
4559
  idleEnd = outTween.at !== void 0 ? local : len - begin;
4487
4560
  keyframes.push({
4488
4561
  at: local,
@@ -4518,6 +4591,10 @@ function resolveTween(spec, which) {
4518
4591
  if (typeof spec === "object" && spec) {
4519
4592
  for (const key of Object.keys(spec)) {
4520
4593
  if (TWEEN_KEYS.includes(key)) continue;
4594
+ if (key === "stagger") {
4595
+ if (which === "in") console.warn("pixi-effects: animateText(): in.stagger is ignored: the entrance wave is the options' `stagger` (only `out` can have a wave of its own: out: { stagger: 0 } leaves all at once)");
4596
+ continue;
4597
+ }
4521
4598
  if (which === "out" && key === "at") {
4522
4599
  if (typeof spec.at !== "number" || !Number.isFinite(spec.at)) console.warn(`pixi-effects: animateText(): out.at must be a number of seconds (the composition second the exit starts at), got ${JSON.stringify(spec.at)}; ignored`);
4523
4600
  continue;
@@ -4529,7 +4606,7 @@ function resolveTween(spec, which) {
4529
4606
  }
4530
4607
  if (t.preset !== void 0 && !(t.preset in PRESETS)) {
4531
4608
  const guess = chunkTWZNOUM7_cjs.suggestName(String(t.preset), PRESET_NAMES);
4532
- throw new Error(`animateText(): ${which} "${t.preset}" is not a preset${guess ? `; did you mean "${guess}"?` : ""} (presets: ${PRESET_NAMES.map((n) => `"${n}"`).join(", ")})`);
4609
+ throw new Error(`animateText(): ${which} "${t.preset}" is not a preset${guess ? `; did you mean "${guess}"?` : ""} (presets: ${PRESET_NAMES.map((n) => `"${n}"`).join(", ")}; for no ${which === "out" ? "exit: omit `out` or write out: false (the pieces stay until the layer ends)" : "entrance: in: false"})`);
4533
4610
  }
4534
4611
  const base = t.preset ? PRESETS[t.preset] : void 0;
4535
4612
  const from = { ...base?.from ?? {}, ...t.from ?? {} };
@@ -4538,11 +4615,14 @@ function resolveTween(spec, which) {
4538
4615
  for (const k of Object.keys(from)) {
4539
4616
  if (!known.has(k) && to[k] === void 0) throw new Error(`animateText(): ${which}.from has "${k}", which has no resting value; give ${which}.to: { ${k}: \u2026 }`);
4540
4617
  }
4541
- if (Object.keys(from).length === 0) throw new Error(`animateText(): ${which} needs a preset or a from`);
4618
+ if (Object.keys(from).length === 0) {
4619
+ throw new Error(`animateText(): ${which} needs a preset or a from${which === "out" && Object.keys(to).length ? ` \u2014 ${which} takes \`from\`: the state the piece leaves to (out: { from: { y: 18, alpha: 0 } }); \`to\` is only the resting value of a property that has none` : ""}`);
4620
+ }
4542
4621
  let ease = t.ease ?? base?.ease ?? "power2.out";
4543
4622
  if (which === "out" && t.ease === void 0) ease = ease.replace(".out", ".in");
4544
4623
  const at = which === "out" && typeof t.at === "number" && Number.isFinite(t.at) ? t.at : void 0;
4545
- return { from, to: { ...to }, duration: t.duration ?? base?.duration ?? 0.5, ease, ...at !== void 0 ? { at } : {} };
4624
+ const wave = which === "out" && t.stagger !== void 0 ? t.stagger === 0 ? { each: 0 } : t.stagger : void 0;
4625
+ return { from, to: { ...to }, duration: t.duration ?? base?.duration ?? 0.5, ease, ...at !== void 0 ? { at } : {}, ...wave ? { stagger: wave } : {} };
4546
4626
  }
4547
4627
 
4548
4628
  // src/presets/react.ts
@@ -4707,7 +4787,7 @@ function computeEnvelope(audio, options = {}) {
4707
4787
  re[i] = k >= 0 && k < length ? mono[k] * win[i] : 0;
4708
4788
  im[i] = 0;
4709
4789
  }
4710
- chunkO66JGPVV_cjs.fft(re, im);
4790
+ chunk23SXKSUI_cjs.fft(re, im);
4711
4791
  for (const n of names) {
4712
4792
  const [lo, hi] = bands[n];
4713
4793
  let p = 0;
@@ -4873,5 +4953,5 @@ exports.splitText = splitText;
4873
4953
  exports.stagger = stagger;
4874
4954
  exports.wiggle = wiggle;
4875
4955
  exports.withFade = withFade;
4876
- //# sourceMappingURL=chunk-7M63Y6MO.cjs.map
4877
- //# sourceMappingURL=chunk-7M63Y6MO.cjs.map
4956
+ //# sourceMappingURL=chunk-I6VUQ2CR.cjs.map
4957
+ //# sourceMappingURL=chunk-I6VUQ2CR.cjs.map