Meters and block sizes (Larger block sizes affect how they look)

Hey everyone, how are you?
Quick question regarding feeding data to meters/histograms in relation to the block size.

Right now, I’m feeding my meters with either an atomic float or by using a fifo that pushes the data from the processBlock() to the timerCallback method in the editor.
This works well and the meters look smooth at lower block sizes.

However, increasing the block size to something like 2048 makes the meters look choppy (way more noticeable in the histograms).

I’m guessing this has something to do with the repaint speed and the the time it takes to process those larger blocks, right? And it’s not getting the most recent data quickly enough?

here’s the histogram with a 128 block size:
histo 1

and here with a block size of 2048:
histo 2

Any idea on what might be happening and how to fix this?
Thanks a lot in advance!

At a 48000 Hz sample rate, a 128-sample block takes about 3 ms to process. A 2048-sample block takes ~43 ms. If your UI is rendering at 60 Hz, there is ~17 ms between repaints. So with a larger block size the audio thread isn’t delivering its data fast enough to the UI.

However, exactly what happens depends on how you’re sending the data. After all, one block of 2048 samples spans the same amount of time as 16 blocks of 128 samples, so there isn’t anything inherently problematic about larger block sizes. But you may need to rethink your drawing approach.

  • If you want smooth UI updates, the updates must be often.
  • If the block size is large, atomic updates will NOT be often.
  • UNLESS - you send many updates from the processor per block (via a FIFO).
  • If you send many updates per block, they will tend to arrive on the UI thread in ‘clumps’, so they need to be timestamped to allow the UI to present them one at a time.

You will also have to accept that by sending multiple updates in ‘clumps’ of timestamped events requires that the updates be presented on the UI with some latency which is at least as much as the block size.

Alright, thank you both.
Will give all of this a try.

I think the simplest way will be to push all of the available samples with the FIFO and adapting the way I use that data in the UI.

What would be a good way of approaching this?

I have this for now and it works a lot better, but it still doesn’t look the same with block sizes of 2048 when compared to something like 256 or lower:

        if (audioProcessor.inputFifo.getNumAvailableForReading() > 0)
        {
            const int samplesPerFrame = 128;

            while (audioProcessor.inputFifo.pull(inputBuffer))
            {
                remainingSamples.insert(remainingSamples.end(),
                    inputBuffer.getReadPointer(0),
                    inputBuffer.getReadPointer(0) + inputBuffer.getNumSamples());

                while (remainingSamples.size() >= samplesPerFrame)
                {
                    juce::AudioBuffer<float> frameBuffer(1, samplesPerFrame);
                    frameBuffer.copyFrom(0, 0, remainingSamples.data(), samplesPerFrame);

                    histogram.update(juce::Decibels::gainToDecibels(
                        frameBuffer.getMagnitude(0, samplesPerFrame)));

                    remainingSamples.erase(remainingSamples.begin(),
                        remainingSamples.begin() + samplesPerFrame);
                }
            }
        }```

Again, thanks for the replies!

It’s possible to use a large block size without sacrificing how smooth the animation looks. But it requires you to offset the pixel offset of each block in proportion to how much time has elapsed between blocks. This timing will also need to be coordinated with how your blocks are enqueued, as processBlock will enqueue them at different rates depending on many factors. One thing which works well is to use a second queue, and only push blocks to the 2nd queue when their due timestamp has elapsed.