odori 0.0.1 → 0.0.2
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/LICENSE +22 -0
- package/README.md +33 -1
- package/dist/brand-D71KbhAe.d.ts +253 -0
- package/dist/chunk-2SROZSZM.js +953 -0
- package/dist/chunk-2SROZSZM.js.map +1 -0
- package/dist/chunk-PXG45HYV.js +68 -0
- package/dist/chunk-PXG45HYV.js.map +1 -0
- package/dist/chunk-VQLDJUK4.js +62 -0
- package/dist/chunk-VQLDJUK4.js.map +1 -0
- package/dist/index.d.ts +464 -0
- package/dist/index.js +889 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest-CEuFXG0U.d.ts +398 -0
- package/dist/manifest.d.ts +4 -0
- package/dist/manifest.js +9 -0
- package/dist/manifest.js.map +1 -0
- package/dist/preview.d.ts +92 -0
- package/dist/preview.js +156 -0
- package/dist/preview.js.map +1 -0
- package/dist/schema-DXxRezrk.d.ts +54 -0
- package/package.json +53 -6
package/dist/index.js
ADDED
|
@@ -0,0 +1,889 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
Audio,
|
|
4
|
+
DUCK_GAIN,
|
|
5
|
+
DUCK_RAMP_FRAMES,
|
|
6
|
+
Easing,
|
|
7
|
+
Fill,
|
|
8
|
+
Freeze,
|
|
9
|
+
Loop,
|
|
10
|
+
OdoriRuntime,
|
|
11
|
+
SAMPLE_RATE,
|
|
12
|
+
SafeArea,
|
|
13
|
+
Scene,
|
|
14
|
+
Stagger,
|
|
15
|
+
Video,
|
|
16
|
+
brandCssVariables,
|
|
17
|
+
chord,
|
|
18
|
+
createAssetRegistry,
|
|
19
|
+
cueId,
|
|
20
|
+
cueSamples,
|
|
21
|
+
cueSignature,
|
|
22
|
+
cueUrl,
|
|
23
|
+
defaultBrand,
|
|
24
|
+
defaultLayout,
|
|
25
|
+
defineBrand,
|
|
26
|
+
defineCue,
|
|
27
|
+
defineVideoLayout,
|
|
28
|
+
duckEnvelope,
|
|
29
|
+
entryDurationInFrames,
|
|
30
|
+
envelopeAtFrame,
|
|
31
|
+
gain,
|
|
32
|
+
gainAtFrame,
|
|
33
|
+
highPass,
|
|
34
|
+
interpolate,
|
|
35
|
+
isAssetReference,
|
|
36
|
+
isCueDefinition,
|
|
37
|
+
loop,
|
|
38
|
+
lowPass,
|
|
39
|
+
mix,
|
|
40
|
+
noise,
|
|
41
|
+
normalize,
|
|
42
|
+
note,
|
|
43
|
+
pad,
|
|
44
|
+
resolveEntryLayout,
|
|
45
|
+
sampleGainCurve,
|
|
46
|
+
seamless,
|
|
47
|
+
seconds,
|
|
48
|
+
seeded,
|
|
49
|
+
sequence,
|
|
50
|
+
shape,
|
|
51
|
+
silence,
|
|
52
|
+
sine,
|
|
53
|
+
sortCues,
|
|
54
|
+
spring,
|
|
55
|
+
step,
|
|
56
|
+
sweep,
|
|
57
|
+
trackDuration,
|
|
58
|
+
trackGainAtFrame,
|
|
59
|
+
triangle,
|
|
60
|
+
useAssets,
|
|
61
|
+
useBrand,
|
|
62
|
+
useDesignScale,
|
|
63
|
+
useFrame,
|
|
64
|
+
useLayout,
|
|
65
|
+
useReadiness,
|
|
66
|
+
useScene,
|
|
67
|
+
useSceneTransition,
|
|
68
|
+
useVideo
|
|
69
|
+
} from "./chunk-2SROZSZM.js";
|
|
70
|
+
import {
|
|
71
|
+
createRenderManifest
|
|
72
|
+
} from "./chunk-VQLDJUK4.js";
|
|
73
|
+
import {
|
|
74
|
+
canonicalJson,
|
|
75
|
+
formatTimecode,
|
|
76
|
+
framesFromDuration,
|
|
77
|
+
hashString,
|
|
78
|
+
hashValue,
|
|
79
|
+
secondsFromFrames
|
|
80
|
+
} from "./chunk-PXG45HYV.js";
|
|
81
|
+
|
|
82
|
+
// src/loudness.ts
|
|
83
|
+
var SHELF = { frequency: 1681.974450955533, gainDb: 3.999843853973347, q: 0.7071752369554196 };
|
|
84
|
+
var HIGH_PASS = { frequency: 38.13547087602444, q: 0.5003270373238773 };
|
|
85
|
+
var shelfCoefficients = (sampleRate) => {
|
|
86
|
+
const amplitude = 10 ** (SHELF.gainDb / 40);
|
|
87
|
+
const omega = 2 * Math.PI * SHELF.frequency / sampleRate;
|
|
88
|
+
const alpha = Math.sin(omega) / (2 * SHELF.q);
|
|
89
|
+
const cos = Math.cos(omega);
|
|
90
|
+
const shared = 2 * Math.sqrt(amplitude) * alpha;
|
|
91
|
+
const a0 = amplitude + 1 - (amplitude - 1) * cos + shared;
|
|
92
|
+
return {
|
|
93
|
+
b0: amplitude * (amplitude + 1 + (amplitude - 1) * cos + shared) / a0,
|
|
94
|
+
b1: -2 * amplitude * (amplitude - 1 + (amplitude + 1) * cos) / a0,
|
|
95
|
+
b2: amplitude * (amplitude + 1 + (amplitude - 1) * cos - shared) / a0,
|
|
96
|
+
a1: 2 * (amplitude - 1 - (amplitude + 1) * cos) / a0,
|
|
97
|
+
a2: (amplitude + 1 - (amplitude - 1) * cos - shared) / a0
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
var highPassCoefficients = (sampleRate) => {
|
|
101
|
+
const omega = 2 * Math.PI * HIGH_PASS.frequency / sampleRate;
|
|
102
|
+
const alpha = Math.sin(omega) / (2 * HIGH_PASS.q);
|
|
103
|
+
const cos = Math.cos(omega);
|
|
104
|
+
const a0 = 1 + alpha;
|
|
105
|
+
return {
|
|
106
|
+
b0: (1 + cos) / 2 / a0,
|
|
107
|
+
b1: -(1 + cos) / a0,
|
|
108
|
+
b2: (1 + cos) / 2 / a0,
|
|
109
|
+
a1: -2 * cos / a0,
|
|
110
|
+
a2: (1 - alpha) / a0
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
var filter = (samples, { b0, b1, b2, a1, a2 }) => {
|
|
114
|
+
const output = new Float32Array(samples.length);
|
|
115
|
+
let x1 = 0;
|
|
116
|
+
let x2 = 0;
|
|
117
|
+
let y1 = 0;
|
|
118
|
+
let y2 = 0;
|
|
119
|
+
for (let index = 0; index < samples.length; index += 1) {
|
|
120
|
+
const x0 = samples[index];
|
|
121
|
+
const y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
|
|
122
|
+
output[index] = y0;
|
|
123
|
+
x2 = x1;
|
|
124
|
+
x1 = x0;
|
|
125
|
+
y2 = y1;
|
|
126
|
+
y1 = y0;
|
|
127
|
+
}
|
|
128
|
+
return output;
|
|
129
|
+
};
|
|
130
|
+
var BLOCK_SECONDS = 0.4;
|
|
131
|
+
var STEP = 0.25;
|
|
132
|
+
var ABSOLUTE_GATE = -70;
|
|
133
|
+
var RELATIVE_GATE = -10;
|
|
134
|
+
var OFFSET = -0.691;
|
|
135
|
+
var loudnessOf = (meanSquares) => OFFSET + 10 * Math.log10(meanSquares.reduce((total, value) => total + value, 0) || Number.MIN_VALUE);
|
|
136
|
+
var integratedLufs = (channels, sampleRate) => {
|
|
137
|
+
if (channels.length === 0 || channels[0].length === 0) return null;
|
|
138
|
+
const weighted = channels.map((channel) => filter(filter(channel, shelfCoefficients(sampleRate)), highPassCoefficients(sampleRate)));
|
|
139
|
+
const blockSize = Math.round(BLOCK_SECONDS * sampleRate);
|
|
140
|
+
const hop = Math.round(BLOCK_SECONDS * STEP * sampleRate);
|
|
141
|
+
if (weighted[0].length < blockSize) return null;
|
|
142
|
+
const blocks = [];
|
|
143
|
+
for (let start = 0; start + blockSize <= weighted[0].length; start += hop) {
|
|
144
|
+
blocks.push(
|
|
145
|
+
weighted.map((channel) => {
|
|
146
|
+
let sum = 0;
|
|
147
|
+
for (let index = start; index < start + blockSize; index += 1) sum += channel[index] * channel[index];
|
|
148
|
+
return sum / blockSize;
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
if (blocks.length === 0) return null;
|
|
153
|
+
const above = blocks.filter((block) => loudnessOf(block) > ABSOLUTE_GATE);
|
|
154
|
+
if (above.length === 0) return null;
|
|
155
|
+
const mean = above[0].map((_, channel) => above.reduce((total, block) => total + block[channel], 0) / above.length);
|
|
156
|
+
const threshold = loudnessOf(mean) + RELATIVE_GATE;
|
|
157
|
+
const gated = above.filter((block) => loudnessOf(block) > threshold);
|
|
158
|
+
if (gated.length === 0) return null;
|
|
159
|
+
const integrated = gated[0].map((_, channel) => gated.reduce((total, block) => total + block[channel], 0) / gated.length);
|
|
160
|
+
return loudnessOf(integrated);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/player.tsx
|
|
164
|
+
import { useCallback as useCallback2, useMemo, useState as useState2 } from "react";
|
|
165
|
+
|
|
166
|
+
// src/playback.ts
|
|
167
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
168
|
+
var advanceFrames = (fractional, deltaMs, fps, rate = 1) => fractional + deltaMs / 1e3 * fps * rate;
|
|
169
|
+
var usePlayback = ({
|
|
170
|
+
fps,
|
|
171
|
+
durationInFrames,
|
|
172
|
+
initialFrame = 0,
|
|
173
|
+
autoPlay = false,
|
|
174
|
+
loop: loop2 = true,
|
|
175
|
+
rate = 1,
|
|
176
|
+
onFrame
|
|
177
|
+
}) => {
|
|
178
|
+
const [frame, setFrame] = useState(initialFrame);
|
|
179
|
+
const [playing, setPlaying] = useState(autoPlay);
|
|
180
|
+
const animation = useRef(null);
|
|
181
|
+
const previousTime = useRef(null);
|
|
182
|
+
const fractional = useRef(initialFrame);
|
|
183
|
+
const frameRef = useRef(initialFrame);
|
|
184
|
+
const commit = useCallback(
|
|
185
|
+
(next) => {
|
|
186
|
+
const clamped = Math.max(0, Math.min(Math.round(next), Math.max(0, durationInFrames - 1)));
|
|
187
|
+
frameRef.current = clamped;
|
|
188
|
+
setFrame(clamped);
|
|
189
|
+
onFrame?.(clamped);
|
|
190
|
+
},
|
|
191
|
+
[durationInFrames, onFrame]
|
|
192
|
+
);
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
if (!playing) {
|
|
195
|
+
previousTime.current = null;
|
|
196
|
+
fractional.current = frameRef.current;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const tick = (now) => {
|
|
200
|
+
const previous = previousTime.current ?? now;
|
|
201
|
+
previousTime.current = now;
|
|
202
|
+
fractional.current = advanceFrames(fractional.current, now - previous, fps, rate);
|
|
203
|
+
if (fractional.current >= durationInFrames) {
|
|
204
|
+
if (!loop2) {
|
|
205
|
+
commit(durationInFrames - 1);
|
|
206
|
+
setPlaying(false);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
fractional.current %= durationInFrames;
|
|
210
|
+
}
|
|
211
|
+
commit(Math.floor(fractional.current));
|
|
212
|
+
animation.current = requestAnimationFrame(tick);
|
|
213
|
+
};
|
|
214
|
+
animation.current = requestAnimationFrame(tick);
|
|
215
|
+
return () => {
|
|
216
|
+
if (animation.current !== null) cancelAnimationFrame(animation.current);
|
|
217
|
+
};
|
|
218
|
+
}, [commit, durationInFrames, fps, loop2, playing, rate]);
|
|
219
|
+
const seek = useCallback(
|
|
220
|
+
(next) => {
|
|
221
|
+
fractional.current = next;
|
|
222
|
+
commit(next);
|
|
223
|
+
},
|
|
224
|
+
[commit]
|
|
225
|
+
);
|
|
226
|
+
return {
|
|
227
|
+
frame,
|
|
228
|
+
playing,
|
|
229
|
+
rate,
|
|
230
|
+
play: () => setPlaying(true),
|
|
231
|
+
pause: () => setPlaying(false),
|
|
232
|
+
toggle: () => setPlaying((value) => !value),
|
|
233
|
+
seek,
|
|
234
|
+
step: (delta) => {
|
|
235
|
+
setPlaying(false);
|
|
236
|
+
seek(frameRef.current + delta);
|
|
237
|
+
},
|
|
238
|
+
restart: () => {
|
|
239
|
+
seek(0);
|
|
240
|
+
setPlaying(true);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// src/audio-playback.ts
|
|
246
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
247
|
+
var useAudioPlayback = ({
|
|
248
|
+
track,
|
|
249
|
+
frame,
|
|
250
|
+
fps,
|
|
251
|
+
playing,
|
|
252
|
+
muted = false,
|
|
253
|
+
masterGain = 1,
|
|
254
|
+
soloCue = null,
|
|
255
|
+
scrubbing = false,
|
|
256
|
+
rate = 1,
|
|
257
|
+
onBlocked,
|
|
258
|
+
onFailed
|
|
259
|
+
}) => {
|
|
260
|
+
const elements = useRef2(/* @__PURE__ */ new Map());
|
|
261
|
+
const sync = useRef2(() => {
|
|
262
|
+
});
|
|
263
|
+
const failed = useRef2(/* @__PURE__ */ new Set());
|
|
264
|
+
useEffect2(() => {
|
|
265
|
+
const table = elements.current;
|
|
266
|
+
const live = new Set((track?.cues ?? []).map((cue) => cue.id));
|
|
267
|
+
for (const [id, element] of table) {
|
|
268
|
+
if (live.has(id)) continue;
|
|
269
|
+
element.pause();
|
|
270
|
+
table.delete(id);
|
|
271
|
+
}
|
|
272
|
+
return () => {
|
|
273
|
+
for (const element of table.values()) element.pause();
|
|
274
|
+
};
|
|
275
|
+
}, [track]);
|
|
276
|
+
useEffect2(() => {
|
|
277
|
+
if (typeof window === "undefined") return;
|
|
278
|
+
const table = elements.current;
|
|
279
|
+
const onTimeUpdate = () => run();
|
|
280
|
+
const run = () => {
|
|
281
|
+
const audible = playing || scrubbing;
|
|
282
|
+
for (const cue of track?.cues ?? []) {
|
|
283
|
+
let element = table.get(cue.id);
|
|
284
|
+
if (!element) {
|
|
285
|
+
element = new window.Audio(cue.src);
|
|
286
|
+
element.preload = "auto";
|
|
287
|
+
element.loop = cue.loop;
|
|
288
|
+
element.addEventListener("timeupdate", onTimeUpdate);
|
|
289
|
+
element.addEventListener("error", () => {
|
|
290
|
+
failed.current.add(cue.src);
|
|
291
|
+
onFailed?.([...failed.current]);
|
|
292
|
+
});
|
|
293
|
+
table.set(cue.id, element);
|
|
294
|
+
}
|
|
295
|
+
const local = frame - cue.fromFrame;
|
|
296
|
+
const inside = local >= 0 && local < cue.durationInFrames;
|
|
297
|
+
const span = element.duration;
|
|
298
|
+
const elapsed = local / fps;
|
|
299
|
+
const position = cue.loop && Number.isFinite(span) && span > 0 ? elapsed % span : elapsed;
|
|
300
|
+
if (element.loop !== cue.loop) element.loop = cue.loop;
|
|
301
|
+
element.volume = Math.max(0, Math.min(1, trackGainAtFrame(cue, track?.cues ?? [], frame) * masterGain));
|
|
302
|
+
element.muted = muted || soloCue !== null && soloCue !== cue.id;
|
|
303
|
+
if (!inside || !audible) {
|
|
304
|
+
if (!element.paused) element.pause();
|
|
305
|
+
if (inside && !audible) {
|
|
306
|
+
const target2 = cue.trimStartSeconds + position;
|
|
307
|
+
if (Math.abs(element.currentTime - target2) > 1 / fps) element.currentTime = target2;
|
|
308
|
+
}
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const target = cue.trimStartSeconds + position;
|
|
312
|
+
if (element.playbackRate !== rate) element.playbackRate = rate;
|
|
313
|
+
if (Math.abs(element.currentTime - target) > 2 / fps * Math.max(1, rate)) element.currentTime = target;
|
|
314
|
+
if (element.paused) {
|
|
315
|
+
void element.play().then(
|
|
316
|
+
() => onBlocked?.(false),
|
|
317
|
+
(error) => onBlocked?.(error?.name === "NotAllowedError")
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
sync.current = run;
|
|
323
|
+
run();
|
|
324
|
+
return () => {
|
|
325
|
+
for (const element of table.values()) element.removeEventListener("timeupdate", onTimeUpdate);
|
|
326
|
+
};
|
|
327
|
+
}, [fps, frame, masterGain, muted, onBlocked, onFailed, playing, rate, scrubbing, soloCue, track]);
|
|
328
|
+
useEffect2(() => {
|
|
329
|
+
if (typeof document === "undefined") return;
|
|
330
|
+
const onVisibility = () => {
|
|
331
|
+
if (document.hidden) {
|
|
332
|
+
for (const element of elements.current.values()) element.pause();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
sync.current();
|
|
336
|
+
};
|
|
337
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
338
|
+
return () => document.removeEventListener("visibilitychange", onVisibility);
|
|
339
|
+
}, []);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// src/player.tsx
|
|
343
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
344
|
+
var Player = ({
|
|
345
|
+
entry,
|
|
346
|
+
input,
|
|
347
|
+
prepared,
|
|
348
|
+
assets,
|
|
349
|
+
layout,
|
|
350
|
+
initialFrame = 0,
|
|
351
|
+
autoPlay = false,
|
|
352
|
+
loop: loop2 = true,
|
|
353
|
+
controls = true,
|
|
354
|
+
muted = false,
|
|
355
|
+
style,
|
|
356
|
+
onFrame,
|
|
357
|
+
onTimeline,
|
|
358
|
+
onAudio
|
|
359
|
+
}) => {
|
|
360
|
+
const resolvedLayout = resolveEntryLayout(entry, layout);
|
|
361
|
+
const { fps, width, height } = resolvedLayout.format;
|
|
362
|
+
const [timeline, setTimeline] = useState2(null);
|
|
363
|
+
const [track, setTrack] = useState2(null);
|
|
364
|
+
const declared = entryDurationInFrames(entry, resolvedLayout);
|
|
365
|
+
const durationInFrames = Math.max(1, declared || timeline?.durationInFrames || fps);
|
|
366
|
+
const playback = usePlayback({ fps, durationInFrames, initialFrame, autoPlay, loop: loop2, onFrame });
|
|
367
|
+
const { frame } = playback;
|
|
368
|
+
useAudioPlayback({ track, frame: playback.frame, fps, playing: playback.playing, muted });
|
|
369
|
+
const handleAudio = useCallback2(
|
|
370
|
+
(next) => {
|
|
371
|
+
setTrack(next);
|
|
372
|
+
onAudio?.(next);
|
|
373
|
+
},
|
|
374
|
+
[onAudio]
|
|
375
|
+
);
|
|
376
|
+
const handleTimeline = useCallback2(
|
|
377
|
+
(next) => {
|
|
378
|
+
setTimeline((current) => JSON.stringify(current) === JSON.stringify(next) ? current : next);
|
|
379
|
+
onTimeline?.(next);
|
|
380
|
+
},
|
|
381
|
+
[onTimeline]
|
|
382
|
+
);
|
|
383
|
+
const aspectRatio = useMemo(() => `${width} / ${height}`, [height, width]);
|
|
384
|
+
const activeScene = timeline?.scenes.find(
|
|
385
|
+
(scene) => frame >= scene.start && frame < scene.start + scene.durationInFrames
|
|
386
|
+
);
|
|
387
|
+
return /* @__PURE__ */ jsxs("div", { className: "odori-player", style: { display: "grid", gap: 12, width: "100%", ...style }, children: [
|
|
388
|
+
/* @__PURE__ */ jsx(
|
|
389
|
+
"div",
|
|
390
|
+
{
|
|
391
|
+
"data-odori-player": true,
|
|
392
|
+
style: {
|
|
393
|
+
aspectRatio,
|
|
394
|
+
background: resolvedLayout.brand.colors.background,
|
|
395
|
+
borderRadius: 10,
|
|
396
|
+
overflow: "hidden",
|
|
397
|
+
position: "relative",
|
|
398
|
+
width: "100%"
|
|
399
|
+
},
|
|
400
|
+
children: /* @__PURE__ */ jsx(
|
|
401
|
+
OdoriRuntime,
|
|
402
|
+
{
|
|
403
|
+
entry,
|
|
404
|
+
frame,
|
|
405
|
+
input,
|
|
406
|
+
prepared,
|
|
407
|
+
assets,
|
|
408
|
+
layout,
|
|
409
|
+
onTimeline: handleTimeline,
|
|
410
|
+
onAudio: handleAudio
|
|
411
|
+
}
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
),
|
|
415
|
+
controls ? /* @__PURE__ */ jsxs("div", { className: "odori-player-controls", style: { alignItems: "center", display: "flex", gap: 10 }, children: [
|
|
416
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: playback.toggle, children: playback.playing ? "Pause" : "Play" }),
|
|
417
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => playback.step(-1), "aria-label": "Previous frame", children: "\u2039" }),
|
|
418
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => playback.step(1), "aria-label": "Next frame", children: "\u203A" }),
|
|
419
|
+
/* @__PURE__ */ jsx(
|
|
420
|
+
"input",
|
|
421
|
+
{
|
|
422
|
+
"aria-label": "Timeline",
|
|
423
|
+
type: "range",
|
|
424
|
+
min: 0,
|
|
425
|
+
max: durationInFrames - 1,
|
|
426
|
+
value: frame,
|
|
427
|
+
onChange: (event) => {
|
|
428
|
+
playback.pause();
|
|
429
|
+
playback.seek(Number(event.currentTarget.value));
|
|
430
|
+
},
|
|
431
|
+
style: { flex: 1 }
|
|
432
|
+
}
|
|
433
|
+
),
|
|
434
|
+
/* @__PURE__ */ jsxs("output", { style: { fontVariantNumeric: "tabular-nums", minWidth: 132, textAlign: "right" }, children: [
|
|
435
|
+
formatTimecode(frame, fps),
|
|
436
|
+
" ",
|
|
437
|
+
"\xB7",
|
|
438
|
+
" ",
|
|
439
|
+
frame,
|
|
440
|
+
"/",
|
|
441
|
+
durationInFrames - 1,
|
|
442
|
+
activeScene ? ` \xB7 ${activeScene.name ?? activeScene.id}` : ""
|
|
443
|
+
] })
|
|
444
|
+
] }) : null
|
|
445
|
+
] });
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// src/render-surface.tsx
|
|
449
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
450
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
451
|
+
var RenderSurface = ({
|
|
452
|
+
entry,
|
|
453
|
+
initialFrame = 0,
|
|
454
|
+
input,
|
|
455
|
+
prepared,
|
|
456
|
+
assets,
|
|
457
|
+
layout
|
|
458
|
+
}) => {
|
|
459
|
+
const [frame, setFrame] = useState3(initialFrame);
|
|
460
|
+
useEffect3(() => {
|
|
461
|
+
window.__ODORI_SET_FRAME__ = setFrame;
|
|
462
|
+
window.__ODORI_READY__ = true;
|
|
463
|
+
return () => {
|
|
464
|
+
delete window.__ODORI_SET_FRAME__;
|
|
465
|
+
delete window.__ODORI_READY__;
|
|
466
|
+
};
|
|
467
|
+
}, []);
|
|
468
|
+
return /* @__PURE__ */ jsx2(
|
|
469
|
+
OdoriRuntime,
|
|
470
|
+
{
|
|
471
|
+
entry,
|
|
472
|
+
frame,
|
|
473
|
+
input,
|
|
474
|
+
prepared,
|
|
475
|
+
assets,
|
|
476
|
+
layout,
|
|
477
|
+
onTimeline: (timeline) => {
|
|
478
|
+
window.__ODORI_TIMELINE__ = timeline;
|
|
479
|
+
},
|
|
480
|
+
onAudio: (track) => {
|
|
481
|
+
window.__ODORI_AUDIO__ = track;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
);
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
// src/wav.ts
|
|
488
|
+
var encodeWav = (signal) => {
|
|
489
|
+
const channels = signal.channels.length || 1;
|
|
490
|
+
const frames = signal.channels[0]?.length ?? 0;
|
|
491
|
+
const bytesPerSample = 2;
|
|
492
|
+
const dataBytes = frames * channels * bytesPerSample;
|
|
493
|
+
const buffer = new ArrayBuffer(44 + dataBytes);
|
|
494
|
+
const view = new DataView(buffer);
|
|
495
|
+
const ascii = (offset2, text) => {
|
|
496
|
+
for (let index = 0; index < text.length; index += 1) view.setUint8(offset2 + index, text.charCodeAt(index));
|
|
497
|
+
};
|
|
498
|
+
ascii(0, "RIFF");
|
|
499
|
+
view.setUint32(4, 36 + dataBytes, true);
|
|
500
|
+
ascii(8, "WAVE");
|
|
501
|
+
ascii(12, "fmt ");
|
|
502
|
+
view.setUint32(16, 16, true);
|
|
503
|
+
view.setUint16(20, 1, true);
|
|
504
|
+
view.setUint16(22, channels, true);
|
|
505
|
+
view.setUint32(24, signal.sampleRate, true);
|
|
506
|
+
view.setUint32(28, signal.sampleRate * channels * bytesPerSample, true);
|
|
507
|
+
view.setUint16(32, channels * bytesPerSample, true);
|
|
508
|
+
view.setUint16(34, 8 * bytesPerSample, true);
|
|
509
|
+
ascii(36, "data");
|
|
510
|
+
view.setUint32(40, dataBytes, true);
|
|
511
|
+
let offset = 44;
|
|
512
|
+
for (let frame = 0; frame < frames; frame += 1) {
|
|
513
|
+
for (let channel = 0; channel < channels; channel += 1) {
|
|
514
|
+
const sample = signal.channels[channel]?.[frame] ?? 0;
|
|
515
|
+
const clamped = Math.max(-1, Math.min(1, sample));
|
|
516
|
+
view.setInt16(offset, Math.round(clamped * 32767), true);
|
|
517
|
+
offset += bytesPerSample;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return new Uint8Array(buffer);
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// src/cursor.ts
|
|
524
|
+
var PRESS_FRAMES = 9;
|
|
525
|
+
var cursorAt = (stops, frame) => {
|
|
526
|
+
if (stops.length === 0) return { x: 0, y: 0, visible: 0, pressed: 0, clicking: false };
|
|
527
|
+
const timed = [];
|
|
528
|
+
let shift = 0;
|
|
529
|
+
for (const stop of stops) {
|
|
530
|
+
const start = stop.frame + shift;
|
|
531
|
+
timed.push({ ...stop, frame: start });
|
|
532
|
+
if (stop.hold) {
|
|
533
|
+
timed.push({ ...stop, frame: start + stop.hold, click: false });
|
|
534
|
+
shift += stop.hold;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const first = timed[0];
|
|
538
|
+
const last = timed[timed.length - 1];
|
|
539
|
+
if (frame <= first.frame) return { x: first.x, y: first.y, visible: 0, pressed: 0, clicking: false };
|
|
540
|
+
const frames = timed.map((stop) => stop.frame);
|
|
541
|
+
const x = interpolate(frame, frames, timed.map((stop) => stop.x), { easing: Easing.standard });
|
|
542
|
+
const y = interpolate(frame, frames, timed.map((stop) => stop.y), { easing: Easing.standard });
|
|
543
|
+
let pressed = 0;
|
|
544
|
+
for (const stop of timed) {
|
|
545
|
+
if (!stop.click || stop.frame > frame) continue;
|
|
546
|
+
const age = frame - stop.frame;
|
|
547
|
+
if (age <= PRESS_FRAMES) pressed = Math.max(pressed, 1 - age / PRESS_FRAMES);
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
x,
|
|
551
|
+
y,
|
|
552
|
+
// Fade in as it arrives and out after the last stop, so a pointer never
|
|
553
|
+
// pops onto a frame it was not part of.
|
|
554
|
+
visible: interpolate(
|
|
555
|
+
frame,
|
|
556
|
+
[first.frame, first.frame + 6, last.frame + 12, last.frame + 20],
|
|
557
|
+
[0, 1, 1, 0],
|
|
558
|
+
{ easing: Easing.standard }
|
|
559
|
+
),
|
|
560
|
+
pressed,
|
|
561
|
+
clicking: pressed > 0
|
|
562
|
+
};
|
|
563
|
+
};
|
|
564
|
+
var cursorDuration = (stops) => {
|
|
565
|
+
const hold = stops.reduce((total, stop) => total + (stop.hold ?? 0), 0);
|
|
566
|
+
return (stops[stops.length - 1]?.frame ?? 0) + hold + 20;
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// src/random.ts
|
|
570
|
+
var hashSeed = (value) => {
|
|
571
|
+
let hash = 2166136261;
|
|
572
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
573
|
+
hash ^= value.charCodeAt(index);
|
|
574
|
+
hash = Math.imul(hash, 16777619);
|
|
575
|
+
}
|
|
576
|
+
return hash >>> 0;
|
|
577
|
+
};
|
|
578
|
+
var toSeed = (seed) => typeof seed === "number" ? Math.floor(seed) >>> 0 : hashSeed(seed);
|
|
579
|
+
var random = (seed) => {
|
|
580
|
+
const key = Array.isArray(seed) ? seed.join(":") : seed;
|
|
581
|
+
let state = toSeed(key) + 1831565813 >>> 0;
|
|
582
|
+
let t = Math.imul(state ^ state >>> 15, 1 | state);
|
|
583
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
584
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
585
|
+
};
|
|
586
|
+
var randomBetween = (seed, min, max) => min + random(seed) * (max - min);
|
|
587
|
+
var randomPick = (seed, items) => items.length === 0 ? void 0 : items[Math.floor(random(seed) * items.length)];
|
|
588
|
+
var randomOrder = (seed, items) => {
|
|
589
|
+
const key = Array.isArray(seed) ? seed.join(":") : String(seed);
|
|
590
|
+
const out = [...items];
|
|
591
|
+
for (let index = out.length - 1; index > 0; index -= 1) {
|
|
592
|
+
const swap = Math.floor(random(`${key}:${index}`) * (index + 1));
|
|
593
|
+
[out[index], out[swap]] = [out[swap], out[index]];
|
|
594
|
+
}
|
|
595
|
+
return out;
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
// src/canvas.ts
|
|
599
|
+
import { useEffect as useEffect4, useLayoutEffect, useRef as useRef3 } from "react";
|
|
600
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect4 : useLayoutEffect;
|
|
601
|
+
var useCanvas = (draw, dependencies = []) => {
|
|
602
|
+
const canvas = useRef3(null);
|
|
603
|
+
const frame = useFrame();
|
|
604
|
+
const { width, height } = useVideo();
|
|
605
|
+
const readiness = useReadiness();
|
|
606
|
+
const latest = useRef3(draw);
|
|
607
|
+
latest.current = draw;
|
|
608
|
+
useIsomorphicLayoutEffect(() => {
|
|
609
|
+
const element = canvas.current;
|
|
610
|
+
if (!element) return;
|
|
611
|
+
const release = readiness.hold();
|
|
612
|
+
try {
|
|
613
|
+
const context = element.getContext("2d", { alpha: true });
|
|
614
|
+
if (!context) return;
|
|
615
|
+
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
616
|
+
context.clearRect(0, 0, element.width, element.height);
|
|
617
|
+
latest.current(context, { frame, width: element.width, height: element.height });
|
|
618
|
+
} finally {
|
|
619
|
+
release();
|
|
620
|
+
}
|
|
621
|
+
}, [frame, width, height, ...dependencies]);
|
|
622
|
+
return canvas;
|
|
623
|
+
};
|
|
624
|
+
var drawHtml = async (context, html, options) => {
|
|
625
|
+
const { width, height, style = "" } = options;
|
|
626
|
+
if (typeof document !== "undefined" && document.fonts?.ready) await document.fonts.ready;
|
|
627
|
+
const svg = [
|
|
628
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">`,
|
|
629
|
+
`<foreignObject width="100%" height="100%">`,
|
|
630
|
+
`<div xmlns="http://www.w3.org/1999/xhtml" style="width:${width}px;height:${height}px;${style}">`,
|
|
631
|
+
html,
|
|
632
|
+
`</div></foreignObject></svg>`
|
|
633
|
+
].join("");
|
|
634
|
+
const encoded = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
635
|
+
await new Promise((done, fail) => {
|
|
636
|
+
const image = new Image();
|
|
637
|
+
image.onload = () => {
|
|
638
|
+
context.drawImage(image, 0, 0, width, height);
|
|
639
|
+
done();
|
|
640
|
+
};
|
|
641
|
+
image.onerror = () => fail(
|
|
642
|
+
new Error(
|
|
643
|
+
"The HTML could not be rasterized. Every element inside must carry inline styles, and images must be data URLs: an SVG foreignObject cannot reach outside itself."
|
|
644
|
+
)
|
|
645
|
+
);
|
|
646
|
+
image.src = encoded;
|
|
647
|
+
});
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
// src/placeholder.ts
|
|
651
|
+
var placeholderSvg = ({
|
|
652
|
+
width = 1600,
|
|
653
|
+
height = 900,
|
|
654
|
+
label,
|
|
655
|
+
from = "#1a1a1a",
|
|
656
|
+
to = "#0a0a0a",
|
|
657
|
+
seed = "placeholder"
|
|
658
|
+
} = {}) => {
|
|
659
|
+
const shapes = Array.from({ length: 14 }, (_, index) => {
|
|
660
|
+
const x = random([seed, "x", index]) * width;
|
|
661
|
+
const y = random([seed, "y", index]) * height;
|
|
662
|
+
const radius = (random([seed, "r", index]) * 0.16 + 0.03) * Math.min(width, height);
|
|
663
|
+
const opacity = (random([seed, "o", index]) * 0.06 + 0.02).toFixed(3);
|
|
664
|
+
return `<circle cx="${x.toFixed(1)}" cy="${y.toFixed(1)}" r="${radius.toFixed(1)}" fill="#ffffff" opacity="${opacity}"/>`;
|
|
665
|
+
}).join("");
|
|
666
|
+
const caption = label ? `<text x="50%" y="50%" fill="#ffffff" fill-opacity="0.42" font-family="ui-monospace, monospace" font-size="${Math.round(
|
|
667
|
+
Math.min(width, height) * 0.06
|
|
668
|
+
)}" text-anchor="middle" dominant-baseline="middle">${label.replace(/[<>&]/g, "")}</text>` : "";
|
|
669
|
+
return [
|
|
670
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">`,
|
|
671
|
+
`<defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">`,
|
|
672
|
+
`<stop offset="0" stop-color="${from}"/><stop offset="1" stop-color="${to}"/>`,
|
|
673
|
+
`</linearGradient></defs>`,
|
|
674
|
+
`<rect width="${width}" height="${height}" fill="url(#g)"/>`,
|
|
675
|
+
shapes,
|
|
676
|
+
`<rect x="1" y="1" width="${width - 2}" height="${height - 2}" fill="none" stroke="#ffffff" stroke-opacity="0.08"/>`,
|
|
677
|
+
caption,
|
|
678
|
+
`</svg>`
|
|
679
|
+
].join("");
|
|
680
|
+
};
|
|
681
|
+
var placeholderImage = (options = {}) => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(placeholderSvg(options))}`;
|
|
682
|
+
var placeholderFrame = (frame, options = {}) => {
|
|
683
|
+
const { width = 1600, height = 900, fps = 30, ...rest } = options;
|
|
684
|
+
const seconds2 = frame / fps;
|
|
685
|
+
const timecode = `${String(Math.floor(seconds2 / 60)).padStart(2, "0")}:${String(Math.floor(seconds2 % 60)).padStart(2, "0")}:${String(
|
|
686
|
+
frame % fps
|
|
687
|
+
).padStart(2, "0")}`;
|
|
688
|
+
const base = placeholderSvg({ ...rest, width, height, label: void 0 });
|
|
689
|
+
const progress = frame % (fps * 4) / (fps * 4);
|
|
690
|
+
const marker = [
|
|
691
|
+
`<rect x="0" y="${height - 12}" width="${(width * progress).toFixed(1)}" height="12" fill="#ffffff" fill-opacity="0.5"/>`,
|
|
692
|
+
`<text x="${width / 2}" y="${height / 2}" fill="#ffffff" fill-opacity="0.5" font-family="ui-monospace, monospace" font-size="${Math.round(
|
|
693
|
+
Math.min(width, height) * 0.08
|
|
694
|
+
)}" text-anchor="middle" dominant-baseline="middle">${timecode}</text>`
|
|
695
|
+
].join("");
|
|
696
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(base.replace("</svg>", `${marker}</svg>`))}`;
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
// src/metadata.ts
|
|
700
|
+
var SEGMENT = /^[a-z0-9][a-z0-9-]*$/i;
|
|
701
|
+
var isValidVideoId = (id) => id.length > 0 && id.split("/").every((segment) => SEGMENT.test(segment));
|
|
702
|
+
var resolveVideoId = (id, pathId) => id || pathId;
|
|
703
|
+
var defineVideoMetadata = (metadata) => {
|
|
704
|
+
if (metadata.id !== void 0 && !isValidVideoId(metadata.id)) {
|
|
705
|
+
throw new Error(`Video id must be alphanumeric path segments with dashes: ${metadata.id}`);
|
|
706
|
+
}
|
|
707
|
+
return { kind: "odori-video-metadata", ...metadata, id: metadata.id ?? "" };
|
|
708
|
+
};
|
|
709
|
+
var definePrepare = (run, options = {}) => ({
|
|
710
|
+
kind: "odori-prepare",
|
|
711
|
+
version: options.version ?? "1",
|
|
712
|
+
run
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
// src/schema.ts
|
|
716
|
+
var validateField = (name, field, value, issues) => {
|
|
717
|
+
if (value === void 0) return field.defaultValue;
|
|
718
|
+
switch (field.type) {
|
|
719
|
+
case "text":
|
|
720
|
+
case "color": {
|
|
721
|
+
if (typeof value !== "string") {
|
|
722
|
+
issues.push(`${name} must be a string`);
|
|
723
|
+
return field.defaultValue;
|
|
724
|
+
}
|
|
725
|
+
if (field.type === "text" && field.maxLength !== void 0 && value.length > field.maxLength) {
|
|
726
|
+
issues.push(`${name} exceeds ${field.maxLength} characters`);
|
|
727
|
+
}
|
|
728
|
+
return value;
|
|
729
|
+
}
|
|
730
|
+
case "number": {
|
|
731
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
732
|
+
issues.push(`${name} must be a finite number`);
|
|
733
|
+
return field.defaultValue;
|
|
734
|
+
}
|
|
735
|
+
if (field.min !== void 0 && value < field.min) issues.push(`${name} is below ${field.min}`);
|
|
736
|
+
if (field.max !== void 0 && value > field.max) issues.push(`${name} is above ${field.max}`);
|
|
737
|
+
return value;
|
|
738
|
+
}
|
|
739
|
+
case "boolean": {
|
|
740
|
+
if (typeof value !== "boolean") {
|
|
741
|
+
issues.push(`${name} must be a boolean`);
|
|
742
|
+
return field.defaultValue;
|
|
743
|
+
}
|
|
744
|
+
return value;
|
|
745
|
+
}
|
|
746
|
+
case "select": {
|
|
747
|
+
if (typeof value !== "string" || !field.options.includes(value)) {
|
|
748
|
+
issues.push(`${name} must be one of ${field.options.join(", ")}`);
|
|
749
|
+
return field.defaultValue;
|
|
750
|
+
}
|
|
751
|
+
return value;
|
|
752
|
+
}
|
|
753
|
+
default:
|
|
754
|
+
return value;
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
var defineInputSchema = (fields) => {
|
|
758
|
+
const defaults = () => Object.fromEntries(Object.entries(fields).map(([name, field]) => [name, field.defaultValue]));
|
|
759
|
+
const safeParse = (input) => {
|
|
760
|
+
if (input !== void 0 && input !== null && typeof input !== "object") {
|
|
761
|
+
return { success: false, issues: ["input must be an object"] };
|
|
762
|
+
}
|
|
763
|
+
const source = input ?? {};
|
|
764
|
+
const issues = [];
|
|
765
|
+
const data = {};
|
|
766
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
767
|
+
data[name] = validateField(name, field, source[name], issues);
|
|
768
|
+
}
|
|
769
|
+
for (const key of Object.keys(source)) {
|
|
770
|
+
if (!(key in fields)) data[key] = source[key];
|
|
771
|
+
}
|
|
772
|
+
return issues.length > 0 ? { success: false, issues } : { success: true, data };
|
|
773
|
+
};
|
|
774
|
+
return {
|
|
775
|
+
kind: "odori-schema",
|
|
776
|
+
fields,
|
|
777
|
+
defaults,
|
|
778
|
+
describe: () => fields,
|
|
779
|
+
safeParse,
|
|
780
|
+
parse(input) {
|
|
781
|
+
const result = safeParse(input);
|
|
782
|
+
if (!result.success) throw new Error(`Invalid video input: ${result.issues.join("; ")}`);
|
|
783
|
+
return result.data;
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
};
|
|
787
|
+
var isOdoriSchema = (schema) => typeof schema === "object" && schema !== null && schema.kind === "odori-schema";
|
|
788
|
+
var parseWithSchema = (schema, input) => schema ? schema.parse(input) : input ?? {};
|
|
789
|
+
export {
|
|
790
|
+
Audio,
|
|
791
|
+
DUCK_GAIN,
|
|
792
|
+
DUCK_RAMP_FRAMES,
|
|
793
|
+
Easing,
|
|
794
|
+
Fill,
|
|
795
|
+
Freeze,
|
|
796
|
+
Loop,
|
|
797
|
+
OdoriRuntime,
|
|
798
|
+
Player,
|
|
799
|
+
RenderSurface,
|
|
800
|
+
SAMPLE_RATE,
|
|
801
|
+
SafeArea,
|
|
802
|
+
Scene,
|
|
803
|
+
Stagger,
|
|
804
|
+
Video,
|
|
805
|
+
advanceFrames,
|
|
806
|
+
brandCssVariables,
|
|
807
|
+
canonicalJson,
|
|
808
|
+
chord,
|
|
809
|
+
createAssetRegistry,
|
|
810
|
+
createRenderManifest,
|
|
811
|
+
cueId,
|
|
812
|
+
cueSamples,
|
|
813
|
+
cueSignature,
|
|
814
|
+
cueUrl,
|
|
815
|
+
cursorAt,
|
|
816
|
+
cursorDuration,
|
|
817
|
+
defaultBrand,
|
|
818
|
+
defaultLayout,
|
|
819
|
+
defineBrand,
|
|
820
|
+
defineCue,
|
|
821
|
+
defineInputSchema,
|
|
822
|
+
definePrepare,
|
|
823
|
+
defineVideoLayout,
|
|
824
|
+
defineVideoMetadata,
|
|
825
|
+
drawHtml,
|
|
826
|
+
duckEnvelope,
|
|
827
|
+
encodeWav,
|
|
828
|
+
entryDurationInFrames,
|
|
829
|
+
envelopeAtFrame,
|
|
830
|
+
formatTimecode,
|
|
831
|
+
framesFromDuration,
|
|
832
|
+
gain,
|
|
833
|
+
gainAtFrame,
|
|
834
|
+
hashString,
|
|
835
|
+
hashValue,
|
|
836
|
+
highPass,
|
|
837
|
+
integratedLufs,
|
|
838
|
+
interpolate,
|
|
839
|
+
isAssetReference,
|
|
840
|
+
isCueDefinition,
|
|
841
|
+
isOdoriSchema,
|
|
842
|
+
isValidVideoId,
|
|
843
|
+
loop,
|
|
844
|
+
lowPass,
|
|
845
|
+
mix,
|
|
846
|
+
noise,
|
|
847
|
+
normalize,
|
|
848
|
+
note,
|
|
849
|
+
pad,
|
|
850
|
+
parseWithSchema,
|
|
851
|
+
placeholderFrame,
|
|
852
|
+
placeholderImage,
|
|
853
|
+
placeholderSvg,
|
|
854
|
+
random,
|
|
855
|
+
randomBetween,
|
|
856
|
+
randomOrder,
|
|
857
|
+
randomPick,
|
|
858
|
+
resolveEntryLayout,
|
|
859
|
+
resolveVideoId,
|
|
860
|
+
sampleGainCurve,
|
|
861
|
+
seamless,
|
|
862
|
+
seconds,
|
|
863
|
+
secondsFromFrames,
|
|
864
|
+
seeded,
|
|
865
|
+
sequence,
|
|
866
|
+
shape,
|
|
867
|
+
silence,
|
|
868
|
+
sine,
|
|
869
|
+
sortCues,
|
|
870
|
+
spring,
|
|
871
|
+
step,
|
|
872
|
+
sweep,
|
|
873
|
+
trackDuration,
|
|
874
|
+
trackGainAtFrame,
|
|
875
|
+
triangle,
|
|
876
|
+
useAssets,
|
|
877
|
+
useAudioPlayback,
|
|
878
|
+
useBrand,
|
|
879
|
+
useCanvas,
|
|
880
|
+
useDesignScale,
|
|
881
|
+
useFrame,
|
|
882
|
+
useLayout,
|
|
883
|
+
usePlayback,
|
|
884
|
+
useReadiness,
|
|
885
|
+
useScene,
|
|
886
|
+
useSceneTransition,
|
|
887
|
+
useVideo
|
|
888
|
+
};
|
|
889
|
+
//# sourceMappingURL=index.js.map
|