If a BufferingAudioSource
is reading from an audio source that cannot deliver enough data, e.g. because it gets data from from a slow network connection, parts of the buffer will be filled with silence, resulting in stuttering and fragmented audio when the buffer is played back.
The problem originates from the method ::readBufferSection
, which is regularly called on a background thread to fill the buffer. It does not take into account the number of samples that are available in it’s source, which in turn causes BufferingAudioSource to contain half filled blocks of audio.
In juce_bufferingAudioSource.cpp:
void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset)
{
if (source->getNextReadPosition() != start)
source->setNextReadPosition (start);
AudioSourceChannelInfo info (&buffer, bufferOffset, length);
source->getNextAudioBlock (info);
}
If source
, or a source later in the chain, cannot fill the entire buffer given by info
with audio, the rest of the samples will be set to zero. This may originate all the way from the beginning of the chain of sources, which in my setup is a WebInputStream that is read by a CoreAudioReader:
WebInputStream -> CoreAudioReader -> AudioFormatReader -> AudioFormatReaderSource -> BufferingAudioSource
If the WebInputStream does not deliver enough samples because of a halting http connection, then CoreAudioReader will clear the rest.
In juce_coreAudioFormat.cpp:
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
int64 startSampleInFile, int numSamples) override
{
clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
startSampleInFile, numSamples, lengthInSamples);
Later in the same method, bytes are read from the input stream, by:
UInt32 numFramesToRead = (UInt32) numThisTime;
OSStatus status = ExtAudioFileRead (audioFileRef, &numFramesToRead, bufferList);
Here, ExtAudioFileRead
upon return sets the variable numFramesToRead
to the number of bytes that were available, but this information is not utilized at a later stage.
To provide enough samples for the audio device output, clearing may be necessary in the top level source, but for intermediate sources it causes a problem. The number of samples that are actually read should propagate back through the chain.
What would you recommend as best approach to overcome the gaps in the resulting buffer?