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.
Files changed (61) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +8 -6
  3. package/ai/chat-template.html +3 -3
  4. package/dist/Composition-PMWLJQPI.cjs +16 -0
  5. package/dist/{Composition-ZYRDRLYB.cjs.map → Composition-PMWLJQPI.cjs.map} +1 -1
  6. package/dist/Composition-ZCRJHBB5.js +7 -0
  7. package/dist/{Composition-HTHQ2RLF.js.map → Composition-ZCRJHBB5.js.map} +1 -1
  8. package/dist/Controller.cjs +3 -0
  9. package/dist/Controller.cjs.map +1 -1
  10. package/dist/Controller.d.cts +1 -1
  11. package/dist/Controller.d.ts +1 -1
  12. package/dist/Controller.js +3 -0
  13. package/dist/Controller.js.map +1 -1
  14. package/dist/{Movie-sV8nJr_f.d.ts → Movie--qSYKgAQ.d.ts} +2 -2
  15. package/dist/{Movie-BqrAWN0g.d.cts → Movie-svZQF-9Z.d.cts} +2 -2
  16. package/dist/{Spans-JYCTJV4V.js → Spans-FMAVPATS.js} +4 -4
  17. package/dist/{Spans-JYCTJV4V.js.map → Spans-FMAVPATS.js.map} +1 -1
  18. package/dist/{Spans-I7P7ZVZO.cjs → Spans-KTUPDFSR.cjs} +9 -9
  19. package/dist/{Spans-I7P7ZVZO.cjs.map → Spans-KTUPDFSR.cjs.map} +1 -1
  20. package/dist/{chunk-34YNRCMT.js → chunk-4PYVHLIK.js} +20 -5
  21. package/dist/chunk-4PYVHLIK.js.map +1 -0
  22. package/dist/{chunk-CJS2S5XU.cjs → chunk-ECLBZV4N.cjs} +20 -5
  23. package/dist/chunk-ECLBZV4N.cjs.map +1 -0
  24. package/dist/{chunk-NCM2WU7R.js → chunk-GX2WQV4A.js} +58 -35
  25. package/dist/chunk-GX2WQV4A.js.map +1 -0
  26. package/dist/chunk-K3XOEC3C.js +53 -0
  27. package/dist/chunk-K3XOEC3C.js.map +1 -0
  28. package/dist/{chunk-AES2L75M.cjs → chunk-NGK4E536.cjs} +103 -80
  29. package/dist/chunk-NGK4E536.cjs.map +1 -0
  30. package/dist/{chunk-DS2EWRV4.cjs → chunk-OGLRSR6C.cjs} +63 -63
  31. package/dist/{chunk-DS2EWRV4.cjs.map → chunk-OGLRSR6C.cjs.map} +1 -1
  32. package/dist/{chunk-RTZ5MW3E.js → chunk-UI7CEUTP.js} +3 -3
  33. package/dist/{chunk-RTZ5MW3E.js.map → chunk-UI7CEUTP.js.map} +1 -1
  34. package/dist/chunk-VQXZ2NOI.cjs +56 -0
  35. package/dist/chunk-VQXZ2NOI.cjs.map +1 -0
  36. package/dist/index.cjs +38 -33
  37. package/dist/index.d.cts +19 -2
  38. package/dist/index.d.ts +19 -2
  39. package/dist/index.js +4 -3
  40. package/dist/loader.css +1 -1
  41. package/dist/present.cjs +4 -3
  42. package/dist/present.cjs.map +1 -1
  43. package/dist/present.d.cts +1 -1
  44. package/dist/present.d.ts +1 -1
  45. package/dist/present.js +4 -3
  46. package/dist/present.js.map +1 -1
  47. package/dist/three.cjs +5 -5
  48. package/dist/three.js +1 -1
  49. package/docs/api.md +17 -2
  50. package/docs/dsl.md +7 -7
  51. package/llms-full.txt +26 -11
  52. package/package.json +1 -1
  53. package/skills/pixi-effects/SKILL.md +1 -1
  54. package/skills/pixi-effects/reference/pitfalls.md +1 -1
  55. package/skills/pixi-effects/template.html +6 -6
  56. package/dist/Composition-HTHQ2RLF.js +0 -7
  57. package/dist/Composition-ZYRDRLYB.cjs +0 -16
  58. package/dist/chunk-34YNRCMT.js.map +0 -1
  59. package/dist/chunk-AES2L75M.cjs.map +0 -1
  60. package/dist/chunk-CJS2S5XU.cjs.map +0 -1
  61. package/dist/chunk-NCM2WU7R.js.map +0 -1
@@ -1,11 +1,11 @@
1
- import { CompositionSequence, maskSourceOf } from './chunk-RTZ5MW3E.js';
2
- 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-34YNRCMT.js';
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
- var qualityMap = {
605
- "very-low": QUALITY_VERY_LOW,
606
- "low": QUALITY_LOW,
607
- "medium": QUALITY_MEDIUM,
608
- "high": QUALITY_HIGH,
609
- "very-high": QUALITY_VERY_HIGH
610
- };
611
- function bitrateOf(v, fallback) {
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 ?? (b.quality ? qualityMap[b.quality] : fallback);
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, QUALITY_HIGH));
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, QUALITY_HIGH) } });
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 videoBitrate = bitrateOf(eff.bitrate ?? "high", QUALITY_HIGH);
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, QUALITY_HIGH)
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-NCM2WU7R.js.map
5004
- //# sourceMappingURL=chunk-NCM2WU7R.js.map
5026
+ //# sourceMappingURL=chunk-GX2WQV4A.js.map
5027
+ //# sourceMappingURL=chunk-GX2WQV4A.js.map