@remotion/media 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.
- package/dist/audio/pitch-shift.d.ts +23 -0
- package/dist/audio-iterator-manager.d.ts +3 -1
- package/dist/debug-overlay/preview-overlay.d.ts +1 -0
- package/dist/esm/index.mjs +545 -17
- package/dist/media-player.d.ts +4 -1
- package/dist/use-common-effects.d.ts +2 -1
- package/dist/validate-tone-frequency.d.ts +4 -0
- package/dist/video/video-for-preview.d.ts +1 -0
- package/package.json +6 -6
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AudioBufferSlice } from '../make-iterator-with-priming';
|
|
2
|
+
type PlanarAudio = Float32Array[];
|
|
3
|
+
export declare class StreamingPitchShifter {
|
|
4
|
+
private readonly numberOfChannels;
|
|
5
|
+
private readonly stretcher;
|
|
6
|
+
private readonly resampler;
|
|
7
|
+
private readonly outputQueue;
|
|
8
|
+
private totalInputFrames;
|
|
9
|
+
private totalOutputFrames;
|
|
10
|
+
constructor({ numberOfChannels, sampleRate, toneFrequency }: {
|
|
11
|
+
numberOfChannels: number;
|
|
12
|
+
sampleRate: number;
|
|
13
|
+
toneFrequency: number;
|
|
14
|
+
});
|
|
15
|
+
append(audio: PlanarAudio): PlanarAudio;
|
|
16
|
+
private takeAvailableOutput;
|
|
17
|
+
finalize(): Float32Array<ArrayBuffer>[];
|
|
18
|
+
}
|
|
19
|
+
export declare function pitchShiftAudioIterator({ iterator, toneFrequency }: {
|
|
20
|
+
iterator: AsyncGenerator<AudioBufferSlice, void, unknown>;
|
|
21
|
+
toneFrequency: number;
|
|
22
|
+
}): AsyncGenerator<AudioBufferSlice, void, unknown>;
|
|
23
|
+
export {};
|
|
@@ -16,7 +16,7 @@ export declare const anchorToContinuousTime: ({ anchor, unloopedTimeInSeconds, p
|
|
|
16
16
|
unloopedTimeInSeconds: number;
|
|
17
17
|
playbackRate: number;
|
|
18
18
|
}) => number;
|
|
19
|
-
export declare const audioIteratorManager: ({ audioTrack, delayPlaybackHandleIfNotPremounting, sharedAudioContext, getSequenceEndTimestamp, getSequenceDurationInSeconds, getMediaEndTimestamp, getStartTime, initialMuted, initialVolume, drawDebugOverlay, }: {
|
|
19
|
+
export declare const audioIteratorManager: ({ audioTrack, delayPlaybackHandleIfNotPremounting, sharedAudioContext, getSequenceEndTimestamp, getSequenceDurationInSeconds, getMediaEndTimestamp, getStartTime, initialMuted, initialVolume, toneFrequency, drawDebugOverlay, }: {
|
|
20
20
|
audioTrack: InputAudioTrack;
|
|
21
21
|
delayPlaybackHandleIfNotPremounting: () => DelayPlaybackIfNotPremounting;
|
|
22
22
|
sharedAudioContext: SharedAudioContextForMediaPlayer;
|
|
@@ -26,6 +26,7 @@ export declare const audioIteratorManager: ({ audioTrack, delayPlaybackHandleIfN
|
|
|
26
26
|
getStartTime: () => number;
|
|
27
27
|
initialMuted: boolean;
|
|
28
28
|
initialVolume: number;
|
|
29
|
+
toneFrequency: number;
|
|
29
30
|
drawDebugOverlay: () => void;
|
|
30
31
|
}) => {
|
|
31
32
|
startAudioIterator: ({ nonce, playbackRate, startFromSecond, unloopedStartFromSecond, scheduleAudioNode, getTargetTime, logLevel, loop, unscheduleAudioNode, getAudioContextCurrentTimeMockedInTest, }: {
|
|
@@ -74,6 +75,7 @@ export declare const audioIteratorManager: ({ audioTrack, delayPlaybackHandleIfN
|
|
|
74
75
|
getTotalAudioScheduledInSeconds: () => number;
|
|
75
76
|
setMuted: (newMuted: boolean) => void;
|
|
76
77
|
setVolume: (volume: number) => void;
|
|
78
|
+
setToneFrequency: (newToneFrequency: number) => void;
|
|
77
79
|
scheduleAudioChunk: ({ buffer, mediaTimestamp, originalUnloopedMediaTimestamp, sourceOffsetInSeconds, sourceDurationInSeconds, playbackRate, scheduleAudioNode, logLevel, }: {
|
|
78
80
|
buffer: AudioBuffer;
|
|
79
81
|
mediaTimestamp: number;
|
|
@@ -53,6 +53,7 @@ export declare const drawPreviewOverlay: ({ context, audioTime, audioContextStat
|
|
|
53
53
|
getTotalAudioScheduledInSeconds: () => number;
|
|
54
54
|
setMuted: (newMuted: boolean) => void;
|
|
55
55
|
setVolume: (volume: number) => void;
|
|
56
|
+
setToneFrequency: (newToneFrequency: number) => void;
|
|
56
57
|
scheduleAudioChunk: ({ buffer, mediaTimestamp, originalUnloopedMediaTimestamp, sourceOffsetInSeconds, sourceDurationInSeconds, playbackRate, scheduleAudioNode, logLevel, }: {
|
|
57
58
|
buffer: AudioBuffer;
|
|
58
59
|
mediaTimestamp: number;
|
package/dist/esm/index.mjs
CHANGED
|
@@ -75,6 +75,19 @@ var getLoopDisplay = ({
|
|
|
75
75
|
};
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
// src/validate-tone-frequency.ts
|
|
79
|
+
var validateToneFrequency = ({
|
|
80
|
+
toneFrequency,
|
|
81
|
+
component
|
|
82
|
+
}) => {
|
|
83
|
+
if (toneFrequency === undefined) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (typeof toneFrequency !== "number" || !Number.isFinite(toneFrequency) || toneFrequency < 0.01 || toneFrequency > 2) {
|
|
87
|
+
throw new TypeError(`The \`toneFrequency\` prop of <${component}> must be a finite number between 0.01 and 2, but got ${String(toneFrequency)}.`);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
78
91
|
// src/audio/audio-for-preview.tsx
|
|
79
92
|
import { useContext as useContext2, useEffect, useMemo, useRef, useState } from "react";
|
|
80
93
|
import {
|
|
@@ -248,6 +261,455 @@ var getTrimStartForAudioNode = ({
|
|
|
248
261
|
return sourceStartOffsetInSeconds + offsetBecauseOfTrim + offsetBecauseOfTooLate;
|
|
249
262
|
};
|
|
250
263
|
|
|
264
|
+
// src/audio/pitch-shift.ts
|
|
265
|
+
var REFERENCE_SAMPLE_RATE = 48000;
|
|
266
|
+
var REFERENCE_HOP_SIZE = 512;
|
|
267
|
+
var makePlanarAudio = (numberOfChannels, length) => {
|
|
268
|
+
return new Array(numberOfChannels).fill(null).map(() => new Float32Array(length));
|
|
269
|
+
};
|
|
270
|
+
var ensurePlanarCapacity = ({
|
|
271
|
+
buffers,
|
|
272
|
+
requiredLength
|
|
273
|
+
}) => {
|
|
274
|
+
if (buffers[0].length >= requiredLength) {
|
|
275
|
+
return buffers;
|
|
276
|
+
}
|
|
277
|
+
let newLength = buffers[0].length;
|
|
278
|
+
while (newLength < requiredLength) {
|
|
279
|
+
newLength *= 2;
|
|
280
|
+
}
|
|
281
|
+
return buffers.map((buffer) => {
|
|
282
|
+
const expanded = new Float32Array(newLength);
|
|
283
|
+
expanded.set(buffer);
|
|
284
|
+
return expanded;
|
|
285
|
+
});
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
class PlanarAudioQueue {
|
|
289
|
+
chunks = [];
|
|
290
|
+
length = 0;
|
|
291
|
+
push(audio) {
|
|
292
|
+
if (audio[0].length === 0) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
this.chunks.push(audio);
|
|
296
|
+
this.length += audio[0].length;
|
|
297
|
+
}
|
|
298
|
+
take(numberOfFrames, numberOfChannels) {
|
|
299
|
+
const framesToTake = Math.min(numberOfFrames, this.length);
|
|
300
|
+
const result = makePlanarAudio(numberOfChannels, framesToTake);
|
|
301
|
+
let written = 0;
|
|
302
|
+
while (written < framesToTake) {
|
|
303
|
+
const first = this.chunks[0];
|
|
304
|
+
const available = first[0].length;
|
|
305
|
+
const count = Math.min(available, framesToTake - written);
|
|
306
|
+
for (let channel = 0;channel < numberOfChannels; channel++) {
|
|
307
|
+
result[channel].set(first[channel].subarray(0, count), written);
|
|
308
|
+
}
|
|
309
|
+
if (count === available) {
|
|
310
|
+
this.chunks.shift();
|
|
311
|
+
} else {
|
|
312
|
+
this.chunks[0] = first.map((channel) => channel.subarray(count));
|
|
313
|
+
}
|
|
314
|
+
written += count;
|
|
315
|
+
this.length -= count;
|
|
316
|
+
}
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
getLength() {
|
|
320
|
+
return this.length;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
class StreamingTimeStretcher {
|
|
325
|
+
numberOfChannels;
|
|
326
|
+
factor;
|
|
327
|
+
hopSize;
|
|
328
|
+
windowSize;
|
|
329
|
+
searchRadius;
|
|
330
|
+
analysisHop;
|
|
331
|
+
input;
|
|
332
|
+
inputLength = 0;
|
|
333
|
+
output;
|
|
334
|
+
outputLength = 0;
|
|
335
|
+
analysisPosition = 0;
|
|
336
|
+
synthesisPosition = 0;
|
|
337
|
+
initialized = false;
|
|
338
|
+
finalized = false;
|
|
339
|
+
totalInputFrames = 0;
|
|
340
|
+
totalOutputFrames = 0;
|
|
341
|
+
constructor({
|
|
342
|
+
numberOfChannels,
|
|
343
|
+
sampleRate,
|
|
344
|
+
factor
|
|
345
|
+
}) {
|
|
346
|
+
this.numberOfChannels = numberOfChannels;
|
|
347
|
+
this.factor = factor;
|
|
348
|
+
this.hopSize = Math.max(32, Math.round(REFERENCE_HOP_SIZE * sampleRate / REFERENCE_SAMPLE_RATE));
|
|
349
|
+
this.windowSize = this.hopSize * 2;
|
|
350
|
+
this.searchRadius = this.hopSize;
|
|
351
|
+
this.analysisHop = this.hopSize / factor;
|
|
352
|
+
this.input = makePlanarAudio(numberOfChannels, 65536);
|
|
353
|
+
this.output = makePlanarAudio(numberOfChannels, 65536);
|
|
354
|
+
}
|
|
355
|
+
append(audio) {
|
|
356
|
+
if (this.finalized) {
|
|
357
|
+
throw new Error("Cannot append audio after the time stretcher was finalized.");
|
|
358
|
+
}
|
|
359
|
+
const { length } = audio[0];
|
|
360
|
+
this.input = ensurePlanarCapacity({
|
|
361
|
+
buffers: this.input,
|
|
362
|
+
requiredLength: this.inputLength + length
|
|
363
|
+
});
|
|
364
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
365
|
+
this.input[channel].set(audio[channel], this.inputLength);
|
|
366
|
+
}
|
|
367
|
+
this.inputLength += length;
|
|
368
|
+
this.totalInputFrames += length;
|
|
369
|
+
this.process();
|
|
370
|
+
return this.drainFinalizedOutput();
|
|
371
|
+
}
|
|
372
|
+
findBestAnalysisPosition({
|
|
373
|
+
expectedPosition,
|
|
374
|
+
nextSynthesisPosition
|
|
375
|
+
}) {
|
|
376
|
+
const minimum = Math.max(0, Math.floor(expectedPosition - this.searchRadius));
|
|
377
|
+
const maximum = Math.min(this.inputLength - this.windowSize, Math.ceil(expectedPosition + this.searchRadius));
|
|
378
|
+
let bestPosition = minimum;
|
|
379
|
+
let bestCorrelation = -Infinity;
|
|
380
|
+
for (let candidate = minimum;candidate <= maximum; candidate += 4) {
|
|
381
|
+
let dotProduct = 0;
|
|
382
|
+
let previousEnergy = 0;
|
|
383
|
+
let candidateEnergy = 0;
|
|
384
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
385
|
+
const previous = this.output[channel];
|
|
386
|
+
const incoming = this.input[channel];
|
|
387
|
+
for (let frame = 0;frame < this.hopSize; frame += 2) {
|
|
388
|
+
const previousValue = previous[nextSynthesisPosition + frame];
|
|
389
|
+
const candidateValue = incoming[candidate + frame];
|
|
390
|
+
dotProduct += previousValue * candidateValue;
|
|
391
|
+
previousEnergy += previousValue * previousValue;
|
|
392
|
+
candidateEnergy += candidateValue * candidateValue;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const correlation = dotProduct / (Math.sqrt(previousEnergy * candidateEnergy) || Number.EPSILON);
|
|
396
|
+
if (correlation > bestCorrelation) {
|
|
397
|
+
bestCorrelation = correlation;
|
|
398
|
+
bestPosition = candidate;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const fineMinimum = Math.max(minimum, bestPosition - 4);
|
|
402
|
+
const fineMaximum = Math.min(maximum, bestPosition + 4);
|
|
403
|
+
for (let candidate = fineMinimum;candidate <= fineMaximum; candidate++) {
|
|
404
|
+
let dotProduct = 0;
|
|
405
|
+
let previousEnergy = 0;
|
|
406
|
+
let candidateEnergy = 0;
|
|
407
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
408
|
+
const previous = this.output[channel];
|
|
409
|
+
const incoming = this.input[channel];
|
|
410
|
+
for (let frame = 0;frame < this.hopSize; frame++) {
|
|
411
|
+
const previousValue = previous[nextSynthesisPosition + frame];
|
|
412
|
+
const candidateValue = incoming[candidate + frame];
|
|
413
|
+
dotProduct += previousValue * candidateValue;
|
|
414
|
+
previousEnergy += previousValue * previousValue;
|
|
415
|
+
candidateEnergy += candidateValue * candidateValue;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
const correlation = dotProduct / (Math.sqrt(previousEnergy * candidateEnergy) || Number.EPSILON);
|
|
419
|
+
if (correlation > bestCorrelation) {
|
|
420
|
+
bestCorrelation = correlation;
|
|
421
|
+
bestPosition = candidate;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return bestPosition;
|
|
425
|
+
}
|
|
426
|
+
process() {
|
|
427
|
+
if (!this.initialized) {
|
|
428
|
+
if (this.inputLength < this.windowSize + this.searchRadius) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
432
|
+
this.output[channel].set(this.input[channel].subarray(0, this.windowSize));
|
|
433
|
+
}
|
|
434
|
+
this.outputLength = this.windowSize;
|
|
435
|
+
this.initialized = true;
|
|
436
|
+
}
|
|
437
|
+
while (true) {
|
|
438
|
+
const expectedPosition = this.analysisPosition + this.analysisHop;
|
|
439
|
+
if (expectedPosition + this.searchRadius + this.windowSize > this.inputLength) {
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
const nextSynthesisPosition = this.synthesisPosition + this.hopSize;
|
|
443
|
+
this.output = ensurePlanarCapacity({
|
|
444
|
+
buffers: this.output,
|
|
445
|
+
requiredLength: nextSynthesisPosition + this.windowSize
|
|
446
|
+
});
|
|
447
|
+
const bestPosition = this.findBestAnalysisPosition({
|
|
448
|
+
expectedPosition,
|
|
449
|
+
nextSynthesisPosition
|
|
450
|
+
});
|
|
451
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
452
|
+
for (let frame = 0;frame < this.hopSize; frame++) {
|
|
453
|
+
const fadeIn = 0.5 - 0.5 * Math.cos(Math.PI * (frame + 1) / (this.hopSize + 1));
|
|
454
|
+
const outputIndex = nextSynthesisPosition + frame;
|
|
455
|
+
this.output[channel][outputIndex] = this.output[channel][outputIndex] * (1 - fadeIn) + this.input[channel][bestPosition + frame] * fadeIn;
|
|
456
|
+
}
|
|
457
|
+
this.output[channel].set(this.input[channel].subarray(bestPosition + this.hopSize, bestPosition + this.windowSize), nextSynthesisPosition + this.hopSize);
|
|
458
|
+
}
|
|
459
|
+
this.analysisPosition = expectedPosition;
|
|
460
|
+
this.synthesisPosition = nextSynthesisPosition;
|
|
461
|
+
this.outputLength = nextSynthesisPosition + this.windowSize;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
drainFinalizedOutput() {
|
|
465
|
+
if (!this.initialized) {
|
|
466
|
+
return makePlanarAudio(this.numberOfChannels, 0);
|
|
467
|
+
}
|
|
468
|
+
const finalizedLength = Math.max(0, this.synthesisPosition + this.hopSize);
|
|
469
|
+
const result = this.output.map((channel) => channel.slice(0, finalizedLength));
|
|
470
|
+
this.totalOutputFrames += finalizedLength;
|
|
471
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
472
|
+
this.output[channel].copyWithin(0, finalizedLength, this.outputLength);
|
|
473
|
+
}
|
|
474
|
+
this.outputLength -= finalizedLength;
|
|
475
|
+
this.synthesisPosition -= finalizedLength;
|
|
476
|
+
const inputFramesToDiscard = Math.max(0, Math.floor(this.analysisPosition) - this.searchRadius);
|
|
477
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
478
|
+
this.input[channel].copyWithin(0, inputFramesToDiscard, this.inputLength);
|
|
479
|
+
}
|
|
480
|
+
this.inputLength -= inputFramesToDiscard;
|
|
481
|
+
this.analysisPosition -= inputFramesToDiscard;
|
|
482
|
+
return result;
|
|
483
|
+
}
|
|
484
|
+
finalize() {
|
|
485
|
+
if (this.finalized) {
|
|
486
|
+
throw new Error("The time stretcher has already been finalized.");
|
|
487
|
+
}
|
|
488
|
+
this.finalized = true;
|
|
489
|
+
const targetLength = Math.round(this.totalInputFrames * this.factor);
|
|
490
|
+
const padding = makePlanarAudio(this.numberOfChannels, this.windowSize + this.searchRadius * 2);
|
|
491
|
+
this.input = ensurePlanarCapacity({
|
|
492
|
+
buffers: this.input,
|
|
493
|
+
requiredLength: this.inputLength + padding[0].length
|
|
494
|
+
});
|
|
495
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
496
|
+
this.input[channel].set(padding[channel], this.inputLength);
|
|
497
|
+
}
|
|
498
|
+
this.inputLength += padding[0].length;
|
|
499
|
+
this.process();
|
|
500
|
+
const finalized = this.drainFinalizedOutput();
|
|
501
|
+
const remaining = Math.max(0, targetLength - this.totalOutputFrames + finalized[0].length);
|
|
502
|
+
if (finalized[0].length >= remaining) {
|
|
503
|
+
return finalized.map((channel) => channel.slice(0, remaining));
|
|
504
|
+
}
|
|
505
|
+
const result = makePlanarAudio(this.numberOfChannels, remaining);
|
|
506
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
507
|
+
result[channel].set(finalized[channel]);
|
|
508
|
+
}
|
|
509
|
+
return result;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
class StreamingLinearResampler {
|
|
514
|
+
numberOfChannels;
|
|
515
|
+
step;
|
|
516
|
+
input;
|
|
517
|
+
inputLength = 0;
|
|
518
|
+
position = 0;
|
|
519
|
+
constructor({
|
|
520
|
+
numberOfChannels,
|
|
521
|
+
step
|
|
522
|
+
}) {
|
|
523
|
+
this.numberOfChannels = numberOfChannels;
|
|
524
|
+
this.step = step;
|
|
525
|
+
this.input = makePlanarAudio(numberOfChannels, 65536);
|
|
526
|
+
}
|
|
527
|
+
append(audio) {
|
|
528
|
+
this.input = ensurePlanarCapacity({
|
|
529
|
+
buffers: this.input,
|
|
530
|
+
requiredLength: this.inputLength + audio[0].length
|
|
531
|
+
});
|
|
532
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
533
|
+
this.input[channel].set(audio[channel], this.inputLength);
|
|
534
|
+
}
|
|
535
|
+
this.inputLength += audio[0].length;
|
|
536
|
+
return this.process(false);
|
|
537
|
+
}
|
|
538
|
+
process(finalizing) {
|
|
539
|
+
const outputLength = Math.max(0, Math.floor((this.inputLength - (finalizing ? 0 : 1) - this.position) / this.step) + 1);
|
|
540
|
+
const result = makePlanarAudio(this.numberOfChannels, outputLength);
|
|
541
|
+
for (let outputFrame = 0;outputFrame < outputLength; outputFrame++) {
|
|
542
|
+
const leftIndex = Math.floor(this.position);
|
|
543
|
+
const rightIndex = Math.min(leftIndex + 1, this.inputLength - 1);
|
|
544
|
+
const fraction = this.position - leftIndex;
|
|
545
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
546
|
+
const left = this.input[channel][leftIndex];
|
|
547
|
+
const right = this.input[channel][rightIndex];
|
|
548
|
+
result[channel][outputFrame] = left + (right - left) * fraction;
|
|
549
|
+
}
|
|
550
|
+
this.position += this.step;
|
|
551
|
+
}
|
|
552
|
+
const discard = Math.min(Math.floor(this.position), this.inputLength);
|
|
553
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
554
|
+
this.input[channel].copyWithin(0, discard, this.inputLength);
|
|
555
|
+
}
|
|
556
|
+
this.inputLength -= discard;
|
|
557
|
+
this.position -= discard;
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
finalize() {
|
|
561
|
+
return this.process(true);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
class StreamingPitchShifter {
|
|
566
|
+
numberOfChannels;
|
|
567
|
+
stretcher;
|
|
568
|
+
resampler;
|
|
569
|
+
outputQueue = new PlanarAudioQueue;
|
|
570
|
+
totalInputFrames = 0;
|
|
571
|
+
totalOutputFrames = 0;
|
|
572
|
+
constructor({
|
|
573
|
+
numberOfChannels,
|
|
574
|
+
sampleRate,
|
|
575
|
+
toneFrequency
|
|
576
|
+
}) {
|
|
577
|
+
this.numberOfChannels = numberOfChannels;
|
|
578
|
+
this.stretcher = new StreamingTimeStretcher({
|
|
579
|
+
numberOfChannels,
|
|
580
|
+
sampleRate,
|
|
581
|
+
factor: toneFrequency
|
|
582
|
+
});
|
|
583
|
+
this.resampler = new StreamingLinearResampler({
|
|
584
|
+
numberOfChannels,
|
|
585
|
+
step: toneFrequency
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
append(audio) {
|
|
589
|
+
this.totalInputFrames += audio[0].length;
|
|
590
|
+
const stretched = this.stretcher.append(audio);
|
|
591
|
+
this.outputQueue.push(this.resampler.append(stretched));
|
|
592
|
+
return this.takeAvailableOutput();
|
|
593
|
+
}
|
|
594
|
+
takeAvailableOutput() {
|
|
595
|
+
const availableInputFrames = this.totalInputFrames - this.totalOutputFrames;
|
|
596
|
+
const framesToTake = Math.min(availableInputFrames, this.outputQueue.getLength());
|
|
597
|
+
const result = this.outputQueue.take(framesToTake, this.numberOfChannels);
|
|
598
|
+
this.totalOutputFrames += framesToTake;
|
|
599
|
+
return result;
|
|
600
|
+
}
|
|
601
|
+
finalize() {
|
|
602
|
+
this.outputQueue.push(this.resampler.append(this.stretcher.finalize()));
|
|
603
|
+
this.outputQueue.push(this.resampler.finalize());
|
|
604
|
+
const remaining = this.totalInputFrames - this.totalOutputFrames;
|
|
605
|
+
const available = this.outputQueue.take(Math.min(remaining, this.outputQueue.getLength()), this.numberOfChannels);
|
|
606
|
+
const result = makePlanarAudio(this.numberOfChannels, remaining);
|
|
607
|
+
for (let channel = 0;channel < this.numberOfChannels; channel++) {
|
|
608
|
+
result[channel].set(available[channel]);
|
|
609
|
+
}
|
|
610
|
+
this.totalOutputFrames += remaining;
|
|
611
|
+
return result;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
var getPlanarSlice = (slice) => {
|
|
615
|
+
const { buffer } = slice.buffer;
|
|
616
|
+
const startFrame = Math.max(0, Math.round(slice.sourceOffsetInSeconds * buffer.sampleRate));
|
|
617
|
+
const numberOfFrames = Math.min(buffer.length - startFrame, Math.round(slice.sourceDurationInSeconds * buffer.sampleRate));
|
|
618
|
+
return new Array(buffer.numberOfChannels).fill(null).map((_, channel) => buffer.getChannelData(channel).slice(startFrame, startFrame + numberOfFrames));
|
|
619
|
+
};
|
|
620
|
+
var makeAudioBufferSlice = ({
|
|
621
|
+
audio,
|
|
622
|
+
timelineTimestamp,
|
|
623
|
+
sampleRate
|
|
624
|
+
}) => {
|
|
625
|
+
const buffer = new AudioBuffer({
|
|
626
|
+
length: audio[0].length,
|
|
627
|
+
numberOfChannels: audio.length,
|
|
628
|
+
sampleRate
|
|
629
|
+
});
|
|
630
|
+
for (let channel = 0;channel < audio.length; channel++) {
|
|
631
|
+
buffer.copyToChannel(new Float32Array(audio[channel]), channel);
|
|
632
|
+
}
|
|
633
|
+
const duration = audio[0].length / sampleRate;
|
|
634
|
+
return {
|
|
635
|
+
buffer: { buffer, timestamp: timelineTimestamp, duration },
|
|
636
|
+
timelineTimestamp,
|
|
637
|
+
sourceOffsetInSeconds: 0,
|
|
638
|
+
sourceDurationInSeconds: duration
|
|
639
|
+
};
|
|
640
|
+
};
|
|
641
|
+
async function* pitchShiftAudioIterator({
|
|
642
|
+
iterator,
|
|
643
|
+
toneFrequency
|
|
644
|
+
}) {
|
|
645
|
+
if (toneFrequency === 1) {
|
|
646
|
+
yield* iterator;
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
let shifter = null;
|
|
650
|
+
let sampleRate = 0;
|
|
651
|
+
let numberOfChannels = 0;
|
|
652
|
+
let segmentStart = 0;
|
|
653
|
+
let segmentInputFrames = 0;
|
|
654
|
+
let segmentOutputFrames = 0;
|
|
655
|
+
const flush = () => {
|
|
656
|
+
if (!shifter) {
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
659
|
+
const audio = shifter.finalize();
|
|
660
|
+
const slice = audio[0].length === 0 ? null : makeAudioBufferSlice({
|
|
661
|
+
audio,
|
|
662
|
+
timelineTimestamp: segmentStart + segmentOutputFrames / sampleRate,
|
|
663
|
+
sampleRate
|
|
664
|
+
});
|
|
665
|
+
shifter = null;
|
|
666
|
+
return slice;
|
|
667
|
+
};
|
|
668
|
+
for await (const slice of iterator) {
|
|
669
|
+
const planar = getPlanarSlice(slice);
|
|
670
|
+
if (planar[0].length === 0) {
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
const nextSampleRate = slice.buffer.buffer.sampleRate;
|
|
674
|
+
const nextNumberOfChannels = slice.buffer.buffer.numberOfChannels;
|
|
675
|
+
const expectedTimestamp = segmentStart + segmentInputFrames / sampleRate;
|
|
676
|
+
const startsNewSegment = !shifter || nextSampleRate !== sampleRate || nextNumberOfChannels !== numberOfChannels || Math.abs(slice.timelineTimestamp - expectedTimestamp) > 1.5 / nextSampleRate;
|
|
677
|
+
if (startsNewSegment) {
|
|
678
|
+
const previousSegmentFinalSlice = flush();
|
|
679
|
+
if (previousSegmentFinalSlice) {
|
|
680
|
+
yield previousSegmentFinalSlice;
|
|
681
|
+
}
|
|
682
|
+
sampleRate = nextSampleRate;
|
|
683
|
+
numberOfChannels = nextNumberOfChannels;
|
|
684
|
+
segmentStart = slice.timelineTimestamp;
|
|
685
|
+
segmentInputFrames = 0;
|
|
686
|
+
segmentOutputFrames = 0;
|
|
687
|
+
shifter = new StreamingPitchShifter({
|
|
688
|
+
numberOfChannels,
|
|
689
|
+
sampleRate,
|
|
690
|
+
toneFrequency
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
if (!shifter) {
|
|
694
|
+
throw new Error("Pitch shifter was not initialized.");
|
|
695
|
+
}
|
|
696
|
+
segmentInputFrames += planar[0].length;
|
|
697
|
+
const output = shifter.append(planar);
|
|
698
|
+
if (output[0].length > 0) {
|
|
699
|
+
yield makeAudioBufferSlice({
|
|
700
|
+
audio: output,
|
|
701
|
+
timelineTimestamp: segmentStart + segmentOutputFrames / sampleRate,
|
|
702
|
+
sampleRate
|
|
703
|
+
});
|
|
704
|
+
segmentOutputFrames += output[0].length;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
const finalSlice = flush();
|
|
708
|
+
if (finalSlice) {
|
|
709
|
+
yield finalSlice;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
251
713
|
// src/audio/sort-by-priority.ts
|
|
252
714
|
class StaleWaiterError extends Error {
|
|
253
715
|
constructor() {
|
|
@@ -436,10 +898,12 @@ var audioIteratorManager = ({
|
|
|
436
898
|
getStartTime,
|
|
437
899
|
initialMuted,
|
|
438
900
|
initialVolume,
|
|
901
|
+
toneFrequency,
|
|
439
902
|
drawDebugOverlay
|
|
440
903
|
}) => {
|
|
441
904
|
let muted = initialMuted;
|
|
442
905
|
let currentVolume = Math.max(0, initialVolume);
|
|
906
|
+
let currentToneFrequency = toneFrequency;
|
|
443
907
|
let currentSeek = null;
|
|
444
908
|
const gainNode = sharedAudioContext.audioContext.createGain();
|
|
445
909
|
gainNode.gain.value = muted ? 0 : currentVolume;
|
|
@@ -651,7 +1115,7 @@ var audioIteratorManager = ({
|
|
|
651
1115
|
mediaStartInSeconds: startFromSecond
|
|
652
1116
|
};
|
|
653
1117
|
const maximumContinuousTimestamp = startFromSecond + getSequenceDurationInSeconds() * playbackRate;
|
|
654
|
-
const
|
|
1118
|
+
const unshiftedSource = loop ? makeLoopingIterator({
|
|
655
1119
|
audioSink,
|
|
656
1120
|
seekTimeInSeconds: startFromSecond,
|
|
657
1121
|
loopStartInSeconds: getStartTime(),
|
|
@@ -662,6 +1126,10 @@ var audioIteratorManager = ({
|
|
|
662
1126
|
timeToSeek: startFromSecond,
|
|
663
1127
|
maximumTimestamp
|
|
664
1128
|
});
|
|
1129
|
+
const source = pitchShiftAudioIterator({
|
|
1130
|
+
iterator: unshiftedSource,
|
|
1131
|
+
toneFrequency: currentToneFrequency
|
|
1132
|
+
});
|
|
665
1133
|
const iterator = makeAudioIterator({
|
|
666
1134
|
startFromSecond,
|
|
667
1135
|
iterator: source,
|
|
@@ -796,6 +1264,9 @@ var audioIteratorManager = ({
|
|
|
796
1264
|
currentVolume = Math.max(0, volume);
|
|
797
1265
|
gainNode.gain.value = muted ? 0 : currentVolume;
|
|
798
1266
|
},
|
|
1267
|
+
setToneFrequency: (newToneFrequency) => {
|
|
1268
|
+
currentToneFrequency = newToneFrequency;
|
|
1269
|
+
},
|
|
799
1270
|
scheduleAudioChunk,
|
|
800
1271
|
waitForNScheduledNodes
|
|
801
1272
|
};
|
|
@@ -1749,6 +2220,7 @@ class MediaPlayer {
|
|
|
1749
2220
|
src;
|
|
1750
2221
|
logLevel;
|
|
1751
2222
|
playbackRate;
|
|
2223
|
+
toneFrequency;
|
|
1752
2224
|
globalPlaybackRate;
|
|
1753
2225
|
audioStreamIndex;
|
|
1754
2226
|
sharedAudioContext;
|
|
@@ -1779,6 +2251,7 @@ class MediaPlayer {
|
|
|
1779
2251
|
trimBefore,
|
|
1780
2252
|
trimAfter,
|
|
1781
2253
|
playbackRate,
|
|
2254
|
+
toneFrequency,
|
|
1782
2255
|
globalPlaybackRate,
|
|
1783
2256
|
audioStreamIndex,
|
|
1784
2257
|
fps,
|
|
@@ -1801,6 +2274,7 @@ class MediaPlayer {
|
|
|
1801
2274
|
this.logLevel = logLevel;
|
|
1802
2275
|
this.sharedAudioContext = sharedAudioContext;
|
|
1803
2276
|
this.playbackRate = playbackRate;
|
|
2277
|
+
this.toneFrequency = toneFrequency;
|
|
1804
2278
|
this.globalPlaybackRate = globalPlaybackRate;
|
|
1805
2279
|
this.loop = loop;
|
|
1806
2280
|
this.trimBefore = trimBefore;
|
|
@@ -1974,6 +2448,7 @@ class MediaPlayer {
|
|
|
1974
2448
|
getStartTime: () => this.getStartTime(),
|
|
1975
2449
|
initialMuted,
|
|
1976
2450
|
initialVolume,
|
|
2451
|
+
toneFrequency: this.toneFrequency,
|
|
1977
2452
|
drawDebugOverlay: this.drawDebugOverlay,
|
|
1978
2453
|
getSequenceDurationInSeconds: () => this.getSequenceDurationInSeconds()
|
|
1979
2454
|
});
|
|
@@ -2132,6 +2607,17 @@ class MediaPlayer {
|
|
|
2132
2607
|
await this.seekTo(unloopedTimeInSeconds);
|
|
2133
2608
|
}
|
|
2134
2609
|
}
|
|
2610
|
+
async setToneFrequency(toneFrequency, unloopedTimeInSeconds) {
|
|
2611
|
+
if (this.toneFrequency !== toneFrequency) {
|
|
2612
|
+
this.toneFrequency = toneFrequency;
|
|
2613
|
+
if (!this.audioIteratorManager) {
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
this.audioIteratorManager.setToneFrequency(toneFrequency);
|
|
2617
|
+
this.audioIteratorManager.destroyIterator();
|
|
2618
|
+
await this.seekTo(unloopedTimeInSeconds);
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2135
2621
|
async setGlobalPlaybackRate(rate, unloopedTimeInSeconds) {
|
|
2136
2622
|
const previousRate = this.globalPlaybackRate;
|
|
2137
2623
|
if (previousRate !== rate) {
|
|
@@ -2319,6 +2805,7 @@ var useCommonEffects = ({
|
|
|
2319
2805
|
effectiveMuted,
|
|
2320
2806
|
userPreferredVolume,
|
|
2321
2807
|
playbackRate,
|
|
2808
|
+
toneFrequency,
|
|
2322
2809
|
globalPlaybackRate,
|
|
2323
2810
|
fps,
|
|
2324
2811
|
sequenceOffset,
|
|
@@ -2394,6 +2881,13 @@ var useCommonEffects = ({
|
|
|
2394
2881
|
}
|
|
2395
2882
|
mediaPlayer.setPlaybackRate(playbackRate, currentTimeRef.current);
|
|
2396
2883
|
}, [playbackRate, mediaPlayerReady, mediaPlayerRef, currentTimeRef]);
|
|
2884
|
+
useLayoutEffect(() => {
|
|
2885
|
+
const mediaPlayer = mediaPlayerRef.current;
|
|
2886
|
+
if (!mediaPlayer || !mediaPlayerReady) {
|
|
2887
|
+
return;
|
|
2888
|
+
}
|
|
2889
|
+
mediaPlayer.setToneFrequency(toneFrequency, currentTimeRef.current);
|
|
2890
|
+
}, [toneFrequency, mediaPlayerReady, mediaPlayerRef, currentTimeRef]);
|
|
2397
2891
|
useLayoutEffect(() => {
|
|
2398
2892
|
const mediaPlayer = mediaPlayerRef.current;
|
|
2399
2893
|
if (!mediaPlayer || !mediaPlayerReady) {
|
|
@@ -2456,7 +2950,6 @@ var useCommonEffects = ({
|
|
|
2456
2950
|
import { jsx } from "react/jsx-runtime";
|
|
2457
2951
|
var {
|
|
2458
2952
|
useUnsafeVideoConfig,
|
|
2459
|
-
Timeline,
|
|
2460
2953
|
SharedAudioContext,
|
|
2461
2954
|
usePlayerMutedState,
|
|
2462
2955
|
useMediaVolumeState,
|
|
@@ -2464,7 +2957,9 @@ var {
|
|
|
2464
2957
|
evaluateVolume,
|
|
2465
2958
|
warnAboutTooHighVolume,
|
|
2466
2959
|
usePreload,
|
|
2467
|
-
SequenceContext
|
|
2960
|
+
SequenceContext,
|
|
2961
|
+
usePlaying,
|
|
2962
|
+
useBuffering
|
|
2468
2963
|
} = Internals9;
|
|
2469
2964
|
var AudioForPreviewAssertedShowing = ({
|
|
2470
2965
|
src,
|
|
@@ -2494,10 +2989,11 @@ var AudioForPreviewAssertedShowing = ({
|
|
|
2494
2989
|
const mediaPlayerRef = useRef(null);
|
|
2495
2990
|
const initialTrimBeforeRef = useRef(trimBefore);
|
|
2496
2991
|
const initialTrimAfterRef = useRef(trimAfter);
|
|
2992
|
+
const initialToneFrequencyRef = useRef(toneFrequency ?? 1);
|
|
2497
2993
|
const [initialRequestInit] = useState(requestInit);
|
|
2498
2994
|
const [mediaPlayerReady, setMediaPlayerReady] = useState(false);
|
|
2499
2995
|
const [shouldFallbackToNativeAudio, setShouldFallbackToNativeAudio] = useState(false);
|
|
2500
|
-
const
|
|
2996
|
+
const playing = usePlaying();
|
|
2501
2997
|
const { playbackRate: globalPlaybackRate } = Internals9.usePlaybackRate();
|
|
2502
2998
|
const sharedAudioContext = useContext2(SharedAudioContext);
|
|
2503
2999
|
const buffer = useBufferState();
|
|
@@ -2524,12 +3020,8 @@ var AudioForPreviewAssertedShowing = ({
|
|
|
2524
3020
|
const isPremounting = Boolean(parentSequence?.premounting);
|
|
2525
3021
|
const isPostmounting = Boolean(parentSequence?.postmounting);
|
|
2526
3022
|
const sequenceOffset = (parentSequence?.absoluteFrom ?? 0) / videoConfig.fps;
|
|
2527
|
-
const bufferingContext = useContext2(Internals9.BufferingContextReact);
|
|
2528
|
-
if (!bufferingContext) {
|
|
2529
|
-
throw new Error("useMediaPlayback must be used inside a <BufferingContext>");
|
|
2530
|
-
}
|
|
2531
3023
|
const effectiveMuted = muted || playerMuted || userPreferredVolume <= 0;
|
|
2532
|
-
const isPlayerBuffering =
|
|
3024
|
+
const isPlayerBuffering = useBuffering();
|
|
2533
3025
|
const initialPlaying = useRef(playing && !isPlayerBuffering);
|
|
2534
3026
|
const initialIsPremounting = useRef(isPremounting);
|
|
2535
3027
|
const initialIsPostmounting = useRef(isPostmounting);
|
|
@@ -2551,6 +3043,7 @@ var AudioForPreviewAssertedShowing = ({
|
|
|
2551
3043
|
effectiveMuted,
|
|
2552
3044
|
userPreferredVolume,
|
|
2553
3045
|
playbackRate,
|
|
3046
|
+
toneFrequency: toneFrequency ?? 1,
|
|
2554
3047
|
globalPlaybackRate,
|
|
2555
3048
|
fps: videoConfig.fps,
|
|
2556
3049
|
sequenceOffset,
|
|
@@ -2594,6 +3087,7 @@ var AudioForPreviewAssertedShowing = ({
|
|
|
2594
3087
|
fps: videoConfig.fps,
|
|
2595
3088
|
canvas: null,
|
|
2596
3089
|
playbackRate: initialPlaybackRate.current,
|
|
3090
|
+
toneFrequency: initialToneFrequencyRef.current,
|
|
2597
3091
|
audioStreamIndex: audioStreamIndex ?? null,
|
|
2598
3092
|
debugOverlay: false,
|
|
2599
3093
|
bufferState: buffer,
|
|
@@ -3582,6 +4076,10 @@ var makeKeyframeBank = async ({
|
|
|
3582
4076
|
};
|
|
3583
4077
|
const ensureEnoughFramesForTimestamp = async (timestampInSeconds, logLevel, fps) => {
|
|
3584
4078
|
while (!hasDecodedEnoughForTimestamp(timestampInSeconds)) {
|
|
4079
|
+
deleteFramesBeforeTimestamp({
|
|
4080
|
+
logLevel: parentLogLevel,
|
|
4081
|
+
timestampInSeconds: timestampInSeconds - getSafeWindowOfMonotonicity(fps)
|
|
4082
|
+
});
|
|
3585
4083
|
const sample = await sampleIterator.next();
|
|
3586
4084
|
if (sample.value) {
|
|
3587
4085
|
addFrame(sample.value, logLevel);
|
|
@@ -5201,6 +5699,7 @@ var { validateMediaProps } = Internals19;
|
|
|
5201
5699
|
var audioSchema = {
|
|
5202
5700
|
src: {
|
|
5203
5701
|
type: "asset",
|
|
5702
|
+
assetType: "audio",
|
|
5204
5703
|
default: undefined,
|
|
5205
5704
|
description: "Source",
|
|
5206
5705
|
keyframable: false
|
|
@@ -5225,6 +5724,16 @@ var audioSchema = {
|
|
|
5225
5724
|
hiddenFromList: false,
|
|
5226
5725
|
keyframable: false
|
|
5227
5726
|
},
|
|
5727
|
+
toneFrequency: {
|
|
5728
|
+
type: "number",
|
|
5729
|
+
min: 0.01,
|
|
5730
|
+
max: 2,
|
|
5731
|
+
step: 0.01,
|
|
5732
|
+
default: 1,
|
|
5733
|
+
description: "Pitch",
|
|
5734
|
+
hiddenFromList: false,
|
|
5735
|
+
keyframable: false
|
|
5736
|
+
},
|
|
5228
5737
|
muted: { type: "boolean", default: false, description: "Muted" },
|
|
5229
5738
|
loop: { type: "boolean", default: false, description: "Loop" }
|
|
5230
5739
|
};
|
|
@@ -5306,6 +5815,10 @@ var AudioInner = (props) => {
|
|
|
5306
5815
|
throw new TypeError(`The \`<Audio>\` tag requires a string for \`src\`, but got ${JSON.stringify(props.src)} instead.`);
|
|
5307
5816
|
}
|
|
5308
5817
|
validateMediaProps({ playbackRate: props.playbackRate, volume: props.volume }, "Audio");
|
|
5818
|
+
validateToneFrequency({
|
|
5819
|
+
toneFrequency: props.toneFrequency,
|
|
5820
|
+
component: "Audio"
|
|
5821
|
+
});
|
|
5309
5822
|
if (sequenceDurationInFrames === 0) {
|
|
5310
5823
|
return null;
|
|
5311
5824
|
}
|
|
@@ -5459,7 +5972,6 @@ var warnAboutObjectFitInStyleOrClassName = ({
|
|
|
5459
5972
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
5460
5973
|
var {
|
|
5461
5974
|
useUnsafeVideoConfig: useUnsafeVideoConfig2,
|
|
5462
|
-
Timeline: Timeline2,
|
|
5463
5975
|
SharedAudioContext: SharedAudioContext2,
|
|
5464
5976
|
usePlayerMutedState: usePlayerMutedState2,
|
|
5465
5977
|
useMediaVolumeState: useMediaVolumeState2,
|
|
@@ -5468,12 +5980,15 @@ var {
|
|
|
5468
5980
|
warnAboutTooHighVolume: warnAboutTooHighVolume2,
|
|
5469
5981
|
usePreload: usePreload2,
|
|
5470
5982
|
SequenceContext: SequenceContext2,
|
|
5471
|
-
useEffectChainState
|
|
5983
|
+
useEffectChainState,
|
|
5984
|
+
usePlaying: usePlaying2,
|
|
5985
|
+
useBuffering: useBuffering2
|
|
5472
5986
|
} = Internals22;
|
|
5473
5987
|
var VideoForPreviewAssertedShowing = ({
|
|
5474
5988
|
src: unpreloadedSrc,
|
|
5475
5989
|
style,
|
|
5476
5990
|
playbackRate,
|
|
5991
|
+
toneFrequency,
|
|
5477
5992
|
logLevel,
|
|
5478
5993
|
className,
|
|
5479
5994
|
muted,
|
|
@@ -5511,7 +6026,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5511
6026
|
const [initialRequestInit] = useState4(requestInit);
|
|
5512
6027
|
const [mediaPlayerReady, setMediaPlayerReady] = useState4(false);
|
|
5513
6028
|
const [shouldFallbackToNativeVideo, setShouldFallbackToNativeVideo] = useState4(false);
|
|
5514
|
-
const
|
|
6029
|
+
const playing = usePlaying2();
|
|
5515
6030
|
const { playbackRate: globalPlaybackRate } = Internals22.usePlaybackRate();
|
|
5516
6031
|
const sharedAudioContext = useContext4(SharedAudioContext2);
|
|
5517
6032
|
const buffer = useBufferState2();
|
|
@@ -5549,17 +6064,14 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5549
6064
|
const currentTimeRef = useRef2(currentTime);
|
|
5550
6065
|
currentTimeRef.current = currentTime;
|
|
5551
6066
|
const preloadedSrc = usePreload2(src);
|
|
5552
|
-
const buffering = useContext4(Internals22.BufferingContextReact);
|
|
5553
|
-
if (!buffering) {
|
|
5554
|
-
throw new Error("useMediaPlayback must be used inside a <BufferingContext>");
|
|
5555
|
-
}
|
|
5556
6067
|
const effectiveMuted = muted || playerMuted || userPreferredVolume <= 0;
|
|
5557
|
-
const isPlayerBuffering =
|
|
6068
|
+
const isPlayerBuffering = useBuffering2();
|
|
5558
6069
|
const initialPlaying = useRef2(playing && !isPlayerBuffering);
|
|
5559
6070
|
const initialIsPremounting = useRef2(isPremounting);
|
|
5560
6071
|
const initialIsPostmounting = useRef2(isPostmounting);
|
|
5561
6072
|
const initialGlobalPlaybackRate = useRef2(globalPlaybackRate);
|
|
5562
6073
|
const initialPlaybackRate = useRef2(playbackRate);
|
|
6074
|
+
const initialToneFrequency = useRef2(toneFrequency);
|
|
5563
6075
|
const initialMuted = useRef2(effectiveMuted);
|
|
5564
6076
|
const initialVolume = useRef2(userPreferredVolume);
|
|
5565
6077
|
const initialSequenceDuration = useRef2(videoConfig.durationInFrames);
|
|
@@ -5620,6 +6132,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5620
6132
|
trimBefore: initialTrimBeforeRef.current,
|
|
5621
6133
|
fps: videoConfig.fps,
|
|
5622
6134
|
playbackRate: initialPlaybackRate.current,
|
|
6135
|
+
toneFrequency: initialToneFrequency.current,
|
|
5623
6136
|
audioStreamIndex,
|
|
5624
6137
|
debugOverlay,
|
|
5625
6138
|
bufferState: buffer,
|
|
@@ -5751,6 +6264,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5751
6264
|
effectiveMuted,
|
|
5752
6265
|
userPreferredVolume,
|
|
5753
6266
|
playbackRate,
|
|
6267
|
+
toneFrequency,
|
|
5754
6268
|
globalPlaybackRate,
|
|
5755
6269
|
fps: videoConfig.fps,
|
|
5756
6270
|
sequenceOffset,
|
|
@@ -5801,6 +6315,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5801
6315
|
trimAfter,
|
|
5802
6316
|
trimBefore,
|
|
5803
6317
|
playbackRate,
|
|
6318
|
+
toneFrequency,
|
|
5804
6319
|
loopVolumeCurveBehavior,
|
|
5805
6320
|
name: "<Html5Video> (fallback)",
|
|
5806
6321
|
loop,
|
|
@@ -6226,6 +6741,7 @@ var {
|
|
|
6226
6741
|
var videoSchema = {
|
|
6227
6742
|
src: {
|
|
6228
6743
|
type: "asset",
|
|
6744
|
+
assetType: "video",
|
|
6229
6745
|
default: undefined,
|
|
6230
6746
|
description: "Source",
|
|
6231
6747
|
keyframable: false
|
|
@@ -6250,6 +6766,16 @@ var videoSchema = {
|
|
|
6250
6766
|
hiddenFromList: false,
|
|
6251
6767
|
keyframable: false
|
|
6252
6768
|
},
|
|
6769
|
+
toneFrequency: {
|
|
6770
|
+
type: "number",
|
|
6771
|
+
min: 0.01,
|
|
6772
|
+
max: 2,
|
|
6773
|
+
step: 0.01,
|
|
6774
|
+
default: 1,
|
|
6775
|
+
description: "Pitch",
|
|
6776
|
+
hiddenFromList: false,
|
|
6777
|
+
keyframable: false
|
|
6778
|
+
},
|
|
6253
6779
|
muted: { type: "boolean", default: false, description: "Muted" },
|
|
6254
6780
|
loop: { type: "boolean", default: false, description: "Loop" },
|
|
6255
6781
|
...Internals24.transformSchema,
|
|
@@ -6309,6 +6835,7 @@ var InnerVideo = ({
|
|
|
6309
6835
|
trimAfter
|
|
6310
6836
|
});
|
|
6311
6837
|
validateMediaProps2({ playbackRate, volume }, "Video");
|
|
6838
|
+
validateToneFrequency({ toneFrequency, component: "Video" });
|
|
6312
6839
|
if (environment.isRendering) {
|
|
6313
6840
|
return /* @__PURE__ */ jsx6(VideoForRendering, {
|
|
6314
6841
|
...props,
|
|
@@ -6350,6 +6877,7 @@ var InnerVideo = ({
|
|
|
6350
6877
|
muted,
|
|
6351
6878
|
onVideoFrame,
|
|
6352
6879
|
playbackRate,
|
|
6880
|
+
toneFrequency: toneFrequency ?? 1,
|
|
6353
6881
|
src,
|
|
6354
6882
|
style,
|
|
6355
6883
|
volume,
|
package/dist/media-player.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export declare class MediaPlayer {
|
|
|
26
26
|
private src;
|
|
27
27
|
private logLevel;
|
|
28
28
|
private playbackRate;
|
|
29
|
+
private toneFrequency;
|
|
29
30
|
private globalPlaybackRate;
|
|
30
31
|
private audioStreamIndex;
|
|
31
32
|
private sharedAudioContext;
|
|
@@ -47,7 +48,7 @@ export declare class MediaPlayer {
|
|
|
47
48
|
private initializationPromise;
|
|
48
49
|
private premountAwareDelayPlayback;
|
|
49
50
|
private seekPromiseChain;
|
|
50
|
-
constructor({ canvas, src, logLevel, sharedAudioContext, loop, trimBefore, trimAfter, playbackRate, globalPlaybackRate, audioStreamIndex, fps, debugOverlay, bufferState, isPremounting, isPostmounting, durationInFrames, onVideoFrameCallback, playing, sequenceOffset, credentials, requestInit, tagType, getEffects, getEffectChainState }: {
|
|
51
|
+
constructor({ canvas, src, logLevel, sharedAudioContext, loop, trimBefore, trimAfter, playbackRate, toneFrequency, globalPlaybackRate, audioStreamIndex, fps, debugOverlay, bufferState, isPremounting, isPostmounting, durationInFrames, onVideoFrameCallback, playing, sequenceOffset, credentials, requestInit, tagType, getEffects, getEffectChainState }: {
|
|
51
52
|
canvas: HTMLCanvasElement | OffscreenCanvas | null;
|
|
52
53
|
src: string;
|
|
53
54
|
logLevel: LogLevel;
|
|
@@ -56,6 +57,7 @@ export declare class MediaPlayer {
|
|
|
56
57
|
trimBefore: number | undefined;
|
|
57
58
|
trimAfter: number | undefined;
|
|
58
59
|
playbackRate: number;
|
|
60
|
+
toneFrequency: number;
|
|
59
61
|
globalPlaybackRate: number;
|
|
60
62
|
audioStreamIndex: number | null;
|
|
61
63
|
fps: number;
|
|
@@ -98,6 +100,7 @@ export declare class MediaPlayer {
|
|
|
98
100
|
setTrimAfter(trimAfter: number | undefined, unloopedTimeInSeconds: number): Promise<void>;
|
|
99
101
|
setDebugOverlay(debugOverlay: boolean): void;
|
|
100
102
|
setPlaybackRate(rate: number, unloopedTimeInSeconds: number): Promise<void>;
|
|
103
|
+
setToneFrequency(toneFrequency: number, unloopedTimeInSeconds: number): Promise<void>;
|
|
101
104
|
setGlobalPlaybackRate(rate: number, unloopedTimeInSeconds: number): Promise<void>;
|
|
102
105
|
setFps(fps: number, unloopedTimeInSeconds: number): Promise<void>;
|
|
103
106
|
setIsPremounting(isPremounting: boolean): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import type { MediaPlayer } from './media-player';
|
|
3
|
-
export declare const useCommonEffects: ({ mediaPlayerRef, mediaPlayerReady, currentTimeRef, playing, isPlayerBuffering, frame, trimBefore, trimAfter, effectiveMuted, userPreferredVolume, playbackRate, globalPlaybackRate, fps, sequenceOffset, loop, durationInFrames, isPremounting, isPostmounting, currentTime, logLevel, label, }: {
|
|
3
|
+
export declare const useCommonEffects: ({ mediaPlayerRef, mediaPlayerReady, currentTimeRef, playing, isPlayerBuffering, frame, trimBefore, trimAfter, effectiveMuted, userPreferredVolume, playbackRate, toneFrequency, globalPlaybackRate, fps, sequenceOffset, loop, durationInFrames, isPremounting, isPostmounting, currentTime, logLevel, label, }: {
|
|
4
4
|
readonly mediaPlayerRef: React.RefObject<MediaPlayer | null>;
|
|
5
5
|
readonly mediaPlayerReady: boolean;
|
|
6
6
|
readonly currentTimeRef: React.RefObject<number>;
|
|
@@ -12,6 +12,7 @@ export declare const useCommonEffects: ({ mediaPlayerRef, mediaPlayerReady, curr
|
|
|
12
12
|
readonly effectiveMuted: boolean;
|
|
13
13
|
readonly userPreferredVolume: number;
|
|
14
14
|
readonly playbackRate: number;
|
|
15
|
+
readonly toneFrequency: number;
|
|
15
16
|
readonly globalPlaybackRate: number;
|
|
16
17
|
readonly fps: number;
|
|
17
18
|
readonly sequenceOffset: number;
|
|
@@ -7,6 +7,7 @@ type VideoForPreviewProps = NativeVideoProps & {
|
|
|
7
7
|
readonly src: string;
|
|
8
8
|
readonly style: React.CSSProperties | undefined;
|
|
9
9
|
readonly playbackRate: number;
|
|
10
|
+
readonly toneFrequency: number;
|
|
10
11
|
readonly logLevel: LogLevel;
|
|
11
12
|
readonly className: string | undefined;
|
|
12
13
|
readonly muted: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/media",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.521",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/esm/index.mjs",
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
"make": "tsgo && bun --env-file=../.env.bundle bundle.ts"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"mediabunny": "1.55.
|
|
26
|
-
"remotion": "4.0.
|
|
27
|
-
"zod": "4.4
|
|
25
|
+
"mediabunny": "1.55.5",
|
|
26
|
+
"remotion": "4.0.521",
|
|
27
|
+
"zod": "4.5.4"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": ">=16.8.0",
|
|
31
31
|
"react-dom": ">=16.8.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
35
|
-
"@remotion/player": "4.0.
|
|
34
|
+
"@remotion/eslint-config-internal": "4.0.521",
|
|
35
|
+
"@remotion/player": "4.0.521",
|
|
36
36
|
"@vitest/browser-webdriverio": "4.0.9",
|
|
37
37
|
"eslint": "9.19.0",
|
|
38
38
|
"react": "19.2.3",
|