32 bit AIFF on windows


#1

Was just bummed to realize that Juce can’t do 32 bit aif files on windows.

Frankly, I think it must be a windows issue … that is, windows media player can’t even play 32 bit aifs, and there doesn’t seem to be any way to expand the systems capabilities.

I’m assuming that when juce loads the wavAudioFormat it is somehow relying on a lower level system file (since, for example, the same 32 bit wavs play on mac without any issue)?


#2

The WavAudioFormat does definitely handle 32-bit files.


#3

This may or may not be relevant, but JUCE only supports reading uncompressed WAV files. (Not sure about Media Player - that thing’s garbage. :D)


#4

Yeah, I noticed after posting this that it claims to be able to read them, but still something isn’t working.

Checking more this afternoon.


#5

Just tried some files myself out of curiosity; WMP doesn’t play this compressed WAV format, from the few I’ve been able to try. Was able to play µ-Law, ACM and Microsoft ADPCM.

Maybe you’re missing codecs? Try this pack out, if you haven’t…


#6

Ok - first off, it’s an AIFF not wav, my mistake.

But … I tried loading the file in the juce demo, crash.
I then tried it on mac … all good.

Then I opened it with ableton, and re-exported as a 16 bit AIFF. All good on Mac and PC.

So … Unless there is something else weird about this file, it definitely loads when 16 bit, but not 32. I’ve actually tried this with a bunch of 32 bit AIFFs in fact (all exported from ableton). Mac says that they are 32 bit AIFF-C audio files, 2 channels.

When using my code (below), I see that it fails, so I cycle through the formats to see what’s happening. The Aiff format claims it can read this file, but returns a null pointer on Windows (fine on Mac of course).

For reference, here are the files (16 and 32 bit):
http://stagecraftsoftware.com/downloads/Am%20salsa%2016.aif
http://stagecraftsoftware.com/downloads/Am%20salsa%2032.aif

Incidentally, I’ve mentioned this before (and you’ve done great work fixing some issues … thanks as always … this one definitely still remains):
http://www.rawmaterialsoftware.com/viewtopic.php?f=2&t=8548&start=15


    // Create a reader
    AudioFormatManager formatManager;
    formatManager.registerBasicFormats();
    AudioFormatReader* reader = formatManager.createReaderFor(myFile);
    
    if (reader == 0) // something didn't work
    {
        DBG("could not read file !");
        for (int i = 0; i < formatManager.getNumKnownFormats(); ++i)
        {
            AudioFormat* const af = formatManager.getKnownFormat(i);
            DBG("format " + af->getFormatName() + " " + String(af->canHandleFile (myFile)));
            
            if (af->canHandleFile (myFile))
            {
                InputStream* const in = myFile.createInputStream();
                
                if (in != nullptr)
                {
                    reader = af->createReaderFor(in, false); // false is "delete stream on fail"
                     jassert(reader != 0); // yup ... we're still getting a problem ... this gets hit for 32 bit aiffs on windows 8
                }
            }
        }
		
    }

#7

Stepping through the AiffAudioFormat::createReaderFor on Windows - the sample rate ends up as 0.0 for your 32-bit file… the algo doesn’t seem to recognize the “compType”?


#8

Ah, I see; 32bit float AIFFs are considered their own compression type, and JUCE doesn’t support it (chunk name “fl32”).


#9

Aha! Thanks for getting to the bottom of that.

Jules - I don’t suppose this is going to change anytime soon?

Could you at least have the Audio format check the comp type when you run the canHandleFile(), so that it correctly returns false?


#10

[quote=“aaronleese”]Aha! Thanks for getting to the bottom of that.

Jules - I don’t suppose this is going to change anytime soon?

Could you at least have the Audio format check the comp type when you run the canHandleFile(), so that it correctly returns false?[/quote]

Sorry, could have told you that AIFF didn’t handle floats, if only you’d not said “WAV” originally!

No immediate plans to add support for that, and can’t check in canHandleFile(), because that method only looks at the filename.

Of course if you wanted to have a go at adding support, I’d be happy to sanity-check for you…


#11

Yeah, thats what I was guessing. One of these days :slight_smile:


#12

