@remotion/media 4.0.353 → 4.0.355
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-rendering.js +37 -27
- package/dist/audio/audio.js +6 -3
- package/dist/audio/props.d.ts +0 -5
- package/dist/audio-extraction/extract-audio.d.ts +6 -3
- package/dist/audio-extraction/extract-audio.js +17 -12
- package/dist/convert-audiodata/apply-volume.d.ts +1 -0
- package/dist/convert-audiodata/apply-volume.js +17 -0
- package/dist/convert-audiodata/convert-audiodata.d.ts +2 -2
- package/dist/convert-audiodata/convert-audiodata.js +13 -7
- package/dist/convert-audiodata/resample-audiodata.d.ts +1 -2
- package/dist/convert-audiodata/resample-audiodata.js +39 -18
- package/dist/esm/index.mjs +328 -13149
- package/dist/extract-frame-and-audio.d.ts +3 -2
- package/dist/extract-frame-and-audio.js +4 -3
- package/dist/get-sink-weak.d.ts +18 -0
- package/dist/get-sink-weak.js +23 -0
- package/dist/looped-frame.d.ts +9 -0
- package/dist/looped-frame.js +10 -0
- package/dist/video/props.d.ts +0 -5
- package/dist/video/video-for-rendering.js +41 -31
- package/dist/video/video.js +2 -2
- package/dist/video-extraction/extract-frame-via-broadcast-channel.d.ts +3 -2
- package/dist/video-extraction/extract-frame-via-broadcast-channel.js +9 -5
- package/dist/video-extraction/extract-frame.d.ts +0 -2
- package/dist/video-extraction/extract-frame.js +5 -5
- package/dist/video-extraction/get-frames-since-keyframe.d.ts +3 -3
- package/dist/video-extraction/get-frames-since-keyframe.js +8 -9
- package/package.json +5 -5
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { useContext, useLayoutEffect,
|
|
1
|
+
import { useContext, useLayoutEffect, useState } from 'react';
|
|
2
2
|
import { cancelRender, Internals, useCurrentFrame, useDelayRender, useRemotionEnvironment, } from 'remotion';
|
|
3
|
+
import { applyVolume } from '../convert-audiodata/apply-volume';
|
|
4
|
+
import { frameForVolumeProp } from '../looped-frame';
|
|
3
5
|
import { extractFrameViaBroadcastChannel } from '../video-extraction/extract-frame-via-broadcast-channel';
|
|
4
6
|
export const AudioForRendering = ({ volume: volumeProp, playbackRate, src, muted, loopVolumeCurveBehavior, delayRenderRetries, delayRenderTimeoutInMilliseconds, logLevel = window.remotion_logLevel, loop, }) => {
|
|
7
|
+
const frame = useCurrentFrame();
|
|
5
8
|
const absoluteFrame = Internals.useTimelinePosition();
|
|
6
9
|
const videoConfig = Internals.useUnsafeVideoConfig();
|
|
7
10
|
const { registerRenderAsset, unregisterRenderAsset } = useContext(Internals.RenderAssetManager);
|
|
8
|
-
const
|
|
9
|
-
const volumePropsFrame = Internals.useFrameForVolumeProp(loopVolumeCurveBehavior ?? 'repeat');
|
|
11
|
+
const startsAt = Internals.useMediaStartsAt();
|
|
10
12
|
const environment = useRemotionEnvironment();
|
|
11
13
|
const [id] = useState(() => `${Math.random()}`.replace('0.', ''));
|
|
12
14
|
if (!videoConfig) {
|
|
@@ -15,24 +17,6 @@ export const AudioForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
15
17
|
if (!src) {
|
|
16
18
|
throw new TypeError('No `src` was passed to <Audio>.');
|
|
17
19
|
}
|
|
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
20
|
const { fps } = videoConfig;
|
|
37
21
|
const { delayRender, continueRender } = useDelayRender();
|
|
38
22
|
useLayoutEffect(() => {
|
|
@@ -43,19 +27,43 @@ export const AudioForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
43
27
|
retries: delayRenderRetries ?? undefined,
|
|
44
28
|
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined,
|
|
45
29
|
});
|
|
30
|
+
const shouldRenderAudio = (() => {
|
|
31
|
+
if (!window.remotion_audioEnabled) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (muted) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
})();
|
|
46
39
|
extractFrameViaBroadcastChannel({
|
|
47
40
|
src,
|
|
48
41
|
timeInSeconds: timestamp,
|
|
49
42
|
durationInSeconds,
|
|
43
|
+
playbackRate: playbackRate ?? 1,
|
|
50
44
|
logLevel: logLevel ?? 'info',
|
|
51
45
|
includeAudio: shouldRenderAudio,
|
|
52
46
|
includeVideo: false,
|
|
53
47
|
isClientSideRendering: environment.isClientSideRendering,
|
|
54
|
-
volume,
|
|
55
48
|
loop: loop ?? false,
|
|
56
49
|
})
|
|
57
|
-
.then(({ audio }) => {
|
|
58
|
-
|
|
50
|
+
.then(({ audio, durationInSeconds: assetDurationInSeconds }) => {
|
|
51
|
+
const volumePropsFrame = frameForVolumeProp({
|
|
52
|
+
behavior: loopVolumeCurveBehavior ?? 'repeat',
|
|
53
|
+
loop: loop ?? false,
|
|
54
|
+
assetDurationInSeconds: assetDurationInSeconds ?? 0,
|
|
55
|
+
fps,
|
|
56
|
+
frame,
|
|
57
|
+
startsAt,
|
|
58
|
+
});
|
|
59
|
+
const volume = Internals.evaluateVolume({
|
|
60
|
+
volume: volumeProp,
|
|
61
|
+
frame: volumePropsFrame,
|
|
62
|
+
mediaVolume: 1,
|
|
63
|
+
});
|
|
64
|
+
Internals.warnAboutTooHighVolume(volume);
|
|
65
|
+
if (audio && volume > 0) {
|
|
66
|
+
applyVolume(audio.data, volume);
|
|
59
67
|
registerRenderAsset({
|
|
60
68
|
type: 'inline-audio',
|
|
61
69
|
id,
|
|
@@ -87,13 +95,15 @@ export const AudioForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
87
95
|
frame,
|
|
88
96
|
id,
|
|
89
97
|
logLevel,
|
|
98
|
+
loop,
|
|
99
|
+
loopVolumeCurveBehavior,
|
|
100
|
+
muted,
|
|
90
101
|
playbackRate,
|
|
91
102
|
registerRenderAsset,
|
|
92
|
-
shouldRenderAudio,
|
|
93
103
|
src,
|
|
104
|
+
startsAt,
|
|
94
105
|
unregisterRenderAsset,
|
|
95
|
-
|
|
96
|
-
loop,
|
|
106
|
+
volumeProp,
|
|
97
107
|
]);
|
|
98
108
|
return null;
|
|
99
109
|
};
|
package/dist/audio/audio.js
CHANGED
|
@@ -4,11 +4,14 @@ import { cancelRender, Internals, Sequence, useRemotionEnvironment, } from 'remo
|
|
|
4
4
|
import { SharedAudioContext } from '../../../core/src/audio/shared-audio-tags';
|
|
5
5
|
import { AudioForRendering } from './audio-for-rendering';
|
|
6
6
|
const { validateMediaTrimProps, resolveTrimProps, validateMediaProps, AudioForPreview, } = Internals;
|
|
7
|
+
// dummy function for now because onError is not supported
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
9
|
+
const onRemotionError = (_e) => { };
|
|
7
10
|
export const Audio = (props) => {
|
|
8
11
|
const audioContext = useContext(SharedAudioContext);
|
|
9
12
|
// Should only destruct `trimBefore` and `trimAfter` from props,
|
|
10
13
|
// rest gets drilled down
|
|
11
|
-
const { trimBefore, trimAfter, name, pauseWhenBuffering, stack, showInTimeline,
|
|
14
|
+
const { trimBefore, trimAfter, name, pauseWhenBuffering, stack, showInTimeline, loop, ...otherProps } = props;
|
|
12
15
|
const environment = useRemotionEnvironment();
|
|
13
16
|
const onDuration = useCallback(() => undefined, []);
|
|
14
17
|
if (typeof props.src !== 'string') {
|
|
@@ -44,7 +47,7 @@ export const Audio = (props) => {
|
|
|
44
47
|
// eslint-disable-next-line no-console
|
|
45
48
|
console.warn(errMessage);
|
|
46
49
|
}
|
|
47
|
-
}, [
|
|
50
|
+
}, [loop]);
|
|
48
51
|
if (typeof trimBeforeValue !== 'undefined' ||
|
|
49
52
|
typeof trimAfterValue !== 'undefined') {
|
|
50
53
|
return (_jsx(Sequence, { layout: "none", from: 0 - (trimBeforeValue ?? 0), showInTimeline: false, durationInFrames: trimAfterValue, name: name, children: _jsx(Audio, { pauseWhenBuffering: pauseWhenBuffering ?? false, ...otherProps }) }));
|
|
@@ -53,7 +56,7 @@ export const Audio = (props) => {
|
|
|
53
56
|
if (environment.isRendering) {
|
|
54
57
|
return _jsx(AudioForRendering, { ...otherProps });
|
|
55
58
|
}
|
|
56
|
-
const {
|
|
59
|
+
const { delayRenderRetries, delayRenderTimeoutInMilliseconds, ...propsForPreview } = otherProps;
|
|
57
60
|
return (_jsx(AudioForPreview, { _remotionInternalNativeLoopPassed: props._remotionInternalNativeLoopPassed ?? false, _remotionInternalStack: stack ?? null, shouldPreMountAudioTags: audioContext !== null && audioContext.numberOfAudioTags > 0, ...propsForPreview, onNativeError: onError, onDuration: onDuration,
|
|
58
61
|
// Proposal: Make this default to true in v5
|
|
59
62
|
pauseWhenBuffering: pauseWhenBuffering ?? false, _remotionInternalNeedsDurationCalculation: Boolean(loop), showInTimeline: showInTimeline ?? true }));
|
package/dist/audio/props.d.ts
CHANGED
|
@@ -8,16 +8,11 @@ export type AudioProps = {
|
|
|
8
8
|
name?: string;
|
|
9
9
|
pauseWhenBuffering?: boolean;
|
|
10
10
|
showInTimeline?: boolean;
|
|
11
|
-
onAutoPlayError?: null | (() => void);
|
|
12
11
|
playbackRate?: number;
|
|
13
12
|
muted?: boolean;
|
|
14
13
|
delayRenderRetries?: number;
|
|
15
14
|
delayRenderTimeoutInMilliseconds?: number;
|
|
16
|
-
crossOrigin?: '' | 'anonymous' | 'use-credentials';
|
|
17
15
|
style?: React.CSSProperties;
|
|
18
|
-
onError?: (err: Error) => void;
|
|
19
|
-
useWebAudioApi?: boolean;
|
|
20
|
-
acceptableTimeShiftInSeconds?: number;
|
|
21
16
|
/**
|
|
22
17
|
* @deprecated For internal use only
|
|
23
18
|
*/
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { type LogLevel } from 'remotion';
|
|
2
2
|
import type { PcmS16AudioData } from '../convert-audiodata/convert-audiodata';
|
|
3
|
-
export declare const extractAudio: ({ src, timeInSeconds: unloopedTimeInSeconds, durationInSeconds,
|
|
3
|
+
export declare const extractAudio: ({ src, timeInSeconds: unloopedTimeInSeconds, durationInSeconds, logLevel, loop, playbackRate, }: {
|
|
4
4
|
src: string;
|
|
5
5
|
timeInSeconds: number;
|
|
6
6
|
durationInSeconds: number;
|
|
7
|
-
volume: number;
|
|
8
7
|
logLevel: LogLevel;
|
|
9
8
|
loop: boolean;
|
|
10
|
-
|
|
9
|
+
playbackRate: number;
|
|
10
|
+
}) => Promise<{
|
|
11
|
+
data: PcmS16AudioData | null;
|
|
12
|
+
durationInSeconds: number | null;
|
|
13
|
+
}>;
|
|
@@ -2,18 +2,18 @@ import { audioManager } from '../caches';
|
|
|
2
2
|
import { combineAudioDataAndClosePrevious } from '../convert-audiodata/combine-audiodata';
|
|
3
3
|
import { convertAudioData } from '../convert-audiodata/convert-audiodata';
|
|
4
4
|
import { TARGET_NUMBER_OF_CHANNELS, TARGET_SAMPLE_RATE, } from '../convert-audiodata/resample-audiodata';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import { getSinkWeak } from '../get-sink-weak';
|
|
6
|
+
export const extractAudio = async ({ src, timeInSeconds: unloopedTimeInSeconds, durationInSeconds, logLevel, loop, playbackRate, }) => {
|
|
7
|
+
const { audio, actualMatroskaTimestamps, isMatroska, getDuration } = await getSinkWeak(src, logLevel);
|
|
8
|
+
let duration = null;
|
|
9
|
+
if (loop) {
|
|
10
|
+
duration = await getDuration();
|
|
10
11
|
}
|
|
11
|
-
const { audio, actualMatroskaTimestamps, isMatroska, getDuration } = await sinkPromises[src];
|
|
12
12
|
if (audio === null) {
|
|
13
|
-
return null;
|
|
13
|
+
return { data: null, durationInSeconds: null };
|
|
14
14
|
}
|
|
15
15
|
const timeInSeconds = loop
|
|
16
|
-
? unloopedTimeInSeconds %
|
|
16
|
+
? unloopedTimeInSeconds % duration
|
|
17
17
|
: unloopedTimeInSeconds;
|
|
18
18
|
const sampleIterator = await audioManager.getIterator({
|
|
19
19
|
src,
|
|
@@ -44,10 +44,15 @@ export const extractAudio = async ({ src, timeInSeconds: unloopedTimeInSeconds,
|
|
|
44
44
|
// amount of samples to shave from start and end
|
|
45
45
|
let trimStartInSeconds = 0;
|
|
46
46
|
let trimEndInSeconds = 0;
|
|
47
|
-
// TODO: Apply playback rate
|
|
48
47
|
// TODO: Apply tone frequency
|
|
49
48
|
if (isFirstSample) {
|
|
50
49
|
trimStartInSeconds = timeInSeconds - sample.timestamp;
|
|
50
|
+
if (trimStartInSeconds < 0 && trimStartInSeconds > -1e-10) {
|
|
51
|
+
trimStartInSeconds = 0;
|
|
52
|
+
}
|
|
53
|
+
if (trimStartInSeconds < 0) {
|
|
54
|
+
throw new Error(`trimStartInSeconds is negative: ${trimStartInSeconds}`);
|
|
55
|
+
}
|
|
51
56
|
}
|
|
52
57
|
if (isLastSample) {
|
|
53
58
|
trimEndInSeconds =
|
|
@@ -62,7 +67,7 @@ export const extractAudio = async ({ src, timeInSeconds: unloopedTimeInSeconds,
|
|
|
62
67
|
trimStartInSeconds,
|
|
63
68
|
trimEndInSeconds,
|
|
64
69
|
targetNumberOfChannels: TARGET_NUMBER_OF_CHANNELS,
|
|
65
|
-
|
|
70
|
+
playbackRate,
|
|
66
71
|
});
|
|
67
72
|
audioDataRaw.close();
|
|
68
73
|
if (audioData.numberOfFrames === 0) {
|
|
@@ -71,8 +76,8 @@ export const extractAudio = async ({ src, timeInSeconds: unloopedTimeInSeconds,
|
|
|
71
76
|
audioDataArray.push(audioData);
|
|
72
77
|
}
|
|
73
78
|
if (audioDataArray.length === 0) {
|
|
74
|
-
return null;
|
|
79
|
+
return { data: null, durationInSeconds: duration };
|
|
75
80
|
}
|
|
76
81
|
const combined = combineAudioDataAndClosePrevious(audioDataArray);
|
|
77
|
-
return combined;
|
|
82
|
+
return { data: combined, durationInSeconds: duration };
|
|
78
83
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const applyVolume: (array: Int16Array, volume: number) => void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const applyVolume = (array, volume) => {
|
|
2
|
+
if (volume === 1) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
for (let i = 0; i < array.length; i++) {
|
|
6
|
+
const newValue = array[i] * volume;
|
|
7
|
+
if (newValue < -32768) {
|
|
8
|
+
array[i] = -32768;
|
|
9
|
+
}
|
|
10
|
+
else if (newValue > 32767) {
|
|
11
|
+
array[i] = 32767;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
array[i] = newValue;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
@@ -4,7 +4,7 @@ export type ConvertAudioDataOptions = {
|
|
|
4
4
|
trimStartInSeconds: number;
|
|
5
5
|
trimEndInSeconds: number;
|
|
6
6
|
targetNumberOfChannels: number;
|
|
7
|
-
|
|
7
|
+
playbackRate: number;
|
|
8
8
|
};
|
|
9
9
|
export type PcmS16AudioData = {
|
|
10
10
|
data: Int16Array;
|
|
@@ -13,4 +13,4 @@ export type PcmS16AudioData = {
|
|
|
13
13
|
numberOfFrames: number;
|
|
14
14
|
timestamp: number;
|
|
15
15
|
};
|
|
16
|
-
export declare const convertAudioData: ({ audioData, newSampleRate, trimStartInSeconds, trimEndInSeconds, targetNumberOfChannels,
|
|
16
|
+
export declare const convertAudioData: ({ audioData, newSampleRate, trimStartInSeconds, trimEndInSeconds, targetNumberOfChannels, playbackRate, }: ConvertAudioDataOptions) => PcmS16AudioData;
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { resampleAudioData } from './resample-audiodata';
|
|
2
2
|
const FORMAT = 's16';
|
|
3
|
-
|
|
3
|
+
const roundButRoundDownZeroPointFive = (value) => {
|
|
4
|
+
if (value % 1 <= 0.5) {
|
|
5
|
+
return Math.floor(value);
|
|
6
|
+
}
|
|
7
|
+
return Math.ceil(value);
|
|
8
|
+
};
|
|
9
|
+
export const convertAudioData = ({ audioData, newSampleRate, trimStartInSeconds, trimEndInSeconds, targetNumberOfChannels, playbackRate, }) => {
|
|
4
10
|
const { numberOfChannels: srcNumberOfChannels, sampleRate: currentSampleRate, numberOfFrames, } = audioData;
|
|
5
11
|
const ratio = currentSampleRate / newSampleRate;
|
|
6
|
-
const frameOffset =
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const
|
|
12
|
+
const frameOffset = roundButRoundDownZeroPointFive(trimStartInSeconds * audioData.sampleRate);
|
|
13
|
+
const unroundedFrameCount = numberOfFrames -
|
|
14
|
+
(trimEndInSeconds + trimStartInSeconds) * audioData.sampleRate;
|
|
15
|
+
const frameCount = Math.round(unroundedFrameCount);
|
|
16
|
+
const newNumberOfFrames = Math.round(unroundedFrameCount / ratio / playbackRate);
|
|
10
17
|
if (newNumberOfFrames === 0) {
|
|
11
18
|
throw new Error('Cannot resample - the given sample rate would result in less than 1 sample');
|
|
12
19
|
}
|
|
@@ -24,7 +31,7 @@ export const convertAudioData = ({ audioData, newSampleRate, trimStartInSeconds,
|
|
|
24
31
|
const chunkSize = frameCount / newNumberOfFrames;
|
|
25
32
|
if (newNumberOfFrames === frameCount &&
|
|
26
33
|
targetNumberOfChannels === srcNumberOfChannels &&
|
|
27
|
-
|
|
34
|
+
playbackRate === 1) {
|
|
28
35
|
return {
|
|
29
36
|
data: srcChannels,
|
|
30
37
|
numberOfChannels: targetNumberOfChannels,
|
|
@@ -39,7 +46,6 @@ export const convertAudioData = ({ audioData, newSampleRate, trimStartInSeconds,
|
|
|
39
46
|
destination: data,
|
|
40
47
|
targetFrames: newNumberOfFrames,
|
|
41
48
|
chunkSize,
|
|
42
|
-
volume,
|
|
43
49
|
});
|
|
44
50
|
const newAudioData = {
|
|
45
51
|
data,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
export declare const TARGET_NUMBER_OF_CHANNELS = 2;
|
|
2
2
|
export declare const TARGET_SAMPLE_RATE = 48000;
|
|
3
|
-
export declare const resampleAudioData: ({ srcNumberOfChannels, sourceChannels, destination, targetFrames, chunkSize,
|
|
3
|
+
export declare const resampleAudioData: ({ srcNumberOfChannels, sourceChannels, destination, targetFrames, chunkSize, }: {
|
|
4
4
|
srcNumberOfChannels: number;
|
|
5
5
|
sourceChannels: Int16Array;
|
|
6
6
|
destination: Int16Array;
|
|
7
7
|
targetFrames: number;
|
|
8
8
|
chunkSize: number;
|
|
9
|
-
volume: number;
|
|
10
9
|
}) => void;
|
|
@@ -2,29 +2,50 @@
|
|
|
2
2
|
export const TARGET_NUMBER_OF_CHANNELS = 2;
|
|
3
3
|
// Remotion exports all videos with 48kHz sample rate.
|
|
4
4
|
export const TARGET_SAMPLE_RATE = 48000;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
const fixFloatingPoint = (value) => {
|
|
6
|
+
if (value % 1 < 0.0000001) {
|
|
7
|
+
return Math.floor(value);
|
|
8
|
+
}
|
|
9
|
+
if (value % 1 > 0.9999999) {
|
|
10
|
+
return Math.ceil(value);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
};
|
|
14
|
+
export const resampleAudioData = ({ srcNumberOfChannels, sourceChannels, destination, targetFrames, chunkSize, }) => {
|
|
15
|
+
const getSourceValues = (startUnfixed, endUnfixed, channelIndex) => {
|
|
16
|
+
const start = fixFloatingPoint(startUnfixed);
|
|
17
|
+
const end = fixFloatingPoint(endUnfixed);
|
|
18
|
+
const startFloor = Math.floor(start);
|
|
19
|
+
const startCeil = Math.ceil(start);
|
|
20
|
+
const startFraction = start - startFloor;
|
|
21
|
+
const endFraction = end - Math.floor(end);
|
|
22
|
+
const endFloor = Math.floor(end);
|
|
23
|
+
let weightedSum = 0;
|
|
24
|
+
let totalWeight = 0;
|
|
25
|
+
// Handle first fractional sample
|
|
26
|
+
if (startFraction > 0) {
|
|
27
|
+
const firstSample = sourceChannels[startFloor * srcNumberOfChannels + channelIndex];
|
|
28
|
+
weightedSum += firstSample * (1 - startFraction);
|
|
29
|
+
totalWeight += 1 - startFraction;
|
|
14
30
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
31
|
+
// Handle full samples
|
|
32
|
+
for (let k = startCeil; k < endFloor; k++) {
|
|
33
|
+
const num = sourceChannels[k * srcNumberOfChannels + channelIndex];
|
|
34
|
+
weightedSum += num;
|
|
35
|
+
totalWeight += 1;
|
|
19
36
|
}
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
// Handle last fractional sample
|
|
38
|
+
if (endFraction > 0) {
|
|
39
|
+
const lastSample = sourceChannels[endFloor * srcNumberOfChannels + channelIndex];
|
|
40
|
+
weightedSum += lastSample * endFraction;
|
|
41
|
+
totalWeight += endFraction;
|
|
22
42
|
}
|
|
23
|
-
|
|
43
|
+
const average = weightedSum / totalWeight;
|
|
44
|
+
return average;
|
|
24
45
|
};
|
|
25
46
|
for (let newFrameIndex = 0; newFrameIndex < targetFrames; newFrameIndex++) {
|
|
26
|
-
const start =
|
|
27
|
-
const end =
|
|
47
|
+
const start = newFrameIndex * chunkSize;
|
|
48
|
+
const end = start + chunkSize;
|
|
28
49
|
if (TARGET_NUMBER_OF_CHANNELS === srcNumberOfChannels) {
|
|
29
50
|
for (let i = 0; i < srcNumberOfChannels; i++) {
|
|
30
51
|
destination[newFrameIndex * srcNumberOfChannels + i] = getSourceValues(start, end, i);
|