audioFormatReader returns 0 length on multichannel WAV


#1

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


#2

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


#3

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


#4

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


#5

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


#6

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


#7

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


#8

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


#9

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


#10

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