skip to Main Content

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

2

Answers


  1.         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);
    Login or Signup to reply.
  2. 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>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search