Registering User-created AudioFormats?

I’ve written some AudioFormat readers/writers for legacy audio formats (Amiga 8SVX .IFF and raw PCM, Super Nintendo .BRR, and Linndrum/Drumulator mu-law encoded .BIN files) but I’m having trouble using the registerFormats() in the AudioFormatManager with them

They work fine if I modify the source code for AudioFormatManager and the juce_audio_formats files to include them, but when I try to do it the “correct” way and make them as separate user-modules, I can’t get them to access the AudioFormat class

Anyone know of any way to add user-created audio formats without having to overwrite the source code?

You have to create your new audio format class derived from juce::AudioFormat and handle the virtual methods.

class MySuperAudioFormat : public AudioFormat
{
public:

    MySuperAudioFormat();

    :

    Array<int> getPossibleSampleRates() override;
    Array<int> getPossibleBitDepths() override;
    
    bool canDoStereo() override;
    bool canDoMono() override;
    
    bool isCompressed() override;
    
    StringArray getQualityOptions() override;
    
    AudioFormatReader* createReaderFor (InputStream* sourceStream,
                                        bool deleteStreamIfOpeningFails) override;
    
    AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
                                        double sampleRateToUse,
                                        unsigned int numberOfChannels,
                                        int bitsPerSample,
                                        const StringPairArray& metadataValues,
                                        int qualityOptionIndex) override;

private:
    
    class Reader;
    class Writer;
    
    :
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MySuperAudioFormat)

Rail

Yes that is what I have. Here’s my header for my IFF reader:

class JUCE_API IffAudioFormat : public AudioFormat
{
    
public:
    //==============================================================================
    /** Creates a format object. */
    IffAudioFormat();
    /** Destructor. */
    ~IffAudioFormat() override;

    //==============================================================================
    Array<int> getPossibleSampleRates() override;
    Array<int> getPossibleBitDepths() override;
    bool canDoStereo() override;
    bool canDoMono() override;
    bool isChannelLayoutSupported (const AudioChannelSet& channelSet) override;

    //==============================================================================
    AudioFormatReader* createReaderFor (InputStream* sourceStream,
                                        bool deleteStreamIfOpeningFails) override;

    AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo);

    AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
                                        double sampleRateToUse,
                                        unsigned int numberOfChannels,
                                        int bitsPerSample,
                                        const StringPairArray& metadataValues,
                                        int qualityOptionIndex) override;

    AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse,
                                        const AudioChannelSet& channelLayout,
                                        int bitsPerSample,
                                        const StringPairArray& metadataValues,
                                        int qualityOptionIndex) override;
    using AudioFormat::createWriterFor;

    //==============================================================================
    /** Utility function to replace the metadata in a iff file with a new set of values.

        If possible, this cheats by overwriting just the metadata region of the file, rather
        than by copying the whole file again.
    */
    bool replaceMetadataInFile (const File& iffFile, const StringPairArray& newMetadata);


private:
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IffAudioFormat)
};

} // namespace juce

However, when I go to initialize the class with

    IffAudioFormat::IffAudioFormat() : AudioFormat(iffFormatName, ".iff .raw \r") {}

I get an error that says AudioFormat is not a member of IffAudioFormat. And if I try to register the format with

    formatManager.registerFormat(new IffAudioFormat(), false);

I get an error saying that it can’t cast to AudioFormat* despite that basically being the same syntax that I use when I write it into the source code.

Just a longshot,
you haven’t forgot “using namespace juce;”?
Otherwise you’ll have to prefix AudioFormat like juce::AudioFormat(iffFormatName, “.iff .raw \r”).

By the way, why the " \r" in the format specification?

You should set up a helper like:

namespace NewAudioFormatHelpers
{
    static const char* const name       = "New file";
    static const char* const extension  = ".new";
    static const int fileHeader         = (int) ByteOrder::littleEndianInt ("newF");
    static const float fVersion         = 1.0f;
}

then in your ctor…

NewAudioFormat::NewAudioFormat() : AudioFormat (NewAudioFormatHelpers::name, NewAudioFormatHelpers::extension)
{
}

and your format shouldn’t be in the juce namespace.

Rail

1 Like

To bring those together, the class declaration should look like:

class IffAudioFormat : public juce::AudioFormat
{
// ...

and NO using namespace, that brings unnecessary ambiguities.

This should resolve the “Cannot cast to juce::AudioFormat” (:crossed_fingers: )

1 Like

Yes I have “using namespace juce” in there. I almost never use “using namespace” but I wanted to keep the syntax for this as similar to the code for WAVAudioFormat since I was basing most of the JUCE-specific code on that. Out of the file formats included with JUCE, WAV is the one I’m most familiar with writing my own format parsers for, so it was the easiest for me to work out what I needed from JUCE to write my own format readers for other formats.

As for the “\r”, Amiga computers rarely used file extensions unless the user saving the file wrote it in there or a program made after extensions became the norm wrote the file; usually only MSDOS or Windows programs that had Amiga file type support would save an Amiga file type with an extension. On the Amiga itself, raw PCM and 8SVX IFF files save without an extension. Including carriage return in the extension list was the only way I could “trick” JUCE into sending extensionless files to my format reader as neither the NUL character nor newline worked for me. Tho tbh I started working on this AWHILE back, only focusing on reading raw PCM files and I haven’t thought to re-check if something better would work since I haven’t had any issues with using the carriage return :sweat_smile:

EDIT: okay so I must’ve done something wrong with the file extension when I wrote it all those many moons ago and have since fixed it without realizing bc I tried the NUL character in there instead of the carriage return and now it works ¯_(ツ)_/¯ not exactly sure what I changed during the course of that time to make it work correctly but hey I’ll take it lol

@railjonrogut I’ll have to give that a try, thank you!