reacthub-pixitweenjs 0.3.0-alpha.3 → 0.3.0-alpha.5
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/README.md +25 -16
- package/dist/components/AepComposition/AepAudioLayer.d.ts +6 -0
- package/dist/components/AepComposition/AepComposition.d.ts +2 -0
- package/dist/components/AepComposition/AudioPlaybackManager.d.ts +38 -0
- package/dist/components/AepComposition/index.d.ts +3 -0
- package/dist/components/AepComposition/model3d/Pixi3DModelLayer.d.ts +9 -0
- package/dist/components/AepComposition/model3d/ThreeOffscreenRenderer.d.ts +49 -0
- package/dist/components/AepComposition/model3d/framing.d.ts +30 -0
- package/dist/components/AepComposition/model3d/geometry.d.ts +27 -0
- package/dist/components/AepComposition/model3d/index.d.ts +14 -0
- package/dist/components/AepComposition/model3d/lighting.d.ts +40 -0
- package/dist/components/AepComposition/model3d/readiness.d.ts +17 -0
- package/dist/components/AepComposition/model3d/sampleModel3D.d.ts +40 -0
- package/dist/components/PixiAudioSprite/HowlerAudioSprite.d.ts +8 -2
- package/dist/components/PixiAudioSprite/PixiAudioSprite.d.ts +37 -1
- package/dist/components/PixiAudioSprite/index.d.ts +3 -0
- package/dist/components/PixiAudioSprite/useAudioVisualizer.d.ts +30 -15
- package/dist/components/PixiCaptionsTrack/PixiCaptionsTrack.d.ts +86 -0
- package/dist/components/PixiCaptionsTrack/index.d.ts +5 -0
- package/dist/components/PixiCaptionsTrack/useCaptionPlayhead.d.ts +31 -0
- package/dist/components/PixiCaptionsTrack/useCaptionStyles.d.ts +45 -0
- package/dist/components/PixiCaptionsTrack/useEmojiTextures.d.ts +8 -0
- package/dist/components/PixiImageSprite/PixiImageSprite.d.ts +17 -0
- package/dist/components/PixiSVGSprite/PixiSvgImageSprite.d.ts +35 -0
- package/dist/components/PixiSVGSprite/PixiSvgPathSprite.d.ts +10 -0
- package/dist/components/PixiSVGSprite/PixiSvgRawSprite.d.ts +30 -0
- package/dist/components/PixiSVGSprite/index.d.ts +4 -0
- package/dist/components/PixiSequence/PixiSequence.d.ts +29 -5
- package/dist/components/PixiTextSprite/PixiTextSplit.d.ts +1 -1
- package/dist/components/PixiTextSprite/PixiTextSprite.d.ts +25 -0
- package/dist/components/PixiThreeSprite/PixiThreeModelSprite.d.ts +23 -0
- package/dist/components/PixiThreeSprite/index.d.ts +2 -0
- package/dist/components/PixiVideoSprite/PixiVideoSprite.d.ts +26 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/constants/animation.d.ts +232 -0
- package/dist/constants/animationConstants.d.ts +90 -0
- package/dist/constants/elementNames.d.ts +4 -0
- package/dist/filters/animated-vfx/AnimatedVfxFilter.d.ts +19 -0
- package/dist/filters/animated-vfx/composeShader.d.ts +2 -0
- package/dist/filters/animated-vfx/index.d.ts +14 -0
- package/dist/filters/animated-vfx/registry/animatedVfxRegistry.d.ts +65 -0
- package/dist/filters/animated-vfx/registry/waveTearVariants.d.ts +14 -0
- package/dist/filters/animated-vfx/toStrength.d.ts +1 -0
- package/dist/filters/animated-vfx/types.d.ts +18 -0
- package/dist/filters/animatedVfxFilters.d.ts +1 -0
- package/dist/filters/blend-mode/createBlendCompositeFilter.d.ts +26 -0
- package/dist/filters/edge-feather/EdgeFeatherFilter.d.ts +19 -0
- package/dist/filters/plate-key/PlateKeyFilter.d.ts +29 -0
- package/dist/hocs/AbstractContainer.d.ts +5 -0
- package/dist/hocs/BorderMaskContainer.d.ts +23 -8
- package/dist/hocs/drawMaskShape.d.ts +6 -2
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/useBlendFilter.d.ts +17 -0
- package/dist/hooks/useDebouncedValue.d.ts +11 -0
- package/dist/hooks/useFontEpoch.d.ts +10 -0
- package/dist/hooks/usePixiAnimation.d.ts +24 -0
- package/dist/hooks/useSpriteFilters.d.ts +54 -12
- package/dist/hooks/useSvgTexture.d.ts +29 -0
- package/dist/hooks/video/useVideoLifecycle.d.ts +23 -0
- package/dist/hooks/video/useVideoTexture.d.ts +34 -0
- package/dist/hooks/video/useVideoTimelineSync.d.ts +24 -0
- package/dist/hooks/withEffectsHook.d.ts +326 -0
- package/dist/hooks/withFiltersHook.d.ts +104 -38
- package/dist/index.es.js +29296 -60769
- package/dist/index.umd.js +1367 -4362
- package/dist/providers/GsapPixieContextProvider.d.ts +7 -0
- package/dist/types/BaseProps.d.ts +41 -14
- package/dist/types/CropTypes.d.ts +12 -0
- package/dist/types/Effects.d.ts +674 -13
- package/dist/types/aep/asset.d.ts +7 -7
- package/dist/types/aep/camera.d.ts +64 -64
- package/dist/types/aep/composition.d.ts +1 -1
- package/dist/types/aep/effect.d.ts +2 -2
- package/dist/types/aep/layer.d.ts +11733 -6766
- package/dist/types/aep/light.d.ts +80 -80
- package/dist/types/aep/postFx.d.ts +2 -2
- package/dist/types/aep/text-animator.d.ts +6 -6
- package/dist/types/aep/transform.d.ts +14 -14
- package/dist/types/aep/transform3D.d.ts +18 -18
- package/dist/types/index.d.ts +1 -0
- package/dist/utils/PixiSelector.d.ts +17 -0
- package/dist/utils/assetRefCount.d.ts +20 -0
- package/dist/utils/audio/audioRegistry.d.ts +51 -0
- package/dist/utils/audio/audioSync.d.ts +98 -0
- package/dist/utils/audio/gainEnvelope.d.ts +162 -0
- package/dist/utils/audio/useAudioRegistry.d.ts +10 -0
- package/dist/utils/blendMode.d.ts +21 -0
- package/dist/utils/captions/captionClock.d.ts +17 -0
- package/dist/utils/captions/captionDisplay.d.ts +57 -0
- package/dist/utils/captions/captionPose.d.ts +107 -0
- package/dist/utils/captions/captionVisual.d.ts +32 -0
- package/dist/utils/captions/captionWordLayout.d.ts +67 -0
- package/dist/utils/captions/groupSegments.d.ts +20 -0
- package/dist/utils/captions/index.d.ts +10 -0
- package/dist/utils/captions/parseSubtitles.d.ts +25 -0
- package/dist/utils/captions/sampleCaption.d.ts +25 -0
- package/dist/utils/captions/timeBase.d.ts +34 -0
- package/dist/utils/captions/types.d.ts +279 -0
- package/dist/utils/entranceOverlap.d.ts +37 -0
- package/dist/utils/fonts/fontLoader.d.ts +33 -0
- package/dist/utils/fonts/fontSpec.d.ts +16 -0
- package/dist/utils/fonts/googleFonts.d.ts +9 -0
- package/dist/utils/gifTiming.d.ts +22 -0
- package/dist/utils/gsapEffects/animationConfigs.d.ts +3802 -0
- package/dist/utils/gsapEffects/backEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/bounceEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/effectGenerator.d.ts +3 -0
- package/dist/utils/gsapEffects/effectTarget.d.ts +11 -0
- package/dist/utils/gsapEffects/fadeEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/flipEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/generalEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/index.d.ts +0 -0
- package/dist/utils/gsapEffects/lightSpeedEffect.d.ts +1 -0
- package/dist/utils/gsapEffects/logoRevealEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/registerEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/riseEffect.d.ts +1 -0
- package/dist/utils/gsapEffects/rollEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/rotateEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/slideEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/staggerTextEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/textUnitEffects.d.ts +1 -0
- package/dist/utils/gsapEffects/textUnitMask.d.ts +1 -0
- package/dist/utils/gsapEffects/textUnits.d.ts +172 -0
- package/dist/utils/gsapEffects/typewriterEffect.d.ts +1 -0
- package/dist/utils/gsapEffects/wipeEffect.d.ts +1 -0
- package/dist/utils/gsapEffects/zoomEffects.d.ts +1 -0
- package/dist/utils/index.d.ts +25 -0
- package/dist/utils/loadSpriteTexture.d.ts +33 -4
- package/dist/utils/sanitizeCanvasText.d.ts +12 -0
- package/dist/utils/sanitizeImageSrc.d.ts +7 -0
- package/dist/utils/svgRaster.d.ts +47 -0
- package/dist/utils/textLayout/index.d.ts +1 -1
- package/dist/utils/textLayout/layoutText.d.ts +5 -0
- package/dist/utils/textStyle.d.ts +81 -0
- package/dist/utils/textStyleV8.d.ts +62 -0
- package/dist/utils/threeModel.d.ts +55 -0
- package/dist/utils/videoAssetCache.d.ts +4 -0
- package/dist/utils/videoReadiness.d.ts +13 -0
- package/package.json +12 -13
|
@@ -96,73 +96,73 @@ export declare const aepTransform3DSchema: z.ZodObject<{
|
|
|
96
96
|
hold?: boolean | undefined;
|
|
97
97
|
}>, "many">]>;
|
|
98
98
|
}, "strict", z.ZodTypeAny, {
|
|
99
|
-
|
|
99
|
+
scale: [number, number] | {
|
|
100
100
|
t: number;
|
|
101
|
-
v: [number, number
|
|
101
|
+
v: [number, number];
|
|
102
102
|
easeIn?: [number, number, number, number] | undefined;
|
|
103
103
|
easeOut?: [number, number, number, number] | undefined;
|
|
104
104
|
hold?: boolean | undefined;
|
|
105
105
|
}[];
|
|
106
|
-
|
|
106
|
+
position: [number, number, number] | {
|
|
107
107
|
t: number;
|
|
108
108
|
v: [number, number, number];
|
|
109
109
|
easeIn?: [number, number, number, number] | undefined;
|
|
110
110
|
easeOut?: [number, number, number, number] | undefined;
|
|
111
111
|
hold?: boolean | undefined;
|
|
112
112
|
}[];
|
|
113
|
-
|
|
113
|
+
anchor: [number, number, number] | {
|
|
114
114
|
t: number;
|
|
115
|
-
v: [number, number];
|
|
115
|
+
v: [number, number, number];
|
|
116
116
|
easeIn?: [number, number, number, number] | undefined;
|
|
117
117
|
easeOut?: [number, number, number, number] | undefined;
|
|
118
118
|
hold?: boolean | undefined;
|
|
119
119
|
}[];
|
|
120
|
-
|
|
120
|
+
rotation: [number, number, number] | {
|
|
121
121
|
t: number;
|
|
122
|
-
v: number;
|
|
122
|
+
v: [number, number, number];
|
|
123
123
|
easeIn?: [number, number, number, number] | undefined;
|
|
124
124
|
easeOut?: [number, number, number, number] | undefined;
|
|
125
125
|
hold?: boolean | undefined;
|
|
126
126
|
}[];
|
|
127
|
-
|
|
127
|
+
opacity: number | {
|
|
128
128
|
t: number;
|
|
129
|
-
v:
|
|
129
|
+
v: number;
|
|
130
130
|
easeIn?: [number, number, number, number] | undefined;
|
|
131
131
|
easeOut?: [number, number, number, number] | undefined;
|
|
132
132
|
hold?: boolean | undefined;
|
|
133
133
|
}[];
|
|
134
134
|
}, {
|
|
135
|
-
|
|
135
|
+
scale: [number, number] | {
|
|
136
136
|
t: number;
|
|
137
|
-
v: [number, number
|
|
137
|
+
v: [number, number];
|
|
138
138
|
easeIn?: [number, number, number, number] | undefined;
|
|
139
139
|
easeOut?: [number, number, number, number] | undefined;
|
|
140
140
|
hold?: boolean | undefined;
|
|
141
141
|
}[];
|
|
142
|
-
|
|
142
|
+
position: [number, number, number] | {
|
|
143
143
|
t: number;
|
|
144
144
|
v: [number, number, number];
|
|
145
145
|
easeIn?: [number, number, number, number] | undefined;
|
|
146
146
|
easeOut?: [number, number, number, number] | undefined;
|
|
147
147
|
hold?: boolean | undefined;
|
|
148
148
|
}[];
|
|
149
|
-
|
|
149
|
+
anchor: [number, number, number] | {
|
|
150
150
|
t: number;
|
|
151
|
-
v: [number, number];
|
|
151
|
+
v: [number, number, number];
|
|
152
152
|
easeIn?: [number, number, number, number] | undefined;
|
|
153
153
|
easeOut?: [number, number, number, number] | undefined;
|
|
154
154
|
hold?: boolean | undefined;
|
|
155
155
|
}[];
|
|
156
|
-
|
|
156
|
+
rotation: [number, number, number] | {
|
|
157
157
|
t: number;
|
|
158
|
-
v: number;
|
|
158
|
+
v: [number, number, number];
|
|
159
159
|
easeIn?: [number, number, number, number] | undefined;
|
|
160
160
|
easeOut?: [number, number, number, number] | undefined;
|
|
161
161
|
hold?: boolean | undefined;
|
|
162
162
|
}[];
|
|
163
|
-
|
|
163
|
+
opacity: number | {
|
|
164
164
|
t: number;
|
|
165
|
-
v:
|
|
165
|
+
v: number;
|
|
166
166
|
easeIn?: [number, number, number, number] | undefined;
|
|
167
167
|
easeOut?: [number, number, number, number] | undefined;
|
|
168
168
|
hold?: boolean | undefined;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as PIXI from "pixi.js";
|
|
2
|
+
/** Stable element label from a uniqueId. */
|
|
3
|
+
export declare const getElementName: (uniqueId: string) => string;
|
|
4
|
+
/** Inverse of `getElementName`; null when the label is not an element label. */
|
|
5
|
+
export declare const getUniqueIdFromName: (name: string | null | undefined) => string | null;
|
|
6
|
+
/** Find a descendant by exact label (recursive). */
|
|
7
|
+
export declare const findByName: (container: PIXI.Container | null | undefined, name: string | null | undefined) => PIXI.Container | null;
|
|
8
|
+
/** Find every descendant whose label matches a `*` glob, e.g. `image-*`. */
|
|
9
|
+
export declare const findAllByPattern: (container: PIXI.Container | null | undefined, pattern: string) => PIXI.Container[];
|
|
10
|
+
export interface PixiSelector {
|
|
11
|
+
(name: string): PIXI.Container | null;
|
|
12
|
+
all: (pattern: string) => PIXI.Container[];
|
|
13
|
+
byId: (uniqueId: string) => PIXI.Container | null;
|
|
14
|
+
byType: (type: string) => PIXI.Container[];
|
|
15
|
+
}
|
|
16
|
+
/** A selector scoped to `container`, like `gsap.utils.selector` for Pixi. */
|
|
17
|
+
export declare const createPixiSelector: (container: PIXI.Container) => PixiSelector;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared ownership of `PIXI.Assets`-cached resources.
|
|
3
|
+
*
|
|
4
|
+
* `PIXI.Assets` caches one texture per URL, so every sprite showing that URL gets the SAME
|
|
5
|
+
* texture. Unloading on one sprite's unmount destroys it under any other sprite still using
|
|
6
|
+
* it, and a remount (a stage swapping canvases with the same assets) runs the old cleanup's
|
|
7
|
+
* unload right as the new mount resolves to the cached texture, leaving a destroyed texture
|
|
8
|
+
* on screen. So: count users per key and unload only once nobody has used it for a grace
|
|
9
|
+
* period, which also lets an immediate remount reuse the loaded texture.
|
|
10
|
+
*
|
|
11
|
+
* Extracted from Clippy's video asset cache so images/SVGs can use it too.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_UNLOAD_GRACE_MS = 3000;
|
|
14
|
+
export interface AssetRefCounter {
|
|
15
|
+
/** Mark `key` in use; cancels a pending unload so a remount keeps the texture. */
|
|
16
|
+
acquire: (key: string) => void;
|
|
17
|
+
/** Release one use; the asset is unloaded after the grace period if still unused. */
|
|
18
|
+
release: (key: string) => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function createAssetRefCounter(graceMs?: number): AssetRefCounter;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry of the <audio> elements the AudioPlaybackManager owns, keyed by clip uniqueId, plus the
|
|
3
|
+
* Web Audio graph nodes built on top of them.
|
|
4
|
+
*
|
|
5
|
+
* Ported from the Clippy editor's `audioRegistry`, with `Howl` replaced by `HTMLAudioElement`
|
|
6
|
+
* (this engine plays through plain elements; see AudioPlaybackManager for why).
|
|
7
|
+
*
|
|
8
|
+
* It lives outside React so the manager (outside the Pixi stage) and the waveform / pan code
|
|
9
|
+
* (inside it) reach the SAME element without bridging a context through the Pixi tree.
|
|
10
|
+
*
|
|
11
|
+
* Why the source-node cache matters: `createMediaElementSource(el)` can be called ONCE per
|
|
12
|
+
* element, ever. Pan and a visualizer both want it, so whoever asks first creates the node and
|
|
13
|
+
* everyone else reuses it via `getOrCreateSourceNode`. That removes the old "pan and waveform
|
|
14
|
+
* are mutually exclusive" limitation.
|
|
15
|
+
*
|
|
16
|
+
* The registry is an instance (`createAudioRegistry`) so two players on one page do not share
|
|
17
|
+
* state; `audioRegistry` is the default shared one, which is what one editor player wants.
|
|
18
|
+
*/
|
|
19
|
+
export interface AudioRegistry {
|
|
20
|
+
get(id: string): HTMLAudioElement | undefined;
|
|
21
|
+
set(id: string, el: HTMLAudioElement, src: string): void;
|
|
22
|
+
srcFor(id: string): string | undefined;
|
|
23
|
+
has(id: string): boolean;
|
|
24
|
+
/** Stop and release the element. Keeps the source node (it cannot be recreated for the element). */
|
|
25
|
+
delete(id: string): void;
|
|
26
|
+
forEach(fn: (el: HTMLAudioElement, id: string) => void): void;
|
|
27
|
+
readonly size: number;
|
|
28
|
+
/** Called whenever an element is added, replaced or removed. Returns the unsubscribe. */
|
|
29
|
+
subscribe(fn: () => void): () => void;
|
|
30
|
+
/**
|
|
31
|
+
* Increments on every change. Read it with `useSyncExternalStore(registry.subscribe, () =>
|
|
32
|
+
* registry.version)` so a component can never miss a change that happens between its render
|
|
33
|
+
* and its subscription (a child's effect runs before its parent's).
|
|
34
|
+
*/
|
|
35
|
+
readonly version: number;
|
|
36
|
+
/** The shared Web Audio context, created on first use. */
|
|
37
|
+
getContext(): AudioContext | null;
|
|
38
|
+
/** The context only if it already exists (resume it without creating one). */
|
|
39
|
+
getContextIfExists(): AudioContext | null;
|
|
40
|
+
/** The element's one-and-only MediaElementAudioSourceNode, created on first request. */
|
|
41
|
+
getOrCreateSourceNode(id: string): MediaElementAudioSourceNode | null;
|
|
42
|
+
getSourceNode(id: string): MediaElementAudioSourceNode | undefined;
|
|
43
|
+
/** Register a node created elsewhere (e.g. by AudioMotion) so others can reuse it. */
|
|
44
|
+
setSourceNode(id: string, node: MediaElementAudioSourceNode): void;
|
|
45
|
+
hasSourceNode(id: string): boolean;
|
|
46
|
+
/** Stop + release every element, drop the source nodes, close the context. */
|
|
47
|
+
clear(): void;
|
|
48
|
+
}
|
|
49
|
+
export declare function createAudioRegistry(): AudioRegistry;
|
|
50
|
+
/** The default registry: one editor player on screen at a time. */
|
|
51
|
+
export declare const audioRegistry: AudioRegistry;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { AudioFade, DuckWindow } from './gainEnvelope';
|
|
2
|
+
/** Seek only when the element has drifted this far from where the timeline says it should be. */
|
|
3
|
+
export declare const DRIFT_TOLERANCE_SEC = 0.2;
|
|
4
|
+
/** Stay this far inside the end of the media so a seek never lands on / past EOF. */
|
|
5
|
+
export declare const END_GUARD_SEC = 0.05;
|
|
6
|
+
export interface AudioClipSpec {
|
|
7
|
+
uniqueId: string;
|
|
8
|
+
src: string;
|
|
9
|
+
/** Timeline position where the clip starts (seconds). */
|
|
10
|
+
start: number;
|
|
11
|
+
/** Timeline position where the clip ends (seconds). */
|
|
12
|
+
end: number;
|
|
13
|
+
/** Offset into the audio FILE the clip starts from (seconds). */
|
|
14
|
+
frameStartAt: number;
|
|
15
|
+
/** Clip volume 0..1. Default 1. */
|
|
16
|
+
volume?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Volume as a function of timeline time (seconds); wins over `volume`. Lets another system
|
|
19
|
+
* (e.g. an animated AEP `level`) drive the base volume while fades/ducking/mute still apply on
|
|
20
|
+
* top. Being a function of `t`, not of mutable state, it is correct under scrub and seek.
|
|
21
|
+
*/
|
|
22
|
+
getVolume?: (timelineTime: number) => number;
|
|
23
|
+
mute?: boolean;
|
|
24
|
+
/** Playback rate. Default 1. */
|
|
25
|
+
speed?: number;
|
|
26
|
+
audioFade?: AudioFade;
|
|
27
|
+
/** Clip-relative windows (seconds) during which the clip ducks. */
|
|
28
|
+
duckWindows?: DuckWindow[];
|
|
29
|
+
/** Linear multiplier applied inside those windows. */
|
|
30
|
+
duckTo?: number;
|
|
31
|
+
}
|
|
32
|
+
/** Master-player state at the moment of a tick. */
|
|
33
|
+
export interface PlayerState {
|
|
34
|
+
playing: boolean;
|
|
35
|
+
/** The playhead just moved backwards. */
|
|
36
|
+
reverse?: boolean;
|
|
37
|
+
/** The user is dragging the scrubber. */
|
|
38
|
+
seeking?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** What the media element is doing right now. */
|
|
41
|
+
export interface ElementSnapshot {
|
|
42
|
+
paused: boolean;
|
|
43
|
+
currentTime: number;
|
|
44
|
+
/** NaN / Infinity until metadata loads. */
|
|
45
|
+
duration: number;
|
|
46
|
+
}
|
|
47
|
+
export type SyncDecision = {
|
|
48
|
+
kind: "pause";
|
|
49
|
+
} | {
|
|
50
|
+
kind: "play";
|
|
51
|
+
/** Seek here before (or while) playing; null = leave the element where it is. */
|
|
52
|
+
seekTo: number | null;
|
|
53
|
+
volume: number;
|
|
54
|
+
rate: number;
|
|
55
|
+
/** The element is paused and must be started. */
|
|
56
|
+
start: boolean;
|
|
57
|
+
};
|
|
58
|
+
/** Is the playhead inside the clip? Start inclusive, end exclusive. */
|
|
59
|
+
export declare const isClipActive: (spec: Pick<AudioClipSpec, "start" | "end">, t: number) => boolean;
|
|
60
|
+
/**
|
|
61
|
+
* File position that should be playing at timeline time `t`.
|
|
62
|
+
*
|
|
63
|
+
* Fix vs Clippy: it used `frameStartAt + (t - start)`, which is right only at speed 1. At speed
|
|
64
|
+
* s the file advances s seconds per timeline second, so a 2x clip drifted by a full second per
|
|
65
|
+
* second and the drift check seeked on every tick. Clamped inside the media so it never seeks
|
|
66
|
+
* past the end.
|
|
67
|
+
*/
|
|
68
|
+
export declare function clipFileTime(spec: Pick<AudioClipSpec, "start" | "frameStartAt" | "speed">, t: number, mediaDuration?: number): number;
|
|
69
|
+
/** Final 0..1 volume for the clip at timeline time `t` (base x fade x duck, or 0 when muted). */
|
|
70
|
+
export declare function clipVolumeAt(spec: AudioClipSpec, t: number): number;
|
|
71
|
+
/**
|
|
72
|
+
* Decide what to do with a clip's element on this tick.
|
|
73
|
+
*
|
|
74
|
+
* - not in range, not playing, playing backwards, or being scrubbed -> pause (silent)
|
|
75
|
+
* - in range and playing, element paused -> seek + start
|
|
76
|
+
* - in range and playing, element running -> follow the volume
|
|
77
|
+
* envelope, and re-seek only if it has drifted more than DRIFT_TOLERANCE_SEC
|
|
78
|
+
*
|
|
79
|
+
* Reverse audio is not supported, so the clip is paused while the playhead runs backwards
|
|
80
|
+
* (Clippy kept playing forward and drift-seeked every 200 ms, which sounds like a stutter).
|
|
81
|
+
*/
|
|
82
|
+
export declare function decideAudioSync(spec: AudioClipSpec, t: number, state: PlayerState, el: ElementSnapshot): SyncDecision;
|
|
83
|
+
/** Minimal slice of HTMLAudioElement we drive (also what tests fake). */
|
|
84
|
+
export interface PlayableAudio {
|
|
85
|
+
paused: boolean;
|
|
86
|
+
currentTime: number;
|
|
87
|
+
volume: number;
|
|
88
|
+
playbackRate: number;
|
|
89
|
+
duration: number;
|
|
90
|
+
play(): Promise<void> | void;
|
|
91
|
+
pause(): void;
|
|
92
|
+
}
|
|
93
|
+
export declare const snapshotOf: (el: PlayableAudio) => ElementSnapshot;
|
|
94
|
+
/**
|
|
95
|
+
* Apply a decision to the element. `onPlayBlocked` fires when the browser rejects `play()`
|
|
96
|
+
* (autoplay policy, no user gesture yet); the sync loop simply tries again on the next tick.
|
|
97
|
+
*/
|
|
98
|
+
export declare function applyAudioDecision(el: PlayableAudio, decision: SyncDecision, onPlayBlocked?: (error: unknown) => void): void;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared audio gain model.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for "how loud is this audio clip at time T".
|
|
5
|
+
* Both playback paths consume it so preview and export cannot diverge:
|
|
6
|
+
*
|
|
7
|
+
* - preview: AudioPlaybackManager (HTMLAudioElement, per GSAP tick)
|
|
8
|
+
* - export: any offline renderer can call `gainAt` per frame with the same spec
|
|
9
|
+
*
|
|
10
|
+
* Gain is composed multiplicatively:
|
|
11
|
+
*
|
|
12
|
+
* gain(t) = baseVolume x fade(t) x duck(t)
|
|
13
|
+
*
|
|
14
|
+
* `fade` is authored per-clip by the user (transformation.audioFade).
|
|
15
|
+
* `duck` is derived from other items on the timeline — a music bed ducks
|
|
16
|
+
* under voiceover. It mirrors the backend envelope built by
|
|
17
|
+
* agent-orchestrator-service/src/services/duck-envelope.ts so a project
|
|
18
|
+
* assembled by the agent sounds the same in the editor as it does on render.
|
|
19
|
+
*
|
|
20
|
+
* Pure and dependency-free. Ported verbatim from the Clippy editor (utils/audio/gain-envelope.ts).
|
|
21
|
+
*/
|
|
22
|
+
/** One side of a user-authored fade. `volume` is a 0–100 percentage. */
|
|
23
|
+
export interface AudioFadeSide {
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
/** seconds */
|
|
26
|
+
duration?: number;
|
|
27
|
+
/** 0–100 — the volume the fade starts from (in) or ends at (out) */
|
|
28
|
+
volume?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface AudioFade {
|
|
31
|
+
fadeIn?: AudioFadeSide;
|
|
32
|
+
fadeOut?: AudioFadeSide;
|
|
33
|
+
}
|
|
34
|
+
/** A window, in seconds relative to the ducked clip's start, during which to attenuate. */
|
|
35
|
+
export interface DuckWindow {
|
|
36
|
+
startSec: number;
|
|
37
|
+
endSec: number;
|
|
38
|
+
}
|
|
39
|
+
/** Backend envelope shape from duck-envelope.ts — times in ms, gain in dB. */
|
|
40
|
+
export interface DuckEnvelopePoint {
|
|
41
|
+
tMs: number;
|
|
42
|
+
gainDb: number;
|
|
43
|
+
}
|
|
44
|
+
export interface GainSpec {
|
|
45
|
+
/** 0–1 clip volume from the item */
|
|
46
|
+
baseVolume: number;
|
|
47
|
+
muted?: boolean;
|
|
48
|
+
/** clip length on the timeline, in seconds */
|
|
49
|
+
clipDuration: number;
|
|
50
|
+
fade?: AudioFade;
|
|
51
|
+
/** Linear multiplier applied inside duck windows. 0.15 ~= -16 dB. */
|
|
52
|
+
duckTo?: number;
|
|
53
|
+
/** Clip-relative windows to duck under. */
|
|
54
|
+
duckWindows?: DuckWindow[];
|
|
55
|
+
/** Ramp length in/out of a duck window, seconds. */
|
|
56
|
+
duckFade?: number;
|
|
57
|
+
}
|
|
58
|
+
/** Matches MUSIC_DUCK_FADE_MS (150ms) in duck-envelope.ts. */
|
|
59
|
+
export declare const DEFAULT_DUCK_FADE_SEC = 0.15;
|
|
60
|
+
/**
|
|
61
|
+
* Matches the `duckTo` emitted by clhq-ai-video-cascade/editor-project-builder.
|
|
62
|
+
* Note the backend's MUSIC_DUCK_DB default of -8 dB is a *different*, shallower
|
|
63
|
+
* duck (~0.4 linear); `duckTo` on the item always wins when present.
|
|
64
|
+
*/
|
|
65
|
+
export declare const DEFAULT_DUCK_TO = 0.15;
|
|
66
|
+
/**
|
|
67
|
+
* Default clip volume for a music bed.
|
|
68
|
+
*
|
|
69
|
+
* Agent-generated music has always arrived at 0.4–0.6 while user-inserted
|
|
70
|
+
* stock music defaulted to 1.0 — roughly 8 dB louder for the same job. Both
|
|
71
|
+
* paths now start from this value so a bed sounds the same however it got
|
|
72
|
+
* onto the timeline.
|
|
73
|
+
*/
|
|
74
|
+
export declare const MUSIC_BED_DEFAULT_VOLUME = 0.6;
|
|
75
|
+
/** trackRole values that count as speech for the purposes of `duckDuring: 'voiceover'`. */
|
|
76
|
+
export declare const VOICE_TRACK_ROLES: readonly ["voiceover", "narration", "dialogue"];
|
|
77
|
+
export declare function dbToLinear(db: number): number;
|
|
78
|
+
export declare function linearToDb(linear: number): number;
|
|
79
|
+
/**
|
|
80
|
+
* Volume at `clipTime` seconds into the clip, applying fade-in / fade-out
|
|
81
|
+
* around `baseVolume`. Returns `baseVolume` when no fade is configured.
|
|
82
|
+
*
|
|
83
|
+
* Fades are linear in amplitude (matching the previous preview behaviour).
|
|
84
|
+
*/
|
|
85
|
+
export declare function fadeGainAt(clipTime: number, clipDuration: number, baseVolume: number, fade?: AudioFade): number;
|
|
86
|
+
/**
|
|
87
|
+
* Duck multiplier (0–1) at `clipTime` seconds into the clip.
|
|
88
|
+
*
|
|
89
|
+
* Each window ramps 1 -> duckTo over `duckFade` seconds *before* the window
|
|
90
|
+
* opens, holds through it, then ramps back over `duckFade` seconds after it
|
|
91
|
+
* closes. Overlapping windows take the deepest (minimum) multiplier, so
|
|
92
|
+
* back-to-back narration lines do not let the bed swell in the gap.
|
|
93
|
+
*/
|
|
94
|
+
export declare function duckGainAt(clipTime: number, duckWindows?: DuckWindow[], duckTo?: number, duckFade?: number): number;
|
|
95
|
+
/**
|
|
96
|
+
* Final gain (0–1) for a clip at `clipTime` seconds past its start.
|
|
97
|
+
*
|
|
98
|
+
* `clipTime` is measured from the start of the clip *on the timeline*, not
|
|
99
|
+
* from the start of the source file — trimming (frameStartAt) shifts which
|
|
100
|
+
* audio plays, not where the fade sits.
|
|
101
|
+
*/
|
|
102
|
+
export declare function gainAt(clipTime: number, spec: GainSpec): number;
|
|
103
|
+
/** Minimal shape needed off a timeline item to decide whether it ducks others. */
|
|
104
|
+
export interface DuckSourceItem {
|
|
105
|
+
type?: string;
|
|
106
|
+
trackRole?: string;
|
|
107
|
+
start?: number;
|
|
108
|
+
end?: number;
|
|
109
|
+
mute?: boolean;
|
|
110
|
+
muted?: boolean;
|
|
111
|
+
volume?: number;
|
|
112
|
+
uniqueId?: string;
|
|
113
|
+
}
|
|
114
|
+
/** Minimal shape of the clip being ducked. */
|
|
115
|
+
export interface DuckTargetItem {
|
|
116
|
+
uniqueId?: string;
|
|
117
|
+
start?: number;
|
|
118
|
+
end?: number;
|
|
119
|
+
trackRole?: string;
|
|
120
|
+
duckTo?: number;
|
|
121
|
+
duckDuring?: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Convert the backend's dB envelope (duck-envelope.ts) into clip-relative
|
|
125
|
+
* windows. Any run of points at a negative gain becomes one window.
|
|
126
|
+
*
|
|
127
|
+
* Used when the agent has already computed `musicDuckEnvelope`; we prefer its
|
|
128
|
+
* voice timings over anything we re-derive from the timeline.
|
|
129
|
+
*/
|
|
130
|
+
export declare function duckWindowsFromEnvelope(points: DuckEnvelopePoint[], clipStartSec?: number): DuckWindow[];
|
|
131
|
+
/**
|
|
132
|
+
* Derive duck windows for `target` from the other items on the timeline.
|
|
133
|
+
*
|
|
134
|
+
* Only runs when the target opts in via `duckDuring`. Today the only value
|
|
135
|
+
* the backend emits is `'voiceover'`, which matches any audible item whose
|
|
136
|
+
* trackRole is in VOICE_TRACK_ROLES.
|
|
137
|
+
*
|
|
138
|
+
* Returned windows are clip-relative seconds and merged so overlapping or
|
|
139
|
+
* near-touching speech becomes one sustained duck rather than a pumping bed.
|
|
140
|
+
*/
|
|
141
|
+
export declare function deriveDuckWindows(target: DuckTargetItem, allItems: DuckSourceItem[], options?: {
|
|
142
|
+
mergeGapSec?: number;
|
|
143
|
+
}): DuckWindow[];
|
|
144
|
+
/**
|
|
145
|
+
* Build the full GainSpec for an audio item given the rest of the timeline.
|
|
146
|
+
* Convenience wrapper so both players construct the spec identically.
|
|
147
|
+
*/
|
|
148
|
+
export declare function buildGainSpec(item: {
|
|
149
|
+
start?: number;
|
|
150
|
+
end?: number;
|
|
151
|
+
volume?: number;
|
|
152
|
+
mute?: boolean;
|
|
153
|
+
muted?: boolean;
|
|
154
|
+
uniqueId?: string;
|
|
155
|
+
trackRole?: string;
|
|
156
|
+
duckTo?: number;
|
|
157
|
+
duckDuring?: string;
|
|
158
|
+
transformation?: {
|
|
159
|
+
audioFade?: AudioFade;
|
|
160
|
+
musicDuckEnvelope?: DuckEnvelopePoint[];
|
|
161
|
+
};
|
|
162
|
+
}, allItems: DuckSourceItem[]): GainSpec;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AudioRegistry } from './audioRegistry';
|
|
2
|
+
/**
|
|
3
|
+
* Re-render when the registry's set of elements changes; returns its version.
|
|
4
|
+
*
|
|
5
|
+
* `useSyncExternalStore` re-reads the version after subscribing, so a change made between render
|
|
6
|
+
* and subscription is not lost. A plain `useEffect(() => registry.subscribe(bump))` misses it
|
|
7
|
+
* whenever a CHILD's effect (the AudioPlaybackManager creating the element) runs before the
|
|
8
|
+
* parent's subscription effect.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useAudioRegistryVersion(registry: AudioRegistry): number;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const BLEND_NORMAL = 0;
|
|
2
|
+
export declare const BLEND_ADD = 1;
|
|
3
|
+
export declare const BLEND_MULTIPLY = 2;
|
|
4
|
+
export declare const BLEND_SCREEN = 3;
|
|
5
|
+
export type BlendStrategy = 'none' | 'native';
|
|
6
|
+
/**
|
|
7
|
+
* Decide how a numeric PIXI blend mode should be realised in the render path.
|
|
8
|
+
*
|
|
9
|
+
* - `'none'` → no filter, default normal blend (or unsupported code → safe degrade).
|
|
10
|
+
* - `'native'` → resolved to a PIXI v8 BLEND_MODES string and set directly.
|
|
11
|
+
*/
|
|
12
|
+
export declare const resolveBlendStrategy: (blendMode: number | undefined | null) => BlendStrategy;
|
|
13
|
+
/** True if the renderer can realise this blend code (was: @pixi/picture set). */
|
|
14
|
+
export declare const isPictureSupported: (blendMode: number) => boolean;
|
|
15
|
+
export type PixiBlendModeString = 'normal' | 'add' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color-dodge' | 'color-burn' | 'hard-light' | 'soft-light';
|
|
16
|
+
/**
|
|
17
|
+
* Translate the editor's numeric blend-mode code (dropdown/state value) into
|
|
18
|
+
* the PIXI v8 string value expected by `DisplayObject.blendMode` / `Filter.blendMode`.
|
|
19
|
+
* Unknown/unsupported codes safely degrade to 'normal'.
|
|
20
|
+
*/
|
|
21
|
+
export declare const resolveBlendModeString: (blendMode: number | undefined | null) => PixiBlendModeString;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CaptionSegment } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* True when cue times are CLIP-LOCAL (0 = the caption item's own start) rather than on the
|
|
4
|
+
* master timeline. This is Clippy's rule, kept identical so both engines agree:
|
|
5
|
+
*
|
|
6
|
+
* - no segments -> false
|
|
7
|
+
* - no usable item end -> clip-local iff the item starts after 0
|
|
8
|
+
* - otherwise -> every cue fits inside the item duration (+0.5 s)
|
|
9
|
+
*
|
|
10
|
+
* Agent-built captions (viral shorts, polish) store clip-local times; editor auto-captions on
|
|
11
|
+
* an untrimmed full-length video store source-absolute times and compare to the playhead.
|
|
12
|
+
*
|
|
13
|
+
* (`timeBase.isClipLocal` differs in the no-end case: it returns false. Use this one to render.)
|
|
14
|
+
*/
|
|
15
|
+
export declare function usesClipLocalTime(segments: ReadonlyArray<Pick<CaptionSegment, "end">>, itemStartSec?: number, itemEndSec?: number): boolean;
|
|
16
|
+
/** Master playhead -> the time the cues are expressed in. */
|
|
17
|
+
export declare const captionTimeAt: (playheadSec: number, clipLocal: boolean, itemStartSec?: number) => number;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { CaptionRenderMode } from './captionPose';
|
|
2
|
+
/** The words to draw, and the index of `words[0]` of that list within the full segment. */
|
|
3
|
+
export declare function displayedWordsFor<T>(mode: CaptionRenderMode, words: ReadonlyArray<T>, activeIndex: number): {
|
|
4
|
+
words: T[];
|
|
5
|
+
offset: number;
|
|
6
|
+
};
|
|
7
|
+
export interface ParsedColor {
|
|
8
|
+
/** 0xRRGGBB */
|
|
9
|
+
color: number;
|
|
10
|
+
/** 0..1 */
|
|
11
|
+
alpha: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse `#rgb`, `#rrggbb`, `#rrggbbaa`, `rgb()`, `rgba()` (comma or space separated, % alpha) and
|
|
15
|
+
* `transparent`. Returns null for anything else (named colours: let the caller fall back to Pixi).
|
|
16
|
+
*
|
|
17
|
+
* Fix vs Clippy: its boxed-caption background only understood `rgba(...)` strings; a hex or
|
|
18
|
+
* `rgb()` background was silently ignored and the default black box drawn instead.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseCaptionColor(input: string | null | undefined): ParsedColor | null;
|
|
21
|
+
export interface BoxedBackground {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
radius: number;
|
|
27
|
+
color: number;
|
|
28
|
+
alpha: number;
|
|
29
|
+
}
|
|
30
|
+
export declare const BOXED_PADDING = 12;
|
|
31
|
+
export declare const DEFAULT_BOXED_COLOR: ParsedColor;
|
|
32
|
+
/** Corner radius per boxed preset. */
|
|
33
|
+
export declare const boxedRadiusFor: (captionStyleId: string | null | undefined) => number;
|
|
34
|
+
/**
|
|
35
|
+
* The rounded box behind the whole caption in `boxed` mode, in coordinates relative to the
|
|
36
|
+
* caption centre. `captionBgColor` unset / `transparent` / unparseable falls back to the
|
|
37
|
+
* default 75% black (Clippy's behaviour).
|
|
38
|
+
*/
|
|
39
|
+
export declare function boxedBackgroundSpec(contentWidth: number, contentHeight: number, captionBgColor: string | null | undefined, captionStyleId: string | null | undefined): BoxedBackground;
|
|
40
|
+
export interface WordboxPill {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
radius: number;
|
|
46
|
+
}
|
|
47
|
+
export declare const WORDBOX_GROW_MS = 120;
|
|
48
|
+
/**
|
|
49
|
+
* The rounded pill behind the active word in `wordbox` mode: grows from the word's centre over
|
|
50
|
+
* 120 ms (ease-out-cubic). Relative to the caption centre.
|
|
51
|
+
*/
|
|
52
|
+
export declare function wordboxPillSpec(pos: {
|
|
53
|
+
x: number;
|
|
54
|
+
y: number;
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
}, totalWidth: number, totalHeight: number, elapsedMs: number, fontSize: number): WordboxPill;
|