Fork me on GitHub

SoLoud::File

SoLoud has a unified file i/o interface. All of the audio sources in SoLoud that require files use the File class internally and support loading through a File class.

This enables SoLoud to load from memory pointers, stream data from memory, as well as support virtual filesystems such as PhysFS by extending the File class.

File class hierarchy

SoLoud has two File-extended classes, DiskFile which uses stdio FILE* interfaces internally, and MemoryFile which uses an in-memory buffer.

The File class only supports loading.

File class

The File class is abstract, and provides some helper functions.


class File
{
public:
    unsigned int read8();
    unsigned int read16();
    unsigned int read32();
    virtual int eof() = 0;
    virtual unsigned int read(unsigned char *aDst, unsigned int aBytes) = 0;
    virtual unsigned int length() = 0;
    virtual void seek(int aOffset) = 0;
    virtual unsigned int pos() = 0;
    virtual FILE * getFilePtr() { return 0; }
    virtual unsigned char * getMemPtr() { return 0; }
};

File.read8(), File.read16(), File.read32()

Helper functions reading 8, 16 and 32 bits from the file stream.


unsigned char  foo = myfile.read8();
unsigned short bar = myfile.read16();
unsigned int   baz = myfile.read32();

Note that the output of these helpers are unsigned, which may cause problems unless you remember to cast to signed when handling signed data, such as samples.

File.getFilePtr()

Returns FILE pointer if available, 0 if not. Useful to check if we're using DiskFile (or compatible), and avoids casting between File class subtypes if access to the FILE pointer is needed.

File.getMemPtr()

Returns memory pointer if available, 0 if not. Useful to check if we're using MemoryFile (or compatible), and avoids casting between File class subtypes if access to the memory pointer is needed.

File.eof()

Returns non-zero if the stream is at end of file, zero otherwise.

File.read()

Read up to aBytes worth of bytes from stream. Return number of bytes actually read.

File.length()

Return length of the file in bytes.

File.seek()

Seek to byte offset from beginning of stream.

File.pos()

Return current byte position in stream.

Typical File Interfaces

Using the File interface we easily support the following kind of interfaces:


load(const char* aFilename);

Load file from disk.


loadMem(unsigned char *aData, unsigned int aDataLength, 
        bool aCopy=false, bool aTakeOwnership=true);

Load file from a memory pointer, optionally taking a copy of the data, and also optionally taking ownership and calling delete[] on the data when closing the file.


LoadFile(File *aFile);

Load file through File class, useful if you have custom File-extended class.


LoadToMem(const char *aFilename);

Load file from disk to a memory buffer, and then use it as a memory file.


LoadFileToMem(File *aFile);

Combination of LoadToMem and LoadFile.

Some interfaces don't supply all of the above, because they either don't need to keep the file data around, or if they are always storing the data in a memory buffer.

soloud_file_hack_on.h, soloud_file_hack_off.h

SoLoud comes with a pair of headers you can use to fool code which uses the FILE* interface to use File* instead.

The files use preprocessor macros to turn FILE* calls into SoLoud's wrapper function calls which in turn use the File class interfaces. Since it's a preprocessor hack, the soloud_file_hack_on.h must be included after stdio.h, or it will break stdio.h.

To switch the hack off again, you can include the soloud_file_hack_off.h, which will undefine the preprocessor macros.

Current version of the hack overrides fgetc, fread, fseek, ftell, fclose and fopen. The wrapper functions can be found in soloud_file.cpp.

Copyright©2013-2020 Jari Komppa