Issue with FlacReader


#1

Hi Jules,
I initially rendered one .wav file to .flac file using juce 1.46.
Then i used the .flac file to render it back to the .wav file.

when i compared the flies in protools , both were exactly same.

Now i changes my code and i rendered the .flac file into two .wav files.( in the same code one by one)
Now i found out that the second wave file is missing some initial samples.

I guess there is something wrong in the FlacAudioformatReader which skips some of the samples from the reader and renders it out.

Can u please guide me to encounter that problem.

Thanks
Godwin.


#2

I’ve never seen a problem with the flac stream before - I suspect you’re probably just not flushing a stream or something in whatever code you’ve written to copy the data.


#3

I m using the same code for the other format. Which read and writes it properly.
Check this image for reference. This might help u.

Almost initial 2000 samples are gone. U can make out from the wave formation. And i can even hear that glitch. One track is playing before the other.


#4

Even if in that case how the first time it rendered properly. It shouldn’t have rendered the first wave file right.

The problem is rendering the file second time onwards.

[quote] // had some problems with flac crashing if the read pos is aligned more
// accurately than this. Probably fixed in newer versions of the library, though.
reservoirStart = (int) (startSampleInFile & ~511);
FLAC__stream_decoder_seek_absolute (decoder, (FLAC__uint64) reservoirStart);[/quote]

Is there anything wrong in this. This code is in the read method of the FlacReader.

Thanks


#5

So you’re re-using the same flac reader each time? Why not just create a new one? That way you don’t have to rely on its seek behaviour being correct (which is always a bit risky with any compressed format)


#6

I Fixed the issue. It was very tricky. The culprit was the “FLAC__stream_decoder_seek_absolute” call.

The documentation of the FlacDecoder says.

Even if it is not mention here, When i debugged the code i found that when we call “FLAC__stream_decoder_seek_absolute()” method from the FlacReader, the Flac decoder will internally call the “FLAC__stream_decoder_process_single()” method where we will read some bytes of data.( look at the line number 3130 of the stream_decoder.c in Libflac ).

So i did small change in the juce FlacReader.

I replaced the code in read method.

 else
            {
				if (startSampleInFile < reservoirStart
                     || startSampleInFile > reservoirStart + jmax (samplesInReservoir, 511))
                {
                    if (startSampleInFile >= (int) lengthInSamples)
                    {
                        samplesInReservoir = 0;
                        break;
                    }

                    // had some problems with flac crashing if the read pos is aligned more
                    // accurately than this. Probably fixed in newer versions of the library, though.
                    reservoirStart = (int) (startSampleInFile & ~511);
                    FLAC__stream_decoder_seek_absolute (decoder, (FLAC__uint64) reservoirStart);
                }
                else
                {
                    reservoirStart += samplesInReservoir;
					
					
                }
                 samplesInReservoir = 0;
					FLAC__stream_decoder_process_single (decoder);
				
				if (samplesInReservoir == 0)
                    break;
            }

with this piece of code

 else
            {
				if (startSampleInFile < reservoirStart
                     || startSampleInFile > reservoirStart + jmax (samplesInReservoir, 511))
                {
                    if (startSampleInFile >= (int) lengthInSamples)
                    {
                        samplesInReservoir = 0;
                        break;
                    }

                    // had some problems with flac crashing if the read pos is aligned more
                    // accurately than this. Probably fixed in newer versions of the library, though.
                    reservoirStart = (int) (startSampleInFile & ~511);
                    FLAC__stream_decoder_seek_absolute (decoder, (FLAC__uint64) reservoirStart);
                }
                else
                {
                    reservoirStart += samplesInReservoir;
					
					samplesInReservoir = 0;
					FLAC__stream_decoder_process_single (decoder);
                }
				
				if (samplesInReservoir == 0)
                    break;
            }

This bug was only visible (i.e. actually happens ) when we render the file twice or more time using the same reader without recreating the reader.
This bug will also be there when we seek back to any point in the file to read back from there.

Here is the result after i made the change to the juce code.

Thanks
Godwin


#7

Hi,

[quote]
So you’re re-using the same flac reader each time? Why not just create a new one? That way you don’t have to rely on its seek behaviour being correct (which is always a bit risky with any compressed format) [/quote]

The problem is not only when rendering the flac to any other format.
The major problem comes when u are using the FlacReader for AudioSource to playback.

The typical DAW should support seeking to any place in real time right!!! And the problem arises in these situations, U seek back - u hear glitch.( initial read bytes are gone)

Thanks
Godwin


#8

Ah! Many thanks for tracking that down!

Just one thing though - I think it should probably set samplesInReservoir to 0 before calling FLAC__stream_decoder_seek_absolute:

[code] if (startSampleInFile < reservoirStart
|| startSampleInFile > reservoirStart + jmax (samplesInReservoir, 511))
{
samplesInReservoir = 0;

                if (startSampleInFile >= (int) lengthInSamples)
                    break;

                // had some problems with flac crashing if the read pos is aligned more
                // accurately than this. Probably fixed in newer versions of the library, though.
                reservoirStart = (int) (startSampleInFile & ~511);
                FLAC__stream_decoder_seek_absolute (decoder, (FLAC__uint64) reservoirStart);
            }
            else

[/code]


#9

Not actually !!!

FLAC__stream_decoder_process_single method will also call the writeCallback_ method where we call use useSamples member to copy to the reservoir and assign samplesInReservoir to the incoming value.

But it will be good. we never know what will beak at time.

So me tension free. So u should be Jules. Keep coding.

Thanks
Godwin


#10

Not actually !!!

FLAC__stream_decoder_process_single method will also call the writeCallback_ method where we call useSamples member to copy to the reservoir and assign samplesInReservoir to the incoming value.

But it will be good. we never know what will break at time.

So me tension free. So u should be Jules. Keep coding.

Thanks
Godwin


#11

just to add to the FlacWriter

we can use the

  static FLAC__StreamEncoderTellStatus encodeTellCallback (const FLAC__StreamEncoder*, FLAC__uint64* absolute_byte_offset , void* client_data )
    {
		if ( ((FlacWriter*) client_data) )
		{
			int64 iPos = ((FlacWriter*) client_data)->output->getPosition();
			*absolute_byte_offset = (FLAC__uint64)iPos;
			return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
		}
		else
		{
			return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED;
		}
    }

instead of

static FLAC__StreamEncoderTellStatus encodeTellCallback (const FLAC__StreamEncoder*, FLAC__uint64*, void*)
    {
        return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED;
    }

hope u will read this Jules

Thanks
Godwin


#12

[quote]FLAC__stream_decoder_process_single method will also call the writeCallback_ method where we call useSamples member to copy to the reservoir and assign samplesInReservoir to the incoming value.
[/quote]

ah yes, I see. Thanks again! And yes, I’ll add that other change too - does it make a big difference to the encoding performance?


#13

…no, hang on: there’s surely a chance that FLAC__stream_decoder_seek_absolute() might fail, in which case the samplesInReservoir will be left at the wrong value, and it’ll think it’s got data when it hasn’t. I think it’s best to zero it before making the call, just in case.


#14

Not much difference but when i read the documentation it says it should be the current position in the stream. so y to leave it empty without doing anything and expecting writer to break somewhere in middle.

yeah u r right!!! thats y i told its better to assign it to null(0).