remotion 4.0.522 → 4.0.524
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/cjs/CompositionManagerContext.d.ts +1 -0
- package/dist/cjs/audio/shared-audio-tags.d.ts +1 -0
- package/dist/cjs/audio/shared-audio-tags.js +2 -6
- package/dist/cjs/audio/streaming-pitch-shifter.d.ts +18 -0
- package/dist/cjs/audio/streaming-pitch-shifter.js +349 -0
- package/dist/cjs/internals.d.ts +1 -0
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/video/video-fragment.js +10 -2
- package/dist/cjs/with-interactivity-schema.d.ts +2 -1
- package/dist/cjs/with-interactivity-schema.js +1 -1
- package/dist/esm/index.mjs +8 -10
- package/dist/esm/version.mjs +1 -1
- package/package.json +2 -2
|
@@ -6,6 +6,7 @@ import type { VideoConfig } from './video-config.js';
|
|
|
6
6
|
export type BaseMetadata = Pick<VideoConfig, 'durationInFrames' | 'fps' | 'props' | 'height' | 'width' | 'defaultCodec' | 'defaultOutName' | 'defaultVideoImageFormat' | 'defaultPixelFormat' | 'defaultProResProfile' | 'defaultSampleRate'>;
|
|
7
7
|
export type AssetPreviewMetadata = BaseMetadata & {
|
|
8
8
|
asset: string;
|
|
9
|
+
src: string;
|
|
9
10
|
};
|
|
10
11
|
export declare const getAssetPreviewCompositionId: (asset: string) => string;
|
|
11
12
|
export type CanvasContent = {
|
|
@@ -91,12 +91,6 @@ const shouldSaveForLater = (state) => {
|
|
|
91
91
|
const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled, previewSampleRate, _experimentalKeepAudioContextAlive, }) => {
|
|
92
92
|
const logLevel = (0, log_level_context_js_1.useLogLevel)();
|
|
93
93
|
const sampleRate = previewSampleRate !== null && previewSampleRate !== void 0 ? previewSampleRate : 48000;
|
|
94
|
-
(0, react_1.useEffect)(() => {
|
|
95
|
-
if (typeof window === 'undefined') {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
window.remotion_sampleRate = sampleRate;
|
|
99
|
-
}, [sampleRate]);
|
|
100
94
|
const ctxAndGain = (0, use_audio_context_js_1.useSingletonAudioContext)({
|
|
101
95
|
logLevel,
|
|
102
96
|
latencyHint: audioLatencyHint,
|
|
@@ -323,6 +317,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
323
317
|
const audioContextValue = (0, react_1.useMemo)(() => {
|
|
324
318
|
var _a, _b;
|
|
325
319
|
return {
|
|
320
|
+
sampleRate,
|
|
326
321
|
audioContext: (_a = ctxAndGain === null || ctxAndGain === void 0 ? void 0 : ctxAndGain.audioContext) !== null && _a !== void 0 ? _a : null,
|
|
327
322
|
getAudioContextState: () => {
|
|
328
323
|
var _a;
|
|
@@ -340,6 +335,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
340
335
|
_experimentalKeepAudioContextAlive,
|
|
341
336
|
};
|
|
342
337
|
}, [
|
|
338
|
+
sampleRate,
|
|
343
339
|
ctxAndGain,
|
|
344
340
|
audioSyncAnchor,
|
|
345
341
|
audioSyncAnchorEmitter,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type PlanarAudio = Float32Array[];
|
|
2
|
+
export declare class StreamingPitchShifter {
|
|
3
|
+
private readonly numberOfChannels;
|
|
4
|
+
private readonly stretcher;
|
|
5
|
+
private readonly resampler;
|
|
6
|
+
private readonly outputQueue;
|
|
7
|
+
private totalInputFrames;
|
|
8
|
+
private totalOutputFrames;
|
|
9
|
+
constructor({ numberOfChannels, sampleRate, toneFrequency }: {
|
|
10
|
+
numberOfChannels: number;
|
|
11
|
+
sampleRate: number;
|
|
12
|
+
toneFrequency: number;
|
|
13
|
+
});
|
|
14
|
+
append(audio: PlanarAudio): PlanarAudio;
|
|
15
|
+
private takeAvailableOutput;
|
|
16
|
+
finalize(): Float32Array<ArrayBuffer>[];
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StreamingPitchShifter = void 0;
|
|
4
|
+
// The pitch-shifting algorithm is adapted from Vanilagy's WSOLA audio
|
|
5
|
+
// stretcher: https://gist.github.com/Vanilagy/05f7901f4c4398356657e3a86c7aee05
|
|
6
|
+
const REFERENCE_SAMPLE_RATE = 48000;
|
|
7
|
+
const REFERENCE_HOP_SIZE = 512;
|
|
8
|
+
const makePlanarAudio = (numberOfChannels, length) => {
|
|
9
|
+
return new Array(numberOfChannels)
|
|
10
|
+
.fill(null)
|
|
11
|
+
.map(() => new Float32Array(length));
|
|
12
|
+
};
|
|
13
|
+
const ensurePlanarCapacity = ({ buffers, requiredLength, }) => {
|
|
14
|
+
if (buffers[0].length >= requiredLength) {
|
|
15
|
+
return buffers;
|
|
16
|
+
}
|
|
17
|
+
let newLength = buffers[0].length;
|
|
18
|
+
while (newLength < requiredLength) {
|
|
19
|
+
newLength *= 2;
|
|
20
|
+
}
|
|
21
|
+
return buffers.map((buffer) => {
|
|
22
|
+
const expanded = new Float32Array(newLength);
|
|
23
|
+
expanded.set(buffer);
|
|
24
|
+
return expanded;
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
class PlanarAudioQueue {
|
|
28
|
+
chunks = [];
|
|
29
|
+
length = 0;
|
|
30
|
+
push(audio) {
|
|
31
|
+
if (audio[0].length === 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this.chunks.push(audio);
|
|
35
|
+
this.length += audio[0].length;
|
|
36
|
+
}
|
|
37
|
+
take(numberOfFrames, numberOfChannels) {
|
|
38
|
+
const framesToTake = Math.min(numberOfFrames, this.length);
|
|
39
|
+
const result = makePlanarAudio(numberOfChannels, framesToTake);
|
|
40
|
+
let written = 0;
|
|
41
|
+
while (written < framesToTake) {
|
|
42
|
+
const first = this.chunks[0];
|
|
43
|
+
const available = first[0].length;
|
|
44
|
+
const count = Math.min(available, framesToTake - written);
|
|
45
|
+
for (let channel = 0; channel < numberOfChannels; channel++) {
|
|
46
|
+
result[channel].set(first[channel].subarray(0, count), written);
|
|
47
|
+
}
|
|
48
|
+
if (count === available) {
|
|
49
|
+
this.chunks.shift();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.chunks[0] = first.map((channel) => channel.subarray(count));
|
|
53
|
+
}
|
|
54
|
+
written += count;
|
|
55
|
+
this.length -= count;
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
getLength() {
|
|
60
|
+
return this.length;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// A streaming implementation of Waveform Similarity Overlap-Add. It changes
|
|
64
|
+
// duration while retaining pitch. Pitch shifting is achieved by following this
|
|
65
|
+
// stage with a resampler that restores the original duration.
|
|
66
|
+
class StreamingTimeStretcher {
|
|
67
|
+
numberOfChannels;
|
|
68
|
+
factor;
|
|
69
|
+
hopSize;
|
|
70
|
+
windowSize;
|
|
71
|
+
searchRadius;
|
|
72
|
+
analysisHop;
|
|
73
|
+
input;
|
|
74
|
+
inputLength = 0;
|
|
75
|
+
output;
|
|
76
|
+
outputLength = 0;
|
|
77
|
+
analysisPosition = 0;
|
|
78
|
+
synthesisPosition = 0;
|
|
79
|
+
initialized = false;
|
|
80
|
+
finalized = false;
|
|
81
|
+
totalInputFrames = 0;
|
|
82
|
+
totalOutputFrames = 0;
|
|
83
|
+
constructor({ numberOfChannels, sampleRate, factor, }) {
|
|
84
|
+
this.numberOfChannels = numberOfChannels;
|
|
85
|
+
this.factor = factor;
|
|
86
|
+
this.hopSize = Math.max(32, Math.round((REFERENCE_HOP_SIZE * sampleRate) / REFERENCE_SAMPLE_RATE));
|
|
87
|
+
this.windowSize = this.hopSize * 2;
|
|
88
|
+
this.searchRadius = this.hopSize;
|
|
89
|
+
this.analysisHop = this.hopSize / factor;
|
|
90
|
+
this.input = makePlanarAudio(numberOfChannels, 65536);
|
|
91
|
+
this.output = makePlanarAudio(numberOfChannels, 65536);
|
|
92
|
+
}
|
|
93
|
+
append(audio) {
|
|
94
|
+
if (this.finalized) {
|
|
95
|
+
throw new Error('Cannot append audio after the time stretcher was finalized.');
|
|
96
|
+
}
|
|
97
|
+
const { length } = audio[0];
|
|
98
|
+
this.input = ensurePlanarCapacity({
|
|
99
|
+
buffers: this.input,
|
|
100
|
+
requiredLength: this.inputLength + length,
|
|
101
|
+
});
|
|
102
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
103
|
+
this.input[channel].set(audio[channel], this.inputLength);
|
|
104
|
+
}
|
|
105
|
+
this.inputLength += length;
|
|
106
|
+
this.totalInputFrames += length;
|
|
107
|
+
this.process();
|
|
108
|
+
return this.drainFinalizedOutput();
|
|
109
|
+
}
|
|
110
|
+
findBestAnalysisPosition({ expectedPosition, nextSynthesisPosition, }) {
|
|
111
|
+
const minimum = Math.max(0, Math.floor(expectedPosition - this.searchRadius));
|
|
112
|
+
const maximum = Math.min(this.inputLength - this.windowSize, Math.ceil(expectedPosition + this.searchRadius));
|
|
113
|
+
let bestPosition = minimum;
|
|
114
|
+
let bestCorrelation = -Infinity;
|
|
115
|
+
for (let candidate = minimum; candidate <= maximum; candidate += 4) {
|
|
116
|
+
let dotProduct = 0;
|
|
117
|
+
let previousEnergy = 0;
|
|
118
|
+
let candidateEnergy = 0;
|
|
119
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
120
|
+
const previous = this.output[channel];
|
|
121
|
+
const incoming = this.input[channel];
|
|
122
|
+
for (let frame = 0; frame < this.hopSize; frame += 2) {
|
|
123
|
+
const previousValue = previous[nextSynthesisPosition + frame];
|
|
124
|
+
const candidateValue = incoming[candidate + frame];
|
|
125
|
+
dotProduct += previousValue * candidateValue;
|
|
126
|
+
previousEnergy += previousValue * previousValue;
|
|
127
|
+
candidateEnergy += candidateValue * candidateValue;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const correlation = dotProduct /
|
|
131
|
+
(Math.sqrt(previousEnergy * candidateEnergy) || Number.EPSILON);
|
|
132
|
+
if (correlation > bestCorrelation) {
|
|
133
|
+
bestCorrelation = correlation;
|
|
134
|
+
bestPosition = candidate;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const fineMinimum = Math.max(minimum, bestPosition - 4);
|
|
138
|
+
const fineMaximum = Math.min(maximum, bestPosition + 4);
|
|
139
|
+
for (let candidate = fineMinimum; candidate <= fineMaximum; candidate++) {
|
|
140
|
+
let dotProduct = 0;
|
|
141
|
+
let previousEnergy = 0;
|
|
142
|
+
let candidateEnergy = 0;
|
|
143
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
144
|
+
const previous = this.output[channel];
|
|
145
|
+
const incoming = this.input[channel];
|
|
146
|
+
for (let frame = 0; frame < this.hopSize; frame++) {
|
|
147
|
+
const previousValue = previous[nextSynthesisPosition + frame];
|
|
148
|
+
const candidateValue = incoming[candidate + frame];
|
|
149
|
+
dotProduct += previousValue * candidateValue;
|
|
150
|
+
previousEnergy += previousValue * previousValue;
|
|
151
|
+
candidateEnergy += candidateValue * candidateValue;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const correlation = dotProduct /
|
|
155
|
+
(Math.sqrt(previousEnergy * candidateEnergy) || Number.EPSILON);
|
|
156
|
+
if (correlation > bestCorrelation) {
|
|
157
|
+
bestCorrelation = correlation;
|
|
158
|
+
bestPosition = candidate;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return bestPosition;
|
|
162
|
+
}
|
|
163
|
+
process() {
|
|
164
|
+
if (!this.initialized) {
|
|
165
|
+
if (this.inputLength < this.windowSize + this.searchRadius) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
169
|
+
this.output[channel].set(this.input[channel].subarray(0, this.windowSize));
|
|
170
|
+
}
|
|
171
|
+
this.outputLength = this.windowSize;
|
|
172
|
+
this.initialized = true;
|
|
173
|
+
}
|
|
174
|
+
while (true) {
|
|
175
|
+
const expectedPosition = this.analysisPosition + this.analysisHop;
|
|
176
|
+
if (expectedPosition + this.searchRadius + this.windowSize >
|
|
177
|
+
this.inputLength) {
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
const nextSynthesisPosition = this.synthesisPosition + this.hopSize;
|
|
181
|
+
this.output = ensurePlanarCapacity({
|
|
182
|
+
buffers: this.output,
|
|
183
|
+
requiredLength: nextSynthesisPosition + this.windowSize,
|
|
184
|
+
});
|
|
185
|
+
const bestPosition = this.findBestAnalysisPosition({
|
|
186
|
+
expectedPosition,
|
|
187
|
+
nextSynthesisPosition,
|
|
188
|
+
});
|
|
189
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
190
|
+
for (let frame = 0; frame < this.hopSize; frame++) {
|
|
191
|
+
const fadeIn = 0.5 - 0.5 * Math.cos((Math.PI * (frame + 1)) / (this.hopSize + 1));
|
|
192
|
+
const outputIndex = nextSynthesisPosition + frame;
|
|
193
|
+
this.output[channel][outputIndex] =
|
|
194
|
+
this.output[channel][outputIndex] * (1 - fadeIn) +
|
|
195
|
+
this.input[channel][bestPosition + frame] * fadeIn;
|
|
196
|
+
}
|
|
197
|
+
this.output[channel].set(this.input[channel].subarray(bestPosition + this.hopSize, bestPosition + this.windowSize), nextSynthesisPosition + this.hopSize);
|
|
198
|
+
}
|
|
199
|
+
// Keep the analysis clock independent from the correlation correction.
|
|
200
|
+
// Periodic signals can have equally good matches at an earlier period; if
|
|
201
|
+
// the correction became the next clock position, the iterator could stop
|
|
202
|
+
// making forward progress.
|
|
203
|
+
this.analysisPosition = expectedPosition;
|
|
204
|
+
this.synthesisPosition = nextSynthesisPosition;
|
|
205
|
+
this.outputLength = nextSynthesisPosition + this.windowSize;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
drainFinalizedOutput() {
|
|
209
|
+
if (!this.initialized) {
|
|
210
|
+
return makePlanarAudio(this.numberOfChannels, 0);
|
|
211
|
+
}
|
|
212
|
+
const finalizedLength = Math.max(0, this.synthesisPosition + this.hopSize);
|
|
213
|
+
const result = this.output.map((channel) => channel.slice(0, finalizedLength));
|
|
214
|
+
this.totalOutputFrames += finalizedLength;
|
|
215
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
216
|
+
this.output[channel].copyWithin(0, finalizedLength, this.outputLength);
|
|
217
|
+
}
|
|
218
|
+
this.outputLength -= finalizedLength;
|
|
219
|
+
this.synthesisPosition -= finalizedLength;
|
|
220
|
+
const inputFramesToDiscard = Math.max(0, Math.floor(this.analysisPosition) - this.searchRadius);
|
|
221
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
222
|
+
this.input[channel].copyWithin(0, inputFramesToDiscard, this.inputLength);
|
|
223
|
+
}
|
|
224
|
+
this.inputLength -= inputFramesToDiscard;
|
|
225
|
+
this.analysisPosition -= inputFramesToDiscard;
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
finalize() {
|
|
229
|
+
if (this.finalized) {
|
|
230
|
+
throw new Error('The time stretcher has already been finalized.');
|
|
231
|
+
}
|
|
232
|
+
this.finalized = true;
|
|
233
|
+
const targetLength = Math.round(this.totalInputFrames * this.factor);
|
|
234
|
+
const padding = makePlanarAudio(this.numberOfChannels, this.windowSize + this.searchRadius * 2);
|
|
235
|
+
this.input = ensurePlanarCapacity({
|
|
236
|
+
buffers: this.input,
|
|
237
|
+
requiredLength: this.inputLength + padding[0].length,
|
|
238
|
+
});
|
|
239
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
240
|
+
this.input[channel].set(padding[channel], this.inputLength);
|
|
241
|
+
}
|
|
242
|
+
this.inputLength += padding[0].length;
|
|
243
|
+
this.process();
|
|
244
|
+
const finalized = this.drainFinalizedOutput();
|
|
245
|
+
const remaining = Math.max(0, targetLength - this.totalOutputFrames + finalized[0].length);
|
|
246
|
+
if (finalized[0].length >= remaining) {
|
|
247
|
+
return finalized.map((channel) => channel.slice(0, remaining));
|
|
248
|
+
}
|
|
249
|
+
const result = makePlanarAudio(this.numberOfChannels, remaining);
|
|
250
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
251
|
+
result[channel].set(finalized[channel]);
|
|
252
|
+
}
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
class StreamingLinearResampler {
|
|
257
|
+
numberOfChannels;
|
|
258
|
+
step;
|
|
259
|
+
input;
|
|
260
|
+
inputLength = 0;
|
|
261
|
+
position = 0;
|
|
262
|
+
constructor({ numberOfChannels, step, }) {
|
|
263
|
+
this.numberOfChannels = numberOfChannels;
|
|
264
|
+
this.step = step;
|
|
265
|
+
this.input = makePlanarAudio(numberOfChannels, 65536);
|
|
266
|
+
}
|
|
267
|
+
append(audio) {
|
|
268
|
+
this.input = ensurePlanarCapacity({
|
|
269
|
+
buffers: this.input,
|
|
270
|
+
requiredLength: this.inputLength + audio[0].length,
|
|
271
|
+
});
|
|
272
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
273
|
+
this.input[channel].set(audio[channel], this.inputLength);
|
|
274
|
+
}
|
|
275
|
+
this.inputLength += audio[0].length;
|
|
276
|
+
return this.process(false);
|
|
277
|
+
}
|
|
278
|
+
process(finalizing) {
|
|
279
|
+
const outputLength = Math.max(0, Math.floor((this.inputLength - (finalizing ? 0 : 1) - this.position) / this.step) + 1);
|
|
280
|
+
const result = makePlanarAudio(this.numberOfChannels, outputLength);
|
|
281
|
+
for (let outputFrame = 0; outputFrame < outputLength; outputFrame++) {
|
|
282
|
+
const leftIndex = Math.floor(this.position);
|
|
283
|
+
const rightIndex = Math.min(leftIndex + 1, this.inputLength - 1);
|
|
284
|
+
const fraction = this.position - leftIndex;
|
|
285
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
286
|
+
const left = this.input[channel][leftIndex];
|
|
287
|
+
const right = this.input[channel][rightIndex];
|
|
288
|
+
result[channel][outputFrame] = left + (right - left) * fraction;
|
|
289
|
+
}
|
|
290
|
+
this.position += this.step;
|
|
291
|
+
}
|
|
292
|
+
const discard = Math.min(Math.floor(this.position), this.inputLength);
|
|
293
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
294
|
+
this.input[channel].copyWithin(0, discard, this.inputLength);
|
|
295
|
+
}
|
|
296
|
+
this.inputLength -= discard;
|
|
297
|
+
this.position -= discard;
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
finalize() {
|
|
301
|
+
return this.process(true);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
class StreamingPitchShifter {
|
|
305
|
+
numberOfChannels;
|
|
306
|
+
stretcher;
|
|
307
|
+
resampler;
|
|
308
|
+
outputQueue = new PlanarAudioQueue();
|
|
309
|
+
totalInputFrames = 0;
|
|
310
|
+
totalOutputFrames = 0;
|
|
311
|
+
constructor({ numberOfChannels, sampleRate, toneFrequency, }) {
|
|
312
|
+
this.numberOfChannels = numberOfChannels;
|
|
313
|
+
this.stretcher = new StreamingTimeStretcher({
|
|
314
|
+
numberOfChannels,
|
|
315
|
+
sampleRate,
|
|
316
|
+
factor: toneFrequency,
|
|
317
|
+
});
|
|
318
|
+
this.resampler = new StreamingLinearResampler({
|
|
319
|
+
numberOfChannels,
|
|
320
|
+
step: toneFrequency,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
append(audio) {
|
|
324
|
+
this.totalInputFrames += audio[0].length;
|
|
325
|
+
const stretched = this.stretcher.append(audio);
|
|
326
|
+
this.outputQueue.push(this.resampler.append(stretched));
|
|
327
|
+
return this.takeAvailableOutput();
|
|
328
|
+
}
|
|
329
|
+
takeAvailableOutput() {
|
|
330
|
+
const availableInputFrames = this.totalInputFrames - this.totalOutputFrames;
|
|
331
|
+
const framesToTake = Math.min(availableInputFrames, this.outputQueue.getLength());
|
|
332
|
+
const result = this.outputQueue.take(framesToTake, this.numberOfChannels);
|
|
333
|
+
this.totalOutputFrames += framesToTake;
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
finalize() {
|
|
337
|
+
this.outputQueue.push(this.resampler.append(this.stretcher.finalize()));
|
|
338
|
+
this.outputQueue.push(this.resampler.finalize());
|
|
339
|
+
const remaining = this.totalInputFrames - this.totalOutputFrames;
|
|
340
|
+
const available = this.outputQueue.take(Math.min(remaining, this.outputQueue.getLength()), this.numberOfChannels);
|
|
341
|
+
const result = makePlanarAudio(this.numberOfChannels, remaining);
|
|
342
|
+
for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
|
343
|
+
result[channel].set(available[channel]);
|
|
344
|
+
}
|
|
345
|
+
this.totalOutputFrames += remaining;
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
exports.StreamingPitchShifter = StreamingPitchShifter;
|
package/dist/cjs/internals.d.ts
CHANGED
|
@@ -816,6 +816,7 @@ export declare const Internals: {
|
|
|
816
816
|
readonly SetMediaVolumeContext: import("react").Context<SetMediaVolumeContextValue>;
|
|
817
817
|
readonly getRemotionEnvironment: () => RemotionEnvironment;
|
|
818
818
|
readonly SharedAudioContext: import("react").Context<{
|
|
819
|
+
sampleRate: number;
|
|
819
820
|
audioContext: AudioContext | null;
|
|
820
821
|
getAudioContextState: () => RemotionAudioContextState | null;
|
|
821
822
|
gainNode: GainNode | null;
|
package/dist/cjs/version.d.ts
CHANGED
package/dist/cjs/version.js
CHANGED
|
@@ -21,8 +21,16 @@ const isIosSafari = () => {
|
|
|
21
21
|
if (typeof window === 'undefined') {
|
|
22
22
|
return false;
|
|
23
23
|
}
|
|
24
|
-
const
|
|
25
|
-
|
|
24
|
+
const { userAgent, platform, maxTouchPoints } = window.navigator;
|
|
25
|
+
const isIpadIPodIPhone = /iP(ad|od|hone)/i.test(userAgent);
|
|
26
|
+
// Since iPadOS 13, Safari on iPad defaults to "Request Desktop Website"
|
|
27
|
+
// and sends a macOS user agent ("Macintosh; Intel Mac OS X 10_15_7"),
|
|
28
|
+
// so the string above never matches. A Mac platform with a touch
|
|
29
|
+
// screen is the only remaining signal — real Macs report 0 touch
|
|
30
|
+
// points. Without this, iPads get the blob + media fragment
|
|
31
|
+
// combination that Mobile Safari refuses to play (#1655).
|
|
32
|
+
const isIpadOsDesktopMode = platform === 'MacIntel' && maxTouchPoints > 1;
|
|
33
|
+
return (isIpadIPodIPhone || isIpadOsDesktopMode) && (0, exports.isSafari)();
|
|
26
34
|
};
|
|
27
35
|
exports.isIosSafari = isIosSafari;
|
|
28
36
|
// https://github.com/remotion-dev/remotion/issues/1655
|
|
@@ -21,7 +21,8 @@ export type WithInteractivitySchemaOptions<S extends InteractivitySchema, Props
|
|
|
21
21
|
readonly controls: SequenceControls | undefined;
|
|
22
22
|
}>;
|
|
23
23
|
componentName: string;
|
|
24
|
-
|
|
24
|
+
/** @internal */
|
|
25
|
+
componentIdentity?: JsxComponentIdentity | null;
|
|
25
26
|
schema: S;
|
|
26
27
|
supportsEffects: boolean;
|
|
27
28
|
};
|
|
@@ -127,7 +127,7 @@ const DisableInteractivityProvider = ({ children }) => {
|
|
|
127
127
|
return react_1.default.createElement(DisableInteractivityContext.Provider, { value: true }, children);
|
|
128
128
|
};
|
|
129
129
|
exports.DisableInteractivityProvider = DisableInteractivityProvider;
|
|
130
|
-
const withInteractivitySchema = ({ Component, componentName, componentIdentity, schema, supportsEffects, }) => {
|
|
130
|
+
const withInteractivitySchema = ({ Component, componentName, componentIdentity = null, schema, supportsEffects, }) => {
|
|
131
131
|
// Schema is static for a component, so we move this outside
|
|
132
132
|
const schemaWithSequenceName = (0, interactivity_schema_js_1.extendSchemaWithSequenceName)(schema);
|
|
133
133
|
const flatSchema = (0, flatten_schema_js_1.getFlatSchemaWithAllKeys)(schemaWithSequenceName);
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1537,7 +1537,7 @@ var Composition = (props) => {
|
|
|
1537
1537
|
};
|
|
1538
1538
|
|
|
1539
1539
|
// src/version.ts
|
|
1540
|
-
var VERSION = "4.0.
|
|
1540
|
+
var VERSION = "4.0.524";
|
|
1541
1541
|
|
|
1542
1542
|
// src/multiple-versions-warning.ts
|
|
1543
1543
|
var checkMultipleRemotionVersions = () => {
|
|
@@ -5215,7 +5215,7 @@ var DisableInteractivityProvider = ({ children }) => {
|
|
|
5215
5215
|
var withInteractivitySchema = ({
|
|
5216
5216
|
Component,
|
|
5217
5217
|
componentName,
|
|
5218
|
-
componentIdentity,
|
|
5218
|
+
componentIdentity = null,
|
|
5219
5219
|
schema,
|
|
5220
5220
|
supportsEffects
|
|
5221
5221
|
}) => {
|
|
@@ -7812,12 +7812,6 @@ var SharedAudioContextProvider = ({
|
|
|
7812
7812
|
}) => {
|
|
7813
7813
|
const logLevel = useLogLevel();
|
|
7814
7814
|
const sampleRate = previewSampleRate ?? 48000;
|
|
7815
|
-
useEffect8(() => {
|
|
7816
|
-
if (typeof window === "undefined") {
|
|
7817
|
-
return;
|
|
7818
|
-
}
|
|
7819
|
-
window.remotion_sampleRate = sampleRate;
|
|
7820
|
-
}, [sampleRate]);
|
|
7821
7815
|
const ctxAndGain = useSingletonAudioContext({
|
|
7822
7816
|
logLevel,
|
|
7823
7817
|
latencyHint: audioLatencyHint,
|
|
@@ -8001,6 +7995,7 @@ var SharedAudioContextProvider = ({
|
|
|
8001
7995
|
}, [ctxAndGain, _experimentalKeepAudioContextAlive]);
|
|
8002
7996
|
const audioContextValue = useMemo21(() => {
|
|
8003
7997
|
return {
|
|
7998
|
+
sampleRate,
|
|
8004
7999
|
audioContext: ctxAndGain?.audioContext ?? null,
|
|
8005
8000
|
getAudioContextState: () => ctxAndGain?.getState() ?? null,
|
|
8006
8001
|
gainNode: ctxAndGain?.gainNode ?? null,
|
|
@@ -8015,6 +8010,7 @@ var SharedAudioContextProvider = ({
|
|
|
8015
8010
|
_experimentalKeepAudioContextAlive
|
|
8016
8011
|
};
|
|
8017
8012
|
}, [
|
|
8013
|
+
sampleRate,
|
|
8018
8014
|
ctxAndGain,
|
|
8019
8015
|
audioSyncAnchor,
|
|
8020
8016
|
audioSyncAnchorEmitter,
|
|
@@ -8299,8 +8295,10 @@ var isIosSafari = () => {
|
|
|
8299
8295
|
if (typeof window === "undefined") {
|
|
8300
8296
|
return false;
|
|
8301
8297
|
}
|
|
8302
|
-
const
|
|
8303
|
-
|
|
8298
|
+
const { userAgent, platform, maxTouchPoints } = window.navigator;
|
|
8299
|
+
const isIpadIPodIPhone = /iP(ad|od|hone)/i.test(userAgent);
|
|
8300
|
+
const isIpadOsDesktopMode = platform === "MacIntel" && maxTouchPoints > 1;
|
|
8301
|
+
return (isIpadIPodIPhone || isIpadOsDesktopMode) && isSafari();
|
|
8304
8302
|
};
|
|
8305
8303
|
var isIOSSafariAndBlob = (actualSrc) => {
|
|
8306
8304
|
return isIosSafari() && actualSrc.startsWith("blob:");
|
package/dist/esm/version.mjs
CHANGED
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/core"
|
|
4
4
|
},
|
|
5
5
|
"name": "remotion",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.524",
|
|
7
7
|
"description": "Make videos programmatically",
|
|
8
8
|
"main": "dist/cjs/index.js",
|
|
9
9
|
"types": "dist/cjs/index.d.ts",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"react-dom": "19.2.3",
|
|
36
36
|
"webpack": "5.105.0",
|
|
37
37
|
"zod": "4.5.4",
|
|
38
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
38
|
+
"@remotion/eslint-config-internal": "4.0.524",
|
|
39
39
|
"eslint": "9.19.0",
|
|
40
40
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|
|
41
41
|
},
|