Using/Reading FMOD (.fsb) Files in Java

1.2k Views Asked by At

Heyo Everyone. I am currently working on an Application which should play music files from an FMOD Database. It currently does it by Extracting the files (using a runtime and an external programm called "fsbextract.exe" (link)) as MP3 and then playing them. I am okay with this as it is, but i would now also love to edit/replace the files within the .fsb file. So my question is: Can i somehow directly acces the MP3 files in there without extracting them? I searched the internet for something like this but couldn't find any help.

For reference, here are some informations i can get from within fsbextract:

FileID is |FSB4> | Version is 4.0 | Number of Entries 7412

Global Flags:

0x40 | FMOD_FSB_SOURCE_MPEG_PADDED4 | MPEG frames are aligned to the nearest 4 bytes, 16 bytes for multichannel (Use Frame Verification option)

And from the first (index 0) file within the fsb file: Format: MP3 (85) Sample rate: 44100Hz Channels: Mono Duration: 01.541 Bitrate: 160,62 kbit/s Bits/Sample 3.64

Sample Mode Flags [0x10000200] 0x200 | FSOUND_MPEG | Sample is stored in MPEG format 0x10000000 | FSOUND_MPEG_LAYER3 | Samples are stored in MPEG Layer 3 format

I hope someone knows more than me, but thanks in advance Marenthyu

1

There are 1 best solutions below

2
On

Jérôme Jouvie wrote a JNI wrapper for FMOD 4+, NativeFmodEx. If your FSB files are not encrypted I think you can use the API to extract streaming data from the FSB banks.

I tried to write my own wrapper as well but I didn't have the time to get past a simple proof of concept.

UPDATE: Sure. So basically what I did was to use JNAerator to wrap the native FMODex libraries and then targeting the resulting glue code to the BridJ runtime environment. That essentially allows you to invoke the native FMOD C library.

I'm not exactly sure if Jerome did something similar or if he created his own JNI stubs to achieve the same result. You'd have to check out and read his project's code.

But essentially, once you're able to make FMOD calls inside your application, you can use the API to do as you please. If you download the latest FMOD studio or FMODex SDK's from the FMOD web site you'll find a help .chm file which contains some documentation for the API.

You should also take a look at the examples in the SDK.

The following code is basically one example translated into Java, using the above mentioned strategy. The code isn't portable, robust or actually useful for an application. It really needs cleanup yet it still illustrates the point.

Like I said, you should be much better off using Jerome's SDK port.

Hope it helps.

package net.unsungstories.fmodex;

import net.unsungstories.fmodex.FmodexLibrary.FMOD_CHANNEL;
import net.unsungstories.fmodex.FmodexLibrary.FMOD_SOUND;
import net.unsungstories.fmodex.FmodexLibrary.FMOD_SYSTEM;

import org.apache.log4j.Logger;
import org.bridj.IntValuedEnum;
import org.bridj.Platform;
import org.bridj.Pointer;
import org.junit.Test;

import static org.bridj.Pointer.*;
import static net.unsungstories.fmodex.FmodexLibrary.*;
import static com.google.common.base.Throwables.*;
import static java.lang.String.format;

public class PlaySound {

    private static final Logger log = Logger.getLogger(PlaySound.class);

    @Test
    public void playSound() {

        log.info("Test started...");

        String soundPath = "./src/test/resources/picus_get_to_finicular_music_0.fsb";
        Platform.addEmbeddedLibraryResourceRoot("net/unsungstories/fmodex/");

        IntValuedEnum<FMOD_RESULT> result;

        Pointer<Pointer<FMOD_SYSTEM>> ppSystem = allocatePointer(FMOD_SYSTEM.class);
        Pointer<Pointer<FMOD_SOUND>> ppSound1 = allocatePointer(FMOD_SOUND.class);
        Pointer<Integer> pSubSounds = allocateInt();
        Pointer<Pointer<FMOD_CHANNEL>> ppChannel = allocatePointer(FMOD_CHANNEL.class);

        @SuppressWarnings("unchecked")
        Pointer<FMOD_CREATESOUNDEXINFO> soundExInfo = Pointer.NULL;
        Pointer<Byte> targetSoundPath = allocateBytes(soundPath.length() + 1);

        targetSoundPath.setCString(soundPath);

        result = FMOD_System_Create(ppSystem);      
        result = FMOD_System_Init(ppSystem.get(), 2, FmodexLibrary.FMOD_INIT_NORMAL, Pointer.NULL);
        result = FMOD_System_CreateSound(ppSystem.get(), targetSoundPath, FMOD_HARDWARE, soundExInfo, ppSound1);
        result = FMOD_Sound_GetNumSubSounds(ppSound1.get(), pSubSounds);

        try {

            Pointer<Integer> pChanelPlaying = allocateInt();

            for (int k = 0; k < pSubSounds.get(); k++) {

                Pointer<Pointer<FMOD_SOUND>> ppSubSound = allocatePointer(FMOD_SOUND.class); 

                result = FMOD_Sound_GetSubSound(ppSound1.get(), k, ppSubSound);
                result = FMOD_System_PlaySound(ppSystem.get(), FMOD_CHANNELINDEX.FMOD_CHANNEL_FREE, ppSubSound.get(), 0, ppChannel);

                FMOD_Channel_IsPlaying(ppChannel.get(), pChanelPlaying);

                while (ppChannel.getBoolean()) {
                    log.info("Playing...");                 
                    Thread.sleep(1000);     
                    FMOD_Channel_IsPlaying(ppChannel.get(), pChanelPlaying);
                }

                result = FMOD_Sound_Release(ppSubSound.get());
            }

            result = FMOD_System_Close(ppSystem.get());
            result = FMOD_System_Release(ppSystem.get());

        } catch (Exception e) {
            log.error(getStackTraceAsString(getRootCause(e)));
        }       

        log.info(format("Finished... %s", result));
    }
}

Hope this helps.