Reservoir abstraction and DRY

There’s a common repeating pattern, which also repeats several times in JUCE, to demonstrate it here are small snippets of some of the repeating logic:

From juce_OggVorbisAudioFormat.cpp:OggReader::readSamples:

auto numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile);
if (startSampleInFile >= reservoirStart && numAvailable > 0)
{
    // got a few samples overlapping, so use them before seeking..
    auto numToUse = jmin (numSamples, numAvailable);
    ...
}

From juce_FlacAudioFormat.cpp:FlacReader::readSamples:

if (startSampleInFile >= reservoirStart
      && startSampleInFile < reservoirStart + samplesInReservoir))
{
    auto num = (int) jmin ((int64) numSamples,
                            reservoirStart + samplesInReservoir - startSampleInFile);
    ...
}

Even if not trivial to notice because of different styles, this is a repetition, and actually there’s a lot more code repeating between the two readers, with probably more reservoirs implemented around JUCE (I also see the term mentioned in juce_win32_WASAPI.cpp and juce_BufferedInputStream.h).

I suggest that JUCE should offer the reservoir abstraction as a standalone class, and use it to DRY and improve its own code in the readers mentioned above.

I’m suggesting this because I find myself needing such a reservoir class too and I don’t like to duplicate existing functionality.

Btw the hardest part of this the JUCE team has already done which is finding a great name for this pattern :slight_smile:

Btw I don’t know if the votes matter or not, but having reached my votes limit, I retracted my vote in the less important Make text boxes in GenericAudioProcessorEditor editable to vote for this, because I’d really like this to happen :slight_smile:

It looks like there are (at least) a couple of slightly different reservoir patterns in JUCE. I’ve attempted to factor out both the patterns I spotted, so that we can reuse the common bits in JUCE. This factored functionality has also been made public.

Firstly, there is a “queue-like” reservoir that is used in places like the WASAPI wrapper and the DryWetMixer. This allows pushing blocks of variable size into some backing storage, and then retrieving blocks of a different size. This might also be useful for applications such as FFT processing, where a plugin may receive audio input in chunks of variable size, and then process an FFT frame whenever enough input has become available.

The second kind of reservoir is used in the audio format readers and BufferedInputStream. This utility function maintains a buffer containing the most recently-read part of a longer stream. When a read is requested, input will be taken from the buffer whenever possible. If the requested data is not available in the buffer, the buffer will be refilled in one go from the backing stream. In this way, the number of read operations is kept to a minimum, which can be useful to speed up operations like reading from disk.

Hopefully the provided docs will make everything clear. If you run into any problems, or have other questions, please let us know in a new thread, and include a link back to this thread.

I’m closing this thread so that spent votes can be reclaimed.

3 Likes