I'm trying to figure out a way to "fill" an array of audio meters to to fit a certain width. For instance, if I say that I need the resulting array to have 12 elements, then,
const input = [ -160, -140, -100, -80 ]
would become
const result = [ -160, -160, -160, -140, -140, -140, -100, -100, -100, -80, -80, -80, ]
Likewise, if the input has more than 12 elements, for instance:
// 20 elements
const input = [ -160, -160, -160, -140, -140, -140, -100, -100, -100, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, ]
then here we know that each meter value represents 1.67 bars (20 elements / 12 required width contraint), so the result should be something like this:
const result = [ -160, -160, -140, -140, -100, -80, -80, -80, -80, -80, -80, -80, ]
Here's one of my attempts at the following:
const input = [ -160, -160, -160, -140, -140, -140, -100, -100, -100, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, ];
const MAX_BARS_FOR_AMPLITUDE = 12;
const metersPerBar = input.length / MAX_BARS_FOR_AMPLITUDE;
const barsPerMeter = 1 / metersPerBar;
const { result } = input.reduce(
(
acc,
meter,
) => {
if (barsPerMeter < 1) {
if (acc.chunkAccumulator.chunks < 1) {
acc.chunkAccumulator.meterValue ||= meter;
acc.chunkAccumulator.chunks = barsPerMeter;
} else {
acc.result.push(acc.chunkAccumulator.meterValue);
acc.chunkAccumulator.meterValue = 0;
acc.chunkAccumulator.chunks = 0;
}
} else {
if (acc.chunkAccumulator.chunks < barsPerMeter) {
acc.chunkAccumulator.meterValue ||= meter;
acc.chunkAccumulator.chunks = barsPerMeter;
} else {
acc.result.push(acc.chunkAccumulator.meterValue);
acc.chunkAccumulator.meterValue = 0;
acc.chunkAccumulator.chunks = 0;
}
}
return acc;
},
{
result: [],
chunkAccumulator: {
meterValue: 0,
chunks: 0,
},
},
);
console.log(result);
I've tried fiddling around, but the above solution never returns a resulting array of required length (MAX_BARS_FOR_AMPLITUDE
), and I feel like I'm taking the wrong approach altogether. I'm also unsure of how I should be handling the case when, for instance, 1.67 meter values represent 1 whole bar.
The idea here is to draw out an audiowave with an always-set number of bars, regardless of the number of meter values in the input
set. For instance, input could be const input = [-20]
, and this would result in an array of length MAX_BARS_FOR_AMPLITUDE
, where each element would be equal to -20
. Metering is recorder every 50ms, so this particular input
array would represent a 50ms audio recording.
By audiowave, I mean something like this:
What am I doing wrong here or is there a better way to handle this altogether?
Here's another attempt, but this doesn't work for an input with a length less that that of MAX_BARS_FOR_AMPLITTUDE
:
const input = [ -160, -160, -160, -140, -140, -140, -100, -100, -100, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, ];
const MAX_BARS_FOR_AMPLITUDE = 40;
const metersPerBar = input.length / MAX_BARS_FOR_AMPLITUDE;
const barsPerMeter = 1 / metersPerBar;
let currentPoint = 0;
let currentChunk = 0;
const result = Array.from({ length: MAX_BARS_FOR_AMPLITUDE }, (_, index) => {
if (barsPerMeter > currentChunk) {
currentChunk = barsPerMeter;
} else {
currentPoint ;
currentChunk = 0
}
return input[currentPoint];
});
console.log(result);
CodePudding user response:
Just map your needed range to range of your actual values:
const input = [ -160, -140, -100, -80 ];
$range.addEventListener('input', e => $pre.textContent = JSON.stringify(spread(input, e.target.valueAsNumber)));
function spread(arr, length){
return Array.from({length}, (_, idx) => arr[idx * (arr.length / length) | 0]);
}
<input type="range" min="4" max="20" id="$range" value="4">
<pre id="$pre"></pre>
CodePudding user response:
function fillAudioMeters(input, desiredWidth) {
const inputLength = input.length;
const output = [];
const outputLength = Math.max(inputLength, desiredWidth);
if (inputLength === 0) {
// If no input values, fill with zeros
return Array(outputLength).fill(0);
}
const step = inputLength / outputLength;
for (let i = 0; i < outputLength; i ) {
const index = Math.floor(i * step);
output.push(input[index < inputLength ? index : inputLength - 1]);
}
return output;
}
// Example usage:
const input1 = [-160, -140, -100, -80];
const result1 = fillAudioMeters(input1, 12);
console.log(result1);
const input2 = [-160, -160, -160, -140, -140, -140, -100, -100, -100, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80];
const result2 = fillAudioMeters(input2, 12);
console.log(result2);