Failing assertions while reading FLAC


#1

I am building a sampler for an instrument with thousands of samples in FLAC. It’s 4-5GB of audio files, so I don’t load the entire lib in memory - small portion of every sample is loaded and the rest I am streaming, DFD.

There is an AudioFormatReader instantiated for every single file.

If I convert the samples to wave (I don’t think I needed to change anything in my code since the AudioFormatManager picks the right AudioFormatReader for me) my sampler works without ever breaking in the decoder code of JUCE.

But using FLAC files, I get exceptions due to unmet assertions in the FLAC decoder. And it’s in at least few different places.

Sometimes I would get an assertion error here:

FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits)
{
        ...
	FLAC__ASSERT(br->consumed_words <= br->words);
        ...
}

The weird thing in this specific instance is that the inspecting the two members, after Xcode breaks on the failing assert, the assertion seems to be correct… consumed_words is <= to words…

Here is another place, where assertion may fail:

int64 FileInputStream::getTotalLength()
{
    // You should always check that a stream opened successfully before using it!
    jassert (openedOk());

    return file.getSize();
}

I see the message, but isn’t this handled by the AudioFormatReader for me?

Another time it will fail with a negative number of bytes to read (buffer isn’t nullptr in the assert) while I clearly can see that I am reading in the available area (getting 4000 samples after the 40 000th in a audioFormatReader with lengthInSamples=700 000 samples), here:

int FileInputStream::read (void* buffer, int bytesToRead)
{
    ...
    // The buffer should never be null, and a negative size is probably a
    // sign that something is broken!
    jassert (buffer != nullptr && bytesToRead >= 0);
    ...
}

… Aaaand another time I would get an assertion error here, because br->bytes=0:

FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits)
{
    ...
    if(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */
        ...
    }
    else {
        /* in this case we're starting our read at a partial tail word;
         * the reader has guaranteed that we have at least 'bits' bits
         * available to read, which makes this case simpler.
         */
        /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */
        if(br->consumed_bits) {
            /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */
            FLAC__ASSERT(br->consumed_bits + bits <= br->bytes*8);
            ...
        }
        else
        {
            ...
        }
    }
}

There might be other exceptions, too, but I don’t think there is any point of getting all of them in this post.

Can someone help?


#2

Few further observations:

  • larger streamBuffers = rarer streaming = rarer these failures happen.
  • The issues are not connected to specific files/samples (I am playing automatically, the exact same sequence of midi messages from a MIDI file and still, every run it fails different or may not fail at all in the time the midi is played (and especially if larger buffer size is selected)).

Hasn’t anyone had a similar problem? :frowning:


#3

Sounds very odd. Maybe there’s some kind of race condition in your app that’s messing things up?


#4

Spot-on.

Thanks @jules.

I wasn’t locking calls to AudioFormatReader::read. I guess I haven’t seen the issue with .wav, because the data isn’t compressed and there is no state in the reading process?