@remotion/timeline-utils 4.0.526 → 4.0.528
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/dist/audio-waveform/get-visible-waveform-volume.d.ts +1 -3
- package/dist/audio-waveform/get-visible-waveform-volume.js +6 -24
- package/dist/audio-waveform/slice-visible-waveform-peaks.js +1 -2
- package/dist/audio-waveform/slice-waveform-peaks.js +12 -2
- package/dist/esm/index.mjs +51 -66
- package/package.json +3 -3
- package/dist/image-thumbnail/draw-repeating-image-thumbnail.d.ts +0 -10
- package/dist/image-thumbnail/draw-repeating-image-thumbnail.js +0 -48
- package/dist/image-thumbnail/get-scaled-image-thumbnail-dimensions.d.ts +0 -8
- package/dist/image-thumbnail/get-scaled-image-thumbnail-dimensions.js +0 -17
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { type TimelineLoopDisplay } from '../loop-display';
|
|
2
1
|
import type { WaveformVolume } from './draw-peaks';
|
|
3
|
-
export declare const getVisibleWaveformVolume: ({ displayDurationInFrames, displayOffsetInFrames,
|
|
2
|
+
export declare const getVisibleWaveformVolume: ({ displayDurationInFrames, displayOffsetInFrames, volume, }: {
|
|
4
3
|
readonly displayDurationInFrames: number;
|
|
5
4
|
readonly displayOffsetInFrames: number;
|
|
6
|
-
readonly loopDisplay: TimelineLoopDisplay | undefined;
|
|
7
5
|
readonly volume: WaveformVolume;
|
|
8
6
|
}) => WaveformVolume;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getVisibleWaveformVolume = void 0;
|
|
4
|
-
const
|
|
5
|
-
const getVisibleWaveformVolume = ({ displayDurationInFrames, displayOffsetInFrames, loopDisplay, volume, }) => {
|
|
4
|
+
const getVisibleWaveformVolume = ({ displayDurationInFrames, displayOffsetInFrames, volume, }) => {
|
|
6
5
|
if (!Array.isArray(volume)) {
|
|
7
6
|
return volume;
|
|
8
7
|
}
|
|
@@ -10,27 +9,10 @@ const getVisibleWaveformVolume = ({ displayDurationInFrames, displayOffsetInFram
|
|
|
10
9
|
displayDurationInFrames <= 0) {
|
|
11
10
|
return [];
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
const segments = (0, loop_display_1.getLoopDisplaySegments)({
|
|
20
|
-
displayDurationInFrames,
|
|
21
|
-
displayOffsetInFrames,
|
|
22
|
-
loopDurationInFrames: loopDisplay.durationInFrames,
|
|
23
|
-
});
|
|
24
|
-
const result = [];
|
|
25
|
-
for (const segment of segments) {
|
|
26
|
-
const start = Math.max(0, Math.floor(segment.loopOffsetInFrames));
|
|
27
|
-
const end = Math.min(volume.length, Math.ceil(segment.loopOffsetInFrames + segment.durationInFrames));
|
|
28
|
-
// Do not `push(...slice)`: Chrome throws RangeError: Invalid array length
|
|
29
|
-
// once a loop segment is longer than ~65k frames.
|
|
30
|
-
for (let index = start; index < end; index++) {
|
|
31
|
-
result.push(volume[index]);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return result;
|
|
12
|
+
// Registered curves already cover the visible composition timeline, including
|
|
13
|
+
// repeat/extend behavior and partial loops introduced by ancestor trimming.
|
|
14
|
+
const start = Math.max(0, Math.floor(displayOffsetInFrames));
|
|
15
|
+
const end = Math.min(volume.length, Math.ceil(displayOffsetInFrames + displayDurationInFrames));
|
|
16
|
+
return volume.slice(start, end);
|
|
35
17
|
};
|
|
36
18
|
exports.getVisibleWaveformVolume = getVisibleWaveformVolume;
|
|
@@ -4,8 +4,7 @@ exports.sliceVisibleWaveformPeaks = void 0;
|
|
|
4
4
|
const loop_display_1 = require("../loop-display");
|
|
5
5
|
const slice_waveform_peaks_1 = require("./slice-waveform-peaks");
|
|
6
6
|
const sliceVisibleWaveformPeaks = ({ displayDurationInFrames, displayOffsetInFrames, durationInFrames, fps, loopDisplay, peaks, playbackRate, startFrom, waveformSampleRate, }) => {
|
|
7
|
-
if (!
|
|
8
|
-
loopDisplay.durationInFrames <= 0) {
|
|
7
|
+
if (!loopDisplay || loopDisplay.durationInFrames <= 0) {
|
|
9
8
|
return (0, slice_waveform_peaks_1.sliceWaveformPeaks)({
|
|
10
9
|
durationInFrames: Math.min(displayDurationInFrames, Math.max(0, durationInFrames - displayOffsetInFrames)),
|
|
11
10
|
fps,
|
|
@@ -8,8 +8,18 @@ const sliceWaveformPeaks = ({ durationInFrames, fps, peaks, playbackRate, startF
|
|
|
8
8
|
}
|
|
9
9
|
const startTimeInSeconds = startFrom / fps;
|
|
10
10
|
const durationInSeconds = (durationInFrames / fps) * playbackRate;
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const start = startTimeInSeconds * waveformSampleRate;
|
|
12
|
+
const end = (startTimeInSeconds + durationInSeconds) * waveformSampleRate;
|
|
13
|
+
// Fractional loop periods can round an exact trim boundary a few ULPs past
|
|
14
|
+
// the next sample. Do not include a peak from outside the trimmed source.
|
|
15
|
+
const startPeakIndex = Math.floor(start +
|
|
16
|
+
(Number.isFinite(start)
|
|
17
|
+
? Number.EPSILON * Math.max(1, Math.abs(start)) * 4
|
|
18
|
+
: 0));
|
|
19
|
+
const endPeakIndex = Math.ceil(end -
|
|
20
|
+
(Number.isFinite(end)
|
|
21
|
+
? Number.EPSILON * Math.max(1, Math.abs(end)) * 4
|
|
22
|
+
: 0));
|
|
13
23
|
if (!Number.isFinite(startPeakIndex) || !Number.isFinite(endPeakIndex)) {
|
|
14
24
|
return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
|
|
15
25
|
}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -99,54 +99,10 @@ var drawBars = ({
|
|
|
99
99
|
}
|
|
100
100
|
ctx.putImageData(imageData, 0, 0);
|
|
101
101
|
};
|
|
102
|
-
// src/loop-display.ts
|
|
103
|
-
var shouldTileLoopDisplay = (loopDisplay) => {
|
|
104
|
-
return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
|
|
105
|
-
};
|
|
106
|
-
var getLoopDisplaySegments = ({
|
|
107
|
-
displayDurationInFrames,
|
|
108
|
-
displayOffsetInFrames,
|
|
109
|
-
loopDurationInFrames
|
|
110
|
-
}) => {
|
|
111
|
-
if (!Number.isFinite(displayDurationInFrames) || displayDurationInFrames <= 0 || !Number.isFinite(displayOffsetInFrames) || !Number.isFinite(loopDurationInFrames) || loopDurationInFrames <= 0) {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
const displayEndInFrames = displayOffsetInFrames + displayDurationInFrames;
|
|
115
|
-
const firstLoopIndex = Math.floor(displayOffsetInFrames / loopDurationInFrames);
|
|
116
|
-
const lastLoopIndex = Math.floor(displayEndInFrames / loopDurationInFrames);
|
|
117
|
-
const segments = [];
|
|
118
|
-
for (let loopIndex = firstLoopIndex;loopIndex <= lastLoopIndex; loopIndex++) {
|
|
119
|
-
const loopStartInFrames = loopIndex * loopDurationInFrames;
|
|
120
|
-
const segmentStart = Math.max(displayOffsetInFrames, loopStartInFrames);
|
|
121
|
-
const segmentEnd = Math.min(displayEndInFrames, loopStartInFrames + loopDurationInFrames);
|
|
122
|
-
const durationInFrames = segmentEnd - segmentStart;
|
|
123
|
-
if (durationInFrames <= 0) {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
segments.push({
|
|
127
|
-
loopIndex,
|
|
128
|
-
absoluteOffsetInFrames: segmentStart,
|
|
129
|
-
loopOffsetInFrames: segmentStart - loopStartInFrames,
|
|
130
|
-
durationInFrames
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
return segments;
|
|
134
|
-
};
|
|
135
|
-
var getLoopDisplayWidth = ({
|
|
136
|
-
visualizationWidth,
|
|
137
|
-
loopDisplay
|
|
138
|
-
}) => {
|
|
139
|
-
if (!shouldTileLoopDisplay(loopDisplay)) {
|
|
140
|
-
return visualizationWidth;
|
|
141
|
-
}
|
|
142
|
-
return visualizationWidth / loopDisplay.numberOfTimes;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
102
|
// src/audio-waveform/get-visible-waveform-volume.ts
|
|
146
103
|
var getVisibleWaveformVolume = ({
|
|
147
104
|
displayDurationInFrames,
|
|
148
105
|
displayOffsetInFrames,
|
|
149
|
-
loopDisplay,
|
|
150
106
|
volume
|
|
151
107
|
}) => {
|
|
152
108
|
if (!Array.isArray(volume)) {
|
|
@@ -155,25 +111,9 @@ var getVisibleWaveformVolume = ({
|
|
|
155
111
|
if (!Number.isFinite(displayDurationInFrames) || displayDurationInFrames <= 0) {
|
|
156
112
|
return [];
|
|
157
113
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return volume.slice(start, end);
|
|
162
|
-
}
|
|
163
|
-
const segments = getLoopDisplaySegments({
|
|
164
|
-
displayDurationInFrames,
|
|
165
|
-
displayOffsetInFrames,
|
|
166
|
-
loopDurationInFrames: loopDisplay.durationInFrames
|
|
167
|
-
});
|
|
168
|
-
const result = [];
|
|
169
|
-
for (const segment of segments) {
|
|
170
|
-
const start = Math.max(0, Math.floor(segment.loopOffsetInFrames));
|
|
171
|
-
const end = Math.min(volume.length, Math.ceil(segment.loopOffsetInFrames + segment.durationInFrames));
|
|
172
|
-
for (let index = start;index < end; index++) {
|
|
173
|
-
result.push(volume[index]);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return result;
|
|
114
|
+
const start = Math.max(0, Math.floor(displayOffsetInFrames));
|
|
115
|
+
const end = Math.min(volume.length, Math.ceil(displayOffsetInFrames + displayDurationInFrames));
|
|
116
|
+
return volume.slice(start, end);
|
|
177
117
|
};
|
|
178
118
|
// src/audio-waveform/load-waveform-peaks.ts
|
|
179
119
|
import { ALL_FORMATS, AudioSampleSink, Input, UrlSource } from "mediabunny";
|
|
@@ -389,6 +329,49 @@ async function loadWaveformPeaks(src, signal, options) {
|
|
|
389
329
|
input?.dispose();
|
|
390
330
|
}
|
|
391
331
|
}
|
|
332
|
+
// src/loop-display.ts
|
|
333
|
+
var shouldTileLoopDisplay = (loopDisplay) => {
|
|
334
|
+
return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
|
|
335
|
+
};
|
|
336
|
+
var getLoopDisplaySegments = ({
|
|
337
|
+
displayDurationInFrames,
|
|
338
|
+
displayOffsetInFrames,
|
|
339
|
+
loopDurationInFrames
|
|
340
|
+
}) => {
|
|
341
|
+
if (!Number.isFinite(displayDurationInFrames) || displayDurationInFrames <= 0 || !Number.isFinite(displayOffsetInFrames) || !Number.isFinite(loopDurationInFrames) || loopDurationInFrames <= 0) {
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
const displayEndInFrames = displayOffsetInFrames + displayDurationInFrames;
|
|
345
|
+
const firstLoopIndex = Math.floor(displayOffsetInFrames / loopDurationInFrames);
|
|
346
|
+
const lastLoopIndex = Math.floor(displayEndInFrames / loopDurationInFrames);
|
|
347
|
+
const segments = [];
|
|
348
|
+
for (let loopIndex = firstLoopIndex;loopIndex <= lastLoopIndex; loopIndex++) {
|
|
349
|
+
const loopStartInFrames = loopIndex * loopDurationInFrames;
|
|
350
|
+
const segmentStart = Math.max(displayOffsetInFrames, loopStartInFrames);
|
|
351
|
+
const segmentEnd = Math.min(displayEndInFrames, loopStartInFrames + loopDurationInFrames);
|
|
352
|
+
const durationInFrames = segmentEnd - segmentStart;
|
|
353
|
+
if (durationInFrames <= 0) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
segments.push({
|
|
357
|
+
loopIndex,
|
|
358
|
+
absoluteOffsetInFrames: segmentStart,
|
|
359
|
+
loopOffsetInFrames: segmentStart - loopStartInFrames,
|
|
360
|
+
durationInFrames
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return segments;
|
|
364
|
+
};
|
|
365
|
+
var getLoopDisplayWidth = ({
|
|
366
|
+
visualizationWidth,
|
|
367
|
+
loopDisplay
|
|
368
|
+
}) => {
|
|
369
|
+
if (!shouldTileLoopDisplay(loopDisplay)) {
|
|
370
|
+
return visualizationWidth;
|
|
371
|
+
}
|
|
372
|
+
return visualizationWidth / loopDisplay.numberOfTimes;
|
|
373
|
+
};
|
|
374
|
+
|
|
392
375
|
// src/audio-waveform/slice-waveform-peaks.ts
|
|
393
376
|
var sliceWaveformPeaks = ({
|
|
394
377
|
durationInFrames,
|
|
@@ -403,8 +386,10 @@ var sliceWaveformPeaks = ({
|
|
|
403
386
|
}
|
|
404
387
|
const startTimeInSeconds = startFrom / fps;
|
|
405
388
|
const durationInSeconds = durationInFrames / fps * playbackRate;
|
|
406
|
-
const
|
|
407
|
-
const
|
|
389
|
+
const start = startTimeInSeconds * waveformSampleRate;
|
|
390
|
+
const end = (startTimeInSeconds + durationInSeconds) * waveformSampleRate;
|
|
391
|
+
const startPeakIndex = Math.floor(start + (Number.isFinite(start) ? Number.EPSILON * Math.max(1, Math.abs(start)) * 4 : 0));
|
|
392
|
+
const endPeakIndex = Math.ceil(end - (Number.isFinite(end) ? Number.EPSILON * Math.max(1, Math.abs(end)) * 4 : 0));
|
|
408
393
|
if (!Number.isFinite(startPeakIndex) || !Number.isFinite(endPeakIndex)) {
|
|
409
394
|
return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
|
|
410
395
|
}
|
|
@@ -432,7 +417,7 @@ var sliceVisibleWaveformPeaks = ({
|
|
|
432
417
|
startFrom,
|
|
433
418
|
waveformSampleRate
|
|
434
419
|
}) => {
|
|
435
|
-
if (!
|
|
420
|
+
if (!loopDisplay || loopDisplay.durationInFrames <= 0) {
|
|
436
421
|
return sliceWaveformPeaks({
|
|
437
422
|
durationInFrames: Math.min(displayDurationInFrames, Math.max(0, durationInFrames - displayOffsetInFrames)),
|
|
438
423
|
fps,
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/timeline-utils"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/timeline-utils",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.528",
|
|
7
7
|
"description": "Internal utilities for rendering Remotion timelines",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"scripts": {
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"mediabunny": "1.56.1",
|
|
25
|
-
"remotion": "4.0.
|
|
25
|
+
"remotion": "4.0.528"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@mediabunny/server": "1.56.1",
|
|
29
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
29
|
+
"@remotion/eslint-config-internal": "4.0.528",
|
|
30
30
|
"eslint": "9.19.0",
|
|
31
31
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|
|
32
32
|
},
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
type ImageWithNaturalDimensions = CanvasImageSource & {
|
|
2
|
-
readonly naturalWidth: number;
|
|
3
|
-
readonly naturalHeight: number;
|
|
4
|
-
};
|
|
5
|
-
export declare const drawRepeatingImageThumbnail: ({ canvas, image, offsetInPixels, }: {
|
|
6
|
-
readonly canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
7
|
-
readonly image: ImageWithNaturalDimensions;
|
|
8
|
-
readonly offsetInPixels?: number | undefined;
|
|
9
|
-
}) => void;
|
|
10
|
-
export {};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.drawRepeatingImageThumbnail = void 0;
|
|
4
|
-
const get_scaled_image_thumbnail_dimensions_1 = require("./get-scaled-image-thumbnail-dimensions");
|
|
5
|
-
const createOffscreenCanvas = (width, height) => {
|
|
6
|
-
if (typeof document !== 'undefined') {
|
|
7
|
-
const canvas = document.createElement('canvas');
|
|
8
|
-
canvas.width = width;
|
|
9
|
-
canvas.height = height;
|
|
10
|
-
return canvas;
|
|
11
|
-
}
|
|
12
|
-
if (typeof OffscreenCanvas !== 'undefined') {
|
|
13
|
-
return new OffscreenCanvas(width, height);
|
|
14
|
-
}
|
|
15
|
-
throw new Error('No canvas implementation available');
|
|
16
|
-
};
|
|
17
|
-
const drawRepeatingImageThumbnail = ({ canvas, image, offsetInPixels = 0, }) => {
|
|
18
|
-
const ctx = canvas.getContext('2d');
|
|
19
|
-
if (!ctx) {
|
|
20
|
-
throw new Error('Failed to get canvas context');
|
|
21
|
-
}
|
|
22
|
-
const { width, height } = canvas;
|
|
23
|
-
if (width === 0 || height === 0) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
const { scaledWidth, scaledHeight } = (0, get_scaled_image_thumbnail_dimensions_1.getScaledImageThumbnailDimensions)({
|
|
27
|
-
naturalWidth: image.naturalWidth,
|
|
28
|
-
naturalHeight: image.naturalHeight,
|
|
29
|
-
canvasHeight: height,
|
|
30
|
-
});
|
|
31
|
-
if (scaledWidth === 0 || scaledHeight === 0) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
const offscreen = createOffscreenCanvas(scaledWidth, scaledHeight);
|
|
35
|
-
const offCtx = offscreen.getContext('2d');
|
|
36
|
-
if (!offCtx) {
|
|
37
|
-
throw new Error('Failed to get offscreen canvas context');
|
|
38
|
-
}
|
|
39
|
-
offCtx.drawImage(image, 0, 0, scaledWidth, scaledHeight);
|
|
40
|
-
const pattern = ctx.createPattern(offscreen, 'repeat-x');
|
|
41
|
-
if (!pattern) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
pattern.setTransform(new DOMMatrix().translate(-offsetInPixels, 0));
|
|
45
|
-
ctx.fillStyle = pattern;
|
|
46
|
-
ctx.fillRect(0, 0, width, height);
|
|
47
|
-
};
|
|
48
|
-
exports.drawRepeatingImageThumbnail = drawRepeatingImageThumbnail;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export declare const getScaledImageThumbnailDimensions: ({ naturalWidth, naturalHeight, canvasHeight, }: {
|
|
2
|
-
readonly naturalWidth: number;
|
|
3
|
-
readonly naturalHeight: number;
|
|
4
|
-
readonly canvasHeight: number;
|
|
5
|
-
}) => {
|
|
6
|
-
scaledWidth: number;
|
|
7
|
-
scaledHeight: number;
|
|
8
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getScaledImageThumbnailDimensions = void 0;
|
|
4
|
-
const getScaledImageThumbnailDimensions = ({ naturalWidth, naturalHeight, canvasHeight, }) => {
|
|
5
|
-
if (naturalWidth === 0 || naturalHeight === 0 || canvasHeight === 0) {
|
|
6
|
-
return {
|
|
7
|
-
scaledWidth: 0,
|
|
8
|
-
scaledHeight: 0,
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
const scale = canvasHeight / naturalHeight;
|
|
12
|
-
return {
|
|
13
|
-
scaledWidth: naturalWidth * scale,
|
|
14
|
-
scaledHeight: canvasHeight,
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
exports.getScaledImageThumbnailDimensions = getScaledImageThumbnailDimensions;
|