@remotion/media 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/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 +539 -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 +5 -5
|
@@ -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,
|
|
@@ -5225,6 +5719,16 @@ var audioSchema = {
|
|
|
5225
5719
|
hiddenFromList: false,
|
|
5226
5720
|
keyframable: false
|
|
5227
5721
|
},
|
|
5722
|
+
toneFrequency: {
|
|
5723
|
+
type: "number",
|
|
5724
|
+
min: 0.01,
|
|
5725
|
+
max: 2,
|
|
5726
|
+
step: 0.01,
|
|
5727
|
+
default: 1,
|
|
5728
|
+
description: "Pitch",
|
|
5729
|
+
hiddenFromList: false,
|
|
5730
|
+
keyframable: false
|
|
5731
|
+
},
|
|
5228
5732
|
muted: { type: "boolean", default: false, description: "Muted" },
|
|
5229
5733
|
loop: { type: "boolean", default: false, description: "Loop" }
|
|
5230
5734
|
};
|
|
@@ -5306,6 +5810,10 @@ var AudioInner = (props) => {
|
|
|
5306
5810
|
throw new TypeError(`The \`<Audio>\` tag requires a string for \`src\`, but got ${JSON.stringify(props.src)} instead.`);
|
|
5307
5811
|
}
|
|
5308
5812
|
validateMediaProps({ playbackRate: props.playbackRate, volume: props.volume }, "Audio");
|
|
5813
|
+
validateToneFrequency({
|
|
5814
|
+
toneFrequency: props.toneFrequency,
|
|
5815
|
+
component: "Audio"
|
|
5816
|
+
});
|
|
5309
5817
|
if (sequenceDurationInFrames === 0) {
|
|
5310
5818
|
return null;
|
|
5311
5819
|
}
|
|
@@ -5459,7 +5967,6 @@ var warnAboutObjectFitInStyleOrClassName = ({
|
|
|
5459
5967
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
5460
5968
|
var {
|
|
5461
5969
|
useUnsafeVideoConfig: useUnsafeVideoConfig2,
|
|
5462
|
-
Timeline: Timeline2,
|
|
5463
5970
|
SharedAudioContext: SharedAudioContext2,
|
|
5464
5971
|
usePlayerMutedState: usePlayerMutedState2,
|
|
5465
5972
|
useMediaVolumeState: useMediaVolumeState2,
|
|
@@ -5468,12 +5975,15 @@ var {
|
|
|
5468
5975
|
warnAboutTooHighVolume: warnAboutTooHighVolume2,
|
|
5469
5976
|
usePreload: usePreload2,
|
|
5470
5977
|
SequenceContext: SequenceContext2,
|
|
5471
|
-
useEffectChainState
|
|
5978
|
+
useEffectChainState,
|
|
5979
|
+
usePlaying: usePlaying2,
|
|
5980
|
+
useBuffering: useBuffering2
|
|
5472
5981
|
} = Internals22;
|
|
5473
5982
|
var VideoForPreviewAssertedShowing = ({
|
|
5474
5983
|
src: unpreloadedSrc,
|
|
5475
5984
|
style,
|
|
5476
5985
|
playbackRate,
|
|
5986
|
+
toneFrequency,
|
|
5477
5987
|
logLevel,
|
|
5478
5988
|
className,
|
|
5479
5989
|
muted,
|
|
@@ -5511,7 +6021,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5511
6021
|
const [initialRequestInit] = useState4(requestInit);
|
|
5512
6022
|
const [mediaPlayerReady, setMediaPlayerReady] = useState4(false);
|
|
5513
6023
|
const [shouldFallbackToNativeVideo, setShouldFallbackToNativeVideo] = useState4(false);
|
|
5514
|
-
const
|
|
6024
|
+
const playing = usePlaying2();
|
|
5515
6025
|
const { playbackRate: globalPlaybackRate } = Internals22.usePlaybackRate();
|
|
5516
6026
|
const sharedAudioContext = useContext4(SharedAudioContext2);
|
|
5517
6027
|
const buffer = useBufferState2();
|
|
@@ -5549,17 +6059,14 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5549
6059
|
const currentTimeRef = useRef2(currentTime);
|
|
5550
6060
|
currentTimeRef.current = currentTime;
|
|
5551
6061
|
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
6062
|
const effectiveMuted = muted || playerMuted || userPreferredVolume <= 0;
|
|
5557
|
-
const isPlayerBuffering =
|
|
6063
|
+
const isPlayerBuffering = useBuffering2();
|
|
5558
6064
|
const initialPlaying = useRef2(playing && !isPlayerBuffering);
|
|
5559
6065
|
const initialIsPremounting = useRef2(isPremounting);
|
|
5560
6066
|
const initialIsPostmounting = useRef2(isPostmounting);
|
|
5561
6067
|
const initialGlobalPlaybackRate = useRef2(globalPlaybackRate);
|
|
5562
6068
|
const initialPlaybackRate = useRef2(playbackRate);
|
|
6069
|
+
const initialToneFrequency = useRef2(toneFrequency);
|
|
5563
6070
|
const initialMuted = useRef2(effectiveMuted);
|
|
5564
6071
|
const initialVolume = useRef2(userPreferredVolume);
|
|
5565
6072
|
const initialSequenceDuration = useRef2(videoConfig.durationInFrames);
|
|
@@ -5620,6 +6127,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5620
6127
|
trimBefore: initialTrimBeforeRef.current,
|
|
5621
6128
|
fps: videoConfig.fps,
|
|
5622
6129
|
playbackRate: initialPlaybackRate.current,
|
|
6130
|
+
toneFrequency: initialToneFrequency.current,
|
|
5623
6131
|
audioStreamIndex,
|
|
5624
6132
|
debugOverlay,
|
|
5625
6133
|
bufferState: buffer,
|
|
@@ -5751,6 +6259,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5751
6259
|
effectiveMuted,
|
|
5752
6260
|
userPreferredVolume,
|
|
5753
6261
|
playbackRate,
|
|
6262
|
+
toneFrequency,
|
|
5754
6263
|
globalPlaybackRate,
|
|
5755
6264
|
fps: videoConfig.fps,
|
|
5756
6265
|
sequenceOffset,
|
|
@@ -5801,6 +6310,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
5801
6310
|
trimAfter,
|
|
5802
6311
|
trimBefore,
|
|
5803
6312
|
playbackRate,
|
|
6313
|
+
toneFrequency,
|
|
5804
6314
|
loopVolumeCurveBehavior,
|
|
5805
6315
|
name: "<Html5Video> (fallback)",
|
|
5806
6316
|
loop,
|
|
@@ -6250,6 +6760,16 @@ var videoSchema = {
|
|
|
6250
6760
|
hiddenFromList: false,
|
|
6251
6761
|
keyframable: false
|
|
6252
6762
|
},
|
|
6763
|
+
toneFrequency: {
|
|
6764
|
+
type: "number",
|
|
6765
|
+
min: 0.01,
|
|
6766
|
+
max: 2,
|
|
6767
|
+
step: 0.01,
|
|
6768
|
+
default: 1,
|
|
6769
|
+
description: "Pitch",
|
|
6770
|
+
hiddenFromList: false,
|
|
6771
|
+
keyframable: false
|
|
6772
|
+
},
|
|
6253
6773
|
muted: { type: "boolean", default: false, description: "Muted" },
|
|
6254
6774
|
loop: { type: "boolean", default: false, description: "Loop" },
|
|
6255
6775
|
...Internals24.transformSchema,
|
|
@@ -6309,6 +6829,7 @@ var InnerVideo = ({
|
|
|
6309
6829
|
trimAfter
|
|
6310
6830
|
});
|
|
6311
6831
|
validateMediaProps2({ playbackRate, volume }, "Video");
|
|
6832
|
+
validateToneFrequency({ toneFrequency, component: "Video" });
|
|
6312
6833
|
if (environment.isRendering) {
|
|
6313
6834
|
return /* @__PURE__ */ jsx6(VideoForRendering, {
|
|
6314
6835
|
...props,
|
|
@@ -6350,6 +6871,7 @@ var InnerVideo = ({
|
|
|
6350
6871
|
muted,
|
|
6351
6872
|
onVideoFrame,
|
|
6352
6873
|
playbackRate,
|
|
6874
|
+
toneFrequency: toneFrequency ?? 1,
|
|
6353
6875
|
src,
|
|
6354
6876
|
style,
|
|
6355
6877
|
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.520",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/esm/index.mjs",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"make": "tsgo && bun --env-file=../.env.bundle bundle.ts"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"mediabunny": "1.55.
|
|
26
|
-
"remotion": "4.0.
|
|
25
|
+
"mediabunny": "1.55.5",
|
|
26
|
+
"remotion": "4.0.520",
|
|
27
27
|
"zod": "4.4.3"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
@@ -31,8 +31,8 @@
|
|
|
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.520",
|
|
35
|
+
"@remotion/player": "4.0.520",
|
|
36
36
|
"@vitest/browser-webdriverio": "4.0.9",
|
|
37
37
|
"eslint": "9.19.0",
|
|
38
38
|
"react": "19.2.3",
|