pixi-effects 0.47.4 → 0.48.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 +18 -0
- package/README.md +8 -6
- package/ai/chat-template.html +3 -3
- package/dist/Composition-PMWLJQPI.cjs +16 -0
- package/dist/{Composition-ZYRDRLYB.cjs.map → Composition-PMWLJQPI.cjs.map} +1 -1
- package/dist/Composition-ZCRJHBB5.js +7 -0
- package/dist/{Composition-HTHQ2RLF.js.map → Composition-ZCRJHBB5.js.map} +1 -1
- package/dist/Controller.cjs +3 -0
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +1 -1
- package/dist/Controller.d.ts +1 -1
- package/dist/Controller.js +3 -0
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-sV8nJr_f.d.ts → Movie--qSYKgAQ.d.ts} +2 -2
- package/dist/{Movie-BqrAWN0g.d.cts → Movie-svZQF-9Z.d.cts} +2 -2
- package/dist/{Spans-JYCTJV4V.js → Spans-FMAVPATS.js} +4 -4
- package/dist/{Spans-JYCTJV4V.js.map → Spans-FMAVPATS.js.map} +1 -1
- package/dist/{Spans-I7P7ZVZO.cjs → Spans-KTUPDFSR.cjs} +9 -9
- package/dist/{Spans-I7P7ZVZO.cjs.map → Spans-KTUPDFSR.cjs.map} +1 -1
- package/dist/{chunk-34YNRCMT.js → chunk-4PYVHLIK.js} +20 -5
- package/dist/chunk-4PYVHLIK.js.map +1 -0
- package/dist/{chunk-CJS2S5XU.cjs → chunk-ECLBZV4N.cjs} +20 -5
- package/dist/chunk-ECLBZV4N.cjs.map +1 -0
- package/dist/{chunk-NCM2WU7R.js → chunk-GX2WQV4A.js} +58 -35
- package/dist/chunk-GX2WQV4A.js.map +1 -0
- package/dist/chunk-K3XOEC3C.js +53 -0
- package/dist/chunk-K3XOEC3C.js.map +1 -0
- package/dist/{chunk-AES2L75M.cjs → chunk-NGK4E536.cjs} +103 -80
- package/dist/chunk-NGK4E536.cjs.map +1 -0
- package/dist/{chunk-DS2EWRV4.cjs → chunk-OGLRSR6C.cjs} +63 -63
- package/dist/{chunk-DS2EWRV4.cjs.map → chunk-OGLRSR6C.cjs.map} +1 -1
- package/dist/{chunk-RTZ5MW3E.js → chunk-UI7CEUTP.js} +3 -3
- package/dist/{chunk-RTZ5MW3E.js.map → chunk-UI7CEUTP.js.map} +1 -1
- package/dist/chunk-VQXZ2NOI.cjs +56 -0
- package/dist/chunk-VQXZ2NOI.cjs.map +1 -0
- package/dist/index.cjs +38 -33
- package/dist/index.d.cts +19 -2
- package/dist/index.d.ts +19 -2
- package/dist/index.js +4 -3
- package/dist/loader.css +1 -1
- package/dist/present.cjs +4 -3
- package/dist/present.cjs.map +1 -1
- package/dist/present.d.cts +1 -1
- package/dist/present.d.ts +1 -1
- package/dist/present.js +4 -3
- package/dist/present.js.map +1 -1
- package/dist/three.cjs +5 -5
- package/dist/three.js +1 -1
- package/docs/api.md +17 -2
- package/docs/dsl.md +7 -7
- package/llms-full.txt +26 -11
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +1 -1
- package/skills/pixi-effects/reference/pitfalls.md +1 -1
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-HTHQ2RLF.js +0 -7
- package/dist/Composition-ZYRDRLYB.cjs +0 -16
- package/dist/chunk-34YNRCMT.js.map +0 -1
- package/dist/chunk-AES2L75M.cjs.map +0 -1
- package/dist/chunk-CJS2S5XU.cjs.map +0 -1
- package/dist/chunk-NCM2WU7R.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { CompositionSequence, maskSourceOf } from './chunk-
|
|
2
|
-
import {
|
|
1
|
+
import { CompositionSequence, maskSourceOf } from './chunk-UI7CEUTP.js';
|
|
2
|
+
import { loadMediabunny } from './chunk-K3XOEC3C.js';
|
|
3
|
+
import { EXPRESSION_NAMES, createTimeline, lengthen, ensureFilterLibrary, checkEase, parseEase, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, trimPolylines, rand, DEG, timeRemapOf, contentLength, resolveAt, parseSpring, springDuration, spansMeta } from './chunk-4PYVHLIK.js';
|
|
3
4
|
import { analyzeAudio, fft, resampleThrough, playSpan, measureLoudness } from './chunk-G4PXCV2B.js';
|
|
4
5
|
import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-JBDBICS2.js';
|
|
5
6
|
import { describeLayer } from './chunk-BFRWSV3R.js';
|
|
6
7
|
import { warnUnknownOptions, suggestName } from './chunk-WGRWFQOB.js';
|
|
7
8
|
import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, Texture, UniformGroup, GpuProgram, GlProgram, defaultFilterVert, Text, CanvasTextGenerator, CanvasPool } from 'pixi.js';
|
|
8
|
-
import { Output, BufferTarget, CanvasSource, AudioBufferSource, Input, ALL_FORMATS, BlobSource, WavOutputFormat, Conversion, VideoSampleSink, OggOutputFormat, QUALITY_VERY_HIGH, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_VERY_LOW, MkvOutputFormat, WebMOutputFormat, MovOutputFormat, Mp4OutputFormat, canEncodeVideo, getFirstEncodableAudioCodec } from 'mediabunny';
|
|
9
9
|
|
|
10
10
|
// src/core/gpuShare.ts
|
|
11
11
|
function sharedGpuOf(renderer) {
|
|
@@ -41,7 +41,8 @@ function ensureLoadersRegistered(audioContext) {
|
|
|
41
41
|
extension: { type: ExtensionType.LoadParser, name: "pixi-effects-video", priority: 1 },
|
|
42
42
|
test: (url) => /\.(mp4|webm|mov|mkv)$/i.test(url),
|
|
43
43
|
async load(url) {
|
|
44
|
-
const blob = await fetch(url).then((r) => r.blob());
|
|
44
|
+
const [M, blob] = await Promise.all([loadMediabunny(), fetch(url).then((r) => r.blob())]);
|
|
45
|
+
const { Input, BlobSource, ALL_FORMATS, Output, WavOutputFormat, BufferTarget, Conversion, VideoSampleSink } = M;
|
|
45
46
|
const input = new Input({ source: new BlobSource(blob), formats: ALL_FORMATS });
|
|
46
47
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
47
48
|
if (!videoTrack || !await videoTrack.canDecode()) {
|
|
@@ -248,6 +249,19 @@ async function normalizeMix(buffer, target, between) {
|
|
|
248
249
|
gainDb = next;
|
|
249
250
|
}
|
|
250
251
|
}
|
|
252
|
+
if (original && final.integratedLufs !== null && m.integratedLufs !== null) {
|
|
253
|
+
const plainDb = Math.min(target - m.integratedLufs, LOUDNESS_CEILING_DB - m.samplePeakDb);
|
|
254
|
+
if (final.integratedLufs < m.integratedLufs + plainDb - 0.1) {
|
|
255
|
+
const k = 10 ** (plainDb / 20);
|
|
256
|
+
for (let ch = 0; ch < chans.length; ch++) {
|
|
257
|
+
const o = original[ch], d = chans[ch];
|
|
258
|
+
for (let i = 0; i < d.length; i++) d[i] = o[i] * k;
|
|
259
|
+
}
|
|
260
|
+
gainDb = plainDb;
|
|
261
|
+
applied = { reductionDb: 0, at: 0 };
|
|
262
|
+
final = measureLoudness(chans, sr);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
251
265
|
let trimDb = 0;
|
|
252
266
|
if (final.truePeakDb > LOUDNESS_CEILING_DB) {
|
|
253
267
|
trimDb = final.truePeakDb - LOUDNESS_CEILING_DB + 0.01;
|
|
@@ -585,10 +599,10 @@ var AUDIO_PREFERENCE = {
|
|
|
585
599
|
mkv: ["opus", "vorbis", "aac", "flac", "mp3"],
|
|
586
600
|
ogg: ["opus", "vorbis", "flac"]
|
|
587
601
|
};
|
|
588
|
-
async function chooseAudioCodec(fmt, requested, container, audio, bitrate) {
|
|
602
|
+
async function chooseAudioCodec(M, fmt, requested, container, audio, bitrate) {
|
|
589
603
|
const supported = container.getSupportedAudioCodecs?.();
|
|
590
604
|
const preferred = AUDIO_PREFERENCE[fmt].filter((c) => !supported || supported.includes(c));
|
|
591
|
-
const canEncode = (list) => getFirstEncodableAudioCodec(list, { numberOfChannels: audio.numberOfChannels ?? 2, sampleRate: audio.sampleRate, bitrate });
|
|
605
|
+
const canEncode = (list) => M.getFirstEncodableAudioCodec(list, { numberOfChannels: audio.numberOfChannels ?? 2, sampleRate: audio.sampleRate, bitrate });
|
|
592
606
|
if (requested) {
|
|
593
607
|
if (await canEncode([requested])) return requested;
|
|
594
608
|
const alt = await canEncode(preferred.filter((c) => c !== requested));
|
|
@@ -601,27 +615,34 @@ async function chooseAudioCodec(fmt, requested, container, audio, bitrate) {
|
|
|
601
615
|
}
|
|
602
616
|
return chosen;
|
|
603
617
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
618
|
+
function qualityOf(M, name) {
|
|
619
|
+
switch (name) {
|
|
620
|
+
case "very-low":
|
|
621
|
+
return M.QUALITY_VERY_LOW;
|
|
622
|
+
case "low":
|
|
623
|
+
return M.QUALITY_LOW;
|
|
624
|
+
case "medium":
|
|
625
|
+
return M.QUALITY_MEDIUM;
|
|
626
|
+
case "very-high":
|
|
627
|
+
return M.QUALITY_VERY_HIGH;
|
|
628
|
+
default:
|
|
629
|
+
return M.QUALITY_HIGH;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function bitrateOf(M, v) {
|
|
612
633
|
const b = parseBitrate(v);
|
|
613
|
-
return b.bps ?? (
|
|
634
|
+
return b.bps ?? qualityOf(M, b.quality ?? "high");
|
|
614
635
|
}
|
|
615
|
-
function makeOutputFormat(name) {
|
|
636
|
+
function makeOutputFormat(M, name) {
|
|
616
637
|
switch (name) {
|
|
617
638
|
case "mp4":
|
|
618
|
-
return new Mp4OutputFormat({ fastStart: "in-memory" });
|
|
639
|
+
return new M.Mp4OutputFormat({ fastStart: "in-memory" });
|
|
619
640
|
case "mov":
|
|
620
|
-
return new MovOutputFormat({ fastStart: "in-memory" });
|
|
641
|
+
return new M.MovOutputFormat({ fastStart: "in-memory" });
|
|
621
642
|
case "webm":
|
|
622
|
-
return new WebMOutputFormat();
|
|
643
|
+
return new M.WebMOutputFormat();
|
|
623
644
|
case "mkv":
|
|
624
|
-
return new MkvOutputFormat();
|
|
645
|
+
return new M.MkvOutputFormat();
|
|
625
646
|
}
|
|
626
647
|
}
|
|
627
648
|
function sliceAudio(buffer, fromSec, toSec, fadeIn, fadeOut) {
|
|
@@ -636,18 +657,19 @@ async function exportAudioOnly(movie, options, fmt, range2) {
|
|
|
636
657
|
throw new Error(`pixi-effects: render({ format: '${fmt}' }): this movie has no sound to export (add an audio layer, an sfx or a music score); for the picture use mp4, webm, mov or mkv`);
|
|
637
658
|
}
|
|
638
659
|
if (options.video) console.warn(`pixi-effects: render({ format: '${fmt}' }): video options are ignored, only the sound is written`);
|
|
660
|
+
const M = await loadMediabunny();
|
|
639
661
|
const audio = range2.partial ? sliceAudio(movie.audioBuffer, range2.fromSec, range2.toSec, range2.fromFrame > 0, range2.toFrame < movie.totalFrames) : movie.audioBuffer;
|
|
640
|
-
const container = fmt === "wav" ? new WavOutputFormat() : new OggOutputFormat();
|
|
662
|
+
const container = fmt === "wav" ? new M.WavOutputFormat() : new M.OggOutputFormat();
|
|
641
663
|
let codec;
|
|
642
664
|
if (fmt === "wav") {
|
|
643
665
|
codec = options.audio?.codec ?? "pcm-s16";
|
|
644
666
|
const held = container.getSupportedAudioCodecs();
|
|
645
667
|
if (!held.includes(codec)) throw new Error(`pixi-effects: render({ format: 'wav', audio: { codec: "${codec}" } }): wav holds ${held.join(", ")} (the usual is pcm-s16)`);
|
|
646
668
|
} else {
|
|
647
|
-
codec = await chooseAudioCodec("ogg", options.audio?.codec, container, audio, bitrateOf(options.audio?.bitrate
|
|
669
|
+
codec = await chooseAudioCodec(M, "ogg", options.audio?.codec, container, audio, bitrateOf(M, options.audio?.bitrate));
|
|
648
670
|
}
|
|
649
|
-
const output = new Output({ format: container, target: new BufferTarget() });
|
|
650
|
-
const source = new AudioBufferSource({ codec, ...fmt === "wav" ? {} : { bitrate: bitrateOf(options.audio?.bitrate
|
|
671
|
+
const output = new M.Output({ format: container, target: new M.BufferTarget() });
|
|
672
|
+
const source = new M.AudioBufferSource({ codec, ...fmt === "wav" ? {} : { bitrate: bitrateOf(M, options.audio?.bitrate) } });
|
|
651
673
|
output.addAudioTrack(source);
|
|
652
674
|
movie.emit("progress", { progress: 0, frame: range2.fromFrame, totalFrames: movie.totalFrames });
|
|
653
675
|
await output.start();
|
|
@@ -665,21 +687,22 @@ async function exportFrames(movie, options = {}) {
|
|
|
665
687
|
if (!["mp4", "mov", "webm", "mkv"].includes(fmt)) throw new Error(`pixi-effects: render({ format: "${String(fmt)}" }): use mp4, mov, webm, mkv (a picture) or wav, ogg (only the sound)`);
|
|
666
688
|
warnUnknownOptions("movie.render({ video })", options.video, ["codec", "bitrate", "hardware", "keyFrameInterval"]);
|
|
667
689
|
warnUnknownOptions("movie.render({ audio })", options.audio, ["codec", "bitrate"]);
|
|
668
|
-
const
|
|
690
|
+
const M = await loadMediabunny();
|
|
691
|
+
const videoBitrate = bitrateOf(M, eff.bitrate ?? "high");
|
|
669
692
|
const opts = {
|
|
670
693
|
format: fmt,
|
|
671
694
|
video: { codec: "", bitrate: videoBitrate },
|
|
672
695
|
audio: {
|
|
673
696
|
codec: options.audio?.codec ?? (fmt === "mp4" || fmt === "mov" ? "aac" : "opus"),
|
|
674
|
-
bitrate: bitrateOf(options.audio?.bitrate
|
|
697
|
+
bitrate: bitrateOf(M, options.audio?.bitrate)
|
|
675
698
|
}
|
|
676
699
|
};
|
|
677
|
-
const container = makeOutputFormat(opts.format);
|
|
700
|
+
const container = makeOutputFormat(M, opts.format);
|
|
678
701
|
const audio = movie.audioBuffer && range2.partial ? sliceAudio(movie.audioBuffer, range2.fromSec, range2.toSec, range2.fromFrame > 0, range2.toFrame < movie.totalFrames) : movie.audioBuffer;
|
|
679
|
-
if (audio) opts.audio.codec = await chooseAudioCodec(fmt, options.audio?.codec, container, audio, opts.audio.bitrate);
|
|
680
|
-
const output = new Output({
|
|
702
|
+
if (audio) opts.audio.codec = await chooseAudioCodec(M, fmt, options.audio?.codec, container, audio, opts.audio.bitrate);
|
|
703
|
+
const output = new M.Output({
|
|
681
704
|
format: container,
|
|
682
|
-
target: new BufferTarget()
|
|
705
|
+
target: new M.BufferTarget()
|
|
683
706
|
});
|
|
684
707
|
const mb = eff.motionBlur !== void 0 ? resolveMotionBlur(eff.motionBlur, "movie.render()") : movie.motionBlur ?? null;
|
|
685
708
|
const stage = movie.app.canvas;
|
|
@@ -691,7 +714,7 @@ async function exportFrames(movie, options = {}) {
|
|
|
691
714
|
fmt,
|
|
692
715
|
requested: options.video?.codec,
|
|
693
716
|
supported: container.getSupportedVideoCodecs(),
|
|
694
|
-
canEncode: (c) => canEncodeVideo(c, { width: picture.width, height: picture.height, bitrate: opts.video.bitrate })
|
|
717
|
+
canEncode: (c) => M.canEncodeVideo(c, { width: picture.width, height: picture.height, bitrate: opts.video.bitrate })
|
|
695
718
|
});
|
|
696
719
|
if (chosen.fellBackFrom) console.warn(`pixi-effects: this browser cannot encode ${chosen.fellBackFrom.toUpperCase()} video, so the ${fmt} carries ${chosen.codec.toUpperCase()} instead (name a codec with render({ video: { codec } }) to choose yourself).`);
|
|
697
720
|
opts.video.codec = chosen.codec;
|
|
@@ -699,14 +722,14 @@ async function exportFrames(movie, options = {}) {
|
|
|
699
722
|
if (hardware !== void 0 && !["no-preference", "prefer-hardware", "prefer-software"].includes(hardware)) {
|
|
700
723
|
throw new Error(`pixi-effects: render({ video: { hardware: "${String(hardware)}" } }): use 'prefer-hardware', 'prefer-software' or 'no-preference'`);
|
|
701
724
|
}
|
|
702
|
-
const canvasSource = new CanvasSource(picture, {
|
|
725
|
+
const canvasSource = new M.CanvasSource(picture, {
|
|
703
726
|
codec: opts.video.codec,
|
|
704
727
|
bitrate: opts.video.bitrate,
|
|
705
728
|
...hardware ? { hardwareAcceleration: hardware } : {}
|
|
706
729
|
});
|
|
707
730
|
output.addVideoTrack(canvasSource, { frameRate: movie.frameRate });
|
|
708
731
|
if (audio) {
|
|
709
|
-
const audioSource = new AudioBufferSource({
|
|
732
|
+
const audioSource = new M.AudioBufferSource({
|
|
710
733
|
codec: opts.audio.codec,
|
|
711
734
|
bitrate: opts.audio.bitrate
|
|
712
735
|
}, { startTimestamp: opts.audio.codec === "aac" ? -2112 / audio.sampleRate : 0 });
|
|
@@ -5000,5 +5023,5 @@ function withFade(spec, opts) {
|
|
|
5000
5023
|
}
|
|
5001
5024
|
|
|
5002
5025
|
export { Movie, animateText, audioEnvelope, bpmEnvelope, cameraPath, computeEnvelope, dismissLoader, failLoader, followPath, kenBurns, measureText, musicBuffer, musicEnvelope, musicEvents, orbit, parseEase2 as parseEase, particles, react, resolveLoader, samplePath, splitText, stagger, wiggle, withFade };
|
|
5003
|
-
//# sourceMappingURL=chunk-
|
|
5004
|
-
//# sourceMappingURL=chunk-
|
|
5026
|
+
//# sourceMappingURL=chunk-GX2WQV4A.js.map
|
|
5027
|
+
//# sourceMappingURL=chunk-GX2WQV4A.js.map
|