Hey fellas - try this code out for AIFF float32 read support;

AiffAudioFormatReader::AiffAudioFormatReader()

//Add the following check
else if (compType == chunkName ("fl32")
            || compType == chunkName ("FL32"))
{
    littleEndian = false;
    usesFloatingPointData = true;
}

Replace AiffAudioFormatReader::readSamples() with this one:

bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
                    int64 startSampleInFile, int numSamples)
{
    clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
                                        startSampleInFile, numSamples, lengthInSamples);

    if (numSamples <= 0)
        return true;

    input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame);

    while (numSamples > 0)
    {
        const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3)
        char tempBuffer [tempBufSize];

        const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples);
        const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame);

        if (bytesRead < numThisTime * bytesPerFrame)
        {
            jassert (bytesRead >= 0);
            zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead));
        }

        if (littleEndian)
        {
            copySampleData<AudioData::LittleEndian> (bitsPerSample, usesFloatingPointData,
                                                        destSamples, startOffsetInDestBuffer, numDestChannels,
                                                        tempBuffer, (int) numChannels, numThisTime);
        }
        else
        {
            copySampleData<AudioData::BigEndian> (bitsPerSample, usesFloatingPointData,
                                                    destSamples, startOffsetInDestBuffer, numDestChannels,
                                                    tempBuffer, (int) numChannels, numThisTime);
        }

        startOffsetInDestBuffer += numThisTime;
        numSamples -= numThisTime;
    }

    return true;
}

Replace AiffAudioFormatReader::copySampleData() with this one:

template <typename Endianness>
static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData,
                            int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels,
                            const void* sourceData, int numChannels, int numSamples) noexcept
{
    switch (bitsPerSample)
    {
        case 8:     ReadHelper<AudioData::Int32, AudioData::Int8, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
        case 16:    ReadHelper<AudioData::Int32, AudioData::Int16, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
        case 24:    ReadHelper<AudioData::Int32, AudioData::Int24, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
        case 32:    if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
                    else                       ReadHelper<AudioData::Int32, AudioData::Int32, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
        default:    jassertfalse; break;
    }
}

Replace MemoryMappedAiffReader::readSamples() with this one:

bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
                    int64 startSampleInFile, int numSamples)
{
    clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
                                        startSampleInFile, numSamples, lengthInSamples);

    if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
    {
        jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
        return false;
    }

    if (littleEndian)
        AiffAudioFormatReader::copySampleData<AudioData::LittleEndian>
                (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer,
                    numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
    else
        AiffAudioFormatReader::copySampleData<AudioData::BigEndian>
                (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer,
                    numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples);

    return true;
}

Quick note: I used the wave format reader as a guideline… noticed that the code now looks the same, except for the endianness checking in the AIFF code


#13

Brilliant!

Works perfectly, thanks. Doesn’t break any of the existing formats either.

Thanks jr.

Jules - I assume you’ll add that to the tip?


#14

Thanks guys! Will have a look at this today!


#15

Doesn’t look like this made it yet. Just dropping a line so it doesn’t get lost.

Thanks


#16

I added it a while ago…


#17

Your the man, thanks as always!


#18

I'm failing to read a 32-bit float .aiff (current JUCE).

        String file = "D:\\path\\to\\21.wav"
        ScopedPointer<AudioFormatReader> reader = pFormatManager->createReaderFor(file);
        jassert(reader != 0); // HITS!

http://pipad.org/tmp/021.aiff

It plays on WMP VLC Audacity etc.

Could someone give it a try?

π


#19

It's because the chunk name is RIFF, which isn't supported.

In fact, changing the extension to .wav appears to make it readable entirely.

Something tells me the file is mistakenly marked with a .aif extension.

 

(I don't even think the AIFF format supports RIFF, tbh.)


#20

The following PDFs show that the chunk ID is supposed to be FORM:​

  • http://www-mmsp.ece.mcgill.ca/documents/audioformats/aiff/Docs/AIFF-C.9.26.91.pdf
  • http://www-mmsp.ece.mcgill.ca/documents/audioformats/aiff/Docs/AIFF-1.3.pdf

Mind you, that specification is not explicit about allowing the ID to be something else.