@remotion/timeline-utils 4.0.519 → 4.0.521

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.
@@ -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 cached = peaksCache.get(url);
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 sampleRate = await audioTrack.getSampleRate();
43
- const durationInSeconds = (_a = (await audioTrack.getDurationFromMetadata({ skipLiveWait: true }))) !== null && _a !== void 0 ? _a : (await audioTrack.computeDuration({ skipLiveWait: true }));
44
- const totalPeaks = Math.ceil(durationInSeconds * constants_1.TARGET_SAMPLE_RATE);
45
- const samplesPerPeak = Math.max(1, Math.floor(sampleRate / constants_1.TARGET_SAMPLE_RATE));
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: (_b = options === null || options === void 0 ? void 0 : options.progressIntervalInMs) !== null && _b !== void 0 ? _b : DEFAULT_PROGRESS_INTERVAL_IN_MS,
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(url, peaks);
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 * constants_1.TARGET_SAMPLE_RATE);
12
- const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * constants_1.TARGET_SAMPLE_RATE);
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 inFlightBySrc = new Map();
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.src, peaks);
18
- inFlightBySrc.delete(load.src);
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
- inFlightBySrc.delete(load.src);
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 cached = peaksCache.get(src);
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 = inFlightBySrc.get(src);
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
- inFlightBySrc.set(src, load);
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 cached = peaksCache.get(url);
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 sampleRate = await audioTrack.getSampleRate();
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 * TARGET_SAMPLE_RATE);
135
- const samplesPerPeak = Math.max(1, Math.floor(sampleRate / TARGET_SAMPLE_RATE));
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(url, peaks);
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);
@@ -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 cached = peaksCache.get(url);
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 sampleRate = await audioTrack.getSampleRate();
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 * TARGET_SAMPLE_RATE);
307
- const samplesPerPeak = Math.max(1, Math.floor(sampleRate / TARGET_SAMPLE_RATE));
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(url, peaks);
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 * TARGET_SAMPLE_RATE);
370
- const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * TARGET_SAMPLE_RATE);
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 inFlightBySrc = new Map;
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.src, peaks);
452
- inFlightBySrc.delete(load.src);
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
- inFlightBySrc.delete(load.src);
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 cached = peaksCache2.get(src);
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 = inFlightBySrc.get(src);
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
- inFlightBySrc.set(src, load);
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 {
@@ -830,9 +846,8 @@ var drawSlot = ({
830
846
  const relativeTimestamp = timestamp - fromSeconds * WEBCODECS_TIMESCALE;
831
847
  const frameIndex = relativeTimestamp / durationOfOneFrame;
832
848
  const thumbnailWidth = frame.displayWidth / devicePixelRatio;
833
- const left = Math.floor(frameIndex * thumbnailWidth);
834
- const right = Math.ceil((frameIndex + 1) * thumbnailWidth);
835
- ctx.drawImage(frame, left, 0, right - left, frame.displayHeight / devicePixelRatio);
849
+ const left = frameIndex * thumbnailWidth;
850
+ ctx.drawImage(frame, left, 0, thumbnailWidth, frame.displayHeight / devicePixelRatio);
836
851
  filledSlots.set(timestamp, frame.timestamp);
837
852
  };
838
853
  var fillWithCachedFrames = ({
@@ -60,9 +60,8 @@ const drawSlot = ({ frame, ctx, filledSlots, visualizationWidth, timestamp, segm
60
60
  const relativeTimestamp = timestamp - fromSeconds * exports.WEBCODECS_TIMESCALE;
61
61
  const frameIndex = relativeTimestamp / durationOfOneFrame;
62
62
  const thumbnailWidth = frame.displayWidth / devicePixelRatio;
63
- const left = Math.floor(frameIndex * thumbnailWidth);
64
- const right = Math.ceil((frameIndex + 1) * thumbnailWidth);
65
- ctx.drawImage(frame, left, 0, right - left, frame.displayHeight / devicePixelRatio);
63
+ const left = frameIndex * thumbnailWidth;
64
+ ctx.drawImage(frame, left, 0, thumbnailWidth, frame.displayHeight / devicePixelRatio);
66
65
  filledSlots.set(timestamp, frame.timestamp);
67
66
  };
68
67
  exports.drawSlot = drawSlot;
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.519",
6
+ "version": "4.0.521",
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.1",
25
- "remotion": "4.0.519"
24
+ "mediabunny": "1.55.5",
25
+ "remotion": "4.0.521"
26
26
  },
27
27
  "devDependencies": {
28
- "@mediabunny/server": "1.55.1",
29
- "@remotion/eslint-config-internal": "4.0.519",
28
+ "@mediabunny/server": "1.55.5",
29
+ "@remotion/eslint-config-internal": "4.0.521",
30
30
  "eslint": "9.19.0",
31
31
  "@typescript/native-preview": "7.0.0-dev.20260217.1"
32
32
  },