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.
- package/CHANGELOG.md +12 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/ai/tools/check.mjs +2 -2
- package/ai/tools/lookup.mjs +6 -1
- package/ai/tools/pixi-effects.mjs +1 -0
- package/ai/tools/render.mjs +17 -1
- package/dist/{chunk-O66JGPVV.cjs → chunk-23SXKSUI.cjs} +18 -4
- package/dist/chunk-23SXKSUI.cjs.map +1 -0
- package/dist/{chunk-K24FF4BT.js → chunk-4SEL6W7H.js} +18 -4
- package/dist/chunk-4SEL6W7H.js.map +1 -0
- package/dist/{chunk-7M63Y6MO.cjs → chunk-I6VUQ2CR.cjs} +105 -25
- package/dist/chunk-I6VUQ2CR.cjs.map +1 -0
- package/dist/{chunk-2E74SP63.js → chunk-W46VWAZC.js} +101 -21
- package/dist/chunk-W46VWAZC.js.map +1 -0
- package/dist/index.cjs +27 -27
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -2
- package/dist/loader.css +1 -1
- package/dist/present.cjs +2 -2
- package/dist/present.js +2 -2
- package/dist/sound.cjs +2 -2
- package/dist/sound.js +1 -1
- package/docs/api.md +1 -1
- package/docs/dsl.md +2 -2
- package/llms-full.txt +7 -7
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +1 -1
- package/skills/pixi-effects/reference/cheatsheet.md +2 -2
- package/skills/pixi-effects/reference/recipes.md +1 -1
- package/skills/pixi-effects/template.html +6 -6
- package/dist/chunk-2E74SP63.js.map +0 -1
- package/dist/chunk-7M63Y6MO.cjs.map +0 -1
- package/dist/chunk-K24FF4BT.js.map +0 -1
- package/dist/chunk-O66JGPVV.cjs.map +0 -1
|
@@ -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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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, ...
|
|
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
|
|
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
|
|
4452
|
-
const
|
|
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
|
|
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 -
|
|
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 +
|
|
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 + (
|
|
4485
|
-
const local = outTween.at !== void 0 ? Math.max(0, outTween.at -
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
4877
|
-
//# sourceMappingURL=chunk-
|
|
4956
|
+
//# sourceMappingURL=chunk-I6VUQ2CR.cjs.map
|
|
4957
|
+
//# sourceMappingURL=chunk-I6VUQ2CR.cjs.map
|