Memory Stream bug with AudioFormatReader

Here is my test case:

  1. I read in a file to AudioBuffer and print out the number of samples
AudioBuffer<float> testBuffer(reader->numChannels, reader->lengthInSamples);
reader->read(&testBuffer, 0, reader->lengthInSamples, 0, true, true);
DBG("Sample size of buffer from original file: " + String(testBuffer.getNumSamples()));
  1. I write to a MemoryOutputStream like so:
wavWriter1 = wavAudioFormat.createWriterFor(wavMemOutputStream, reader->sampleRate, reader->numChannels, reader->bitsPerSample, reader->metadataValues, 0);
wavWriter1->writeFromAudioSampleBuffer(testBuffer, 0, reader->lengthInSamples);
  1. I then read the memory stream into an AudioBuffer and print the number of samples like so:
AudioFormatReader* reader2 = wavAudioFormat.createReaderFor(wavMemInputStream, false);
AudioBuffer<float> testBuffer2(reader2->numChannels, reader2->lengthInSamples);
reader2->read(&testBuffer2, 0, reader2->lengthInSamples, 0, true, true);
DBG("Sample size of buffer after memory and back: " + String(testBuffer2.getNumSamples()));

This issue is: testBuffer and testBuffer2 should have the same number of samples. However, they do not. The bug seems to be when reader2 is created.

Here is the output:

Sample size of buffer from original file: 736278
Sample size of buffer after memory and back: 0

Any help is much appreciated!

3 Likes

Hello there.
Wow, I have the same problem!!!

Make sure you flush() your wavWriter1 before attempting to read what it has written (this happens automatically when the writer is destroyed).
If you try to read from the memoryblock while the writer is still in existence, its content may still be in an inconsistent state (e.g. the samples number stated there may be still 0 because that gets updated on flush)

That may work, but you will still get a sample size mismatch with the original file when using a MemoryOutputStream containing a Flac buffer. Your sample size will always be : SizeOriginal - 1152

Change my mind

Thanks for the flush tip. Adding flush() works for WavAudioFormat now, but I am getting the same problem as @Piston with FlacAudioFormat (even with the flush()).

Here is the output log for flac (same code as before but with FlacAudioFormat):

Sample size of buffer from original file: 736278
Sample size of buffer after memory and back: 736128

Digging deeper into juce::FlacAudioFormat reveals that juce is not using metadataValues for setting up the blocksize for the flac encoding.

AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out,
                                                     double sampleRate,
                                                     unsigned int numberOfChannels,
                                                     int bitsPerSample,
                                                     const StringPairArray& /*metadataValues*/,
                                                     int qualityOptionIndex)
{
    if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
    {
        std::unique_ptr<FlacWriter> w (new FlacWriter (out, sampleRate, numberOfChannels,
                                                     (uint32) bitsPerSample, qualityOptionIndex));
        if (w->ok)
            return w.release();
    }

    return nullptr;
}

This means the flac encoder defaults to a fixed block size of 1152 for max_lpc_order of 0, which itself is set due to a qualityOptionIndex of 0 (from FlacAudioFormat::createWriterFor()) . In stream_encoder.c:

if(encoder->protected_->blocksize == 0) {
		if(encoder->protected_->max_lpc_order == 0)
			encoder->protected_->blocksize = 1152;
		else
			encoder->protected_->blocksize = 4096;
	}

Is it on the horizon to be able to set the flac min and max blocksize with FlacAudioFormat::createWriterFor() via metadataValues?

Or, are we expected to set qualityOptionIndex to 0, pad the AudioBuffer with 0s to make its size a multiple of 1152, and then make sure the receiving AudioBuffer knows the original length (i.e. to trim the padding zeroes)?

1 Like