pixi-effects 0.47.1 → 0.47.3
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 +23 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/dist/Composition-6TXRN65C.js +7 -0
- package/dist/{Composition-ME3L3EFT.js.map → Composition-6TXRN65C.js.map} +1 -1
- package/dist/Composition-IC274CKO.cjs +16 -0
- package/dist/{Composition-AZEATHJB.cjs.map → Composition-IC274CKO.cjs.map} +1 -1
- package/dist/{Spans-YV7K4X5I.js → Spans-3T6ZEEN4.js} +6 -6
- package/dist/{Spans-YV7K4X5I.js.map → Spans-3T6ZEEN4.js.map} +1 -1
- package/dist/{Spans-LTKEUHDN.cjs → Spans-M5KMHT6K.cjs} +11 -11
- package/dist/{Spans-LTKEUHDN.cjs.map → Spans-M5KMHT6K.cjs.map} +1 -1
- package/dist/{blendModes-56NHK2MP.js → blendModes-S72MBYKY.js} +4 -4
- package/dist/{blendModes-56NHK2MP.js.map → blendModes-S72MBYKY.js.map} +1 -1
- package/dist/{blendModes-ONOPMMAB.cjs → blendModes-Y56QAMC4.cjs} +5 -5
- package/dist/{blendModes-ONOPMMAB.cjs.map → blendModes-Y56QAMC4.cjs.map} +1 -1
- package/dist/{chunk-4NOTVQ3I.cjs → chunk-33OVRSXF.cjs} +29 -29
- package/dist/{chunk-4NOTVQ3I.cjs.map → chunk-33OVRSXF.cjs.map} +1 -1
- package/dist/{chunk-OVTTCX2E.js → chunk-6LA7W362.js} +136 -64
- package/dist/chunk-6LA7W362.js.map +1 -0
- package/dist/{chunk-7IDEWQPH.js → chunk-G4PXCV2B.js} +12 -8
- package/dist/chunk-G4PXCV2B.js.map +1 -0
- package/dist/{chunk-BFERTUXK.cjs → chunk-GDY56V74.cjs} +92 -92
- package/dist/{chunk-BFERTUXK.cjs.map → chunk-GDY56V74.cjs.map} +1 -1
- package/dist/{chunk-6MHJKCXO.js → chunk-HGKXEPDJ.js} +5 -5
- package/dist/{chunk-6MHJKCXO.js.map → chunk-HGKXEPDJ.js.map} +1 -1
- package/dist/{chunk-QMVK55LC.js → chunk-HX73PCVU.js} +5 -5
- package/dist/{chunk-QMVK55LC.js.map → chunk-HX73PCVU.js.map} +1 -1
- package/dist/{chunk-NVKHUAHU.js → chunk-JVO2W77G.js} +5 -5
- package/dist/chunk-JVO2W77G.js.map +1 -0
- package/dist/{chunk-UTDELFOU.js → chunk-PC7DFDCV.js} +4 -4
- package/dist/{chunk-UTDELFOU.js.map → chunk-PC7DFDCV.js.map} +1 -1
- package/dist/{chunk-53DXJEO7.cjs → chunk-RU4N3B5B.cjs} +5 -5
- package/dist/{chunk-53DXJEO7.cjs.map → chunk-RU4N3B5B.cjs.map} +1 -1
- package/dist/{chunk-YXIEPTFG.cjs → chunk-VTGJRKW4.cjs} +182 -110
- package/dist/chunk-VTGJRKW4.cjs.map +1 -0
- package/dist/{chunk-NEMWRJQS.cjs → chunk-YSTOH5PC.cjs} +5 -5
- package/dist/chunk-YSTOH5PC.cjs.map +1 -0
- package/dist/{chunk-SMHTOWXG.cjs → chunk-ZAHCGFGC.cjs} +12 -8
- package/dist/chunk-ZAHCGFGC.cjs.map +1 -0
- package/dist/index.cjs +37 -37
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +6 -6
- package/dist/loader.css +1 -1
- package/dist/present.cjs +6 -6
- package/dist/present.js +6 -6
- package/dist/sound.cjs +2 -2
- package/dist/sound.js +1 -1
- package/dist/three.cjs +93 -26
- package/dist/three.cjs.map +1 -1
- package/dist/three.d.cts +19 -4
- package/dist/three.d.ts +19 -4
- package/dist/three.js +89 -22
- package/dist/three.js.map +1 -1
- package/docs/dsl.md +9 -4
- package/llms-full.txt +12 -7
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +1 -1
- package/skills/pixi-effects/reference/cheatsheet.md +1 -1
- package/skills/pixi-effects/reference/recipes.md +1 -1
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-AZEATHJB.cjs +0 -16
- package/dist/Composition-ME3L3EFT.js +0 -7
- package/dist/chunk-7IDEWQPH.js.map +0 -1
- package/dist/chunk-NEMWRJQS.cjs.map +0 -1
- package/dist/chunk-NVKHUAHU.js.map +0 -1
- package/dist/chunk-OVTTCX2E.js.map +0 -1
- package/dist/chunk-SMHTOWXG.cjs.map +0 -1
- package/dist/chunk-YXIEPTFG.cjs.map +0 -1
package/docs/dsl.md
CHANGED
|
@@ -63,7 +63,7 @@ interface SequenceCommon {
|
|
|
63
63
|
| `text`, `image`, `video` | the **top-left** corner | `anchorX` / `anchorY: 0.5` to centre |
|
|
64
64
|
| `composition` | its top-left corner | `pivotX` / `pivotY` = the point that sits at `x, y` and that rotation / scale turn about |
|
|
65
65
|
|
|
66
|
-
Shape geometry (`width height radius anchorX …`) may be written at the top level or in `initial`; the top level wins. `rotation`, `skew*` and `rotationX/Y` are in **degrees**.
|
|
66
|
+
Shape geometry (`width height radius anchorX …`) may be written at the top level or in `initial`; the top level wins. `rotation`, `skew*` and `rotationX/Y` are in **degrees**. **`skewX` / `skewY` are PixiJS's, not CSS's shear:** they turn an axis about the layer's anchor (the anchored point stays), so `skewX: 40` tilts the layer's vertical edges by 40° and shortens them to `cos 40°` of their length (a CSS `skewX(40deg)` or After Effects' skew keeps the height and moves the bottom by `h · tan 40°`). A point (x, y) of the layer, measured from its anchor, lands at `(x cos β + y sin α, x sin β + y cos α)` for `skewX: α`, `skewY: β`. Pinned by `tests/films/geometry-skew`.
|
|
67
67
|
|
|
68
68
|
A layer is **not removed** when its lifespan `[at, at + duration)` ends: it is only hidden, and it keeps its last animated values. Outside the lifespan it is invisible. **z-order is array order** (later = on top). Build scenes by giving layers `at` / `duration` and stacking them; a full-screen background rect in each scene hides what is below it.
|
|
69
69
|
|
|
@@ -90,7 +90,7 @@ Renders a [PIXI.Text](https://pixijs.com/8.x/guides/components/scene-objects/tex
|
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
**Wrapping and fonts for Japanese, Chinese and Korean.** `wordWrap: true` breaks a line at spaces. Japanese and Chinese have none: with `wordWrap` alone a long line does not wrap and runs off the canvas, and `breakWords: true` makes it break between any two characters, so a line can end with one to three characters alone (an orphan) and split a word or phrase in the middle. For a clean break write the line break yourself (`'あの日言えなかった言葉が、\n風に溶けていく'`, after a comma or a particle) and size the font so each line fits; `check` lists a line that is cut by the canvas edge, but it does not judge an ugly break, so look at the sheet. (Korean writes spaces between words, so `wordWrap` breaks there; a very long word still needs `breakWords`.) No font is bundled: the machine's own fonts are used, and a font that lacks the script's characters is replaced for those characters by whatever the machine has, so the look differs from machine to machine. For a result that looks the same everywhere, load a web font that covers the script (for example Noto Sans JP, Noto Sans KR for Hangul, Noto Sans SC) and `await document.fonts.load('40px "Noto Sans KR"')` before `movie.init` (see pitfall 103); give the family in `style.fontFamily`.
|
|
93
|
+
**Wrapping and fonts for Japanese, Chinese and Korean.** `wordWrap: true` breaks a line at spaces. Japanese and Chinese have none: with `wordWrap` alone a long line does not wrap and runs off the canvas, and `breakWords: true` makes it break between any two characters, so a line can end with one to three characters alone (an orphan) and split a word or phrase in the middle. **There are no line-start rules (kinsoku) in horizontal wrapping:** with `breakWords` a line may START with `、`, `。` or a small kana such as `ゃ` (pinned by `tests/films/text-japanese-wrap`; vertical `splitText` has none either). For a clean break write the line break yourself (`'あの日言えなかった言葉が、\n風に溶けていく'`, after a comma or a particle) and size the font so each line fits; `check` lists a line that is cut by the canvas edge, but it does not judge an ugly break, so look at the sheet. (Korean writes spaces between words, so `wordWrap` breaks there; a very long word still needs `breakWords`.) No font is bundled: the machine's own fonts are used, and a font that lacks the script's characters is replaced for those characters by whatever the machine has, so the look differs from machine to machine. For a result that looks the same everywhere, load a web font that covers the script (for example Noto Sans JP, Noto Sans KR for Hangul, Noto Sans SC) and `await document.fonts.load('40px "Noto Sans KR"')` before `movie.init` (see pitfall 103); give the family in `style.fontFamily`.
|
|
94
94
|
|
|
95
95
|
**Right-to-left and complex scripts (measured in trials).** With a font that covers the script the text is shaped correctly: Arabic letters connect and read right to left, Hebrew reads right to left, Thai and Devanagari marks stack. The direction follows the first strong letter of the line; place it with `anchorX: 1` and `style.align: 'right'`. Three traps: (1) marks that hang below the font's box can be cut off (the dots of an Arabic ي turn "في" into "فى"): give the layer `style.padding` (30 at a size of 120), and `check` cannot see it, so look at the picture; (2) `splitText` and `animateText` place words and letters from left to right, so an Arabic line comes out in reverse order, and `by: 'chars'` breaks the connected forms: split the line by words yourself and place each word at the right edge minus `measureText` of the logical text up to that word; (3) `visibleChars` shapes each prefix alone, so the last letter can show in its isolated form until the next one arrives: uncovering the whole line with a mask whose width grows keeps the connected forms.
|
|
96
96
|
|
|
@@ -862,6 +862,7 @@ three({ type: 'three', width: 'GW', height: 'GH',
|
|
|
862
862
|
setup: async (ctx) => {
|
|
863
863
|
const screen = await threeLayerTexture(ctx, 'screen', { resolution: 2 }); // { material, texture, width, height, dispose }
|
|
864
864
|
ctx.scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.6, 0.9), screen.material));
|
|
865
|
+
ctx.camera.position.set(0, 0, 1.6); // the layer's camera starts at the origin, inside the plane: step back (a plane of this size fills the picture at about 1.6)
|
|
865
866
|
} })
|
|
866
867
|
```
|
|
867
868
|
|
|
@@ -871,8 +872,12 @@ three({ type: 'three', width: 'GW', height: 'GH',
|
|
|
871
872
|
- **A function of the time.** The 2D layer is drawn when the three layer draws, after the timeline has put every layer at the frame: a seek, an export and a repeat give the same picture (`check` draws the layer twice, in two orders, as for any three layer). A layer that has not started yet, or has ended, shows nothing on the mesh.
|
|
872
873
|
- **A rebuild** (`setVars`, `resize`) keeps the three layer and shows the layer of the new tree (the text that changed shows on the mesh); a layer that changed size is drawn stretched to the texture.
|
|
873
874
|
- **Seen small or at a slant** the texture is filtered, not aliased (1-pixel stripes on a plane a quarter of the picture come out as one even grey, not black and white stripes; four taps along a slant). The mip levels are PixiJS's own on the shared GPU, averaged in the stored sRGB values, so very fine bright detail seen very small is a little darker there than on a read-back canvas, whose mipmaps three makes in linear light (stripes average to 128 and 188).
|
|
874
|
-
- **A click reaches the screen.** `movie.pick(x, y)` on a point of the mesh answers for the layers INSIDE the 2D layer, at the point of its picture under the pointer: the top hit is `screen/button` (a flat hit with `local` in the layer's own pixels), with `via: { path: 'phone', object: 'face' }` naming the three layer and the mesh it came through; under it come the screen's other layers, then the mesh itself. `only` / `ignore` see through the mesh (`only: ['screen']` is the screen's layers wherever they are shown), `movie.hover` and the click recipes work on a phone's screen as they do on a flat layer, and a layer that is not alive does not answer. A page asks the layer by its own name, so the button is one `pick` away: `if (movie.pick(...movie.canvasPoint(event), { only: ['button'] })) …`.
|
|
875
|
-
- **Not in this version
|
|
875
|
+
- **A click reaches the screen.** `movie.pick(x, y)` on a point of the mesh answers for the layers INSIDE the 2D layer, at the point of its picture under the pointer: the top hit is `screen/button` (a flat hit with `local` in the layer's own pixels), with `via: { path: 'phone', object: 'face' }` naming the three layer and the mesh it came through (`object` is the mesh's `name`: set it, `face.name = 'face'`, or it is empty); under it come the screen's other layers, then the mesh itself. `only` / `ignore` see through the mesh (`only: ['screen']` is the screen's layers wherever they are shown), `movie.hover` and the click recipes work on a phone's screen as they do on a flat layer, and a layer that is not alive does not answer. A page asks the layer by its own name, so the button is one `pick` away: `if (movie.pick(...movie.canvasPoint(event), { only: ['button'] })) …`.
|
|
876
|
+
- **Not in this version** (found by a review of 0.47.1; what the picture shows is right, what `pick` says is not in these cases):
|
|
877
|
+
- The ray stops at the first mesh it touches, even a nearly clear one: a glass pane in front of a screen, or the empty part of a `transparent: true` sticker in front of something, answers for the glass or the sticker, not for what shows through it.
|
|
878
|
+
- `hit.three.grab()` of a three layer that is itself inside a screen moves with the canvas point, not with the point on the mesh: a drag of an object inside a screen jumps on its first move.
|
|
879
|
+
- A screen composition must be only a screen: it cannot also be a matte for another layer (that layer is cut away completely), and a screen that is part of a transition (wipe, iris, dissolve, zoom, luma) is replaced by the transition's own picture.
|
|
880
|
+
- A mask or matte on the screen composition itself is placed in its PARENT's space while its content is drawn from the origin: unless the screen sits at x: 0, y: 0 (anchor 0, scale 1, rotation 0) the mask lands shifted on the mesh (a warning says so); mask a layer INSIDE the screen instead.
|
|
876
881
|
|
|
877
882
|
### Keyframe paths: `three.<name>.<path>`
|
|
878
883
|
|
package/llms-full.txt
CHANGED
|
@@ -6,7 +6,7 @@ Concatenation of the skill, cheatsheet, pitfalls, recipes and the DSL / API / me
|
|
|
6
6
|
|
|
7
7
|
<!-- ===== skills/pixi-effects/SKILL.md ===== -->
|
|
8
8
|
|
|
9
|
-
> Written for pixi-effects 0.47.
|
|
9
|
+
> Written for pixi-effects 0.47.3. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
|
|
10
10
|
|
|
11
11
|
# pixi-effects — write a video as data
|
|
12
12
|
|
|
@@ -168,7 +168,7 @@ threeD: true // opt into 2.5D (see below)
|
|
|
168
168
|
blendMode: 'normal' | 'add' | 'screen' | 'multiply' | 'overlay' | 'soft-light' | 'hard-light' | 'color-dodge' | 'color-burn' | 'darken' | 'lighten' | 'difference' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity' | 'linear-burn' // CSS names (hyphenated); blends with EVERYTHING BELOW the layer; add / screen brighten (glows), multiply darkens, soft-light / overlay for colour casts and contrast, color-dodge for light sources; on a composition the children each blend (to blend it as one picture: threeD: true or a filter); the 14 new ones cost ~2 ms a layer on WebGL (10+ on screen warns)
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
Animatable props (`initial` / keyframes): `x y alpha rotation scale scaleX scaleY pivotX pivotY anchorX anchorY skewX skewY tint width height visible autoAlpha`, plus for shapes their style/geometry, for text `fill`, for audio `volume`, for 3D `z rotationX rotationY`, filter params as `'filters.<name>.<param>'`, three objects as `'three.<obj>.<path>'`. **These go in `initial` / keyframes, never at the top level of a shape or text layer** (top-level `x`, `rotation`, `strokeColor`, `fillAlpha` each warn "is not a
|
|
171
|
+
Animatable props (`initial` / keyframes): `x y alpha rotation scale scaleX scaleY pivotX pivotY anchorX anchorY skewX skewY tint width height visible autoAlpha`, plus for shapes their style/geometry, for text `fill`, for audio `volume`, for 3D `z rotationX rotationY`, filter params as `'filters.<name>.<param>'`, three objects as `'three.<obj>.<path>'`. **These go in `initial` / keyframes, never at the top level of a shape or text layer** (top-level `x`, `rotation`, `strokeColor`, `fillAlpha` each warn `layer "dot": "x" is a property, not a rect shape key, so it is ignored: write it in initial (initial: { x: 40 }) or in a keyframe`; every warning of this kind names the layer, or says "unnamed <type> layer"): the top level of a shape holds its geometry (`width height radius d startAngle endAngle cornerRadius anchorX anchorY`), `trimStart` / `trimEnd`, `strokeCap` / `strokeJoin`, `fillGradient`.
|
|
172
172
|
|
|
173
173
|
Keyframe kinds: `set` (jump at `at`; undone when you seek back), `to` (animate to), `from` (animate from), `from`+`to`; a value may be relative to where the layer stands (`from: { x: '-=36' }` slides in from 36 px to the left of its place). `from` / `from+to` show their start value from the layer's start, so a delayed fade-in needs no `initial.alpha`. `repeat` (finite extra plays), `yoyo`, `repeatDelay` loop any of them (endless repeats are not allowed: `repeat: -1` (the GSAP habit) is refused with a warning, write a count that covers the layer, `repeat: 40`; a `from` keyframe with a later `at` puts its start value on the layer from frame 0, the layer does not wait for the `at`). `ease` = an ease name (`'none'`, `'power2.out'`, `'expo.out'`, `'back.out(1.7)'`, `'elastic.out(1,0.5)'`, `'sine.inOut'`, `'bounce.out'`, **`'cubic-bezier(.4, 0, .2, 1)'`** (CSS syntax, x1 and x2 in 0…1; `.4, 0, .2, 1` is a gentle in-out; `.2, .8, .2, 1` is FRONT-LOADED, not slow motion: half of the change in the first 13 % of the time, 90 % by 39 %, 99 % by 74 %, then a long settle, so what sits at the END of the content gets the glide; `.2, .8, .5, 1` has a shorter tail). `power1…4` are the powers 2…5: `power2` is a cubic, `power2.in` has done only 12.5 % at half time (42 % at three quarters)… and springs: `'spring(mass, stiffness, damping)'` or `'spring.gentle' | 'snappy' | 'bouncy' | 'wobbly' | 'slow'`; **`duration: 'auto'` = the time the spring takes to settle** (presets, settle time / highest value reached as a multiple of the target: `snappy` 0.42 s / 1.01, `gentle` 0.72 s / 1.02, `bouncy` 1.08 s / 1.28, `slow` 1.29 s / 1.00, `wobbly` 1.61 s / 1.40: time the next beat from these) (real physics; with a number `duration` is the settle time and only the damping ratio shapes it; a damping ratio of 1 or more never overshoots, so `spring(1,170,26)` does not bounce: use damping below 20 at stiffness 170; `'auto'` works on keyframes only, not in `animateText` / `orbit` tweens; a `stagger` ease may be a spring, its delays are held inside the spread). Default ease is linear.
|
|
174
174
|
|
|
@@ -1878,7 +1878,7 @@ return [
|
|
|
1878
1878
|
const screen = await threeLayerTexture(ctx, 'screen', { resolution: 2 }); // a screen that is seen large: 2 x the pixels
|
|
1879
1879
|
const body = new THREE.Mesh(new THREE.BoxGeometry(1.0, 2.1, 0.09), new THREE.MeshStandardMaterial({ color: 0x15171c, metalness: 0.6, roughness: 0.35 }));
|
|
1880
1880
|
const face = new THREE.Mesh(new THREE.PlaneGeometry(0.92, 2.0), screen.material); // the layer's colours, as they are in 2D
|
|
1881
|
-
face.position.z = 0.046;
|
|
1881
|
+
face.position.z = 0.046; face.name = 'face'; // just in front of the body (a name makes hit.via.object say which mesh a tap came through)
|
|
1882
1882
|
phone = new THREE.Group(); phone.add(body, face);
|
|
1883
1883
|
ctx.scene.add(phone, new THREE.AmbientLight(0xffffff, 0.8), new THREE.DirectionalLight(0xffffff, 2.2));
|
|
1884
1884
|
ctx.camera.position.set(0, 0, 2.9);
|
|
@@ -2373,7 +2373,7 @@ interface SequenceCommon {
|
|
|
2373
2373
|
| `text`, `image`, `video` | the **top-left** corner | `anchorX` / `anchorY: 0.5` to centre |
|
|
2374
2374
|
| `composition` | its top-left corner | `pivotX` / `pivotY` = the point that sits at `x, y` and that rotation / scale turn about |
|
|
2375
2375
|
|
|
2376
|
-
Shape geometry (`width height radius anchorX …`) may be written at the top level or in `initial`; the top level wins. `rotation`, `skew*` and `rotationX/Y` are in **degrees**.
|
|
2376
|
+
Shape geometry (`width height radius anchorX …`) may be written at the top level or in `initial`; the top level wins. `rotation`, `skew*` and `rotationX/Y` are in **degrees**. **`skewX` / `skewY` are PixiJS's, not CSS's shear:** they turn an axis about the layer's anchor (the anchored point stays), so `skewX: 40` tilts the layer's vertical edges by 40° and shortens them to `cos 40°` of their length (a CSS `skewX(40deg)` or After Effects' skew keeps the height and moves the bottom by `h · tan 40°`). A point (x, y) of the layer, measured from its anchor, lands at `(x cos β + y sin α, x sin β + y cos α)` for `skewX: α`, `skewY: β`. Pinned by `tests/films/geometry-skew`.
|
|
2377
2377
|
|
|
2378
2378
|
A layer is **not removed** when its lifespan `[at, at + duration)` ends: it is only hidden, and it keeps its last animated values. Outside the lifespan it is invisible. **z-order is array order** (later = on top). Build scenes by giving layers `at` / `duration` and stacking them; a full-screen background rect in each scene hides what is below it.
|
|
2379
2379
|
|
|
@@ -2400,7 +2400,7 @@ Renders a [PIXI.Text](https://pixijs.com/8.x/guides/components/scene-objects/tex
|
|
|
2400
2400
|
}
|
|
2401
2401
|
```
|
|
2402
2402
|
|
|
2403
|
-
**Wrapping and fonts for Japanese, Chinese and Korean.** `wordWrap: true` breaks a line at spaces. Japanese and Chinese have none: with `wordWrap` alone a long line does not wrap and runs off the canvas, and `breakWords: true` makes it break between any two characters, so a line can end with one to three characters alone (an orphan) and split a word or phrase in the middle. For a clean break write the line break yourself (`'あの日言えなかった言葉が、\n風に溶けていく'`, after a comma or a particle) and size the font so each line fits; `check` lists a line that is cut by the canvas edge, but it does not judge an ugly break, so look at the sheet. (Korean writes spaces between words, so `wordWrap` breaks there; a very long word still needs `breakWords`.) No font is bundled: the machine's own fonts are used, and a font that lacks the script's characters is replaced for those characters by whatever the machine has, so the look differs from machine to machine. For a result that looks the same everywhere, load a web font that covers the script (for example Noto Sans JP, Noto Sans KR for Hangul, Noto Sans SC) and `await document.fonts.load('40px "Noto Sans KR"')` before `movie.init` (see pitfall 103); give the family in `style.fontFamily`.
|
|
2403
|
+
**Wrapping and fonts for Japanese, Chinese and Korean.** `wordWrap: true` breaks a line at spaces. Japanese and Chinese have none: with `wordWrap` alone a long line does not wrap and runs off the canvas, and `breakWords: true` makes it break between any two characters, so a line can end with one to three characters alone (an orphan) and split a word or phrase in the middle. **There are no line-start rules (kinsoku) in horizontal wrapping:** with `breakWords` a line may START with `、`, `。` or a small kana such as `ゃ` (pinned by `tests/films/text-japanese-wrap`; vertical `splitText` has none either). For a clean break write the line break yourself (`'あの日言えなかった言葉が、\n風に溶けていく'`, after a comma or a particle) and size the font so each line fits; `check` lists a line that is cut by the canvas edge, but it does not judge an ugly break, so look at the sheet. (Korean writes spaces between words, so `wordWrap` breaks there; a very long word still needs `breakWords`.) No font is bundled: the machine's own fonts are used, and a font that lacks the script's characters is replaced for those characters by whatever the machine has, so the look differs from machine to machine. For a result that looks the same everywhere, load a web font that covers the script (for example Noto Sans JP, Noto Sans KR for Hangul, Noto Sans SC) and `await document.fonts.load('40px "Noto Sans KR"')` before `movie.init` (see pitfall 103); give the family in `style.fontFamily`.
|
|
2404
2404
|
|
|
2405
2405
|
**Right-to-left and complex scripts (measured in trials).** With a font that covers the script the text is shaped correctly: Arabic letters connect and read right to left, Hebrew reads right to left, Thai and Devanagari marks stack. The direction follows the first strong letter of the line; place it with `anchorX: 1` and `style.align: 'right'`. Three traps: (1) marks that hang below the font's box can be cut off (the dots of an Arabic ي turn "في" into "فى"): give the layer `style.padding` (30 at a size of 120), and `check` cannot see it, so look at the picture; (2) `splitText` and `animateText` place words and letters from left to right, so an Arabic line comes out in reverse order, and `by: 'chars'` breaks the connected forms: split the line by words yourself and place each word at the right edge minus `measureText` of the logical text up to that word; (3) `visibleChars` shapes each prefix alone, so the last letter can show in its isolated form until the next one arrives: uncovering the whole line with a mask whose width grows keeps the connected forms.
|
|
2406
2406
|
|
|
@@ -3172,6 +3172,7 @@ three({ type: 'three', width: 'GW', height: 'GH',
|
|
|
3172
3172
|
setup: async (ctx) => {
|
|
3173
3173
|
const screen = await threeLayerTexture(ctx, 'screen', { resolution: 2 }); // { material, texture, width, height, dispose }
|
|
3174
3174
|
ctx.scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.6, 0.9), screen.material));
|
|
3175
|
+
ctx.camera.position.set(0, 0, 1.6); // the layer's camera starts at the origin, inside the plane: step back (a plane of this size fills the picture at about 1.6)
|
|
3175
3176
|
} })
|
|
3176
3177
|
```
|
|
3177
3178
|
|
|
@@ -3181,8 +3182,12 @@ three({ type: 'three', width: 'GW', height: 'GH',
|
|
|
3181
3182
|
- **A function of the time.** The 2D layer is drawn when the three layer draws, after the timeline has put every layer at the frame: a seek, an export and a repeat give the same picture (`check` draws the layer twice, in two orders, as for any three layer). A layer that has not started yet, or has ended, shows nothing on the mesh.
|
|
3182
3183
|
- **A rebuild** (`setVars`, `resize`) keeps the three layer and shows the layer of the new tree (the text that changed shows on the mesh); a layer that changed size is drawn stretched to the texture.
|
|
3183
3184
|
- **Seen small or at a slant** the texture is filtered, not aliased (1-pixel stripes on a plane a quarter of the picture come out as one even grey, not black and white stripes; four taps along a slant). The mip levels are PixiJS's own on the shared GPU, averaged in the stored sRGB values, so very fine bright detail seen very small is a little darker there than on a read-back canvas, whose mipmaps three makes in linear light (stripes average to 128 and 188).
|
|
3184
|
-
- **A click reaches the screen.** `movie.pick(x, y)` on a point of the mesh answers for the layers INSIDE the 2D layer, at the point of its picture under the pointer: the top hit is `screen/button` (a flat hit with `local` in the layer's own pixels), with `via: { path: 'phone', object: 'face' }` naming the three layer and the mesh it came through; under it come the screen's other layers, then the mesh itself. `only` / `ignore` see through the mesh (`only: ['screen']` is the screen's layers wherever they are shown), `movie.hover` and the click recipes work on a phone's screen as they do on a flat layer, and a layer that is not alive does not answer. A page asks the layer by its own name, so the button is one `pick` away: `if (movie.pick(...movie.canvasPoint(event), { only: ['button'] })) …`.
|
|
3185
|
-
- **Not in this version
|
|
3185
|
+
- **A click reaches the screen.** `movie.pick(x, y)` on a point of the mesh answers for the layers INSIDE the 2D layer, at the point of its picture under the pointer: the top hit is `screen/button` (a flat hit with `local` in the layer's own pixels), with `via: { path: 'phone', object: 'face' }` naming the three layer and the mesh it came through (`object` is the mesh's `name`: set it, `face.name = 'face'`, or it is empty); under it come the screen's other layers, then the mesh itself. `only` / `ignore` see through the mesh (`only: ['screen']` is the screen's layers wherever they are shown), `movie.hover` and the click recipes work on a phone's screen as they do on a flat layer, and a layer that is not alive does not answer. A page asks the layer by its own name, so the button is one `pick` away: `if (movie.pick(...movie.canvasPoint(event), { only: ['button'] })) …`.
|
|
3186
|
+
- **Not in this version** (found by a review of 0.47.1; what the picture shows is right, what `pick` says is not in these cases):
|
|
3187
|
+
- The ray stops at the first mesh it touches, even a nearly clear one: a glass pane in front of a screen, or the empty part of a `transparent: true` sticker in front of something, answers for the glass or the sticker, not for what shows through it.
|
|
3188
|
+
- `hit.three.grab()` of a three layer that is itself inside a screen moves with the canvas point, not with the point on the mesh: a drag of an object inside a screen jumps on its first move.
|
|
3189
|
+
- A screen composition must be only a screen: it cannot also be a matte for another layer (that layer is cut away completely), and a screen that is part of a transition (wipe, iris, dissolve, zoom, luma) is replaced by the transition's own picture.
|
|
3190
|
+
- A mask or matte on the screen composition itself is placed in its PARENT's space while its content is drawn from the origin: unless the screen sits at x: 0, y: 0 (anchor 0, scale 1, rotation 0) the mask lands shifted on the mesh (a warning says so); mask a layer INSIDE the screen instead.
|
|
3186
3191
|
|
|
3187
3192
|
### Keyframe paths: `three.<name>.<path>`
|
|
3188
3193
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ name: pixi-effects
|
|
|
3
3
|
description: Use when asked to make, animate, preview or export a video, motion graphics, an animated title, a lower third, kinetic type, a promo, a slideshow with transitions, an animated chart or data story, a social clip, a 2.5D / depth scene, or music and sound for a video, written as code in JavaScript or HTML. pixi-effects is a JS library where a video is a plain-object composition (text, shape, image, video, audio and file-free sfx / music, camera, light and shader layers with keyframes) that plays in the browser, is checked by a tool the agent can run, and exports MP4 / WebM / MOV with no server. Not for a project that already uses another video tool (Remotion, Motion Canvas, ffmpeg); follow that tool instead.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
> Written for pixi-effects 0.47.
|
|
6
|
+
> Written for pixi-effects 0.47.3. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
|
|
7
7
|
|
|
8
8
|
# pixi-effects — write a video as data
|
|
9
9
|
|
|
@@ -45,7 +45,7 @@ threeD: true // opt into 2.5D (see below)
|
|
|
45
45
|
blendMode: 'normal' | 'add' | 'screen' | 'multiply' | 'overlay' | 'soft-light' | 'hard-light' | 'color-dodge' | 'color-burn' | 'darken' | 'lighten' | 'difference' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity' | 'linear-burn' // CSS names (hyphenated); blends with EVERYTHING BELOW the layer; add / screen brighten (glows), multiply darkens, soft-light / overlay for colour casts and contrast, color-dodge for light sources; on a composition the children each blend (to blend it as one picture: threeD: true or a filter); the 14 new ones cost ~2 ms a layer on WebGL (10+ on screen warns)
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
Animatable props (`initial` / keyframes): `x y alpha rotation scale scaleX scaleY pivotX pivotY anchorX anchorY skewX skewY tint width height visible autoAlpha`, plus for shapes their style/geometry, for text `fill`, for audio `volume`, for 3D `z rotationX rotationY`, filter params as `'filters.<name>.<param>'`, three objects as `'three.<obj>.<path>'`. **These go in `initial` / keyframes, never at the top level of a shape or text layer** (top-level `x`, `rotation`, `strokeColor`, `fillAlpha` each warn "is not a
|
|
48
|
+
Animatable props (`initial` / keyframes): `x y alpha rotation scale scaleX scaleY pivotX pivotY anchorX anchorY skewX skewY tint width height visible autoAlpha`, plus for shapes their style/geometry, for text `fill`, for audio `volume`, for 3D `z rotationX rotationY`, filter params as `'filters.<name>.<param>'`, three objects as `'three.<obj>.<path>'`. **These go in `initial` / keyframes, never at the top level of a shape or text layer** (top-level `x`, `rotation`, `strokeColor`, `fillAlpha` each warn `layer "dot": "x" is a property, not a rect shape key, so it is ignored: write it in initial (initial: { x: 40 }) or in a keyframe`; every warning of this kind names the layer, or says "unnamed <type> layer"): the top level of a shape holds its geometry (`width height radius d startAngle endAngle cornerRadius anchorX anchorY`), `trimStart` / `trimEnd`, `strokeCap` / `strokeJoin`, `fillGradient`.
|
|
49
49
|
|
|
50
50
|
Keyframe kinds: `set` (jump at `at`; undone when you seek back), `to` (animate to), `from` (animate from), `from`+`to`; a value may be relative to where the layer stands (`from: { x: '-=36' }` slides in from 36 px to the left of its place). `from` / `from+to` show their start value from the layer's start, so a delayed fade-in needs no `initial.alpha`. `repeat` (finite extra plays), `yoyo`, `repeatDelay` loop any of them (endless repeats are not allowed: `repeat: -1` (the GSAP habit) is refused with a warning, write a count that covers the layer, `repeat: 40`; a `from` keyframe with a later `at` puts its start value on the layer from frame 0, the layer does not wait for the `at`). `ease` = an ease name (`'none'`, `'power2.out'`, `'expo.out'`, `'back.out(1.7)'`, `'elastic.out(1,0.5)'`, `'sine.inOut'`, `'bounce.out'`, **`'cubic-bezier(.4, 0, .2, 1)'`** (CSS syntax, x1 and x2 in 0…1; `.4, 0, .2, 1` is a gentle in-out; `.2, .8, .2, 1` is FRONT-LOADED, not slow motion: half of the change in the first 13 % of the time, 90 % by 39 %, 99 % by 74 %, then a long settle, so what sits at the END of the content gets the glide; `.2, .8, .5, 1` has a shorter tail). `power1…4` are the powers 2…5: `power2` is a cubic, `power2.in` has done only 12.5 % at half time (42 % at three quarters)… and springs: `'spring(mass, stiffness, damping)'` or `'spring.gentle' | 'snappy' | 'bouncy' | 'wobbly' | 'slow'`; **`duration: 'auto'` = the time the spring takes to settle** (presets, settle time / highest value reached as a multiple of the target: `snappy` 0.42 s / 1.01, `gentle` 0.72 s / 1.02, `bouncy` 1.08 s / 1.28, `slow` 1.29 s / 1.00, `wobbly` 1.61 s / 1.40: time the next beat from these) (real physics; with a number `duration` is the settle time and only the damping ratio shapes it; a damping ratio of 1 or more never overshoots, so `spring(1,170,26)` does not bounce: use damping below 20 at stiffness 170; `'auto'` works on keyframes only, not in `animateText` / `orbit` tweens; a `stagger` ease may be a spring, its delays are held inside the spread). Default ease is linear.
|
|
51
51
|
|
|
@@ -1407,7 +1407,7 @@ return [
|
|
|
1407
1407
|
const screen = await threeLayerTexture(ctx, 'screen', { resolution: 2 }); // a screen that is seen large: 2 x the pixels
|
|
1408
1408
|
const body = new THREE.Mesh(new THREE.BoxGeometry(1.0, 2.1, 0.09), new THREE.MeshStandardMaterial({ color: 0x15171c, metalness: 0.6, roughness: 0.35 }));
|
|
1409
1409
|
const face = new THREE.Mesh(new THREE.PlaneGeometry(0.92, 2.0), screen.material); // the layer's colours, as they are in 2D
|
|
1410
|
-
face.position.z = 0.046;
|
|
1410
|
+
face.position.z = 0.046; face.name = 'face'; // just in front of the body (a name makes hit.via.object say which mesh a tap came through)
|
|
1411
1411
|
phone = new THREE.Group(); phone.add(body, face);
|
|
1412
1412
|
ctx.scene.add(phone, new THREE.AmbientLight(0xffffff, 0.8), new THREE.DirectionalLight(0xffffff, 2.2));
|
|
1413
1413
|
ctx.camera.position.set(0, 0, 2.9);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>pixi-effects video</title>
|
|
7
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.47.
|
|
7
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.47.3/dist/loader.css" /> <!-- the loader: HTML + CSS only, so it is on screen at once and keeps moving while the page builds the video -->
|
|
8
8
|
<style>
|
|
9
9
|
body { background: #0d1220; margin: 0; padding: 16px; }
|
|
10
10
|
.stage { position: relative; width: min(960px, 100%); } /* the player and the loader both sit inside this box */
|
|
@@ -37,17 +37,17 @@
|
|
|
37
37
|
"imports": {
|
|
38
38
|
"pixi.js": "https://esm.sh/pixi.js@8.22.0?bundle-deps",
|
|
39
39
|
"mediabunny": "https://esm.sh/mediabunny",
|
|
40
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.47.
|
|
41
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.47.
|
|
42
|
-
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.47.
|
|
43
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.47.
|
|
40
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.47.3/dist/index.js",
|
|
41
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.47.3/dist/Controller.js",
|
|
42
|
+
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.47.3/dist/present.js",
|
|
43
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.47.3/dist/three.js",
|
|
44
44
|
"pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js",
|
|
45
45
|
"three": "https://esm.sh/three@0.178.0",
|
|
46
46
|
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.178.0/examples/jsm/"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
</script>
|
|
50
|
-
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.47.
|
|
50
|
+
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.47.3/dist/index.js" /> <!-- after the import map: the library downloads while the page is parsed -->
|
|
51
51
|
|
|
52
52
|
<script type="module">
|
|
53
53
|
import { Movie, kenBurns, withFade } from 'pixi-effects';
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunkBFERTUXK_cjs = require('./chunk-BFERTUXK.cjs');
|
|
4
|
-
require('./chunk-4NOTVQ3I.cjs');
|
|
5
|
-
require('./chunk-53DXJEO7.cjs');
|
|
6
|
-
require('./chunk-NEMWRJQS.cjs');
|
|
7
|
-
require('./chunk-TWZNOUM7.cjs');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Object.defineProperty(exports, "CompositionSequence", {
|
|
12
|
-
enumerable: true,
|
|
13
|
-
get: function () { return chunkBFERTUXK_cjs.CompositionSequence; }
|
|
14
|
-
});
|
|
15
|
-
//# sourceMappingURL=Composition-AZEATHJB.cjs.map
|
|
16
|
-
//# sourceMappingURL=Composition-AZEATHJB.cjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { CompositionSequence } from './chunk-QMVK55LC.js';
|
|
2
|
-
import './chunk-6MHJKCXO.js';
|
|
3
|
-
import './chunk-UTDELFOU.js';
|
|
4
|
-
import './chunk-NVKHUAHU.js';
|
|
5
|
-
import './chunk-BXOCEZ4U.js';
|
|
6
|
-
//# sourceMappingURL=Composition-ME3L3EFT.js.map
|
|
7
|
-
//# sourceMappingURL=Composition-ME3L3EFT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/audioRemap.ts","../src/core/spectrum.ts","../src/audio/loudness.ts","../src/core/inspectAudio.ts"],"names":[],"mappings":"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","file":"chunk-7IDEWQPH.js","sourcesContent":["import type { AudioDescriptor } from '../types';\n\ntype RemapFields = Pick<AudioDescriptor, 'start' | 'end' | 'loop' | 'initialVolume' | 'volumeKeyframes' | 'warp' | 'sourceMap'>;\n\n/** The gain of a sound at its own time `u`: `initialVolume` from `start`, then straight lines to each point (what the Web Audio ramps did). */\nfunction gainAt(a: RemapFields, u: number): number {\n let prevT = a.start, prevV = a.initialVolume ?? 1;\n if (u <= prevT) return prevV;\n for (const kf of a.volumeKeyframes ?? []) {\n if (u <= kf.time) return kf.time === prevT ? kf.value : prevV + (kf.value - prevV) * ((u - prevT) / (kf.time - prevT));\n prevT = kf.time; prevV = kf.value;\n }\n return prevV;\n}\n\nconst CELL = 1 / 1000;\n\n/**\n * Render a sound through its time maps. For every output sample T: u = warp(T) (sampled every millisecond, interpolated), the position\n * in the sound is sourceMap(u) (or u - start), and the sample is read with linear interpolation. Where the position hardly moves (a\n * freeze) it fades to silence rather than hold one sample as a DC offset. Returns null when it is never audible. `at` is where in the\n * mix the returned samples begin. Pitch follows speed (a resample, like a tape).\n */\nexport function resampleThrough(\n a: RemapFields, left: Float32Array, right: Float32Array, srcRate: number, outRate: number, total: number,\n): { L: Float32Array; R: Float32Array; at: number } | null {\n const nCell = Math.ceil(total / CELL) + 2;\n const U = new Float64Array(nCell);\n let first = -1, last = -1;\n for (let k = 0; k < nCell; k++) {\n const u = a.warp ? a.warp(k * CELL) : k * CELL;\n U[k] = u;\n if (Number.isFinite(u) && u >= a.start && u < a.end) { if (first < 0) first = k; last = k; }\n }\n if (first < 0) return null;\n const lo = Math.max(0, (first - 1) * CELL), hi = Math.min(total, (last + 2) * CELL);\n const n = Math.max(1, Math.ceil((hi - lo) * outRate));\n const L = new Float32Array(n), R = new Float32Array(n);\n const dur = left.length / srcRate;\n let prevPos = NaN;\n for (let i = 0; i < n; i++) {\n const T = lo + i / outRate;\n const x = T / CELL, k = Math.min(Math.floor(x), nCell - 2), f = x - k;\n const u0 = U[k]!, u1 = U[k + 1]!;\n if (!(Number.isFinite(u0) && Number.isFinite(u1))) { prevPos = NaN; continue; }\n const u = u0 * (1 - f) + u1 * f;\n if (u < a.start || u >= a.end) { prevPos = NaN; continue; }\n const raw = a.sourceMap ? a.sourceMap(u) : u - a.start;\n let pos = raw;\n if (a.loop) pos = ((pos % dur) + dur) % dur;\n const rate = Number.isNaN(prevPos) ? 1 : Math.abs(raw - prevPos) * outRate;\n prevPos = raw;\n if (pos < 0 || pos >= dur) continue;\n const g = gainAt(a, u) * Math.min(1, rate / 0.02);\n const sp = pos * srcRate, j = Math.floor(sp), fr = sp - j, j1 = Math.min(j + 1, left.length - 1);\n L[i] = (left[j]! * (1 - fr) + left[j1]! * fr) * g;\n R[i] = (right[j]! * (1 - fr) + right[j1]! * fr) * g;\n }\n return { L, R, at: lo };\n}\n\n/**\n * When a sound plays, in the MOVIE's time. Without a `warp` that is `start`..`end`; with one (the sound sits in a remapped composition)\n * `start` / `end` are the composition's local times, so the span is where the warp lands inside them. `total` is the movie's length.\n */\nexport function playSpan(a: Pick<AudioDescriptor, 'start' | 'end' | 'warp'>, total: number): { start: number; end: number } {\n if (!a.warp) return { start: a.start, end: a.end };\n let first = -1, last = -1;\n const n = Math.ceil(total / CELL) + 1;\n for (let k = 0; k <= n; k++) {\n const u = a.warp(k * CELL);\n if (Number.isFinite(u) && u >= a.start && u < a.end) { if (first < 0) first = k; last = k; }\n }\n return first < 0 ? { start: total, end: total } : { start: first * CELL, end: (last + 1) * CELL };\n}\n\n/**\n * The farthest local time the warp (the clock of the composition the sound sits in) reaches during the movie. A sound whose local `end` is\n * beyond it is cut short by that clock (it stops or stands still) — a different cause than the end of the movie.\n */\nexport function clockReach(a: Pick<AudioDescriptor, 'warp'>, total: number): number {\n let reach = -Infinity;\n const n = Math.ceil(total / CELL) + 1;\n for (let k = 0; k <= n; k++) {\n const u = a.warp ? a.warp(k * CELL) : k * CELL;\n if (Number.isFinite(u) && u > reach) reach = u;\n }\n return reach;\n}\n","/** In-place radix-2 FFT (re/im arrays of the same power-of-two length). */\nexport function fft(re: Float64Array, im: Float64Array): void {\n const n = re.length;\n for (let i = 1, j = 0; i < n; i++) {\n let bit = n >> 1;\n for (; j & bit; bit >>= 1) j ^= bit;\n j ^= bit;\n if (i < j) { [re[i], re[j]] = [re[j]!, re[i]!]; [im[i], im[j]] = [im[j]!, im[i]!]; }\n }\n for (let len = 2; len <= n; len <<= 1) {\n const ang = (-2 * Math.PI) / len;\n for (let i = 0; i < n; i += len) {\n for (let k = 0; k < len / 2; k++) {\n const wr = Math.cos(ang * k), wi = Math.sin(ang * k);\n const a = i + k, b = a + len / 2;\n const xr = re[b]! * wr - im[b]! * wi, xi = re[b]! * wi + im[b]! * wr;\n re[b] = re[a]! - xr; im[b] = im[a]! - xi;\n re[a] = re[a]! + xr; im[a] = im[a]! + xi;\n }\n }\n }\n}\n\nconst FRAME = 2048;\n\n/**\n * Spectral centroid (\"brightness\") in Hz of `samples[from, to)`: the power-weighted mean frequency\n * over Hann-windowed 2048-sample frames (louder frames count more). 0 for silence.\n * Rough guide: < 500 Hz dark / boomy, 1–3 kHz mid, > 4 kHz bright / airy.\n */\nexport function spectralCentroid(samples: Float32Array, sampleRate: number, from = 0, to = samples.length): number {\n let num = 0;\n let den = 0;\n const re = new Float64Array(FRAME);\n const im = new Float64Array(FRAME);\n const end = Math.min(to, samples.length);\n for (let start = Math.max(0, from); start < end; start += FRAME / 2) {\n for (let i = 0; i < FRAME; i++) {\n const k = start + i;\n const w = 0.5 - 0.5 * Math.cos((2 * Math.PI * i) / (FRAME - 1));\n re[i] = k < end ? samples[k]! * w : 0;\n im[i] = 0;\n }\n fft(re, im);\n for (let b = 1; b < FRAME / 2; b++) {\n const power = re[b]! * re[b]! + im[b]! * im[b]!;\n num += power * ((b * sampleRate) / FRAME);\n den += power;\n }\n }\n return den > 1e-9 ? Math.round(num / den / 10) * 10 : 0;\n}\n","/** Loudness of a mix, ITU-R BS.1770-4 / EBU R128, without a dependency. Pure: takes channels, returns numbers. */\nexport interface LoudnessReport { integratedLufs: number | null; truePeakDb: number | null; samplePeakDb: number; clippedSamples: number }\n\n/** Where the advice in `inspectAudio().notes` starts: one place, so the docs can point at it. */\nexport const LOUDNESS_NOTES = { quietBelow: -18, loudAbove: -9, headroomDb: -1 } as const;\n\ninterface Biquad { b: [number, number, number]; a: [number, number, number] }\n\n/** The K-weighting of BS.1770-4: a high shelf, then a 38 Hz high-pass, as biquads for any sample rate (bilinear transform). */\nfunction kWeighting(fs: number): [Biquad, Biquad] {\n const f0 = 1681.974450955533, G = 3.999843853973347, Q = 0.7071752369554196;\n const K = Math.tan(Math.PI * f0 / fs), Vh = 10 ** (G / 20), Vb = Vh ** 0.4996667741545416;\n const a0 = 1 + K / Q + K * K;\n const shelf: Biquad = {\n b: [(Vh + Vb * K / Q + K * K) / a0, 2 * (K * K - Vh) / a0, (Vh - Vb * K / Q + K * K) / a0],\n a: [1, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0],\n };\n const f1 = 38.13547087602444, Q1 = 0.5003270373238773, K1 = Math.tan(Math.PI * f1 / fs), a1 = 1 + K1 / Q1 + K1 * K1;\n const high: Biquad = { b: [1, -2, 1], a: [1, 2 * (K1 * K1 - 1) / a1, (1 - K1 / Q1 + K1 * K1) / a1] };\n return [shelf, high];\n}\n\nfunction filter(x: Float32Array, stages: Biquad[]): Float32Array {\n let cur = x;\n for (const { b, a } of stages) {\n const out = new Float32Array(cur.length); // Float32 between stages: half the memory, far below the measurement's resolution\n let x1 = 0, x2 = 0, y1 = 0, y2 = 0;\n for (let i = 0; i < cur.length; i++) {\n const xi = cur[i]!, y = b[0] * xi + b[1] * x1 + b[2] * x2 - a[1] * y1 - a[2] * y2;\n x2 = x1; x1 = xi; y2 = y1; y1 = y; out[i] = y;\n }\n cur = out;\n }\n return cur;\n}\n\nconst toDb = (x: number): number => Math.round(200 * Math.log10(Math.max(x, 1e-6))) / 10; // 0.1 dB, floor -120\n\nconst TP_HALF = 12, TP_BLOCK = 32;\nconst TP_KERNELS = [0.25, 0.5, 0.75].map(p => Float64Array.from({ length: 2 * TP_HALF }, (_, j) => {\n const k = j - TP_HALF + 1, x = k - p, s = x === 0 ? 1 : Math.sin(Math.PI * x) / (Math.PI * x);\n return s * (0.5 + 0.5 * Math.cos(Math.PI * x / (TP_HALF + 1)));\n}));\n/** No interpolated value can exceed (the loudest sample around it) x this: where that cannot beat the peak so far, skip the sums. */\nconst TP_GAIN = Math.max(...TP_KERNELS.map(k => k.reduce((a, v) => a + Math.abs(v), 0)));\n\n/** 4x oversampled peak (windowed-sinc interpolation, 12 samples either side): catches peaks between samples. Equal to checking every phase of every sample, only faster. */\nfunction truePeak(chans: Float32Array[]): number {\n let peak = 0;\n for (const c of chans) {\n const n = c.length;\n const blocks = Math.ceil(n / TP_BLOCK), blockMax = new Float32Array(blocks + 2); // [b + 1] is block b; the ends stay 0\n for (let i = 0; i < n; i++) { const a = Math.abs(c[i]!), b = (i / TP_BLOCK | 0) + 1; if (a > blockMax[b]!) blockMax[b] = a; if (a > peak) peak = a; }\n const padded = new Float32Array(n + 2 * TP_HALF); // zeros around, so the sums need no bounds check\n padded.set(c, TP_HALF);\n for (let i = 0; i < n; i++) {\n const b = (i / TP_BLOCK | 0) + 1;\n const local = Math.max(blockMax[b - 1]!, blockMax[b]!, blockMax[b + 1]!); // the 24 samples around i lie in these three blocks\n if (local * TP_GAIN <= peak) continue;\n for (let pi = 0; pi < 3; pi++) {\n const k = TP_KERNELS[pi]!;\n let v = 0;\n for (let j = 0; j < 2 * TP_HALF; j++) v += padded[i + 1 + j]! * k[j]!;\n const a = Math.abs(v);\n if (a > peak) peak = a;\n }\n }\n }\n return peak;\n}\n\nexport function measureLoudness(channels: Float32Array[], sampleRate: number, opts: { truePeak?: boolean } = {}): LoudnessReport {\n let samplePeak = 0, clipped = 0;\n for (const c of channels) for (let i = 0; i < c.length; i++) { const a = Math.abs(c[i]!); if (a > samplePeak) samplePeak = a; if (a >= 1) clipped++; }\n const stages = kWeighting(sampleRate);\n const weighted = channels.map(c => filter(c, stages));\n const block = Math.round(0.4 * sampleRate), hop = Math.round(0.1 * sampleRate);\n const energies: number[] = [];\n for (let s = 0; s + block <= (weighted[0]?.length ?? 0); s += hop) {\n let sum = 0;\n for (const w of weighted) { let z = 0; for (let i = s; i < s + block; i++) z += w[i]! * w[i]!; sum += z / block; } // the weight is 1 for L, R and mono\n energies.push(sum);\n }\n const lufs = (e: number): number => -0.691 + 10 * Math.log10(e);\n const absolute = energies.filter(e => e > 0 && lufs(e) > -70);\n let integrated: number | null = null;\n if (absolute.length) {\n const relative = lufs(absolute.reduce((a, b) => a + b, 0) / absolute.length) - 10;\n const gated = absolute.filter(e => lufs(e) > relative);\n if (gated.length) integrated = Math.round(10 * lufs(gated.reduce((a, b) => a + b, 0) / gated.length)) / 10;\n }\n return { integratedLufs: integrated, truePeakDb: opts.truePeak === false ? null : toDb(truePeak(channels)), samplePeakDb: toDb(samplePeak), clippedSamples: clipped };\n}\n\n/** Runs of at least `minLength` seconds where every channel stays below `thresholdDb` (default -60 dBFS). */\nexport function findSilences(channels: Float32Array[], sampleRate: number, opts: { minLength?: number; thresholdDb?: number } = {}): Array<{ from: number; to: number }> {\n const min = Math.round((opts.minLength ?? 1) * sampleRate), limit = 10 ** ((opts.thresholdDb ?? -60) / 20);\n const n = channels[0]?.length ?? 0, out: Array<{ from: number; to: number }> = [];\n let start = -1;\n const quiet = (i: number): boolean => channels.every(c => Math.abs(c[i]!) < limit);\n for (let i = 0; i <= n; i++) {\n const q = i < n && quiet(i);\n if (q && start < 0) start = i;\n if (!q && start >= 0) { if (i - start >= min) out.push({ from: Math.round(start / sampleRate * 1000) / 1000, to: Math.round(i / sampleRate * 1000) / 1000 }); start = -1; }\n }\n return out;\n}\n","import type { AudioDescriptor } from '../types';\nimport type { MusicSummary } from '../audio/music/events';\nimport { playSpan, clockReach } from './audioRemap';\nimport type { MixStats } from './AudioMixer';\nimport { spectralCentroid } from './spectrum';\nimport { findSilences, LOUDNESS_NOTES, measureLoudness } from '../audio/loudness';\n\nexport interface AudioInspectOptions {\n /** Width of each loudness window in seconds. Default 0.1. */\n window?: number;\n /** The scenes to measure one by one (`movie.inspectAudio()` fills this from the movie's named top-level compositions). Default: the whole movie as one. */\n scenes?: Array<{ name: string; start: number; end: number }>;\n}\n\n/** One sound measured ON ITS OWN (not mixed), so overlapping layers do not blur it. */\nexport interface SoundMeasure {\n /** Seconds of audio the source has (an sfx: its length; a file: the file's length). */\n length: number;\n /** Its own loudest sample, dBFS (before the layer's volume). */\n peakDb: number;\n /** Seconds from its start to its loudest sample — the moment to line up with the picture. */\n loudestAt: number;\n /** Spectral centroid in Hz: < 500 dark / boomy, 1–4 k mid, > 8 k airy / hissy. */\n brightnessHz: number;\n}\n\nexport interface AudioSourceReport {\n /** `layer \"pop-1\"` (or `unnamed audio layer`). */\n layer: string;\n /** `sfx \"pop\"`, `asset \"bgm\"`, `video \"green\"`. */\n source: string;\n /** Movie time, seconds. */\n start: number;\n end: number;\n /** Loudest sample of the MIX while this source plays, dBFS (−120 = silence). */\n peakDb: number;\n sound: SoundMeasure;\n /**\n * How far below the loudest sample of the MIX around its own loudest moment this source is, dB (0 = it is the loudest thing there; 20 = the mix is 20 dB louder, so\n * the source is hardly heard). Absent for a sound inside a remapped composition (its time is not the movie's).\n */\n belowMixDb?: number;\n /** For a `music` layer: its score read back (bars per track, what starts in each bar, the drum spans). */\n music?: MusicSummary;\n}\n\nexport interface AudioReport {\n duration: number;\n sampleRate: number;\n /** Loudest sample of the mix before limiting, dBFS. Above 0 the mix was limited. */\n peakDb: number;\n peakAt: number;\n /** Every audio layer / video soundtrack in the mix, in start order. */\n sources: AudioSourceReport[];\n /** The mix over time: one row per window, `t` = window start. */\n windows: Array<{ t: number; rmsDb: number; peakDb: number; brightnessHz: number }>;\n /** Problems worth fixing: limiting, clipping, a true peak above 0 dBTP, inaudible layers, sounds cut off by the end of the movie. Read this first. */\n issues: string[];\n /** The mix as a whole (ITU-R BS.1770 loudness; `integratedLufs` is null for a mix shorter than 0.4 s or with nothing above −70 LUFS). */\n loudness: { integratedLufs: number | null; truePeakDb: number; clippedSamples: number; silences: Array<{ from: number; to: number }> };\n /** The same measure for each scene (`silent`: nothing above −60 dBFS). */\n scenes: Array<{ name: string; from: number; to: number; lufs: number | null; peakDb: number; silent: boolean }>;\n /** When each sound starts: what a waveform marks. */\n cues: Array<{ t: number; name: string }>;\n /** Advice, not faults: a mix that is quiet or loud for web video, little headroom, silent stretches. */\n notes: string[];\n}\n\ninterface PcmLike {\n numberOfChannels: number;\n sampleRate: number;\n length: number;\n getChannelData(ch: number): Float32Array;\n}\n\n/** A short sound effect this many dB under the loudest sample of the mix around its loudest moment is hardly heard. */\nconst BURIED_DB = 12;\n/** Below this a source counts as inaudible. */\nconst INAUDIBLE_DB = -50;\n\nconst db = (x: number): number => Math.round(10 * Math.max(-120, 20 * Math.log10(x))) / 10;\nconst mono = (chans: Float32Array[]): Float32Array => {\n if (chans.length === 1) return chans[0]!;\n const out = new Float32Array(chans[0]!.length);\n for (const d of chans) for (let i = 0; i < out.length; i++) out[i] = out[i]! + d[i]! / chans.length;\n return out;\n};\n\nfunction measure(chans: Float32Array[], sampleRate: number): SoundMeasure {\n let peak = 0;\n let at = 0;\n for (const d of chans) for (let i = 0; i < d.length; i++) if (Math.abs(d[i]!) > peak) { peak = Math.abs(d[i]!); at = i; }\n return {\n length: Math.round((chans[0]!.length / sampleRate) * 1000) / 1000,\n peakDb: db(peak),\n loudestAt: Math.round((at / sampleRate) * 1000) / 1000,\n brightnessHz: spectralCentroid(mono(chans), sampleRate),\n };\n}\n\n/**\n * Measure the soundtrack so an author who cannot listen can still check it: every sound on its own\n * (length, level, loudest moment, brightness), when it plays and how loud the mix is there, the mix over\n * time, and issues. Pure: takes the mix and the list of sources that went into it.\n */\nexport function analyzeAudio(\n mix: PcmLike | null, sources: AudioDescriptor[], stats: MixStats | null, movieDuration: number, opts: AudioInspectOptions = {},\n): AudioReport {\n // A sound inside a remapped composition has `start` / `end` in that composition's local time: report when it plays in the MOVIE's time.\n const warped = new Map<AudioDescriptor, { localEnd: number; reach: number }>();\n sources = sources.map(s => {\n if (!s.warp) return s;\n const moved = { ...s, ...playSpan(s, movieDuration) };\n warped.set(moved, { localEnd: s.end, reach: clockReach(s, movieDuration) });\n return moved;\n });\n if (!mix) {\n return { duration: movieDuration, sampleRate: 0, peakDb: -120, peakAt: 0, sources: [], windows: [],\n loudness: { integratedLufs: null, truePeakDb: -120, clippedSamples: 0, silences: [] }, scenes: [], cues: [], notes: [], issues: ['no audio: the movie has no audio layers (sound effects need no files: { type: \"audio\", sfx: \"pop\", at: 1 })'] };\n }\n const sr = mix.sampleRate;\n const chans = Array.from({ length: mix.numberOfChannels }, (_, c) => mix.getChannelData(c));\n const mixMono = mono(chans);\n const peakIn = (t0: number, t1: number): number => {\n let p = 0;\n const i1 = Math.min(mix.length, Math.ceil(t1 * sr));\n for (const d of chans) for (let i = Math.max(0, Math.floor(t0 * sr)); i < i1; i++) p = Math.max(p, Math.abs(d[i]!));\n return p;\n };\n\n // at least 10 ms: a smaller window would ask for a spectrum per sample\n const win = opts.window && opts.window > 0 ? Math.max(0.01, opts.window) : 0.1;\n const windows: AudioReport['windows'] = [];\n for (let t = 0; t < movieDuration - 1e-9; t += win) {\n const i0 = Math.floor(t * sr);\n const i1 = Math.min(mix.length, Math.floor((t + win) * sr));\n let sum = 0;\n let peak = 0;\n for (const d of chans) {\n for (let i = i0; i < i1; i++) {\n sum += d[i]! * d[i]!;\n peak = Math.max(peak, Math.abs(d[i]!));\n }\n }\n const n = Math.max(1, (i1 - i0) * chans.length);\n windows.push({ t: Math.round(t * 1000) / 1000, rmsDb: db(Math.sqrt(sum / n)), peakDb: db(peak), brightnessHz: spectralCentroid(mixMono, sr, i0, i1) });\n }\n\n const issues: string[] = [];\n if (stats && stats.peak > 1) {\n issues.push(`the mix peaks at ${db(stats.peak)} dBFS at ${stats.peakAt.toFixed(2)}s and was limited — lower the volume of the layers playing there`);\n }\n const rendered = new Map<string, SoundMeasure>();\n const soundOf = (s: AudioDescriptor): SoundMeasure => {\n if (s.synth) {\n let m = rendered.get(s.synth.key);\n if (!m) rendered.set(s.synth.key, (m = measure(s.synth.render(sr), sr)));\n return m;\n }\n const b = s.buffer!;\n return measure(Array.from({ length: b.numberOfChannels }, (_, c) => b.getChannelData(c)), b.sampleRate);\n };\n const scoreNotes: string[] = [];\n const buried: Array<{ layer: string; source: string; ownDb: number; topVolume: number; around: number; belowMixDb: number; at: number }> = [];\n const list = [...sources].sort((a, b) => a.start - b.start).map(s => {\n const layer = s.layer ?? 'an audio layer';\n const source = s.source ?? 'audio';\n const peakDb = db(peakIn(s.start, Math.min(s.end, movieDuration)));\n const sound = soundOf(s);\n // The layer's OWN level: the sound's peak at the loudest volume it ever has. Judging the mix instead would\n // hide a nearly silent layer under a loud one.\n const topVolume = Math.max(s.initialVolume ?? 1, ...(s.volumeKeyframes ?? []).map(k => k.value));\n const ownDb = db(Math.pow(10, sound.peakDb / 20) * Math.max(0, topVolume));\n if (s.start >= movieDuration) {\n issues.push(`${layer} (${source}) starts at ${s.start.toFixed(2)}s, after the movie ends (${movieDuration}s)`);\n } else if (ownDb < INAUDIBLE_DB || peakDb < INAUDIBLE_DB) {\n issues.push(`${layer} (${source}) is inaudible from ${s.start.toFixed(2)}s to ${s.end.toFixed(2)}s (its own peak ${ownDb} dBFS at volume ${topVolume}; mix peak there ${peakDb} dBFS) — check its volume`);\n }\n const clock = warped.get(s);\n if (s.synth && s.start < movieDuration) {\n if (clock) {\n // inside a remapped composition: it is cut short when the composition's clock never gets to the end of the sound\n if (clock.reach < clock.localEnd - 1e-3) {\n issues.push(`${layer} (${source}) is cut off: its composition's clock only reaches ${clock.reach.toFixed(2)}s (the sound is ${clock.localEnd.toFixed(2)}s long), because the clock stops or the movie ends first — give the layer a shorter duration, or let the clock run on`);\n }\n } else if (s.end > movieDuration + 1e-6) {\n issues.push(`${layer} (${source}) is cut off by the end of the movie at ${movieDuration}s (it runs to ${s.end.toFixed(2)}s)`);\n }\n }\n // A written score (`music`) much longer than what plays: said as a NOTE (a trial: 19.2 s of music under a 10 s film; the read-back showed it but nothing said it).\n // Not a fault: music that outlasts the movie ends with it (see Audio.ts). A tail under 2 s or 15 % over is only the reverb tail; a looping layer is meant to be cut.\n if (s.music && !s.loop && s.start < movieDuration && !clock) {\n const plays = Math.min(s.end, movieDuration) - s.start;\n const over = s.music.seconds - plays;\n if (over > 2 && over / s.music.seconds > 0.15) scoreNotes.push(`${layer} (${source}): the score is ${Math.round(s.music.seconds * 10) / 10}s long but only ${Math.round(plays * 100) / 100}s of it play, so it is cut off mid-phrase. Write fewer bars (or a faster tempo) so it ends with the movie`);\n }\n // A track a bar or more shorter than the longest one is silent from its end on: usually a miscount of the beats (a trial: three agents; only the read-back showed it). A note, not a fault: a bell may come and go on purpose.\n if (s.music && s.music.tracks.length > 1) {\n const short = s.music.tracks.filter(t => t.beats <= s.music!.beats - s.music!.meter);\n if (short.length > 0) {\n scoreNotes.push(`${layer} (${source}): ${short.slice(0, 3).map(t => `the ${t.inst} track is ${Math.round(t.beats * 100) / 100} beats long against ${s.music!.beats} for the longest and falls silent from beat ${Math.round(t.beats * 100) / 100} (bar ${Math.floor(t.beats / s.music!.meter) + 1})`).join('; ')}. If that is a miscount, add its missing beats (a rest is \\`_:4\\`); if it is on purpose, ignore this`);\n }\n }\n // Buried: the sound's own peak against the mix around its loudest moment. A short sound effect 12 dB or more under what plays with it is a hit nobody hears.\n // (A layer with its own speed or `time` plays the sound at another pace: its loudest moment is not at start + loudestAt, so it is left out.)\n let belowMixDb: number | undefined;\n if (!clock && !s.sourceMap && s.start < movieDuration && ownDb > INAUDIBLE_DB) {\n const at = s.start + sound.loudestAt;\n const around = db(peakIn(Math.max(0, at - 0.05), Math.min(movieDuration, at + 0.05)));\n belowMixDb = Math.max(0, Math.round((around - ownDb) * 10) / 10);\n if (!s.music && source.startsWith('sfx') && sound.length <= 4 && belowMixDb >= BURIED_DB) {\n buried.push({ layer, source, ownDb, topVolume, around, belowMixDb, at: Math.round(at * 100) / 100 });\n }\n }\n return { layer, source, start: s.start, end: s.end, peakDb, sound, ...(belowMixDb !== undefined ? { belowMixDb } : {}), ...(s.music ? { music: s.music } : {}) };\n });\n\n // ONE note for the movie (an accent that is meant to be subtle is common: the numbers are in every source's `belowMixDb`): how many, and the worst\n if (buried.length > 0) {\n const worst = buried.reduce((a, b) => (b.belowMixDb > a.belowMixDb ? b : a));\n // layers with no name all carry the same label, so they are counted one by one; a named layer that plays many times counts once\n const distinct = new Set(buried.map(x => (x.layer === 'an audio layer' ? `#${buried.indexOf(x)}` : x.layer) + x.source)).size;\n scoreNotes.push(`${distinct === 1 ? '1 sound effect is' : `${distinct} sound effects are`} buried (${BURIED_DB} dB or more under the loudest sample of the mix around its loudest moment${buried.length > distinct ? `, ${buried.length} times in all` : ''}): the worst is ${worst.layer} (${worst.source}), its own peak ${worst.ownDb} dBFS at volume ${Math.round(worst.topVolume * 100) / 100} against ${worst.around} dBFS at ${worst.at} s, ${worst.belowMixDb} dB louder. If it is meant to be heard, raise its volume (this one to about ${Math.round(Math.min(4, worst.topVolume * 10 ** ((worst.belowMixDb - 3) / 20)) * 10) / 10}, which puts it about 3 dB under the mix; an accent over music needs 2 to 3) or lower what plays there; every source's belowMixDb has its own number`);\n }\n // the mix as a whole, scene by scene, and the advice\n const whole = measureLoudness(chans, sr);\n const silences = findSilences(chans, sr, { minLength: 1 });\n if (whole.clippedSamples > 0) issues.push(`${whole.clippedSamples} sample(s) of the mix are at full scale (clipped) — lower the volume of the layers that play loudest`);\n if (whole.truePeakDb! > 0) issues.push(`the mix's true peak is ${whole.truePeakDb} dBTP: it will distort when the video is encoded — lower the volume`);\n const notes: string[] = [...scoreNotes];\n if (whole.integratedLufs !== null) {\n if (whole.integratedLufs < LOUDNESS_NOTES.quietBelow) notes.push(`the mix is quiet for web video: ${whole.integratedLufs} LUFS (typical −14 to −16); raise the volume, or ask for a loudness: movie.init({ loudness: -16 }), if that is not on purpose`);\n else if (whole.integratedLufs > LOUDNESS_NOTES.loudAbove) notes.push(`the mix is loud: ${whole.integratedLufs} LUFS (typical web video −14 to −16)`);\n }\n if (whole.truePeakDb! > LOUDNESS_NOTES.headroomDb && whole.truePeakDb! <= 0) notes.push(`the true peak is ${whole.truePeakDb} dBTP: little headroom left for encoding (keep it below ${LOUDNESS_NOTES.headroomDb} dBTP)`);\n const asked = stats?.loudness;\n if (asked) {\n // which layers play at a moment (the ones a peak there is made of); an unnamed layer is told by what it plays\n const playingAt = (t: number): string => {\n const at = sources.filter(x => x.start <= t && t < Math.min(x.end, movieDuration)).map(x => (x.layer && x.layer !== 'an audio layer' ? `${x.layer}${x.source ? ` (${x.source})` : ''}` : x.source ?? 'audio'));\n return [...new Set(at)].slice(0, 6).join(', ') || 'nothing that is listed';\n };\n if (asked.gainDb !== 0) notes.push(`the whole mix was ${asked.gainDb > 0 ? 'raised' : 'lowered'} by ${Math.abs(asked.gainDb)} dB to reach ${asked.target} LUFS (init({ loudness: ${asked.target} }))`);\n if (asked.limited) {\n notes.push(`the mix could not reach ${asked.target} LUFS: it stopped at ${asked.reachedLufs} LUFS because its loudest moment${asked.limiterAt !== undefined ? ` (at ${asked.limiterAt} s, where ${playingAt(asked.limiterAt)} play)` : ''} would need more than ${asked.limiterDb ?? 10} dB of limiting to stay under -1 dBTP; lower the sound that peaks there (a click or hit on top of a quiet bed), or ask for less`);\n } else if (asked.limiterDb !== undefined && asked.limiterDb >= 3) {\n notes.push(`a limiter took up to ${asked.limiterDb} dB off the loudest peak (at ${asked.limiterAt} s, where ${playingAt(asked.limiterAt ?? 0)} play) so that the mix reaches ${asked.target} LUFS without passing -1 dBTP: if that sound should hit harder, lower what plays under it`);\n }\n }\n for (const q of silences.slice(0, 3)) notes.push(`the mix is silent from ${q.from} s to ${q.to} s (${Math.round((q.to - q.from) * 10) / 10} s)`);\n if (silences.length > 3) notes.push(`and ${silences.length - 3} more silent stretch(es) (loudness.silences lists them all)`);\n const sceneList = (opts.scenes && opts.scenes.length ? opts.scenes : [{ name: 'movie', start: 0, end: movieDuration }]).map(sc => {\n const a = Math.max(0, Math.round(sc.start * sr)), b = Math.min(mix.length, Math.round(sc.end * sr));\n const part = chans.map(c => c.subarray(a, Math.max(a, b)));\n const m = measureLoudness(part, sr, { truePeak: false }); // the true peak is the slow part and is only wanted for the whole mix\n return { name: sc.name, from: sc.start, to: sc.end, lufs: m.integratedLufs, peakDb: m.samplePeakDb, silent: m.samplePeakDb < -60 };\n });\n\n return {\n duration: movieDuration,\n sampleRate: sr,\n peakDb: db(stats?.peak ?? peakIn(0, movieDuration)),\n peakAt: stats?.peakAt ?? 0,\n sources: list,\n windows,\n issues,\n loudness: { integratedLufs: whole.integratedLufs, truePeakDb: whole.truePeakDb ?? -120, clippedSamples: whole.clippedSamples, silences },\n scenes: sceneList,\n cues: list.map(s => ({ t: s.start, name: `${s.layer} (${s.source})` })),\n notes,\n };\n}\n\n/** What `inspectSound` says about a sound. */\nexport interface SoundReport {\n /** Seconds, the sample rate and the number of channels of what was measured. */\n duration: number;\n sampleRate: number;\n channels: number;\n /** The loudest sample, dBFS. */\n peakDb: number;\n /** ITU-R BS.1770 loudness, true peak, samples at full scale and silences, as `check` reports them for a movie's mix. */\n loudness: AudioReport['loudness'];\n /** The level over time: the RMS of each window (`window` seconds, 0.1 by default), dBFS. */\n windows: Array<{ t: number; rmsDb: number }>;\n /** Advice (quiet or loud for web video, little headroom, silent stretches) and what would distort when encoded. */\n notes: string[];\n}\n\n/**\n * Measure the sound of any file, not only a movie's mix: its loudness, peak, clipping, silences and level over time, with the same code `movie.inspectAudio()` uses, so\n * `check` and the `pixi-effects sound` command give the same numbers for the same samples. Takes decoded PCM (an AudioBuffer works).\n */\nexport function inspectSound(pcm: PcmLike, opts: { window?: number } = {}): SoundReport {\n const duration = pcm.length / pcm.sampleRate;\n const r = analyzeAudio(pcm, [], null, duration, opts.window ? { window: opts.window } : {});\n // a file that ends a few ms into a window would show that sliver as a row (a 36 ms tail of encoder padding reads as −90 dBFS): a last window under half the width is dropped\n const width = opts.window ?? 0.1;\n let windows = r.windows;\n if (windows.length > 1 && duration - windows[windows.length - 1]!.t < width / 2) windows = windows.slice(0, -1);\n return {\n duration: Math.round(duration * 1000) / 1000, sampleRate: pcm.sampleRate, channels: pcm.numberOfChannels, peakDb: r.peakDb,\n loudness: r.loudness, windows: windows.map(w => ({ t: w.t, rmsDb: w.rmsDb })), notes: [...r.notes, ...r.issues],\n };\n}\n\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/space/specKeys.ts","../src/core/layerKeys.ts","../src/core/lint.ts"],"names":["suggestName"],"mappings":"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","file":"chunk-NEMWRJQS.cjs","sourcesContent":["import type { Keyframe } from '../types';\n\n/** Every prop key a spec touches in `initial` and in keyframe `set` / `to` / `from`. */\nexport function collectPropKeys(spec: {\n initial?: Record<string, unknown>;\n keyframes?: Keyframe[];\n}): Set<string> {\n const out = new Set<string>();\n for (const k of Object.keys(spec.initial ?? {})) out.add(k);\n for (const kf of spec.keyframes ?? []) {\n for (const bag of [kf.set, kf.to, kf.from]) {\n for (const k of Object.keys(bag ?? {})) out.add(k);\n }\n }\n return out;\n}\n","import type {\n SequenceCommon, VideoSequenceSpec, ImageSequenceSpec, TextSequenceSpec, AudioAssetSpec, AudioSfxSpec, AudioMusicSpec,\n CompositionSequenceSpec, CameraSequenceSpec, LightSequenceSpec, ShaderSequenceSpec, CanvasSequenceSpec, NullSequenceSpec, SequenceSpec,\n RectShapeSpec, CircleShapeSpec, EllipseShapeSpec, ArcShapeSpec, LineShapeSpec, PolygonShapeSpec, PathShapeSpec,\n} from '../types';\n\n/**\n * The keys a layer of each kind may have, for `lintKeys`. They are tables of names, so each one is checked against its type twice, by\n * the compiler: `satisfies` rejects a name the type does not have, and `Covers` fails the build when the type has a key the table lacks\n * (add a key to a spec type and `tsc` tells you to add it here, so the warning never calls a real key unknown).\n */\ntype KeysOfUnion<T> = T extends unknown ? keyof T : never;\ntype Covers<T, K extends readonly string[]> = [Exclude<KeysOfUnion<T>, K[number]>] extends [never] ? true : { missing: Exclude<KeysOfUnion<T>, K[number]> };\ntype Assert<T extends true> = T;\n\nconst COMMON = ['type', 'name', 'parent', 'at', 'duration', 'initial', 'keyframes', 'filters', 'filterArea', 'mask', 'blendMode', 'threeD', 'hideBehindCamera', 'lit', 'castsShadows'] as const satisfies readonly KeysOfUnion<SequenceCommon | { type: string }>[];\n\nconst VIDEO = [...COMMON, 'asset', 'loop', 'audio', 'volume', 'speed'] as const satisfies readonly KeysOfUnion<VideoSequenceSpec>[];\nconst IMAGE = [...COMMON, 'asset', 'colorSpace', 'scaleMode'] as const satisfies readonly KeysOfUnion<ImageSequenceSpec>[];\nconst TEXT = [...COMMON, 'text', 'spans', 'format', 'resolution', 'style', 'colorSpace', 'fillGradient'] as const satisfies readonly KeysOfUnion<TextSequenceSpec>[];\nconst AUDIO = [...COMMON, 'volume', 'asset', 'loop', 'sfx', 'music', 'speed'] as const satisfies readonly KeysOfUnion<AudioAssetSpec | AudioSfxSpec | AudioMusicSpec>[];\nconst COMPOSITION = [...COMMON, 'width', 'height', 'sequences', 'transitions', 'speed'] as const satisfies readonly KeysOfUnion<CompositionSequenceSpec>[];\nconst CAMERA = [...COMMON] as const satisfies readonly KeysOfUnion<CameraSequenceSpec>[];\nconst LIGHT = [...COMMON, 'kind', 'falloff'] as const satisfies readonly KeysOfUnion<LightSequenceSpec>[];\nconst SHADER = [...COMMON, 'fragment', 'uniforms', 'width', 'height', 'resolution', 'transparent'] as const satisfies readonly KeysOfUnion<ShaderSequenceSpec>[];\nconst CANVAS = [...COMMON, 'draw', 'setup', 'width', 'height', 'resolution', 'params', 'seed', 'smoothing'] as const satisfies readonly KeysOfUnion<CanvasSequenceSpec>[];\nconst NULL = [...COMMON] as const satisfies readonly KeysOfUnion<NullSequenceSpec>[];\n\nconst SHAPE = [...COMMON, 'shape', 'fillGradient', 'colorSpace', 'strokeCap', 'strokeJoin'] as const;\nconst TRIM = ['trimStart', 'trimEnd', 'trimEach'] as const;\nconst RECT = [...SHAPE, ...TRIM, 'width', 'height', 'cornerRadius', 'anchorX', 'anchorY'] as const satisfies readonly KeysOfUnion<RectShapeSpec>[];\nconst CIRCLE = [...SHAPE, ...TRIM, 'radius', 'anchorX', 'anchorY'] as const satisfies readonly KeysOfUnion<CircleShapeSpec>[];\nconst ELLIPSE = [...SHAPE, ...TRIM, 'radiusX', 'radiusY', 'anchorX', 'anchorY'] as const satisfies readonly KeysOfUnion<EllipseShapeSpec>[];\nconst ARC = [...SHAPE, 'radius', 'innerRadius', 'startAngle', 'endAngle'] as const satisfies readonly KeysOfUnion<ArcShapeSpec>[];\nconst LINE = [...SHAPE, ...TRIM, 'from', 'to'] as const satisfies readonly KeysOfUnion<LineShapeSpec>[];\nconst POLYGON = [...SHAPE, ...TRIM, 'points', 'open'] as const satisfies readonly KeysOfUnion<PolygonShapeSpec>[];\nconst PATH = [...SHAPE, ...TRIM, 'd', 'fillRule', 'morphTo', 'morph', 'morphPoints'] as const satisfies readonly KeysOfUnion<PathShapeSpec>[];\n\n/** The compiler checks every table covers its type (this type is never used: it exists to fail the build). */\nexport type __Covered = [\n Assert<Covers<VideoSequenceSpec, typeof VIDEO>>, Assert<Covers<ImageSequenceSpec, typeof IMAGE>>, Assert<Covers<TextSequenceSpec, typeof TEXT>>,\n Assert<Covers<AudioAssetSpec | AudioSfxSpec | AudioMusicSpec, typeof AUDIO>>, Assert<Covers<CompositionSequenceSpec, typeof COMPOSITION>>,\n Assert<Covers<CameraSequenceSpec, typeof CAMERA>>, Assert<Covers<LightSequenceSpec, typeof LIGHT>>, Assert<Covers<ShaderSequenceSpec, typeof SHADER>>, Assert<Covers<CanvasSequenceSpec, typeof CANVAS>>, Assert<Covers<NullSequenceSpec, typeof NULL>>,\n Assert<Covers<RectShapeSpec, typeof RECT>>, Assert<Covers<CircleShapeSpec, typeof CIRCLE>>, Assert<Covers<EllipseShapeSpec, typeof ELLIPSE>>,\n Assert<Covers<ArcShapeSpec, typeof ARC>>, Assert<Covers<LineShapeSpec, typeof LINE>>, Assert<Covers<PolygonShapeSpec, typeof POLYGON>>,\n Assert<Covers<PathShapeSpec, typeof PATH>>,\n];\n\nconst SHAPES: Record<string, readonly string[]> = { rect: RECT, circle: CIRCLE, ellipse: ELLIPSE, arc: ARC, line: LINE, polygon: POLYGON, path: PATH };\nconst BY_TYPE: Record<string, readonly string[]> = { video: VIDEO, image: IMAGE, text: TEXT, audio: AUDIO, composition: COMPOSITION, camera: CAMERA, light: LIGHT, shader: SHADER, canvas: CANVAS, null: NULL };\n\n/** The keys that shape what is drawn: a layer that is never drawn (audio, a camera, a light) accepts them in its type but they do nothing there. */\nconst PICTURE_KEYS = ['filters', 'filterArea', 'mask', 'blendMode', 'threeD', 'hideBehindCamera', 'lit', 'castsShadows'] as const;\nconst NOT_DRAWN: Record<string, string> = {\n audio: 'an audio layer: it is not drawn (it plays)',\n camera: 'a camera layer: it is not drawn, it frames the picture (its place and lens go in `initial` / `keyframes`: x y z lookAtX … fov)',\n light: 'a light layer: it is not drawn, it lights `threeD` layers (its numbers go in `initial` / `keyframes`: intensity color …)',\n};\n\n/** For a layer that is never drawn: the picture keys it carries, each with what the layer is (the keys do nothing there). A light keeps `castsShadows`, its own switch. */\nexport function pictureKeysIgnored(spec: { type: string } & Record<string, unknown>): Array<{ key: string; why: string }> {\n const why = NOT_DRAWN[spec.type];\n if (!why) return [];\n return PICTURE_KEYS.filter(k => spec[k] !== undefined && !(spec.type === 'light' && k === 'castsShadows')).map(key => ({ key, why }));\n}\n\n/** What a layer may be called, for the message: `text layer`, `rect shape`. */\nexport function kindName(spec: { type: string; shape?: string }): string {\n return spec.type === 'shape' && spec.shape ? `${spec.shape} shape` : `${spec.type} layer`;\n}\n\n/** The keys a layer may have, or null for a kind this library does not know (a `three` layer: registered elsewhere, so not checked). */\nexport function layerKeys(spec: { type: string; shape?: string }): readonly string[] | null {\n if (spec.type === 'shape') return (spec.shape && SHAPES[spec.shape]) || null;\n return BY_TYPE[spec.type] ?? null;\n}\n\n/** Which kinds have `key` (for \"it belongs to a circle shape\"). */\nexport function kindsWithKey(key: string): string[] {\n const out: string[] = [];\n for (const [shape, keys] of Object.entries(SHAPES)) if (keys.includes(key)) out.push(`${shape} shape`);\n for (const [type, keys] of Object.entries(BY_TYPE)) if (keys.includes(key)) out.push(`${type} layer`);\n return out;\n}\n\n/** Names a keyframe or `initial` may animate. Dotted paths (`filters.g.amount`, `three.box.x`) belong to their routers and are not looked up here. */\nexport const PROP_KEYS: ReadonlySet<string> = new Set([\n // transform and look\n 'x', 'y', 'z', 'alpha', 'rotation', 'rotationX', 'rotationY', 'width', 'height', 'visible', 'autoAlpha',\n 'scale', 'scaleX', 'scaleY', 'anchor', 'anchorX', 'anchorY', 'pivot', 'pivotX', 'pivotY', 'skew', 'skewX', 'skewY',\n 'position', 'positionX', 'positionY', 'tilePosition', 'tilePositionX', 'tilePositionY', 'tileScale', 'tileScaleX', 'tileScaleY', 'tint',\n // shapes\n 'fillColor', 'fillAlpha', 'strokeColor', 'strokeAlpha', 'strokeWidth', 'cornerRadius', 'radius', 'radiusX', 'radiusY',\n 'innerRadius', 'startAngle', 'endAngle', 'trimStart', 'trimEnd', 'trimEach', 'morph', 'fillGradient', 'strokeCap', 'strokeJoin',\n // text, audio, video\n 'fill', 'value', 'visibleChars', 'text', 'volume', 'time',\n // camera\n 'lookAtX', 'lookAtY', 'lookAtZ', 'fov', 'focus', 'aperture', 'offsetX', 'offsetY', 'offsetZ', 'lookOffsetX', 'lookOffsetY', 'lookOffsetZ',\n // light + fog\n 'intensity', 'color', 'coneAngle', 'coneFeather', 'falloffDistance', 'shadowDarkness', 'shadowDiffusion', 'fogNear', 'fogFar', 'fogColor', 'fogAmount',\n]);\n\n/** The text `style` keys (PixiJS TextStyle) that this library passes on. */\nexport const STYLE_KEYS: readonly string[] = [\n 'fontSize', 'fontFamily', 'fontWeight', 'fontStyle', 'fontVariant', 'fill', 'stroke', 'dropShadow', 'letterSpacing', 'lineHeight', 'leading',\n 'align', 'wordWrap', 'wordWrapWidth', 'breakWords', 'whiteSpace', 'padding', 'textBaseline', 'trim', 'lineJoin', 'miterLimit', 'filters', 'tagStyles',\n];\n\nexport type { SequenceSpec };\n","import type { SequenceSpec } from '../types';\nimport { collectPropKeys } from '../space/specKeys';\nimport { suggestName } from './options';\nimport { layerKeys, kindName, kindsWithKey, pictureKeysIgnored, PROP_KEYS, STYLE_KEYS } from './layerKeys';\n\ntype Warn = (message: string, kind?: 'late-keyframe') => void;\nconst defaultWarn: Warn = m => console.warn(m);\n\n/** \"layer \"name\"\" or \"unnamed text layer\" — how warnings refer to a sequence. */\nexport function describeLayer(spec: { name?: string; type: string }): string {\n return spec.name ? `layer \"${spec.name}\"` : `unnamed ${spec.type} layer`;\n}\n\n/**\n * Timing mistakes that otherwise fail silently: a layer that starts after its\n * composition has ended, or a keyframe that starts after its own layer has\n * ended (the usual cause: writing keyframe `at` in composition time instead of\n * time since the start of the layer).\n */\nexport function lintTiming(spec: SequenceSpec, parentDuration: number, warn: Warn = defaultWarn): void {\n const who = describeLayer(spec);\n const at = spec.at ?? 0;\n if (at >= parentDuration) {\n warn(`pixi-effects: ${who} starts at ${at}s, after its composition ends (${parentDuration}s), so it is never visible`);\n }\n // An sfx or a piece of music without a duration is as long as its sound, which only the audio layer knows (it checks its own keyframes).\n if (spec.type === 'audio' && ((spec as { sfx?: unknown }).sfx !== undefined || (spec as { music?: unknown }).music !== undefined) && spec.duration === undefined) return;\n if (at >= parentDuration) return; // never visible: said above, and its keyframes say nothing more\n const lives = spec.duration ?? Math.max(0, parentDuration - at); // without a duration a layer lives until its composition ends\n const ms = (x: number) => Math.round(x * 1000) / 1000;\n (spec.keyframes ?? []).forEach((kf, i) => {\n const kat = kf.at ?? 0;\n if (kat >= lives) {\n warn(\n `pixi-effects: ${who}: keyframes[${i}] starts at ${kat}s, after the layer ends (duration ${ms(lives)}s), so it never plays. ` +\n `Keyframe \\`at\\` is measured from the start of this layer, not from the start of the composition.`,\n 'late-keyframe',\n );\n return;\n }\n // Starts in time but is cut off by the layer's end: a FADE (to alpha 0 or 1, or from 0 / 1) that never finishes (a trial: a fade-out that stopped half way, a 1.2 s fade-in on a 0.6 s layer).\n // Not a drift, a fall, a zoom or a decaying glow (to 0.22) that is still going when its layer ends: those are common and on purpose. A `set` is instant, a repeating tween\n // (a blink) and a spring ('auto') are meant to run over, and a cut of under 0.2 s (a few frames: the last keyframe of a generated loop) is nothing.\n const length = kf.duration;\n // a fade ends (almost) opaque or (almost) clear: `to` alpha, or, when only `from` names alpha, the value it fades from (a fade-in `from: { alpha: 0 }` ends where it was)\n const toAlpha = (kf.to as Record<string, unknown> | undefined)?.alpha;\n const fromAlpha = (kf.from as Record<string, unknown> | undefined)?.alpha;\n const nearEnd = (v: unknown) => typeof v === 'number' && (v <= 0.1 || v >= 0.9);\n const fade = toAlpha !== undefined ? nearEnd(toAlpha) : nearEnd(fromAlpha);\n if (fade && typeof length === 'number' && length > 0 && !kf.repeat) {\n const start = kat < 0 ? lives + kat : kat; // a negative `at` counts back from the end of the layer\n const cut = (start + length - lives) / length;\n if (start >= 0 && cut > 0.25 && cut * length >= 0.2) {\n warn(\n `pixi-effects: ${who}: keyframes[${i}] runs from ${ms(start)}s to ${ms(start + length)}s but the layer ends at ${ms(lives)}s, so the last ${Math.round(cut * 100)}% of it never plays ` +\n `(a fade stops half way). Keyframe \\`at\\` is measured from the start of this layer: start it earlier, make it shorter, or make the layer longer.`,\n 'late-keyframe',\n );\n }\n }\n });\n}\n\n/**\n * A loop that builds a hundred layers makes the same mistake a hundred times. Print the first few in full and\n * say how many more there are: the rest are the same loop.\n */\nexport function summarizeWarnings(messages: string[], shown = 3): string[] {\n if (messages.length <= shown) return messages;\n return [...messages.slice(0, shown), `pixi-effects: ${messages.length - shown} more layers with the same problem (the same loop or helper is probably building them): fix the ones above and these go away.`];\n}\n\n/** A text layer that animates `value` but has nowhere to print it. */\nexport function lintText(spec: SequenceSpec, warn: Warn = defaultWarn): void {\n if (spec.type !== 'text') return;\n // Every string the layer ever shows: its `text` and any `set: { text }` swap.\n const strings = [spec.text ?? '', ...(spec.keyframes ?? []).map(kf => String((kf.set as Record<string, unknown> | undefined)?.text ?? ''))];\n const hasPlaceholder = strings.some(t => t.includes('{value}'));\n if (hasPlaceholder && !collectPropKeys(spec).has('value')) {\n warn(`pixi-effects: ${describeLayer(spec)}: the text has a {value} placeholder but nothing sets or animates \\`value\\`, so it prints 0. Set initial: { value: … } or animate it; {value} is reserved for counters (there is no way to print a literal \"{value}\").`);\n }\n if (collectPropKeys(spec).has('value') && !hasPlaceholder) {\n warn(`pixi-effects: ${describeLayer(spec)}: \\`value\\` is set or animated but the text has no {value} placeholder, so nothing shows it. Use text: '{value}' (or e.g. '{value} users').`);\n }\n}\n\nconst REMAPPABLE = new Set(['video', 'audio', 'composition']);\n/** Top-level names that mean a time remap and have their own message. */\nconst REMAP_NAMES = new Set(['time', 'timeRemap', 'reverse']);\nconst TIME_BAGS = ['set', 'to', 'from'] as const;\n\n/** The mistakes a time remap can make (`speed` / `time`), said once when the layer is built. Called from `lintKeys`. */\nfunction lintRemap(spec: SequenceSpec, warn: Warn): void {\n const s = spec as unknown as Record<string, any>;\n const who = describeLayer(spec);\n const kfs: Array<Record<string, any>> = s.keyframes ?? [];\n const bags: Array<Record<string, any> | undefined> = [s.initial, ...kfs.flatMap(kf => TIME_BAGS.map(b => kf[b]))];\n const timeInInitial = !!s.initial && s.initial.time !== undefined;\n const timeInKeyframes = kfs.some(kf => TIME_BAGS.some(b => kf[b] && kf[b].time !== undefined));\n const timed = timeInInitial || timeInKeyframes;\n if (bags.some(bag => bag && bag.speed !== undefined)) {\n warn(`pixi-effects: ${who}: speed is a fixed setting, not an animatable property: write it on the layer (speed: 2); to change the speed over time, animate time (keyframes: [{ at: 0, from: { time: 0 }, to: { time: 4 }, duration: 2, ease: 'power2.in' }])`);\n }\n if (!REMAPPABLE.has(spec.type)) {\n if (timed) warn(`pixi-effects: ${who}: \"time\" only works on video, audio and composition layers; put this layer in a composition and remap that (it is ignored here)`);\n return; // a top-level speed here is the key warning's business (\"not a ... key\")\n }\n if (s.reverse !== undefined) warn(`pixi-effects: ${who}: \"reverse\" is not a key: write speed: -1 to play backward`);\n if (s.timeRemap !== undefined) warn(`pixi-effects: ${who}: \"timeRemap\" is not a key: animate time in keyframes (from / to / set), or write speed`);\n if (s.time !== undefined) warn(`pixi-effects: ${who}: \"time\" belongs in initial or keyframes (initial: { time: 2 }), not on the layer`);\n if (spec.type === 'audio' && (s.sfx !== undefined || s.music !== undefined) && (s.speed !== undefined || timed)) {\n warn(`pixi-effects: ${who}: speed and time do not apply to a synthesised sound (sfx, music); change an sfx's pitch with pitch (semitones), or put it in a composition and remap that`);\n return;\n }\n if (s.speed !== undefined && (typeof s.speed !== 'number' || !Number.isFinite(s.speed) || s.speed === 0)) {\n warn(`pixi-effects: ${who}: speed ${JSON.stringify(s.speed)} cannot be used: write a number other than 0 (negative plays backward); to hold a moment, key time to the same value twice`);\n }\n if (s.speed !== undefined && timeInKeyframes) {\n warn(`pixi-effects: ${who}: speed and keyframed time are both set; time wins and speed is ignored. Use one of them`);\n }\n if (timeInInitial && typeof s.initial.time !== 'number') {\n warn(`pixi-effects: ${who}: initial.time must be a number of seconds, got ${JSON.stringify(s.initial.time)}`);\n }\n kfs.forEach((kf, i) => {\n for (const b of TIME_BAGS) {\n const v = kf[b]?.time;\n if (v !== undefined && typeof v !== 'number' && typeof v !== 'string') warn(`pixi-effects: ${who}: keyframes[${i}].${b}.time must be a number of seconds, got ${JSON.stringify(v)}`);\n }\n });\n}\n\n/**\n * Keys that do not exist, which the library used to ignore without a word (a typo in a layer key, a mistyped property in a keyframe, a\n * style key Pixi does not know). Said once each, with what was probably meant. A warning, never an error. Not checked here: dotted\n * paths (`filters.g.amount`, `three.box.x`: their routers say what is wrong), the names `fillGradient` says itself (`gradientAngle`),\n * and a kind of layer registered elsewhere (`three`).\n */\n/** Keys the library used to have: the sentence says what to write now. */\nconst REMOVED_KEYS: Record<string, string> = {\n maskInverted: 'a hole instead of a crop is `mask: { layer: <the mask layer>, invert: true }`',\n};\n\n/** The knobs of a sound effect: they are keys of `sfx: { … }`, not of the layer. */\nconst SFX_KNOBS = new Set(['pitch', 'brightness', 'seed']);\n\n/** Where a layer is and how it is turned: always `initial` or a keyframe, even for a kind of layer that has such a name as a key of another meaning (`anchorX` on a shape). */\nconst TRANSFORM_KEYS = new Set(['x', 'y', 'z', 'alpha', 'rotation', 'rotationX', 'rotationY', 'scale', 'scaleX', 'scaleY', 'anchor', 'anchorX', 'anchorY',\n 'pivot', 'pivotX', 'pivotY', 'skew', 'skewX', 'skewY', 'position', 'positionX', 'positionY', 'tint', 'visible', 'autoAlpha']);\n\nexport function lintKeys(spec: SequenceSpec, warn: Warn = defaultWarn): void {\n const who = describeLayer(spec);\n const valid = layerKeys(spec as { type: string; shape?: string });\n if (valid) {\n for (const key of Object.keys(spec)) {\n if (valid.includes(key) || (spec as unknown as Record<string, unknown>)[key] === undefined) continue; // a spread that sets a key only when it applies leaves `undefined`\n if (REMAPPABLE.has(spec.type) && REMAP_NAMES.has(key)) continue; // lintRemap has its own message for these\n if (REMOVED_KEYS[key]) { warn(`pixi-effects: ${who}: \"${key}\" is not a ${kindName(spec as { type: string; shape?: string })} key: ${REMOVED_KEYS[key]}`); continue; }\n // `pitch`, `brightness`, `seed` are knobs of the sound effect, they go inside `sfx` (a trial: three agents wrote them beside `sfx:`)\n if (spec.type === 'audio' && SFX_KNOBS.has(key) && (spec as { sfx?: unknown }).sfx !== undefined) {\n const sfx = (spec as { sfx?: unknown }).sfx;\n const preset = typeof sfx === 'string' ? sfx : (sfx as { preset?: string } | null)?.preset;\n const v = (spec as unknown as Record<string, unknown>)[key];\n const shown = typeof v === 'number' || (typeof v === 'string' && v.length <= 24) ? JSON.stringify(v) : '…';\n warn(`pixi-effects: ${who}: \"${key}\" is a knob of the sound effect, so it is ignored beside \\`sfx\\`: write it inside — sfx: { ${preset ? `preset: '${preset}', ` : ''}${key}: ${shown} }${key === 'pitch' ? ' (pitch is in semitones, ±24: 12 is an octave up)' : key === 'brightness' ? ' (brightness is −1 dark … 1 bright)' : ''}`);\n continue;\n }\n // `z`, `x`, `alpha`, `rotationX` … are properties of the picture: they go in `initial` (or a keyframe), not on the layer. Say that, not a guess at another key.\n if (TRANSFORM_KEYS.has(key) || (PROP_KEYS.has(key) && kindsWithKey(key).length === 0)) { // (a key some other kind of layer owns, `radius` on a rect, keeps its \"it belongs to\" sentence below)\n const v = (spec as unknown as Record<string, unknown>)[key];\n const shown = typeof v === 'number' || (typeof v === 'string' && v.length <= 24) ? JSON.stringify(v) : '…';\n warn(`pixi-effects: ${who}: \"${key}\" is a property, not a ${kindName(spec as { type: string; shape?: string })} key, so it is ignored: write it in initial (initial: { ${key}: ${shown} }) or in a keyframe`);\n continue;\n }\n const guess = suggestName(key, valid.filter(k => k !== 'type'));\n const owners = kindsWithKey(key);\n const belongs = owners.length ? ` (it belongs to ${owners.slice(0, 3).join(', ')})` : '';\n const hint = guess ? ` — did you mean \"${guess}\"?${belongs}` : belongs;\n warn(`pixi-effects: ${who}: \"${key}\" is not a ${kindName(spec as { type: string; shape?: string })} key${hint}. Valid keys: ${valid.join(', ')}`);\n }\n }\n for (const { key, why } of pictureKeysIgnored(spec as unknown as { type: string } & Record<string, unknown>)) warn(`pixi-effects: ${who}: \"${key}\" does nothing on ${why}`);\n lintRemap(spec, warn);\n const said = new Set<string>();\n const bags: Array<Record<string, unknown> | undefined> = [spec.initial, ...(spec.keyframes ?? []).flatMap(kf => [kf.set, kf.to, kf.from])] as Array<Record<string, unknown> | undefined>;\n for (const bag of bags) {\n for (const key of Object.keys(bag ?? {})) {\n if (bag![key] === undefined) continue;\n if (PROP_KEYS.has(key) || key.includes('.') || /^gradient/i.test(key) || said.has(key) || key === 'speed') continue; // (speed: lintRemap says what to write)\n said.add(key);\n const guess = suggestName(key, [...PROP_KEYS]);\n warn(`pixi-effects: ${who}: \"${key}\" is not an animatable property${guess ? ` — did you mean \"${guess}\"?` : ''} (in initial, set, to or from). See the cheatsheet for the properties of this kind of layer`);\n }\n }\n if (spec.type === 'text' && spec.style && typeof spec.style === 'object') {\n for (const key of Object.keys(spec.style)) {\n if (STYLE_KEYS.includes(key) || (spec.style as Record<string, unknown>)[key] === undefined) continue;\n const guess = suggestName(key, STYLE_KEYS);\n warn(`pixi-effects: ${who}: style.${key} is not a text style key${guess ? ` — did you mean \"${guess}\"?` : ''} (it is ignored). Keys: ${STYLE_KEYS.join(', ')}`);\n }\n }\n}\n"]}
|