Home > other >  Creating a resulting set of numbers which is "filled" to fit a certain width based on the
Creating a resulting set of numbers which is "filled" to fit a certain width based on the

Time:11-06

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:

img1

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);

  • Related