audioFormatReader returns 0 length on multichannel WAV

Hi everyone,

I came across some inconsistent behavior in AudioFormatWriter/AudioFormatReader. I create a multichannel (four channels) WAV file with AudioFormatWriter. The file can be read by Audacity, no problem. But when I read the very same file with AudioFormatReader, it returns the length of 0 samples through its lengthInSamples field (public!) . All other parameters like bitsPerSample, numChannels and sampleRate are correct. (Public again, tut-tut!). The problem disappears when I change the number of channels to two (stereo).

Here is the code that reproduces the problem:

[code] int sampleCount(1024);
unsigned int channelCount(4); // This causes AudioFormatReader to return length of 0 samples in a non-empty file
// When changed to 2 (stereo) it works correctly

AudioFormatManager	audioFormatManager;
WavAudioFormat*		wavAudioFormat = new WavAudioFormat();
ScopedPointer<AudioFormatReader>	audioFormatReader;

audioFormatManager.registerFormat(wavAudioFormat,	true);

// create 4 channel WAV file in 32 bit float format
{
	ScopedPointer<AudioFormatWriter>	audioFormatWriter;
	File af(  File::getCurrentWorkingDirectory().getChildFile ("sample.wav") );
	af.deleteFile();

	FileOutputStream* outStream = af.createOutputStream();
	
	double sampleRate(48000.0);
	int bitsPerSample(32);
	int qualityOption(0);

	audioFormatWriter = wavAudioFormat->createWriterFor(outStream, sampleRate, channelCount, bitsPerSample, nullptr, qualityOption);

	AudioSampleBuffer abuf(channelCount, sampleCount);
	abuf.clear();

	audioFormatWriter->write( (const int**) abuf.getArrayOfChannels(), sampleCount );

	std::cout << "Saved audio file: " << audioFormatWriter->getNumChannels() << " channels, " 
		 << audioFormatWriter->getBitsPerSample() << " bits, "
		 << audioFormatWriter->getSampleRate() << " Hz, "
		 << sampleCount << " samples.\n\n";

}

// read WAV file
File af( File::getCurrentWorkingDirectory().getChildFile ("sample.wav") );

audioFormatReader = audioFormatManager.createReaderFor(af);

if (audioFormatReader == nullptr)
	return 1;

std::cout << "Loaded audio file: " << audioFormatReader->numChannels << " channels, " 
	 << audioFormatReader->bitsPerSample << " bits, "
	 << audioFormatReader->sampleRate << " Hz, "
	 << audioFormatReader->lengthInSamples << " samples. \n\n";

audioFormatManager.clearFormats();

[/code]

At the same time, if I use AudioSampleBuffer::readFromAudioReader method, it seems to read the content of the file correctly.

Any ideas what is wrong here?

Cheers,
Roman

You didn’t say whether you’re using the latest version of juce… Are you?

Hi Jules,

I’ve just tested it on v2.0.21 and still have 0 samples length.
I run the tests of Windows7 64 and 32 bit, but I don’t think it’s platform-specific.

Cheers,
Roman

If you can assure me that it’s still happening in the tip, then I’ll have a look.

Hi Jules,

I can confirm that the tip freshly cloned from GIT has the same bug.

I didn’t have time to debug it properly but it seems that the WAV file is saved in IEEEFloatFormat subFormat,
but the reader thinks it is ambisonics and sets bytesPerFrame = 0.
If I comment this statement out I get my 1024 samples no problem.
Could you please have a look at this?

The code that I previously posted should be able to replicate the bug.

Cheers,
Roman

Ok, thanks… Try what I’ve just checked-in - I think it was just neglecting to check for the IEEE format.

(Your changes fixed the following: http://www.rawmaterialsoftware.com/viewtopic.php?f=2&t=9463&hilit=freemake)

Ah good, thanks - I hadn’t noticed that thread!

Hi Jules,

I got the latest update, noted the changes, but unfortunately it did not fix my problem.
I think I found the bug, though.

When we are saving WAVE_FORMAT_EXTENSIBLE we are doing this:

if (isWaveFmtEx) { output->writeShort (22); // cbSize (size of the extension) output->writeShort ((short) bitsPerSample); // wValidBitsPerSample output->writeInt (getChannelMask ((int) numChannels));

But when we are reading it we do:

else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/) { if (length < 40) // too short { bytesPerFrame = 0; } else { input->skipNextBytes (6); // skip over bitsPerSample

I could not understand the magic number of 6, so I replaced the input->skipNextBytes(6) with:

short cbSize = input->readShort(); short wValidBitsPerSample = input->readShort();
and it seems to fix it.

Cheers,
Roman

Hmm, yes, that 6 does seem to be wrong. Thanks, I’ll take a look.