@remotion/media-utils 4.0.266 → 4.0.268
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/create-smooth-svg-path.d.ts +7 -0
- package/dist/create-smooth-svg-path.js +49 -0
- package/dist/get-wave-form-samples.d.ts +6 -1
- package/dist/get-wave-form-samples.js +5 -17
- package/dist/get-waveform-portion.d.ts +8 -3
- package/dist/get-waveform-portion.js +40 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -1
- package/dist/validate-channel.d.ts +1 -0
- package/dist/validate-channel.js +21 -0
- package/dist/visualize-audio-waveform.d.ts +11 -0
- package/dist/visualize-audio-waveform.js +38 -0
- package/package.json +3 -3
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSmoothSvgPath = void 0;
|
|
4
|
+
const line = (pointA, pointB) => {
|
|
5
|
+
const lengthX = pointB.x - pointA.x;
|
|
6
|
+
const lengthY = pointB.y - pointA.y;
|
|
7
|
+
return {
|
|
8
|
+
length: Math.sqrt(lengthX ** 2 + lengthY ** 2),
|
|
9
|
+
angle: Math.atan2(lengthY, lengthX),
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const controlPoint = ({ current, previous, next, reverse, }) => {
|
|
13
|
+
const p = previous || current;
|
|
14
|
+
const n = next || current;
|
|
15
|
+
// The smoothing ratio
|
|
16
|
+
const smoothing = 0.2;
|
|
17
|
+
// Properties of the opposed-line
|
|
18
|
+
const o = line(p, n);
|
|
19
|
+
const angle = o.angle + (reverse ? Math.PI : 0);
|
|
20
|
+
const length = o.length * smoothing;
|
|
21
|
+
const x = current.x + Math.cos(angle) * length;
|
|
22
|
+
const y = current.y + Math.sin(angle) * length;
|
|
23
|
+
return { x, y };
|
|
24
|
+
};
|
|
25
|
+
const createSmoothSvgPath = ({ points }) => {
|
|
26
|
+
return points.reduce((acc, current, i, a) => {
|
|
27
|
+
if (i === 0) {
|
|
28
|
+
return `M ${current.x},${current.y}`;
|
|
29
|
+
}
|
|
30
|
+
const { x, y } = current;
|
|
31
|
+
const previous = a[i - 1];
|
|
32
|
+
const twoPrevious = a[i - 2];
|
|
33
|
+
const next = a[i + 1];
|
|
34
|
+
const { x: cp1x, y: cp1y } = controlPoint({
|
|
35
|
+
current: previous,
|
|
36
|
+
previous: twoPrevious,
|
|
37
|
+
next: current,
|
|
38
|
+
reverse: false,
|
|
39
|
+
});
|
|
40
|
+
const { x: cp2x, y: cp2y } = controlPoint({
|
|
41
|
+
current,
|
|
42
|
+
previous,
|
|
43
|
+
next,
|
|
44
|
+
reverse: true,
|
|
45
|
+
});
|
|
46
|
+
return `${acc} C ${cp1x},${cp1y} ${cp2x},${cp2y} ${x},${y}`;
|
|
47
|
+
}, '');
|
|
48
|
+
};
|
|
49
|
+
exports.createSmoothSvgPath = createSmoothSvgPath;
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type SampleOutputRange = 'minus-one-to-one' | 'zero-to-one';
|
|
2
|
+
export declare const getWaveformSamples: ({ audioBuffer, numberOfSamples, outputRange, }: {
|
|
3
|
+
audioBuffer: Float32Array;
|
|
4
|
+
numberOfSamples: number;
|
|
5
|
+
outputRange: SampleOutputRange;
|
|
6
|
+
}) => number[];
|
|
@@ -1,33 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getWaveformSamples = void 0;
|
|
4
|
-
const
|
|
5
|
-
const blockSize = Math.floor(audioBuffer.length /
|
|
4
|
+
const getWaveformSamples = ({ audioBuffer, numberOfSamples, outputRange, }) => {
|
|
5
|
+
const blockSize = Math.floor(audioBuffer.length / numberOfSamples); // the number of samples in each subdivision
|
|
6
6
|
if (blockSize === 0) {
|
|
7
7
|
return [];
|
|
8
8
|
}
|
|
9
9
|
const filteredData = [];
|
|
10
|
-
for (let i = 0; i <
|
|
10
|
+
for (let i = 0; i < numberOfSamples; i++) {
|
|
11
11
|
const blockStart = blockSize * i; // the location of the first sample in the block
|
|
12
12
|
let sum = 0;
|
|
13
13
|
for (let j = 0; j < blockSize; j++) {
|
|
14
14
|
sum += Math.abs(audioBuffer[blockStart + j]); // find the sum of all the samples in the block
|
|
15
15
|
}
|
|
16
|
-
filteredData.push(sum / blockSize)
|
|
16
|
+
filteredData.push((sum / blockSize) *
|
|
17
|
+
(i % 2 === 0 && outputRange === 'minus-one-to-one' ? -1 : 1)); // divide the sum by the block size to get the average
|
|
17
18
|
}
|
|
18
19
|
return filteredData;
|
|
19
20
|
};
|
|
20
|
-
const normalizeData = (filteredData) => {
|
|
21
|
-
const max = Math.max(...filteredData);
|
|
22
|
-
// If the maximum amplitude is below this threshold, treat it as silence
|
|
23
|
-
const MINIMUM_AMPLITUDE_THRESHOLD = 0.001;
|
|
24
|
-
if (max < MINIMUM_AMPLITUDE_THRESHOLD) {
|
|
25
|
-
return new Array(filteredData.length).fill(0);
|
|
26
|
-
}
|
|
27
|
-
const multiplier = max ** -1;
|
|
28
|
-
return filteredData.map((n) => n * multiplier);
|
|
29
|
-
};
|
|
30
|
-
const getWaveformSamples = (waveform, sampleAmount) => {
|
|
31
|
-
return normalizeData(filterData(waveform, sampleAmount));
|
|
32
|
-
};
|
|
33
21
|
exports.getWaveformSamples = getWaveformSamples;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import type { SampleOutputRange } from './get-wave-form-samples';
|
|
1
2
|
import type { AudioData } from './types';
|
|
2
3
|
type Bar = {
|
|
3
4
|
index: number;
|
|
4
5
|
amplitude: number;
|
|
5
6
|
};
|
|
6
|
-
export
|
|
7
|
+
export type GetWaveformPortion = {
|
|
7
8
|
audioData: AudioData;
|
|
8
9
|
startTimeInSeconds: number;
|
|
9
10
|
durationInSeconds: number;
|
|
10
11
|
numberOfSamples: number;
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
channel?: number;
|
|
13
|
+
outputRange?: SampleOutputRange;
|
|
14
|
+
dataOffsetInSeconds?: number;
|
|
15
|
+
};
|
|
16
|
+
export declare const getWaveformPortion: ({ audioData, startTimeInSeconds, durationInSeconds, numberOfSamples, channel, outputRange, dataOffsetInSeconds, }: GetWaveformPortion) => Bar[];
|
|
17
|
+
export { Bar };
|
|
@@ -1,17 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getWaveformPortion = void 0;
|
|
4
|
+
const no_react_1 = require("remotion/no-react");
|
|
4
5
|
const get_wave_form_samples_1 = require("./get-wave-form-samples");
|
|
6
|
+
const validate_channel_1 = require("./validate-channel");
|
|
7
|
+
const concatArrays = (arrays) => {
|
|
8
|
+
// sum of individual array lengths
|
|
9
|
+
const totalLength = arrays.reduce((acc, value) => acc + value.length, 0);
|
|
10
|
+
const result = new Float32Array(totalLength);
|
|
11
|
+
// for each array - copy it over result
|
|
12
|
+
// next array is copied right after the previous one
|
|
13
|
+
let length = 0;
|
|
14
|
+
for (const array of arrays) {
|
|
15
|
+
result.set(array, length);
|
|
16
|
+
length += array.length;
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
5
20
|
/*
|
|
6
21
|
* @description Takes bulky waveform data (for example fetched by getAudioData()) and returns a trimmed and simplified version of it, for simpler visualization
|
|
7
22
|
* @see [Documentation](https://remotion.dev/docs/get-waveform-portion)
|
|
8
23
|
*/
|
|
9
|
-
const getWaveformPortion = ({ audioData, startTimeInSeconds, durationInSeconds, numberOfSamples, }) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
const getWaveformPortion = ({ audioData, startTimeInSeconds, durationInSeconds, numberOfSamples, channel = 0, outputRange = 'zero-to-one', dataOffsetInSeconds, }) => {
|
|
25
|
+
(0, validate_channel_1.validateChannel)(channel, audioData.numberOfChannels);
|
|
26
|
+
const waveform = audioData.channelWaveforms[channel];
|
|
27
|
+
const startSample = Math.floor((startTimeInSeconds - (dataOffsetInSeconds !== null && dataOffsetInSeconds !== void 0 ? dataOffsetInSeconds : 0)) * audioData.sampleRate);
|
|
28
|
+
const endSample = Math.floor((startTimeInSeconds - (dataOffsetInSeconds !== null && dataOffsetInSeconds !== void 0 ? dataOffsetInSeconds : 0) + durationInSeconds) *
|
|
29
|
+
audioData.sampleRate);
|
|
30
|
+
const samplesBeforeStart = 0 - startSample;
|
|
31
|
+
const samplesAfterEnd = endSample - waveform.length;
|
|
32
|
+
const clampedStart = Math.max(startSample, 0);
|
|
33
|
+
const clampedEnd = Math.min(waveform.length, endSample);
|
|
34
|
+
const padStart = samplesBeforeStart > 0
|
|
35
|
+
? new Float32Array(samplesBeforeStart).fill(0)
|
|
36
|
+
: null;
|
|
37
|
+
const padEnd = samplesAfterEnd > 0 ? new Float32Array(samplesAfterEnd).fill(0) : null;
|
|
38
|
+
const arrs = [
|
|
39
|
+
padStart,
|
|
40
|
+
waveform.slice(clampedStart, clampedEnd),
|
|
41
|
+
padEnd,
|
|
42
|
+
].filter(no_react_1.NoReactInternals.truthy);
|
|
43
|
+
const audioBuffer = arrs.length === 1 ? arrs[0] : concatArrays(arrs);
|
|
44
|
+
return (0, get_wave_form_samples_1.getWaveformSamples)({
|
|
45
|
+
audioBuffer,
|
|
46
|
+
numberOfSamples,
|
|
47
|
+
outputRange,
|
|
48
|
+
}).map((w, i) => {
|
|
15
49
|
return {
|
|
16
50
|
index: i,
|
|
17
51
|
amplitude: w,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { audioBufferToDataUrl } from './audio-buffer/audio-url-helpers';
|
|
2
|
+
export { createSmoothSvgPath } from './create-smooth-svg-path';
|
|
2
3
|
export { getAudioData } from './get-audio-data';
|
|
3
4
|
export { getAudioDuration, getAudioDurationInSeconds, } from './get-audio-duration-in-seconds';
|
|
4
5
|
export { getImageDimensions } from './get-image-dimensions';
|
|
@@ -11,3 +12,4 @@ export type { AudioData, VideoMetadata as VideoData } from './types';
|
|
|
11
12
|
export { useAudioData } from './use-audio-data';
|
|
12
13
|
export { UseWindowedAudioDataOptions, UseWindowedAudioDataReturnValue, useWindowedAudioData, } from './use-windowed-audio-data';
|
|
13
14
|
export { VisualizeAudioOptions, visualizeAudio } from './visualize-audio';
|
|
15
|
+
export { VisualizeAudioWaveformOptions, visualizeAudioWaveform, } from './visualize-audio-waveform';
|
package/dist/index.js
CHANGED
|
@@ -14,9 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.visualizeAudio = exports.useWindowedAudioData = exports.useAudioData = exports.probeWaveFile = exports.getWaveformPortion = exports.getVideoMetadata = exports.getPartialWaveData = exports.getImageDimensions = exports.getAudioDurationInSeconds = exports.getAudioDuration = exports.getAudioData = exports.audioBufferToDataUrl = void 0;
|
|
17
|
+
exports.visualizeAudioWaveform = exports.visualizeAudio = exports.useWindowedAudioData = exports.useAudioData = exports.probeWaveFile = exports.getWaveformPortion = exports.getVideoMetadata = exports.getPartialWaveData = exports.getImageDimensions = exports.getAudioDurationInSeconds = exports.getAudioDuration = exports.getAudioData = exports.createSmoothSvgPath = exports.audioBufferToDataUrl = void 0;
|
|
18
18
|
var audio_url_helpers_1 = require("./audio-buffer/audio-url-helpers");
|
|
19
19
|
Object.defineProperty(exports, "audioBufferToDataUrl", { enumerable: true, get: function () { return audio_url_helpers_1.audioBufferToDataUrl; } });
|
|
20
|
+
var create_smooth_svg_path_1 = require("./create-smooth-svg-path");
|
|
21
|
+
Object.defineProperty(exports, "createSmoothSvgPath", { enumerable: true, get: function () { return create_smooth_svg_path_1.createSmoothSvgPath; } });
|
|
20
22
|
var get_audio_data_1 = require("./get-audio-data");
|
|
21
23
|
Object.defineProperty(exports, "getAudioData", { enumerable: true, get: function () { return get_audio_data_1.getAudioData; } });
|
|
22
24
|
var get_audio_duration_in_seconds_1 = require("./get-audio-duration-in-seconds");
|
|
@@ -39,3 +41,5 @@ var use_windowed_audio_data_1 = require("./use-windowed-audio-data");
|
|
|
39
41
|
Object.defineProperty(exports, "useWindowedAudioData", { enumerable: true, get: function () { return use_windowed_audio_data_1.useWindowedAudioData; } });
|
|
40
42
|
var visualize_audio_1 = require("./visualize-audio");
|
|
41
43
|
Object.defineProperty(exports, "visualizeAudio", { enumerable: true, get: function () { return visualize_audio_1.visualizeAudio; } });
|
|
44
|
+
var visualize_audio_waveform_1 = require("./visualize-audio-waveform");
|
|
45
|
+
Object.defineProperty(exports, "visualizeAudioWaveform", { enumerable: true, get: function () { return visualize_audio_waveform_1.visualizeAudioWaveform; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const validateChannel: (channel: unknown, numberOfChannels: number) => void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateChannel = void 0;
|
|
4
|
+
const validateChannel = (channel, numberOfChannels) => {
|
|
5
|
+
if (typeof channel !== 'number') {
|
|
6
|
+
throw new TypeError(`"channel" must be a number`);
|
|
7
|
+
}
|
|
8
|
+
if (channel % 1 !== 0) {
|
|
9
|
+
throw new TypeError(`"channel" must an integer, got ${channel}`);
|
|
10
|
+
}
|
|
11
|
+
if (Number.isNaN(channel)) {
|
|
12
|
+
throw new TypeError(`The channel parameter is NaN.`);
|
|
13
|
+
}
|
|
14
|
+
if (channel < 0) {
|
|
15
|
+
throw new TypeError('"channel" cannot be negative');
|
|
16
|
+
}
|
|
17
|
+
if (channel > numberOfChannels - 1) {
|
|
18
|
+
throw new TypeError(`"channel" must be ${numberOfChannels - 1} or lower. The audio has ${numberOfChannels} channels`);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
exports.validateChannel = validateChannel;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AudioData } from './types';
|
|
2
|
+
export type VisualizeAudioWaveformOptions = {
|
|
3
|
+
audioData: AudioData;
|
|
4
|
+
frame: number;
|
|
5
|
+
fps: number;
|
|
6
|
+
windowInSeconds: number;
|
|
7
|
+
numberOfSamples: number;
|
|
8
|
+
channel?: number;
|
|
9
|
+
dataOffsetInSeconds?: number;
|
|
10
|
+
};
|
|
11
|
+
export declare const visualizeAudioWaveform: (parameters: VisualizeAudioWaveformOptions) => number[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.visualizeAudioWaveform = void 0;
|
|
4
|
+
const get_waveform_portion_1 = require("./get-waveform-portion");
|
|
5
|
+
const cache = {};
|
|
6
|
+
const visualizeAudioWaveformFrame = ({ audioData, frame, fps, numberOfSamples, windowInSeconds, channel, dataOffsetInSeconds, }) => {
|
|
7
|
+
if (windowInSeconds * audioData.sampleRate < numberOfSamples) {
|
|
8
|
+
throw new TypeError(windowInSeconds +
|
|
9
|
+
's audiodata does not have ' +
|
|
10
|
+
numberOfSamples +
|
|
11
|
+
' bars. Increase windowInSeconds or decrease numberOfSamples');
|
|
12
|
+
}
|
|
13
|
+
const cacheKey = audioData.resultId +
|
|
14
|
+
frame +
|
|
15
|
+
fps +
|
|
16
|
+
numberOfSamples +
|
|
17
|
+
'waveform' +
|
|
18
|
+
dataOffsetInSeconds;
|
|
19
|
+
if (cache[cacheKey]) {
|
|
20
|
+
return cache[cacheKey];
|
|
21
|
+
}
|
|
22
|
+
const time = frame / fps;
|
|
23
|
+
const startTimeInSeconds = time - windowInSeconds / 2;
|
|
24
|
+
return (0, get_waveform_portion_1.getWaveformPortion)({
|
|
25
|
+
audioData,
|
|
26
|
+
startTimeInSeconds,
|
|
27
|
+
durationInSeconds: windowInSeconds,
|
|
28
|
+
numberOfSamples,
|
|
29
|
+
outputRange: 'minus-one-to-one',
|
|
30
|
+
channel,
|
|
31
|
+
dataOffsetInSeconds,
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
const visualizeAudioWaveform = (parameters) => {
|
|
35
|
+
const data = visualizeAudioWaveformFrame(parameters);
|
|
36
|
+
return data.map((value) => value.amplitude);
|
|
37
|
+
};
|
|
38
|
+
exports.visualizeAudioWaveform = visualizeAudioWaveform;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/media-utils"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/media-utils",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.268",
|
|
7
7
|
"description": "Utilities for working with media files",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"sideEffects": false,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "https://github.com/remotion-dev/remotion/issues"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"remotion": "4.0.
|
|
16
|
+
"remotion": "4.0.268"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"react": ">=16.8.0",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"eslint": "9.19.0",
|
|
24
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
24
|
+
"@remotion/eslint-config-internal": "4.0.268"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
27
27
|
"remotion",
|