loadBank and resource package files

Hi all,
the fmod studio system provide a method to load a bank file using the filename as input, so, we can not put the bank file in our resource package file. There is any method to pass a file descriptor or a “memory file” to load the bank?
Thanks, Salvo

We do plan to support loading from memory, but unfortunately we haven’t had time to implement it yet. Is this a major obstacle for you at the moment?

Thanks for the reply… at the moment is not a problem, I’ll wait for the support.
Bye, Salvo

OK, we’ll let you know when it’s implemented.

This problem is bothering me too,Is there anybody found a way to solve the problem?

Have you tried overriding the low level file system with System::setFileSystem?

System::setFileSystem is useful for ordinary file such as wav, but is not work for bank file.
I have received an email from your company,tell me that in the 1.2 version will have this function.
Thanks anyway.

This use case is supported in Studio API 1.2, which should be released soon. The System::loadBank function has been renamed to loadBankFile, and two new functions have been added: loadBankMemory and loadBankCustom.

The loadBankMemory function reads the bank from a memory buffer. You can specify FMOD_STUDIO_LOAD_MEMORY to make FMOD copy the buffer internally, or FMOD_STUDIO_LOAD_MEMORY_POINT to make FMOD just point to your buffer (in which case you need to keep the buffer around as long as the bank is loaded so that sample data can be read from it).

The loadBankCustom function allows you to specify a set of file callbacks for loading the bank, and a userdata pointer that will be passed to those file callbacks (for example, this could be a package entry descriptor, a path string, or a file descriptor). This gives you extra control over the loading process, and is a good alternative to loadBankMemory if you want to avoid the cost of reading the whole bank file into memory at once.

Thanks for the “loadBankMemory” :-), but how I could handle stream banks?
I mean, I have a bank with a lot of music inside (stream activated in FMod Studio) and if I use “loadBankFile” I will see that allocations are small. The problem arise if I use the loadBankMemory, in this case I have to read the entire file (and using FMOD_STUDIO_LOAD_MEMORY, consequently, Fmod will do another “big” allocation). Any method to avoid this?
Bye, Salvo

This is what loadBankCustom is designed for. You can specify file open, close, read and seek callbacks for your bank by setting them in the FMOD_STUDIO_BANK_INFO struct, and have them read from inside your package file (you can store information about which package file to read from in FMOD_STUDIO_BANK_INFO.userData). FMOD will use your callbacks to read the event metadata from the bank. When the time comes to load the streaming music, FMOD will once again use your callbacks to read the audio sample data.

Here’s a code sample demonstrating the usage of loadBankCustom. Please note that this is completely untested code with very little error checking, so use at your own risk!

// structure describing a chunk inside a package file
struct PackageEntry
{
    const char* packageFilePath;
    size_t offset;
    size_t size;
};

// callbacks for reading from within a package file
FMOD_RESULT F_CALLBACK fileOpen(const char *name, int unicode,
    unsigned int *filesize, void **handle, void *userdata)
{
    const PackageEntry* entry = reinterpret_cast<const PackageEntry*>(userdata);

    FILE* file = fopen(entry->packageFilePath, "rb");
    if (!file)
    {
        return FMOD_ERR_FILE_NOTFOUND;
    }

    fseek(file, entry->offset, SEEK_SET);

    *handle = file;
    *filesize = entry->size;

    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK fileClose(void *handle, void *userdata)
{
    FILE* file = reinterpret_cast<FILE*>(handle);

    fclose(file);

    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK fileRead(void *handle, void *buffer,
    unsigned int sizebytes, unsigned int *bytesread, void *userdata)
{
    const PackageEntry* entry = reinterpret_cast<const PackageEntry*>(userdata);

    FILE* file = reinterpret_cast<FILE*>(handle);

    const size_t seekPosition = ftell(file) - entry->offset;
    const size_t bytesRemaining = entry->size - seekPosition;

    size_t clampedSize = sizebytes;

    if (clampedSize > bytesRemaining)
    {
        // avoid reading past the end of the package entry
        clampedSize = bytesRemaining;
    }

    size_t readCount = fread(buffer, 1, clampedSize, file);

    *bytesread = readCount;

    if (readCount < sizebytes)
    {
        // FMOD requested more bytes than we could provide
        return FMOD_ERR_FILE_EOF;
    }

    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK fileSeek(void *handle, unsigned int pos, void *userdata)
{
    const PackageEntry* entry = reinterpret_cast<const PackageEntry*>(userdata);

    if (pos > entry->size)
    {
        // avoid seeking past the end of the package entry
        return FMOD_ERR_FILE_EOF;
    }

    FILE* file = reinterpret_cast<FILE*>(handle);

    fseek(file, pos + entry->offset, SEEK_SET);

    return FMOD_OK;
}

/* ... */

    // loading a bank from within a package file
    PackageEntry packageEntry;
    packageEntry.packageFilePath = "main.pak";
    packageEntry.offset = 54135;
    packageEntry.size = 624264;

    FMOD_STUDIO_BANK_INFO bankInfo;
    memset(&bankInfo, 0, sizeof(bankInfo));
    bankInfo.size = sizeof(bankInfo);
    bankInfo.userData = &packageEntry;
    // make FMOD copy packageEntry internally, rather than pointing to a stack variable
    bankInfo.userDataLength = sizeof(packageEntry);
    bankInfo.openCallback = fileOpen;
    bankInfo.closeCallback = fileClose;
    bankInfo.readCallback = fileRead;
    bankInfo.seekCallback = fileSeek;
    
    FMOD::Studio::Bank bank;
    ERRCHECK( system.loadBankCustom(&bankInfo, &bank) );

Great :-)!
It is working (I am loading only a “streaming bank”), but, with the same bank, I have more openCallback than closeCallback…how it is possible?
Thanks, Salvo.

Thanks for reporting this. It was actually a bug where the Studio API was creating unused streams during the preloading stage when you create an event instance. You should get matching close callbacks when you release the event instance, as that releases the unused streams. This bug has now been fixed for the next release.