Multiple buffers?

So I was searching around for how to go about plotting a synth wave on a graph and I found some code that I am trying sense of

    static constexpr size_t bufferSize = 512;

// Number of container elements of audio buffer for plotting held in this class
   static constexpr size_t numBuffers = 5;

   void push( SampleType* dataToPush, size_t numSamples)
        jassert(numSamples <= bufferSize);
        int start1, size1, start2, size2;
        abstractFifo.prepareToWrite(1, start1, size1, start2, size2);
        if (size1 > 0) {
            FloatVectorOperations::copy(buffers[(size_t)start1].data(), dataToPush, (int)jmin(bufferSize, numSamples));
    void pop(SampleType* outputBuffer)
        int start1, size1, start2, size2;
        abstractFifo.prepareToRead(1, start1, size1, start2, size2);
        if (size1 > 0) {
            FloatVectorOperations::copy(outputBuffer, buffers[(size_t)start1].data(), (int)bufferSize);

    std::array <std::array<SampleType, bufferSize>, numBuffers> buffers;
    AbstractFifo abstractFifo{ numBuffers };

In the private field where the array contains a certain number of buffers, why would you need multiple buffers when plotting on a graph? Would one always have multiple buffers of a certain size to plot points?

The author uses not a circular buffer, but instead a circle of 5 individual buffers, that he/she fills and reads round robin.

I wouldn’t use the AbstractFifo in this case, because the benefit only unfolds, when you copy blocks of samples, and not single elements (each element happens to be one block here). But it is perfectly fine to do so.

Also the code is not very stable, because when you pull from those buffers, you don’t know, how many samples were copied into each buffer. So if the caller used different block sizes when writing, it reads garbage behind the shorter blocks.

I see so if I wanted to achieve the same feat I would use a circular buffer instead? And how would I go about doing so? if you do not mind.

Yes, I would prefer a circular buffer, since that will work regardless of size of incoming buffers.

The AbstractFIFO class is a great helper here, although there is one difference: a FIFO keeps and delivers every sample and only once.
What you actually want is to look into the latest past, so a write pointer is actually sufficient.

std::atomic<int> writePos = 0;
AudioBuffer<float> fifo;

pushSamples (const AudioBuffer<float>& buffer)
    jassert (buffer.getNumSamples() < fifo.getNumSamples());

    int firstHalf = std::min (buffer.getNumSamples(), fifo.getNumSamples() - writePos);
    // repeat the next 3 lines for each channel
    fifo.copyFrom (0, writePos, buffer.getReadPointer (0), firstHalf);
    if (firstHalf < buffer.getNumSamples)
        fifo.copyFrom (0, 0, buffer.getReadPointer (0, firstHalf), buffer.getNumSamples() - firstHalf);

    // do this in an extra variable to keep the update of the atomic, erm atomic
    auto nextPos = writePos + buffer.getNumSamples();
    if (nextPos >= fifo.getNumSamples() ) 
        nextPos -= fifo.getNumSamples(); (nextPos);

void readLatestSamples (AudioBuffer<float>& buffer)
    auto startSample = writePos.load() - buffer.getNumSamples();
    if (startSample < 0)
        startSample += fifo.getNumSamples();

    int firstHalf = std::min (buffer.getNumSamples(), fifo.getNumSamples() - startSample);
    // repeat the next 3 lines for each channel
    buffer.copyFrom (0, 0, fifo.getReadPointer (0, startSample), firstHalf);
    if (firstHalf < buffer.getNumSamples)
        buffer.copyFrom (0, firstHalf, fifo.getReadPointer (0), buffer.getNumSamples() - firstHalf);

    // no need to update writePos

have the fifo large enough, so you never write to the place where you read, or you get into undefined behaviour, so minimum in realtime 2*estimatedSamplesPerBlock, but since you won’t necessarily paint each block, rather each 5th, it’s better to have a much larger fifo

1 Like