@remotion/timeline-utils 4.0.519 → 4.0.520
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/audio-waveform-worker-types.d.ts +1 -0
- package/dist/audio-waveform/load-waveform-peaks.d.ts +1 -0
- package/dist/audio-waveform/load-waveform-peaks.js +14 -8
- package/dist/audio-waveform/slice-visible-waveform-peaks.d.ts +2 -1
- package/dist/audio-waveform/slice-visible-waveform-peaks.js +3 -1
- package/dist/audio-waveform/slice-waveform-peaks.d.ts +2 -1
- package/dist/audio-waveform/slice-waveform-peaks.js +3 -3
- package/dist/audio-waveform/subscribe-to-waveform-peaks.d.ts +2 -1
- package/dist/audio-waveform/subscribe-to-waveform-peaks.js +15 -8
- package/dist/audio-waveform-worker.js +1 -0
- package/dist/esm/audio-waveform-worker.mjs +12 -6
- package/dist/esm/index.mjs +36 -20
- package/package.json +5 -5
|
@@ -2,6 +2,7 @@ export type AudioWaveformWorkerLoadMessage = {
|
|
|
2
2
|
readonly type: 'load';
|
|
3
3
|
readonly requestId: number;
|
|
4
4
|
readonly src: string;
|
|
5
|
+
readonly waveformSampleRate: number;
|
|
5
6
|
};
|
|
6
7
|
export type AudioWaveformWorkerIncomingMessage = AudioWaveformWorkerLoadMessage;
|
|
7
8
|
export type AudioWaveformWorkerPeaksMessage = {
|
|
@@ -9,5 +9,6 @@ type Progress = {
|
|
|
9
9
|
type LoadWaveformPeaksOptions = {
|
|
10
10
|
readonly onProgress?: (progress: Progress) => void;
|
|
11
11
|
readonly progressIntervalInMs?: number;
|
|
12
|
+
readonly waveformSampleRate?: number;
|
|
12
13
|
};
|
|
13
14
|
export declare function loadWaveformPeaks(url: string, signal: AbortSignal, options?: LoadWaveformPeaksOptions): Promise<Float32Array>;
|
|
@@ -10,8 +10,13 @@ const waveform_peak_processor_1 = require("./waveform-peak-processor");
|
|
|
10
10
|
const DEFAULT_PROGRESS_INTERVAL_IN_MS = 50;
|
|
11
11
|
const peaksCache = new Map();
|
|
12
12
|
async function loadWaveformPeaks(url, signal, options) {
|
|
13
|
-
var _a, _b;
|
|
14
|
-
const
|
|
13
|
+
var _a, _b, _c;
|
|
14
|
+
const waveformSampleRate = (_a = options === null || options === void 0 ? void 0 : options.waveformSampleRate) !== null && _a !== void 0 ? _a : constants_1.TARGET_SAMPLE_RATE;
|
|
15
|
+
if (!Number.isFinite(waveformSampleRate) || waveformSampleRate <= 0) {
|
|
16
|
+
throw new Error('The waveform sample rate must be a positive number.');
|
|
17
|
+
}
|
|
18
|
+
const cacheKey = `${waveformSampleRate}:${url}`;
|
|
19
|
+
const cached = peaksCache.get(cacheKey);
|
|
15
20
|
if (cached) {
|
|
16
21
|
(0, waveform_peak_processor_1.emitWaveformProgress)({
|
|
17
22
|
peaks: cached,
|
|
@@ -39,16 +44,17 @@ async function loadWaveformPeaks(url, signal, options) {
|
|
|
39
44
|
throw new Error('Streams with UNIX timestamps are not currently supported by Remotion. Sorry! Source: ' +
|
|
40
45
|
url);
|
|
41
46
|
}
|
|
42
|
-
const
|
|
43
|
-
const durationInSeconds = (
|
|
44
|
-
const totalPeaks = Math.ceil(durationInSeconds *
|
|
45
|
-
const samplesPerPeak = Math.max(1, Math.floor(
|
|
47
|
+
const audioSampleRate = await audioTrack.getSampleRate();
|
|
48
|
+
const durationInSeconds = (_b = (await audioTrack.getDurationFromMetadata({ skipLiveWait: true }))) !== null && _b !== void 0 ? _b : (await audioTrack.computeDuration({ skipLiveWait: true }));
|
|
49
|
+
const totalPeaks = Math.ceil(durationInSeconds * waveformSampleRate);
|
|
50
|
+
const samplesPerPeak = Math.max(1, Math.floor(audioSampleRate / waveformSampleRate));
|
|
46
51
|
const sink = new mediabunny_1.AudioSampleSink(audioTrack);
|
|
47
52
|
const processor = (0, waveform_peak_processor_1.createWaveformPeakProcessor)({
|
|
48
53
|
totalPeaks,
|
|
49
54
|
samplesPerPeak,
|
|
50
55
|
onProgress: options === null || options === void 0 ? void 0 : options.onProgress,
|
|
51
|
-
progressIntervalInMs: (
|
|
56
|
+
progressIntervalInMs: (_c = options === null || options === void 0 ? void 0 : options.progressIntervalInMs) !== null && _c !== void 0 ? _c : DEFAULT_PROGRESS_INTERVAL_IN_MS *
|
|
57
|
+
Math.max(1, waveformSampleRate / constants_1.TARGET_SAMPLE_RATE),
|
|
52
58
|
now: () => Date.now(),
|
|
53
59
|
});
|
|
54
60
|
for await (const sample of sink.samples()) {
|
|
@@ -85,7 +91,7 @@ async function loadWaveformPeaks(url, signal, options) {
|
|
|
85
91
|
}
|
|
86
92
|
processor.finalize();
|
|
87
93
|
const { peaks } = processor;
|
|
88
|
-
peaksCache.set(
|
|
94
|
+
peaksCache.set(cacheKey, peaks);
|
|
89
95
|
return peaks;
|
|
90
96
|
}
|
|
91
97
|
finally {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TimelineLoopDisplay } from '../loop-display';
|
|
2
|
-
export declare const sliceVisibleWaveformPeaks: ({ displayDurationInFrames, displayOffsetInFrames, durationInFrames, fps, loopDisplay, peaks, playbackRate, startFrom, }: {
|
|
2
|
+
export declare const sliceVisibleWaveformPeaks: ({ displayDurationInFrames, displayOffsetInFrames, durationInFrames, fps, loopDisplay, peaks, playbackRate, startFrom, waveformSampleRate, }: {
|
|
3
3
|
readonly displayDurationInFrames: number;
|
|
4
4
|
readonly displayOffsetInFrames: number;
|
|
5
5
|
readonly durationInFrames: number;
|
|
@@ -8,4 +8,5 @@ export declare const sliceVisibleWaveformPeaks: ({ displayDurationInFrames, disp
|
|
|
8
8
|
readonly peaks: Float32Array<ArrayBufferLike>;
|
|
9
9
|
readonly playbackRate: number;
|
|
10
10
|
readonly startFrom: number;
|
|
11
|
+
readonly waveformSampleRate?: number | undefined;
|
|
11
12
|
}) => Float32Array<ArrayBufferLike>;
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
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
|
-
const sliceVisibleWaveformPeaks = ({ displayDurationInFrames, displayOffsetInFrames, durationInFrames, fps, loopDisplay, peaks, playbackRate, startFrom, }) => {
|
|
6
|
+
const sliceVisibleWaveformPeaks = ({ displayDurationInFrames, displayOffsetInFrames, durationInFrames, fps, loopDisplay, peaks, playbackRate, startFrom, waveformSampleRate, }) => {
|
|
7
7
|
if (!(0, loop_display_1.shouldTileLoopDisplay)(loopDisplay) ||
|
|
8
8
|
loopDisplay.durationInFrames <= 0) {
|
|
9
9
|
return (0, slice_waveform_peaks_1.sliceWaveformPeaks)({
|
|
@@ -12,6 +12,7 @@ const sliceVisibleWaveformPeaks = ({ displayDurationInFrames, displayOffsetInFra
|
|
|
12
12
|
peaks,
|
|
13
13
|
playbackRate,
|
|
14
14
|
startFrom: startFrom + displayOffsetInFrames * playbackRate,
|
|
15
|
+
waveformSampleRate,
|
|
15
16
|
});
|
|
16
17
|
}
|
|
17
18
|
const segments = (0, loop_display_1.getLoopDisplaySegments)({
|
|
@@ -28,6 +29,7 @@ const sliceVisibleWaveformPeaks = ({ displayDurationInFrames, displayOffsetInFra
|
|
|
28
29
|
peaks,
|
|
29
30
|
playbackRate,
|
|
30
31
|
startFrom: startFrom + segment.loopOffsetInFrames * playbackRate,
|
|
32
|
+
waveformSampleRate,
|
|
31
33
|
});
|
|
32
34
|
parts.push(part);
|
|
33
35
|
totalLength += part.length;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export declare const sliceWaveformPeaks: ({ durationInFrames, fps, peaks, playbackRate, startFrom, }: {
|
|
1
|
+
export declare const sliceWaveformPeaks: ({ durationInFrames, fps, peaks, playbackRate, startFrom, waveformSampleRate, }: {
|
|
2
2
|
readonly peaks: Float32Array<ArrayBufferLike>;
|
|
3
3
|
readonly startFrom: number;
|
|
4
4
|
readonly durationInFrames: number;
|
|
5
5
|
readonly fps: number;
|
|
6
6
|
readonly playbackRate: number;
|
|
7
|
+
readonly waveformSampleRate?: number | undefined;
|
|
7
8
|
}) => Float32Array<ArrayBufferLike>;
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.sliceWaveformPeaks = void 0;
|
|
4
4
|
const constants_1 = require("./constants");
|
|
5
|
-
const sliceWaveformPeaks = ({ durationInFrames, fps, peaks, playbackRate, startFrom, }) => {
|
|
5
|
+
const sliceWaveformPeaks = ({ durationInFrames, fps, peaks, playbackRate, startFrom, waveformSampleRate = constants_1.TARGET_SAMPLE_RATE, }) => {
|
|
6
6
|
if (peaks.length === 0) {
|
|
7
7
|
return peaks;
|
|
8
8
|
}
|
|
9
9
|
const startTimeInSeconds = startFrom / fps;
|
|
10
10
|
const durationInSeconds = (durationInFrames / fps) * playbackRate;
|
|
11
|
-
const startPeakIndex = Math.floor(startTimeInSeconds *
|
|
12
|
-
const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) *
|
|
11
|
+
const startPeakIndex = Math.floor(startTimeInSeconds * waveformSampleRate);
|
|
12
|
+
const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * waveformSampleRate);
|
|
13
13
|
if (!Number.isFinite(startPeakIndex) || !Number.isFinite(endPeakIndex)) {
|
|
14
14
|
return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
|
|
15
15
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export declare const subscribeToWaveformPeaks: ({ src, onPeaks, onError, }: {
|
|
1
|
+
export declare const subscribeToWaveformPeaks: ({ src, waveformSampleRate, onPeaks, onError, }: {
|
|
2
2
|
readonly src: string;
|
|
3
|
+
readonly waveformSampleRate?: number | undefined;
|
|
3
4
|
readonly onPeaks: (peaks: Float32Array<ArrayBufferLike>, final: boolean) => void;
|
|
4
5
|
readonly onError: (error: Error) => void;
|
|
5
6
|
}) => () => void;
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.subscribeToWaveformPeaks = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
4
5
|
const load_waveform_peaks_1 = require("./load-waveform-peaks");
|
|
5
6
|
const make_audio_waveform_worker_1 = require("./make-audio-waveform-worker");
|
|
6
7
|
// All AudioWaveform instances share one decode worker and one peaks cache,
|
|
7
8
|
// so remounts (timeline virtualization, zoom window cropping) and multiple
|
|
8
9
|
// clips with the same src never re-fetch or re-decode the audio.
|
|
9
10
|
const peaksCache = new Map();
|
|
10
|
-
const
|
|
11
|
+
const inFlightByCacheKey = new Map();
|
|
11
12
|
const inFlightByRequestId = new Map();
|
|
12
13
|
let worker = null;
|
|
13
14
|
let workerFailed = false;
|
|
14
15
|
let nextRequestId = 0;
|
|
15
16
|
const emitPeaks = (load, peaks, final) => {
|
|
16
17
|
if (final) {
|
|
17
|
-
peaksCache.set(load.
|
|
18
|
-
|
|
18
|
+
peaksCache.set(load.cacheKey, peaks);
|
|
19
|
+
load.latestPeaks = null;
|
|
20
|
+
inFlightByCacheKey.delete(load.cacheKey);
|
|
19
21
|
if (load.requestId !== null) {
|
|
20
22
|
inFlightByRequestId.delete(load.requestId);
|
|
21
23
|
}
|
|
@@ -28,7 +30,7 @@ const emitPeaks = (load, peaks, final) => {
|
|
|
28
30
|
}
|
|
29
31
|
};
|
|
30
32
|
const emitError = (load, error) => {
|
|
31
|
-
|
|
33
|
+
inFlightByCacheKey.delete(load.cacheKey);
|
|
32
34
|
if (load.requestId !== null) {
|
|
33
35
|
inFlightByRequestId.delete(load.requestId);
|
|
34
36
|
}
|
|
@@ -42,6 +44,7 @@ const startMainThreadLoad = (load) => {
|
|
|
42
44
|
// decode fills the cache for the next mount.
|
|
43
45
|
const controller = new AbortController();
|
|
44
46
|
(0, load_waveform_peaks_1.loadWaveformPeaks)(load.src, controller.signal, {
|
|
47
|
+
waveformSampleRate: load.waveformSampleRate,
|
|
45
48
|
onProgress: ({ peaks, final }) => {
|
|
46
49
|
if (final) {
|
|
47
50
|
return;
|
|
@@ -102,14 +105,15 @@ const getOrCreateWorker = () => {
|
|
|
102
105
|
});
|
|
103
106
|
return worker;
|
|
104
107
|
};
|
|
105
|
-
const subscribeToWaveformPeaks = ({ src, onPeaks, onError, }) => {
|
|
106
|
-
const
|
|
108
|
+
const subscribeToWaveformPeaks = ({ src, waveformSampleRate = constants_1.TARGET_SAMPLE_RATE, onPeaks, onError, }) => {
|
|
109
|
+
const cacheKey = `${waveformSampleRate}:${src}`;
|
|
110
|
+
const cached = peaksCache.get(cacheKey);
|
|
107
111
|
if (cached) {
|
|
108
112
|
onPeaks(cached, true);
|
|
109
113
|
return () => undefined;
|
|
110
114
|
}
|
|
111
115
|
const listener = { onPeaks, onError };
|
|
112
|
-
const existing =
|
|
116
|
+
const existing = inFlightByCacheKey.get(cacheKey);
|
|
113
117
|
if (existing) {
|
|
114
118
|
existing.listeners.add(listener);
|
|
115
119
|
if (existing.latestPeaks) {
|
|
@@ -121,11 +125,13 @@ const subscribeToWaveformPeaks = ({ src, onPeaks, onError, }) => {
|
|
|
121
125
|
}
|
|
122
126
|
const load = {
|
|
123
127
|
src,
|
|
128
|
+
cacheKey,
|
|
129
|
+
waveformSampleRate,
|
|
124
130
|
listeners: new Set([listener]),
|
|
125
131
|
latestPeaks: null,
|
|
126
132
|
requestId: null,
|
|
127
133
|
};
|
|
128
|
-
|
|
134
|
+
inFlightByCacheKey.set(cacheKey, load);
|
|
129
135
|
const workerInstance = getOrCreateWorker();
|
|
130
136
|
if (workerInstance) {
|
|
131
137
|
const requestId = nextRequestId++;
|
|
@@ -135,6 +141,7 @@ const subscribeToWaveformPeaks = ({ src, onPeaks, onError, }) => {
|
|
|
135
141
|
type: 'load',
|
|
136
142
|
requestId,
|
|
137
143
|
src,
|
|
144
|
+
waveformSampleRate,
|
|
138
145
|
};
|
|
139
146
|
workerInstance.postMessage(message);
|
|
140
147
|
}
|
|
@@ -26,6 +26,7 @@ self.addEventListener('message', (event) => {
|
|
|
26
26
|
const message = event.data;
|
|
27
27
|
const controller = new AbortController();
|
|
28
28
|
(0, load_waveform_peaks_1.loadWaveformPeaks)(message.src, controller.signal, {
|
|
29
|
+
waveformSampleRate: message.waveformSampleRate,
|
|
29
30
|
onProgress: ({ peaks, final }) => {
|
|
30
31
|
if (!final) {
|
|
31
32
|
postPeaks(message.requestId, peaks, false);
|
|
@@ -103,7 +103,12 @@ var createWaveformPeakProcessor = ({
|
|
|
103
103
|
var DEFAULT_PROGRESS_INTERVAL_IN_MS = 50;
|
|
104
104
|
var peaksCache = new Map;
|
|
105
105
|
async function loadWaveformPeaks(url, signal, options) {
|
|
106
|
-
const
|
|
106
|
+
const waveformSampleRate = options?.waveformSampleRate ?? TARGET_SAMPLE_RATE;
|
|
107
|
+
if (!Number.isFinite(waveformSampleRate) || waveformSampleRate <= 0) {
|
|
108
|
+
throw new Error("The waveform sample rate must be a positive number.");
|
|
109
|
+
}
|
|
110
|
+
const cacheKey = `${waveformSampleRate}:${url}`;
|
|
111
|
+
const cached = peaksCache.get(cacheKey);
|
|
107
112
|
if (cached) {
|
|
108
113
|
emitWaveformProgress({
|
|
109
114
|
peaks: cached,
|
|
@@ -129,16 +134,16 @@ async function loadWaveformPeaks(url, signal, options) {
|
|
|
129
134
|
if (await audioTrack.isRelativeToUnixEpoch()) {
|
|
130
135
|
throw new Error("Streams with UNIX timestamps are not currently supported by Remotion. Sorry! Source: " + url);
|
|
131
136
|
}
|
|
132
|
-
const
|
|
137
|
+
const audioSampleRate = await audioTrack.getSampleRate();
|
|
133
138
|
const durationInSeconds = await audioTrack.getDurationFromMetadata({ skipLiveWait: true }) ?? await audioTrack.computeDuration({ skipLiveWait: true });
|
|
134
|
-
const totalPeaks = Math.ceil(durationInSeconds *
|
|
135
|
-
const samplesPerPeak = Math.max(1, Math.floor(
|
|
139
|
+
const totalPeaks = Math.ceil(durationInSeconds * waveformSampleRate);
|
|
140
|
+
const samplesPerPeak = Math.max(1, Math.floor(audioSampleRate / waveformSampleRate));
|
|
136
141
|
const sink = new AudioSampleSink(audioTrack);
|
|
137
142
|
const processor = createWaveformPeakProcessor({
|
|
138
143
|
totalPeaks,
|
|
139
144
|
samplesPerPeak,
|
|
140
145
|
onProgress: options?.onProgress,
|
|
141
|
-
progressIntervalInMs: options?.progressIntervalInMs ?? DEFAULT_PROGRESS_INTERVAL_IN_MS,
|
|
146
|
+
progressIntervalInMs: options?.progressIntervalInMs ?? DEFAULT_PROGRESS_INTERVAL_IN_MS * Math.max(1, waveformSampleRate / TARGET_SAMPLE_RATE),
|
|
142
147
|
now: () => Date.now()
|
|
143
148
|
});
|
|
144
149
|
for await (const sample of sink.samples()) {
|
|
@@ -175,7 +180,7 @@ async function loadWaveformPeaks(url, signal, options) {
|
|
|
175
180
|
}
|
|
176
181
|
processor.finalize();
|
|
177
182
|
const { peaks } = processor;
|
|
178
|
-
peaksCache.set(
|
|
183
|
+
peaksCache.set(cacheKey, peaks);
|
|
179
184
|
return peaks;
|
|
180
185
|
} finally {
|
|
181
186
|
input.dispose();
|
|
@@ -205,6 +210,7 @@ self.addEventListener("message", (event) => {
|
|
|
205
210
|
const message = event.data;
|
|
206
211
|
const controller = new AbortController;
|
|
207
212
|
loadWaveformPeaks(message.src, controller.signal, {
|
|
213
|
+
waveformSampleRate: message.waveformSampleRate,
|
|
208
214
|
onProgress: ({ peaks, final }) => {
|
|
209
215
|
if (!final) {
|
|
210
216
|
postPeaks(message.requestId, peaks, false);
|
package/dist/esm/index.mjs
CHANGED
|
@@ -275,7 +275,12 @@ var createWaveformPeakProcessor = ({
|
|
|
275
275
|
var DEFAULT_PROGRESS_INTERVAL_IN_MS = 50;
|
|
276
276
|
var peaksCache = new Map;
|
|
277
277
|
async function loadWaveformPeaks(url, signal, options) {
|
|
278
|
-
const
|
|
278
|
+
const waveformSampleRate = options?.waveformSampleRate ?? TARGET_SAMPLE_RATE;
|
|
279
|
+
if (!Number.isFinite(waveformSampleRate) || waveformSampleRate <= 0) {
|
|
280
|
+
throw new Error("The waveform sample rate must be a positive number.");
|
|
281
|
+
}
|
|
282
|
+
const cacheKey = `${waveformSampleRate}:${url}`;
|
|
283
|
+
const cached = peaksCache.get(cacheKey);
|
|
279
284
|
if (cached) {
|
|
280
285
|
emitWaveformProgress({
|
|
281
286
|
peaks: cached,
|
|
@@ -301,16 +306,16 @@ async function loadWaveformPeaks(url, signal, options) {
|
|
|
301
306
|
if (await audioTrack.isRelativeToUnixEpoch()) {
|
|
302
307
|
throw new Error("Streams with UNIX timestamps are not currently supported by Remotion. Sorry! Source: " + url);
|
|
303
308
|
}
|
|
304
|
-
const
|
|
309
|
+
const audioSampleRate = await audioTrack.getSampleRate();
|
|
305
310
|
const durationInSeconds = await audioTrack.getDurationFromMetadata({ skipLiveWait: true }) ?? await audioTrack.computeDuration({ skipLiveWait: true });
|
|
306
|
-
const totalPeaks = Math.ceil(durationInSeconds *
|
|
307
|
-
const samplesPerPeak = Math.max(1, Math.floor(
|
|
311
|
+
const totalPeaks = Math.ceil(durationInSeconds * waveformSampleRate);
|
|
312
|
+
const samplesPerPeak = Math.max(1, Math.floor(audioSampleRate / waveformSampleRate));
|
|
308
313
|
const sink = new AudioSampleSink(audioTrack);
|
|
309
314
|
const processor = createWaveformPeakProcessor({
|
|
310
315
|
totalPeaks,
|
|
311
316
|
samplesPerPeak,
|
|
312
317
|
onProgress: options?.onProgress,
|
|
313
|
-
progressIntervalInMs: options?.progressIntervalInMs ?? DEFAULT_PROGRESS_INTERVAL_IN_MS,
|
|
318
|
+
progressIntervalInMs: options?.progressIntervalInMs ?? DEFAULT_PROGRESS_INTERVAL_IN_MS * Math.max(1, waveformSampleRate / TARGET_SAMPLE_RATE),
|
|
314
319
|
now: () => Date.now()
|
|
315
320
|
});
|
|
316
321
|
for await (const sample of sink.samples()) {
|
|
@@ -347,7 +352,7 @@ async function loadWaveformPeaks(url, signal, options) {
|
|
|
347
352
|
}
|
|
348
353
|
processor.finalize();
|
|
349
354
|
const { peaks } = processor;
|
|
350
|
-
peaksCache.set(
|
|
355
|
+
peaksCache.set(cacheKey, peaks);
|
|
351
356
|
return peaks;
|
|
352
357
|
} finally {
|
|
353
358
|
input.dispose();
|
|
@@ -359,15 +364,16 @@ var sliceWaveformPeaks = ({
|
|
|
359
364
|
fps,
|
|
360
365
|
peaks,
|
|
361
366
|
playbackRate,
|
|
362
|
-
startFrom
|
|
367
|
+
startFrom,
|
|
368
|
+
waveformSampleRate = TARGET_SAMPLE_RATE
|
|
363
369
|
}) => {
|
|
364
370
|
if (peaks.length === 0) {
|
|
365
371
|
return peaks;
|
|
366
372
|
}
|
|
367
373
|
const startTimeInSeconds = startFrom / fps;
|
|
368
374
|
const durationInSeconds = durationInFrames / fps * playbackRate;
|
|
369
|
-
const startPeakIndex = Math.floor(startTimeInSeconds *
|
|
370
|
-
const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) *
|
|
375
|
+
const startPeakIndex = Math.floor(startTimeInSeconds * waveformSampleRate);
|
|
376
|
+
const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * waveformSampleRate);
|
|
371
377
|
if (!Number.isFinite(startPeakIndex) || !Number.isFinite(endPeakIndex)) {
|
|
372
378
|
return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
|
|
373
379
|
}
|
|
@@ -392,7 +398,8 @@ var sliceVisibleWaveformPeaks = ({
|
|
|
392
398
|
loopDisplay,
|
|
393
399
|
peaks,
|
|
394
400
|
playbackRate,
|
|
395
|
-
startFrom
|
|
401
|
+
startFrom,
|
|
402
|
+
waveformSampleRate
|
|
396
403
|
}) => {
|
|
397
404
|
if (!shouldTileLoopDisplay(loopDisplay) || loopDisplay.durationInFrames <= 0) {
|
|
398
405
|
return sliceWaveformPeaks({
|
|
@@ -400,7 +407,8 @@ var sliceVisibleWaveformPeaks = ({
|
|
|
400
407
|
fps,
|
|
401
408
|
peaks,
|
|
402
409
|
playbackRate,
|
|
403
|
-
startFrom: startFrom + displayOffsetInFrames * playbackRate
|
|
410
|
+
startFrom: startFrom + displayOffsetInFrames * playbackRate,
|
|
411
|
+
waveformSampleRate
|
|
404
412
|
});
|
|
405
413
|
}
|
|
406
414
|
const segments = getLoopDisplaySegments({
|
|
@@ -416,7 +424,8 @@ var sliceVisibleWaveformPeaks = ({
|
|
|
416
424
|
fps,
|
|
417
425
|
peaks,
|
|
418
426
|
playbackRate,
|
|
419
|
-
startFrom: startFrom + segment.loopOffsetInFrames * playbackRate
|
|
427
|
+
startFrom: startFrom + segment.loopOffsetInFrames * playbackRate,
|
|
428
|
+
waveformSampleRate
|
|
420
429
|
});
|
|
421
430
|
parts.push(part);
|
|
422
431
|
totalLength += part.length;
|
|
@@ -441,15 +450,16 @@ var makeAudioWaveformWorker = () => {
|
|
|
441
450
|
|
|
442
451
|
// src/audio-waveform/subscribe-to-waveform-peaks.ts
|
|
443
452
|
var peaksCache2 = new Map;
|
|
444
|
-
var
|
|
453
|
+
var inFlightByCacheKey = new Map;
|
|
445
454
|
var inFlightByRequestId = new Map;
|
|
446
455
|
var worker = null;
|
|
447
456
|
var workerFailed = false;
|
|
448
457
|
var nextRequestId = 0;
|
|
449
458
|
var emitPeaks = (load, peaks, final) => {
|
|
450
459
|
if (final) {
|
|
451
|
-
peaksCache2.set(load.
|
|
452
|
-
|
|
460
|
+
peaksCache2.set(load.cacheKey, peaks);
|
|
461
|
+
load.latestPeaks = null;
|
|
462
|
+
inFlightByCacheKey.delete(load.cacheKey);
|
|
453
463
|
if (load.requestId !== null) {
|
|
454
464
|
inFlightByRequestId.delete(load.requestId);
|
|
455
465
|
}
|
|
@@ -461,7 +471,7 @@ var emitPeaks = (load, peaks, final) => {
|
|
|
461
471
|
}
|
|
462
472
|
};
|
|
463
473
|
var emitError = (load, error) => {
|
|
464
|
-
|
|
474
|
+
inFlightByCacheKey.delete(load.cacheKey);
|
|
465
475
|
if (load.requestId !== null) {
|
|
466
476
|
inFlightByRequestId.delete(load.requestId);
|
|
467
477
|
}
|
|
@@ -473,6 +483,7 @@ var startMainThreadLoad = (load) => {
|
|
|
473
483
|
load.requestId = null;
|
|
474
484
|
const controller = new AbortController;
|
|
475
485
|
loadWaveformPeaks(load.src, controller.signal, {
|
|
486
|
+
waveformSampleRate: load.waveformSampleRate,
|
|
476
487
|
onProgress: ({ peaks, final }) => {
|
|
477
488
|
if (final) {
|
|
478
489
|
return;
|
|
@@ -528,10 +539,12 @@ var getOrCreateWorker = () => {
|
|
|
528
539
|
};
|
|
529
540
|
var subscribeToWaveformPeaks = ({
|
|
530
541
|
src,
|
|
542
|
+
waveformSampleRate = TARGET_SAMPLE_RATE,
|
|
531
543
|
onPeaks,
|
|
532
544
|
onError
|
|
533
545
|
}) => {
|
|
534
|
-
const
|
|
546
|
+
const cacheKey = `${waveformSampleRate}:${src}`;
|
|
547
|
+
const cached = peaksCache2.get(cacheKey);
|
|
535
548
|
if (cached) {
|
|
536
549
|
onPeaks(cached, true);
|
|
537
550
|
return () => {
|
|
@@ -539,7 +552,7 @@ var subscribeToWaveformPeaks = ({
|
|
|
539
552
|
};
|
|
540
553
|
}
|
|
541
554
|
const listener = { onPeaks, onError };
|
|
542
|
-
const existing =
|
|
555
|
+
const existing = inFlightByCacheKey.get(cacheKey);
|
|
543
556
|
if (existing) {
|
|
544
557
|
existing.listeners.add(listener);
|
|
545
558
|
if (existing.latestPeaks) {
|
|
@@ -551,11 +564,13 @@ var subscribeToWaveformPeaks = ({
|
|
|
551
564
|
}
|
|
552
565
|
const load = {
|
|
553
566
|
src,
|
|
567
|
+
cacheKey,
|
|
568
|
+
waveformSampleRate,
|
|
554
569
|
listeners: new Set([listener]),
|
|
555
570
|
latestPeaks: null,
|
|
556
571
|
requestId: null
|
|
557
572
|
};
|
|
558
|
-
|
|
573
|
+
inFlightByCacheKey.set(cacheKey, load);
|
|
559
574
|
const workerInstance = getOrCreateWorker();
|
|
560
575
|
if (workerInstance) {
|
|
561
576
|
const requestId = nextRequestId++;
|
|
@@ -564,7 +579,8 @@ var subscribeToWaveformPeaks = ({
|
|
|
564
579
|
const message = {
|
|
565
580
|
type: "load",
|
|
566
581
|
requestId,
|
|
567
|
-
src
|
|
582
|
+
src,
|
|
583
|
+
waveformSampleRate
|
|
568
584
|
};
|
|
569
585
|
workerInstance.postMessage(message);
|
|
570
586
|
} else {
|
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.520",
|
|
7
7
|
"description": "Internal utilities for rendering Remotion timelines",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"scripts": {
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"url": "https://github.com/remotion-dev/remotion/issues"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"mediabunny": "1.55.
|
|
25
|
-
"remotion": "4.0.
|
|
24
|
+
"mediabunny": "1.55.5",
|
|
25
|
+
"remotion": "4.0.520"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@mediabunny/server": "1.55.
|
|
29
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
28
|
+
"@mediabunny/server": "1.55.5",
|
|
29
|
+
"@remotion/eslint-config-internal": "4.0.520",
|
|
30
30
|
"eslint": "9.19.0",
|
|
31
31
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|
|
32
32
|
},
|