@remotion/media 4.0.390 → 4.0.392
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/audio-for-preview.js +2 -0
- package/dist/audio/audio-preview-iterator.d.ts +3 -2
- package/dist/audio/audio-preview-iterator.js +2 -2
- package/dist/audio-for-rendering.d.ts +3 -0
- package/dist/audio-for-rendering.js +94 -0
- package/dist/audio-iterator-manager.d.ts +5 -1
- package/dist/audio-iterator-manager.js +21 -5
- package/dist/audio.d.ts +3 -0
- package/dist/audio.js +60 -0
- package/dist/audiodata-to-array.d.ts +0 -0
- package/dist/audiodata-to-array.js +1 -0
- package/dist/convert-audiodata/data-types.d.ts +1 -0
- package/dist/convert-audiodata/data-types.js +22 -0
- package/dist/convert-audiodata/is-planar-format.d.ts +1 -0
- package/dist/convert-audiodata/is-planar-format.js +3 -0
- package/dist/convert-audiodata/log-audiodata.d.ts +1 -0
- package/dist/convert-audiodata/log-audiodata.js +8 -0
- package/dist/convert-audiodata/trim-audiodata.d.ts +0 -0
- package/dist/convert-audiodata/trim-audiodata.js +1 -0
- package/dist/deserialized-audiodata.d.ts +15 -0
- package/dist/deserialized-audiodata.js +26 -0
- package/dist/esm/index.mjs +185 -27
- package/dist/extract-audio.d.ts +7 -0
- package/dist/extract-audio.js +98 -0
- package/dist/extract-frame-via-broadcast-channel.d.ts +15 -0
- package/dist/extract-frame-via-broadcast-channel.js +104 -0
- package/dist/extract-frame.d.ts +27 -0
- package/dist/extract-frame.js +21 -0
- package/dist/extrct-audio.d.ts +7 -0
- package/dist/extrct-audio.js +94 -0
- package/dist/get-frames-since-keyframe.d.ts +22 -0
- package/dist/get-frames-since-keyframe.js +41 -0
- package/dist/get-time-in-seconds.d.ts +8 -0
- package/dist/get-time-in-seconds.js +15 -0
- package/dist/is-network-error.d.ts +6 -0
- package/dist/is-network-error.js +17 -0
- package/dist/keyframe-bank.d.ts +25 -0
- package/dist/keyframe-bank.js +120 -0
- package/dist/keyframe-manager.d.ts +23 -0
- package/dist/keyframe-manager.js +170 -0
- package/dist/log.d.ts +10 -0
- package/dist/log.js +33 -0
- package/dist/media-player.d.ts +4 -1
- package/dist/media-player.js +56 -17
- package/dist/new-video-for-rendering.d.ts +3 -0
- package/dist/new-video-for-rendering.js +108 -0
- package/dist/new-video.d.ts +3 -0
- package/dist/new-video.js +37 -0
- package/dist/prewarm-iterator-for-looping.d.ts +17 -0
- package/dist/prewarm-iterator-for-looping.js +56 -0
- package/dist/props.d.ts +29 -0
- package/dist/props.js +1 -0
- package/dist/remember-actual-matroska-timestamps.d.ts +4 -0
- package/dist/remember-actual-matroska-timestamps.js +19 -0
- package/dist/serialize-videoframe.d.ts +0 -0
- package/dist/serialize-videoframe.js +1 -0
- package/dist/video/media-player.d.ts +62 -0
- package/dist/video/media-player.js +361 -0
- package/dist/video/new-video-for-preview.d.ts +10 -0
- package/dist/video/new-video-for-preview.js +108 -0
- package/dist/video/timeout-utils.d.ts +2 -0
- package/dist/video/timeout-utils.js +18 -0
- package/dist/video/video-for-preview.js +2 -0
- package/dist/video/video-preview-iterator.d.ts +3 -2
- package/dist/video/video-preview-iterator.js +2 -2
- package/dist/video-extraction/media-player.d.ts +64 -0
- package/dist/video-extraction/media-player.js +501 -0
- package/dist/video-extraction/new-video-for-preview.d.ts +10 -0
- package/dist/video-extraction/new-video-for-preview.js +114 -0
- package/dist/video-for-rendering.d.ts +3 -0
- package/dist/video-for-rendering.js +108 -0
- package/dist/video-iterator-manager.d.ts +4 -1
- package/dist/video-iterator-manager.js +27 -4
- package/dist/video.d.ts +3 -0
- package/dist/video.js +37 -0
- package/package.json +3 -3
|
@@ -69,6 +69,7 @@ const AudioForPreviewAssertedShowing = ({ src, playbackRate, logLevel, muted, vo
|
|
|
69
69
|
throw new Error('useMediaPlayback must be used inside a <BufferingContext>');
|
|
70
70
|
}
|
|
71
71
|
const isPlayerBuffering = Internals.useIsPlayerBuffering(bufferingContext);
|
|
72
|
+
const initialPlaying = useRef(playing && !isPlayerBuffering);
|
|
72
73
|
const initialIsPremounting = useRef(isPremounting);
|
|
73
74
|
const initialIsPostmounting = useRef(isPostmounting);
|
|
74
75
|
const initialGlobalPlaybackRate = useRef(globalPlaybackRate);
|
|
@@ -96,6 +97,7 @@ const AudioForPreviewAssertedShowing = ({ src, playbackRate, logLevel, muted, vo
|
|
|
96
97
|
isPremounting: initialIsPremounting.current,
|
|
97
98
|
globalPlaybackRate: initialGlobalPlaybackRate.current,
|
|
98
99
|
onVideoFrameCallback: null,
|
|
100
|
+
playing: initialPlaying.current,
|
|
99
101
|
});
|
|
100
102
|
mediaPlayerRef.current = player;
|
|
101
103
|
player
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { WrappedAudioBuffer } from 'mediabunny';
|
|
2
|
+
import type { PrewarmedAudioIteratorCache } from '../prewarm-iterator-for-looping';
|
|
2
3
|
import { type AllowWait } from './allow-wait';
|
|
3
4
|
export declare const HEALTHY_BUFFER_THRESHOLD_SECONDS = 1;
|
|
4
5
|
export type QueuedNode = {
|
|
@@ -6,7 +7,7 @@ export type QueuedNode = {
|
|
|
6
7
|
timestamp: number;
|
|
7
8
|
buffer: AudioBuffer;
|
|
8
9
|
};
|
|
9
|
-
export declare const makeAudioIterator: (
|
|
10
|
+
export declare const makeAudioIterator: (startFromSecond: number, cache: PrewarmedAudioIteratorCache) => {
|
|
10
11
|
destroy: () => void;
|
|
11
12
|
getNext: () => Promise<IteratorResult<WrappedAudioBuffer, void>>;
|
|
12
13
|
isDestroyed: () => boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { roundTo4Digits } from '../helpers/round-to-4-digits';
|
|
2
2
|
import { allowWaitRoutine } from './allow-wait';
|
|
3
3
|
export const HEALTHY_BUFFER_THRESHOLD_SECONDS = 1;
|
|
4
|
-
export const makeAudioIterator = (
|
|
4
|
+
export const makeAudioIterator = (startFromSecond, cache) => {
|
|
5
5
|
let destroyed = false;
|
|
6
|
-
const iterator =
|
|
6
|
+
const iterator = cache.makeIteratorOrUsePrewarmed(startFromSecond);
|
|
7
7
|
const queuedAudioNodes = [];
|
|
8
8
|
const audioChunksForAfterResuming = [];
|
|
9
9
|
let mostRecentTimestamp = -Infinity;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { useContext, useLayoutEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { cancelRender, Internals, useCurrentFrame, useDelayRender, useRemotionEnvironment, } from 'remotion';
|
|
3
|
+
import { extractFrameViaBroadcastChannel } from './extract-frame-via-broadcast-channel';
|
|
4
|
+
export const AudioForRendering = ({ volume: volumeProp, playbackRate, src, muted, loopVolumeCurveBehavior, delayRenderRetries, delayRenderTimeoutInMilliseconds, logLevel = window.remotion_logLevel, }) => {
|
|
5
|
+
const absoluteFrame = Internals.useTimelinePosition();
|
|
6
|
+
const videoConfig = Internals.useUnsafeVideoConfig();
|
|
7
|
+
const { registerRenderAsset, unregisterRenderAsset } = useContext(Internals.RenderAssetManager);
|
|
8
|
+
const frame = useCurrentFrame();
|
|
9
|
+
const volumePropsFrame = Internals.useFrameForVolumeProp(loopVolumeCurveBehavior ?? 'repeat');
|
|
10
|
+
const environment = useRemotionEnvironment();
|
|
11
|
+
const [id] = useState(() => `${Math.random()}`.replace('0.', ''));
|
|
12
|
+
if (!videoConfig) {
|
|
13
|
+
throw new Error('No video config found');
|
|
14
|
+
}
|
|
15
|
+
if (!src) {
|
|
16
|
+
throw new TypeError('No `src` was passed to <Video>.');
|
|
17
|
+
}
|
|
18
|
+
const volume = Internals.evaluateVolume({
|
|
19
|
+
volume: volumeProp,
|
|
20
|
+
frame: volumePropsFrame,
|
|
21
|
+
mediaVolume: 1,
|
|
22
|
+
});
|
|
23
|
+
Internals.warnAboutTooHighVolume(volume);
|
|
24
|
+
const shouldRenderAudio = useMemo(() => {
|
|
25
|
+
if (!window.remotion_audioEnabled) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (muted) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (volume <= 0) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}, [muted, volume]);
|
|
36
|
+
const { fps } = videoConfig;
|
|
37
|
+
const { delayRender, continueRender } = useDelayRender();
|
|
38
|
+
useLayoutEffect(() => {
|
|
39
|
+
const actualFps = playbackRate ? fps / playbackRate : fps;
|
|
40
|
+
const timestamp = frame / actualFps;
|
|
41
|
+
const durationInSeconds = 1 / actualFps;
|
|
42
|
+
const newHandle = delayRender(`Extracting frame number ${frame}`, {
|
|
43
|
+
retries: delayRenderRetries ?? undefined,
|
|
44
|
+
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined,
|
|
45
|
+
});
|
|
46
|
+
extractFrameViaBroadcastChannel({
|
|
47
|
+
src,
|
|
48
|
+
timeInSeconds: timestamp,
|
|
49
|
+
durationInSeconds,
|
|
50
|
+
logLevel: logLevel ?? 'info',
|
|
51
|
+
shouldRenderAudio,
|
|
52
|
+
isClientSideRendering: environment.isClientSideRendering,
|
|
53
|
+
})
|
|
54
|
+
.then(({ audio }) => {
|
|
55
|
+
if (audio) {
|
|
56
|
+
registerRenderAsset({
|
|
57
|
+
type: 'inline-audio',
|
|
58
|
+
id,
|
|
59
|
+
audio: Array.from(audio.data),
|
|
60
|
+
sampleRate: audio.sampleRate,
|
|
61
|
+
numberOfChannels: audio.numberOfChannels,
|
|
62
|
+
frame: absoluteFrame,
|
|
63
|
+
timestamp: audio.timestamp,
|
|
64
|
+
duration: (audio.numberOfFrames / audio.sampleRate) * 1000000,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
continueRender(newHandle);
|
|
68
|
+
})
|
|
69
|
+
.catch((error) => {
|
|
70
|
+
cancelRender(error);
|
|
71
|
+
});
|
|
72
|
+
return () => {
|
|
73
|
+
continueRender(newHandle);
|
|
74
|
+
unregisterRenderAsset(id);
|
|
75
|
+
};
|
|
76
|
+
}, [
|
|
77
|
+
absoluteFrame,
|
|
78
|
+
continueRender,
|
|
79
|
+
delayRender,
|
|
80
|
+
delayRenderRetries,
|
|
81
|
+
delayRenderTimeoutInMilliseconds,
|
|
82
|
+
environment.isClientSideRendering,
|
|
83
|
+
fps,
|
|
84
|
+
frame,
|
|
85
|
+
id,
|
|
86
|
+
logLevel,
|
|
87
|
+
playbackRate,
|
|
88
|
+
registerRenderAsset,
|
|
89
|
+
shouldRenderAudio,
|
|
90
|
+
src,
|
|
91
|
+
unregisterRenderAsset,
|
|
92
|
+
]);
|
|
93
|
+
return null;
|
|
94
|
+
};
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type { InputAudioTrack, WrappedAudioBuffer } from 'mediabunny';
|
|
2
2
|
import type { useBufferState } from 'remotion';
|
|
3
3
|
import type { Nonce } from './nonce-manager';
|
|
4
|
-
export declare const audioIteratorManager: ({ audioTrack, delayPlaybackHandleIfNotPremounting, sharedAudioContext, }: {
|
|
4
|
+
export declare const audioIteratorManager: ({ audioTrack, delayPlaybackHandleIfNotPremounting, sharedAudioContext, getIsLooping, getEndTime, getStartTime, updatePlaybackTime, }: {
|
|
5
5
|
audioTrack: InputAudioTrack;
|
|
6
6
|
delayPlaybackHandleIfNotPremounting: () => {
|
|
7
7
|
unblock: () => void;
|
|
8
8
|
};
|
|
9
9
|
sharedAudioContext: AudioContext;
|
|
10
|
+
getIsLooping: () => boolean;
|
|
11
|
+
getEndTime: () => number;
|
|
12
|
+
getStartTime: () => number;
|
|
13
|
+
updatePlaybackTime: (time: number) => void;
|
|
10
14
|
}) => {
|
|
11
15
|
startAudioIterator: ({ nonce, playbackRate, startFromSecond, getIsPlaying, scheduleAudioNode, }: {
|
|
12
16
|
startFromSecond: number;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { AudioBufferSink, InputDisposedError } from 'mediabunny';
|
|
2
2
|
import { isAlreadyQueued, makeAudioIterator, } from './audio/audio-preview-iterator';
|
|
3
|
-
|
|
3
|
+
import { makePrewarmedAudioIteratorCache } from './prewarm-iterator-for-looping';
|
|
4
|
+
export const audioIteratorManager = ({ audioTrack, delayPlaybackHandleIfNotPremounting, sharedAudioContext, getIsLooping, getEndTime, getStartTime, updatePlaybackTime, }) => {
|
|
4
5
|
let muted = false;
|
|
5
6
|
let currentVolume = 1;
|
|
6
7
|
const gainNode = sharedAudioContext.createGain();
|
|
7
8
|
gainNode.connect(sharedAudioContext.destination);
|
|
8
9
|
const audioSink = new AudioBufferSink(audioTrack);
|
|
10
|
+
const prewarmedAudioIteratorCache = makePrewarmedAudioIteratorCache(audioSink);
|
|
9
11
|
let audioBufferIterator = null;
|
|
10
12
|
let audioIteratorsCreated = 0;
|
|
13
|
+
let currentDelayHandle = null;
|
|
11
14
|
const scheduleAudioChunk = ({ buffer, mediaTimestamp, playbackRate, scheduleAudioNode, }) => {
|
|
12
15
|
if (!audioBufferIterator) {
|
|
13
16
|
throw new Error('Audio buffer iterator not found');
|
|
@@ -43,9 +46,11 @@ export const audioIteratorManager = ({ audioTrack, delayPlaybackHandleIfNotPremo
|
|
|
43
46
|
}
|
|
44
47
|
};
|
|
45
48
|
const startAudioIterator = async ({ nonce, playbackRate, startFromSecond, getIsPlaying, scheduleAudioNode, }) => {
|
|
49
|
+
updatePlaybackTime(startFromSecond);
|
|
46
50
|
audioBufferIterator?.destroy();
|
|
47
51
|
const delayHandle = delayPlaybackHandleIfNotPremounting();
|
|
48
|
-
|
|
52
|
+
currentDelayHandle = delayHandle;
|
|
53
|
+
const iterator = makeAudioIterator(startFromSecond, prewarmedAudioIteratorCache);
|
|
49
54
|
audioIteratorsCreated++;
|
|
50
55
|
audioBufferIterator = iterator;
|
|
51
56
|
try {
|
|
@@ -53,16 +58,13 @@ export const audioIteratorManager = ({ audioTrack, delayPlaybackHandleIfNotPremo
|
|
|
53
58
|
for (let i = 0; i < 3; i++) {
|
|
54
59
|
const result = await iterator.getNext();
|
|
55
60
|
if (iterator.isDestroyed()) {
|
|
56
|
-
delayHandle.unblock();
|
|
57
61
|
return;
|
|
58
62
|
}
|
|
59
63
|
if (nonce.isStale()) {
|
|
60
|
-
delayHandle.unblock();
|
|
61
64
|
return;
|
|
62
65
|
}
|
|
63
66
|
if (!result.value) {
|
|
64
67
|
// media ended
|
|
65
|
-
delayHandle.unblock();
|
|
66
68
|
return;
|
|
67
69
|
}
|
|
68
70
|
onAudioChunk({
|
|
@@ -83,6 +85,7 @@ export const audioIteratorManager = ({ audioTrack, delayPlaybackHandleIfNotPremo
|
|
|
83
85
|
}
|
|
84
86
|
finally {
|
|
85
87
|
delayHandle.unblock();
|
|
88
|
+
currentDelayHandle = null;
|
|
86
89
|
}
|
|
87
90
|
};
|
|
88
91
|
const pausePlayback = () => {
|
|
@@ -92,6 +95,14 @@ export const audioIteratorManager = ({ audioTrack, delayPlaybackHandleIfNotPremo
|
|
|
92
95
|
audioBufferIterator.moveQueuedChunksToPauseQueue();
|
|
93
96
|
};
|
|
94
97
|
const seek = async ({ newTime, nonce, fps, playbackRate, getIsPlaying, scheduleAudioNode, bufferState, }) => {
|
|
98
|
+
if (getIsLooping()) {
|
|
99
|
+
// If less than 1 second from the end away, we pre-warm a new iterator
|
|
100
|
+
if (getEndTime() - newTime < 1) {
|
|
101
|
+
prewarmedAudioIteratorCache.prewarmIteratorForLooping({
|
|
102
|
+
timeToSeek: getStartTime(),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
95
106
|
if (!audioBufferIterator) {
|
|
96
107
|
await startAudioIterator({
|
|
97
108
|
nonce,
|
|
@@ -194,8 +205,13 @@ export const audioIteratorManager = ({ audioTrack, delayPlaybackHandleIfNotPremo
|
|
|
194
205
|
pausePlayback,
|
|
195
206
|
getAudioBufferIterator: () => audioBufferIterator,
|
|
196
207
|
destroyIterator: () => {
|
|
208
|
+
prewarmedAudioIteratorCache.destroy();
|
|
197
209
|
audioBufferIterator?.destroy();
|
|
198
210
|
audioBufferIterator = null;
|
|
211
|
+
if (currentDelayHandle) {
|
|
212
|
+
currentDelayHandle.unblock();
|
|
213
|
+
currentDelayHandle = null;
|
|
214
|
+
}
|
|
199
215
|
},
|
|
200
216
|
seek,
|
|
201
217
|
getAudioIteratorsCreated: () => audioIteratorsCreated,
|
package/dist/audio.d.ts
ADDED
package/dist/audio.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useContext } from 'react';
|
|
3
|
+
import { cancelRender, Internals, Sequence, useRemotionEnvironment, } from 'remotion';
|
|
4
|
+
import { SharedAudioContext } from '../../core/src/audio/shared-audio-tags';
|
|
5
|
+
import { AudioForRendering } from './audio-for-rendering';
|
|
6
|
+
const { validateMediaTrimProps, resolveTrimProps, validateMediaProps, AudioForPreview, } = Internals;
|
|
7
|
+
export const Audio = (props) => {
|
|
8
|
+
const audioContext = useContext(SharedAudioContext);
|
|
9
|
+
// Should only destruct `trimBefore` and `trimAfter` from props,
|
|
10
|
+
// rest gets drilled down
|
|
11
|
+
const { trimBefore, trimAfter, name, pauseWhenBuffering, stack, showInTimeline, onError: onRemotionError, loop, ...otherProps } = props;
|
|
12
|
+
const environment = useRemotionEnvironment();
|
|
13
|
+
const onDuration = useCallback(() => undefined, []);
|
|
14
|
+
if (typeof props.src !== 'string') {
|
|
15
|
+
throw new TypeError(`The \`<Audio>\` tag requires a string for \`src\`, but got ${JSON.stringify(props.src)} instead.`);
|
|
16
|
+
}
|
|
17
|
+
validateMediaTrimProps({
|
|
18
|
+
startFrom: undefined,
|
|
19
|
+
endAt: undefined,
|
|
20
|
+
trimBefore,
|
|
21
|
+
trimAfter,
|
|
22
|
+
});
|
|
23
|
+
const { trimBeforeValue, trimAfterValue } = resolveTrimProps({
|
|
24
|
+
startFrom: undefined,
|
|
25
|
+
endAt: undefined,
|
|
26
|
+
trimBefore,
|
|
27
|
+
trimAfter,
|
|
28
|
+
});
|
|
29
|
+
const onError = useCallback((e) => {
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
console.log(e.currentTarget.error);
|
|
32
|
+
// If there is no `loop` property, we don't need to get the duration
|
|
33
|
+
// and this does not need to be a fatal error
|
|
34
|
+
const errMessage = `Could not play audio: ${e.currentTarget.error}. See https://remotion.dev/docs/media-playback-error for help.`;
|
|
35
|
+
if (loop) {
|
|
36
|
+
if (onRemotionError) {
|
|
37
|
+
onRemotionError(new Error(errMessage));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
cancelRender(new Error(errMessage));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
onRemotionError?.(new Error(errMessage));
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.warn(errMessage);
|
|
46
|
+
}
|
|
47
|
+
}, [onRemotionError, loop]);
|
|
48
|
+
if (typeof trimBeforeValue !== 'undefined' ||
|
|
49
|
+
typeof trimAfterValue !== 'undefined') {
|
|
50
|
+
return (_jsx(Sequence, { layout: "none", from: 0 - (trimBeforeValue ?? 0), showInTimeline: false, durationInFrames: trimAfterValue, name: name, children: _jsx(Audio, { pauseWhenBuffering: pauseWhenBuffering ?? false, ...otherProps }) }));
|
|
51
|
+
}
|
|
52
|
+
validateMediaProps(props, 'Video');
|
|
53
|
+
if (environment.isRendering) {
|
|
54
|
+
return _jsx(AudioForRendering, { ...otherProps });
|
|
55
|
+
}
|
|
56
|
+
const { onAutoPlayError, crossOrigin, delayRenderRetries, delayRenderTimeoutInMilliseconds, ...propsForPreview } = otherProps;
|
|
57
|
+
return (_jsx(AudioForPreview, { _remotionInternalNativeLoopPassed: props._remotionInternalNativeLoopPassed ?? false, _remotionInternalStack: stack ?? null, shouldPreMountAudioTags: audioContext !== null && audioContext.numberOfAudioTags > 0, ...propsForPreview, onNativeError: onError, onDuration: onDuration,
|
|
58
|
+
// Proposal: Make this default to true in v5
|
|
59
|
+
pauseWhenBuffering: pauseWhenBuffering ?? false, _remotionInternalNeedsDurationCalculation: Boolean(loop), showInTimeline: showInTimeline ?? true }));
|
|
60
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getDataTypeForAudioFormat: (format: AudioSampleFormat) => Float32ArrayConstructor | Int16ArrayConstructor | Uint8ArrayConstructor | Int32ArrayConstructor;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const getDataTypeForAudioFormat = (format) => {
|
|
2
|
+
switch (format) {
|
|
3
|
+
case 'f32':
|
|
4
|
+
return Float32Array;
|
|
5
|
+
case 'f32-planar':
|
|
6
|
+
return Float32Array;
|
|
7
|
+
case 's16':
|
|
8
|
+
return Int16Array;
|
|
9
|
+
case 's16-planar':
|
|
10
|
+
return Int16Array;
|
|
11
|
+
case 'u8':
|
|
12
|
+
return Uint8Array;
|
|
13
|
+
case 'u8-planar':
|
|
14
|
+
return Uint8Array;
|
|
15
|
+
case 's32':
|
|
16
|
+
return Int32Array;
|
|
17
|
+
case 's32-planar':
|
|
18
|
+
return Int32Array;
|
|
19
|
+
default:
|
|
20
|
+
throw new Error(`Unsupported audio format: ${format}`);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isPlanarFormat: (format: AudioSampleFormat) => boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const logAudioData: (audioData: AudioData) => string;
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DataType } from './convert-audiodata/data-types';
|
|
2
|
+
export type SerializableAudioData = {
|
|
3
|
+
data: DataType[];
|
|
4
|
+
format: AudioSampleFormat;
|
|
5
|
+
numberOfChannels: number;
|
|
6
|
+
numberOfFrames: number;
|
|
7
|
+
sampleRate: number;
|
|
8
|
+
};
|
|
9
|
+
export declare const turnAudioDataIntoSerializableData: (audioData: AudioData) => {
|
|
10
|
+
data: (Float32Array<ArrayBuffer> | Int32Array<ArrayBuffer> | Int16Array<ArrayBuffer> | Uint8Array<ArrayBuffer>)[];
|
|
11
|
+
format: AudioSampleFormat;
|
|
12
|
+
numberOfChannels: number;
|
|
13
|
+
numberOfFrames: number;
|
|
14
|
+
sampleRate: number;
|
|
15
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getDataTypeForAudioFormat } from './convert-audiodata/data-types';
|
|
2
|
+
import { isPlanarFormat } from './convert-audiodata/is-planar-format';
|
|
3
|
+
export const turnAudioDataIntoSerializableData = (audioData) => {
|
|
4
|
+
if (!audioData.format) {
|
|
5
|
+
throw new Error('AudioData format is not set');
|
|
6
|
+
}
|
|
7
|
+
const DataType = getDataTypeForAudioFormat(audioData.format);
|
|
8
|
+
const isPlanar = isPlanarFormat(audioData.format);
|
|
9
|
+
const planes = isPlanar ? audioData.numberOfChannels : 1;
|
|
10
|
+
const srcChannels = new Array(planes)
|
|
11
|
+
.fill(true)
|
|
12
|
+
.map(() => new DataType((isPlanar ? 1 : audioData.numberOfChannels) *
|
|
13
|
+
audioData.numberOfFrames));
|
|
14
|
+
for (let i = 0; i < planes; i++) {
|
|
15
|
+
audioData.copyTo(srcChannels[i], {
|
|
16
|
+
planeIndex: i,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
data: srcChannels,
|
|
21
|
+
format: audioData.format,
|
|
22
|
+
numberOfChannels: audioData.numberOfChannels,
|
|
23
|
+
numberOfFrames: audioData.numberOfFrames,
|
|
24
|
+
sampleRate: audioData.sampleRate,
|
|
25
|
+
};
|
|
26
|
+
};
|