86 lines
3.7 KiB
JavaScript
86 lines
3.7 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.convertAudioData = void 0;
|
|
const data_types_1 = require("./audio-data/data-types");
|
|
const is_planar_format_1 = require("./audio-data/is-planar-format");
|
|
const validateRange = (format, value) => {
|
|
if (format === 'f32' || format === 'f32-planar') {
|
|
if (value < -1 || value > 1) {
|
|
throw new Error('All values in a Float32 array must be between -1 and 1');
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Converts an `AudioData` object to a new `AudioData` object with a different sample rate or format.
|
|
* @see [Documentation](https://remotion.dev/docs/webcodecs/convert-audiodata)
|
|
*/
|
|
const convertAudioData = ({ audioData, newSampleRate = audioData.sampleRate, format = audioData.format, }) => {
|
|
const { numberOfChannels, sampleRate: currentSampleRate, numberOfFrames: currentNumberOfFrames, } = audioData;
|
|
const ratio = currentSampleRate / newSampleRate;
|
|
const newNumberOfFrames = Math.floor(currentNumberOfFrames / ratio);
|
|
if (newNumberOfFrames === 0) {
|
|
throw new Error('Cannot resample - the given sample rate would result in less than 1 sample');
|
|
}
|
|
if (newSampleRate < 3000 || newSampleRate > 768000) {
|
|
throw new Error('newSampleRate must be between 3000 and 768000');
|
|
}
|
|
if (!format) {
|
|
throw new Error('AudioData format is not set');
|
|
}
|
|
if (format === audioData.format &&
|
|
newNumberOfFrames === currentNumberOfFrames) {
|
|
return audioData.clone();
|
|
}
|
|
const DataType = (0, data_types_1.getDataTypeForAudioFormat)(format);
|
|
const isPlanar = (0, is_planar_format_1.isPlanarFormat)(format);
|
|
const planes = isPlanar ? numberOfChannels : 1;
|
|
const srcChannels = new Array(planes)
|
|
.fill(true)
|
|
.map(() => new DataType((isPlanar ? 1 : numberOfChannels) * currentNumberOfFrames));
|
|
for (let i = 0; i < planes; i++) {
|
|
audioData.clone().copyTo(srcChannels[i], {
|
|
planeIndex: i,
|
|
format,
|
|
});
|
|
}
|
|
const data = new DataType(newNumberOfFrames * numberOfChannels);
|
|
const chunkSize = currentNumberOfFrames / newNumberOfFrames;
|
|
for (let newFrameIndex = 0; newFrameIndex < newNumberOfFrames; newFrameIndex++) {
|
|
const start = Math.floor(newFrameIndex * chunkSize);
|
|
const end = Math.max(Math.floor(start + chunkSize), start + 1);
|
|
if (isPlanar) {
|
|
for (let channelIndex = 0; channelIndex < numberOfChannels; channelIndex++) {
|
|
const chunk = srcChannels[channelIndex].slice(start, end);
|
|
const average = chunk.reduce((a, b) => {
|
|
return a + b;
|
|
}, 0) / chunk.length;
|
|
validateRange(format, average);
|
|
data[newFrameIndex + channelIndex * newNumberOfFrames] = average;
|
|
}
|
|
}
|
|
else {
|
|
const sampleCountAvg = end - start;
|
|
for (let channelIndex = 0; channelIndex < numberOfChannels; channelIndex++) {
|
|
const items = [];
|
|
for (let k = 0; k < sampleCountAvg; k++) {
|
|
const num = srcChannels[0][(start + k) * numberOfChannels + channelIndex];
|
|
items.push(num);
|
|
}
|
|
const average = items.reduce((a, b) => a + b, 0) / items.length;
|
|
validateRange(format, average);
|
|
data[newFrameIndex * numberOfChannels + channelIndex] = average;
|
|
}
|
|
}
|
|
}
|
|
const newAudioData = new AudioData({
|
|
data,
|
|
format,
|
|
numberOfChannels,
|
|
numberOfFrames: newNumberOfFrames,
|
|
sampleRate: newSampleRate,
|
|
timestamp: audioData.timestamp,
|
|
});
|
|
return newAudioData;
|
|
};
|
|
exports.convertAudioData = convertAudioData;
|