Using FMOD to load an audio file with a specified path in Unity

Hello!
I am trying to make a music game in Unity. The music game contains its own music, but I also want the player to be able to load their own music.
For the music that the player loads, we can’t pre-make it in FMOD Studio, meaning that we don’t have a .bank file.
This causes me a lot of problems when creating instances because I can’t find the relevant code to read it via the file path.

*Audio files here refer to files such as .wav, .mp3, not .bank files.
No 3D effect is needed.

In the project, I can provide the address (absolute path) of the audio file, may I ask how to realize the above requirements?

Also would like to ask: how to modify the mixing effect and its intensity in real time for the above mentioned audio read via path via code? Because we can’t pre-configure it in FMOD Studio.

I tried a piece of content in the forum, but it ended in failure:
General advice on user-friendly process for players to upload audio assets then pipe them through FMOD-powered Unity?

Hi,

What version of the FMOD integration are you using?

Could you elaborate on how using the code in the linked forum failed?

Hello!
I’m using FMOD for Unity and FMOD Studio, both version 2.02.19!

Please see the sample code below:

using UnityEngine;
using System;
using System.Runtime.InteropServices;

public class Audio_Test : MonoBehaviour
{
public FMOD.Studio.EVENT_CALLBACK callBack;
//public FMODUnity.EventReference eventReference;

void Start()
{
    callBack = new FMOD.Studio.EVENT_CALLBACK(CallBackFunction);
    PlayAudioFile(@"D:\UnityRepos\Test\1.flac");
}

public void PlayAudioFile(string location)
{
    var eventInstance = FMODUnity.RuntimeManager.CreateInstance(FMODUnity.RuntimeManager.PathToGUID(location));	// Here
    // ...
}

private static FMOD.RESULT CallBackFunction(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
{
    FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);

    if (instance.isValid() == false)
    {
        Debug.Log("Failed to create instance, instance invalid");
        return FMOD.RESULT.ERR_EVENT_NOTFOUND;
    }

    IntPtr stringPtr;
    instance.getUserData(out stringPtr);

    GCHandle stringHandle = GCHandle.FromIntPtr(stringPtr);
    string location = stringHandle.Target as string;

    switch (type)
    {
    // ...
    }

}

In the commented line of code, an error is reported at runtime:
EventNotFoundException: [FMOD] Event not found: ‘D:\UnityRepos\Test\1.flac’

I realized that its asking to pass in the path to the event that was still created in FMOD Studio.
In the original reply, it states in the opening position that it needs to instantiate an FMODUnity.EventReference object:

Create an variable which will be used to instantiate your FMODUnity.EventReferenceProgrammer Instrument from the FMOD Project.

I created an empty event (2D Timeline) in FMOD Studio, generated it and sent it to Unity.
Modified the code as below:

public class Audio_Test : MonoBehaviour
{
public FMOD.Studio.EVENT_CALLBACK callBack;
public FMODUnity.EventReference eventReference;

void Start()
{
    callBack = new FMOD.Studio.EVENT_CALLBACK(CallBackFunction);
    PlayAudioFile(@"D:\UnityRepos\Test\1.flac");
}

public void PlayAudioFile(string location)
{
    var eventInstance = FMODUnity.RuntimeManager.CreateInstance(eventReference);
    
    //var eventInstance = FMODUnity.RuntimeManager.CreateInstance(FMODUnity.RuntimeManager.PathToGUID(location));

    FMOD.RESULT result;
    if (!eventInstance.isValid())
    {
        Debug.Log("Failed to create instance, instance invalid");
        return;
    }

    GCHandle stringLocation = GCHandle.Alloc(location);
    result = eventInstance.setUserData(GCHandle.ToIntPtr(stringLocation));

    if (result != FMOD.RESULT.OK)
    {
        Debug.Log("Failed to create event instance with a result of " + result);
        return;
    }

    result = eventInstance.setCallback(callBack);
    result = eventInstance.start();
}

// The CallBackFunction function is the same as the code above.

}

Mounted the script to an empty object and set up the events that were just generated in FMOD Studio.
After running it, Unity doesn’t have any output messages and the audio doesn’t play.
I think the original answer may not accomplish what I need.

Also, due to my poor English, I used translation software to translate most of the English documents, so I may have misunderstood, so I am very sorry if I am wrong about the original answer.

1 Like

Hi,

Thank you for the code. I think the Programmer Sounds example may be the solution you are looking for. It would allow you to pass a Key to an event to play and that could be a file location. The only change you would need is:

if (key.Contains("."))
{
    FMOD.Sound dialogueSound;
    #CHANGE---
    var soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(Application.streamingAssetsPath + "/" + key, soundMode, out dialogueSound);
    #TO---
    var soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(key, soundMode, out dialogueSound);
    #---
    if (soundResult == FMOD.RESULT.OK)
    {
        parameter.sound = dialogueSound.handle;
        parameter.subsoundIndex = -1;
        Marshal.StructureToPtr(parameter, parameterPtr, false);
    }
}
#REMOVE---
else
{
    FMOD.Studio.SOUND_INFO dialogueSoundInfo;
    var keyResult = FMODUnity.RuntimeManager.StudioSystem.getSoundInfo(key, out dialogueSoundInfo);
    if (keyResult != FMOD.RESULT.OK)
    {
        break;
    }
    FMOD.Sound dialogueSound;
    var soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(dialogueSoundInfo.name_or_data, soundMode | dialogueSoundInfo.mode, ref dialogueSoundInfo.exinfo, out dialogueSound);
    if (soundResult == FMOD.RESULT.OK)
    {
        parameter.sound = dialogueSound.handle;
        parameter.subsoundIndex = dialogueSoundInfo.subsoundindex;
        Marshal.StructureToPtr(parameter, parameterPtr, false);
    }
}
---
If you need a more detailed code example please do not hesitate to ask.

Hello.
Thank you for your help. I checked the sample code you mentioned and modified the code according to your instructions.
Just in case I misunderstood, the code is shown below:

using System;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;

class ScriptUsageProgrammerSounds : MonoBehaviour
{
FMOD.Studio.EVENT_CALLBACK dialogueCallback;

public FMODUnity.EventReference EventName;

void Start()
{
    dialogueCallback = new FMOD.Studio.EVENT_CALLBACK(DialogueEventCallback);
}

void PlayDialogue(string key)
{
    var dialogueInstance = FMODUnity.RuntimeManager.CreateInstance(EventName);

    GCHandle stringHandle = GCHandle.Alloc(key);
    dialogueInstance.setUserData(GCHandle.ToIntPtr(stringHandle));

    dialogueInstance.setCallback(dialogueCallback);
    dialogueInstance.start();
    dialogueInstance.release();
}

[AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
static FMOD.RESULT DialogueEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
{
    FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);

    IntPtr stringPtr;
    instance.getUserData(out stringPtr);

    GCHandle stringHandle = GCHandle.FromIntPtr(stringPtr);
    String key = stringHandle.Target as String;

    switch (type)
    {
        case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
            {
                FMOD.MODE soundMode = FMOD.MODE.LOOP_NORMAL | FMOD.MODE.CREATECOMPRESSEDSAMPLE | FMOD.MODE.NONBLOCKING;
                var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));

                if (key.Contains("."))
                {
                    FMOD.Sound dialogueSound;
                    var soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(key, soundMode, out dialogueSound);
                    if (soundResult == FMOD.RESULT.OK)
                    {
                        parameter.sound = dialogueSound.handle;
                        parameter.subsoundIndex = -1;
                        Marshal.StructureToPtr(parameter, parameterPtr, false);
                    }
                }
                break;
            }
        case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
            {
                var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
                var sound = new FMOD.Sound(parameter.sound);
                sound.release();

                break;
            }
        case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
            {
                stringHandle.Free();
                break;
            }
    }
    return FMOD.RESULT.OK;
}

void Update()
{
    if (Input.GetKeyDown(KeyCode.Alpha1))
    {
        PlayDialogue("event:/BGM/Main");
    }
    if (Input.GetKeyDown(KeyCode.Alpha2))
    {
        PlayDialogue("Main");
    }
    if (Input.GetKeyDown(KeyCode.Alpha3))
    {
        PlayDialogue("D:/a.mp3");
    }
    if (Input.GetKeyDown(KeyCode.Alpha0))
    {
        EventName = FMODUnity.EventReference.Find("event:/BGM/Login");
        //EventName = FMODUnity.EventReference.Find("D:/BGM.bank");
        //EventName = FMODUnity.EventReference.Find("D:/a.mp3");
        Debug.Log("Find");
    }
}

}

I tried various pass-in values in the Update() function and found that although its a pass-in path, the pass-in is still the path of the event that needs to be played, and other pass-in values will report an error.

---------- ---------- ---------- ---------- ------
For reading an audio file, if we can’t pass the absolute path of the file, we can also pass a byte array of the audio file, but I didn’t find a function for that.

Just in case my original description was wrong, allow me to explain my needs again:
I need to allow the player to pass in the path of an audio file when the Unity application is running (this audio file means a music file that can be played directly: for example, a file in .mp3 .wav extension format. Not the .bank file that is generated in FMOD Studio), the audio file is read successfully and then handed over to FMOD to manage to achieve effects such as pause, real-time mixing, etc.

Hi,

Using your code and the input for Alpha3 I was able to play an audio file from my computer. What are you finding when using this input method?

Correct, this should be achieveable by using the Alpha3 method. If you provide a way for the player to pass in their file path then you should be able to play that file and add effects to the programmer event instance in Studio.

Hello.
Thank you for your help!

I went to try again and still can’t read the audio properly.
Here’s what I did:
1. Create a new script called “ScriptUsageProgrammerSounds” and paste the code from the original answer into it.
2. In Unity, create an empty object called “Test” and mount the script from step 1 and add the “FMOD Stuidio Listener” script.
The mounted script will have an optional “Event Name” in the Inspector window.
3.1 If you run the programme without selecting this item and press the numbers 1, 2 or 3, the following errors and warnings will appear:

Warning:
[FMOD] ObjectLookup::get : Lookup failed for EventModel: {00000000-0000-0000-0000-000000000000}

UnityEngine.Debug:LogWarning (object)
FMODUnity.RuntimeUtils:DebugLogWarning (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:564)
FMODUnity.RuntimeManager:DEBUG_CALLBACK (FMOD.DEBUG_FLAGS,intptr,int,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:95)
FMOD.Studio.System:getEventByID (FMOD.GUID,FMOD.Studio.EventDescription&) (at Assets/Plugins/FMOD/src/fmod_studio.cs:464)
FMODUnity.RuntimeManager:GetEventDescription (FMOD.GUID) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:1267)
FMODUnity.RuntimeManager:CreateInstance (FMOD.GUID) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:1146)
FMODUnity.RuntimeManager:CreateInstance (FMODUnity.EventReference) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:1123)
ScriptUsageProgrammerSounds:PlayDialogue (string) (at Assets/Scripts/Sound/ScriptUsageProgrammerSounds.cs:18)
ScriptUsageProgrammerSounds:Update () (at Assets/Scripts/Sound/ScriptUsageProgrammerSounds.cs:80)

Error:
EventNotFoundException: [FMOD] Event not found: {00000000-0000-0000-0000-000000000000} ()
FMODUnity.RuntimeManager.CreateInstance (FMODUnity.EventReference eventReference) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:1127)
ScriptUsageProgrammerSounds.PlayDialogue (System.String key) (at Assets/Scripts/Sound/ScriptUsageProgrammerSounds.cs:18)
ScriptUsageProgrammerSounds.Update () (at Assets/Scripts/Sound/ScriptUsageProgrammerSounds.cs:80)

3.2 Add an event to the “Event Name” lookup in the Inspector window (event:/BGM/Edit_Chart) and run the programme.
Pressing the numbers 1, 2 and 3 will play the music with the event name set in the Inspector window, which is not what I expected.

The above is all I have done, and I may have got it wrong at some stage, so please correct me.
I think I may have misunderstood some of the code. If you can, I would like to ask you to write relevant code that meets the requirements, preferably with detailed comments. Declaring a path variable of type string (or a file variable of type byte array) and an object that can be dynamically remixed at runtime and using them in the code would be greatly appreciated.

1 Like

Hi,

Thank you for the reproduction steps.

The issue may be that you need to use a Programmer Instrument for the event in the inspector window:


image
Let me know if that works.

Thank you very much for your help, I can now successfully play music dynamically at runtime!
But after playing Unity outputs a warning message:

[FMOD] SoundSourceInstrumentInstance::startChannelIfReady : Loading delay exceeded, sound may play at incorrect time
UnityEngine.Debug:LogWarning (object)

Other than that, how can I dynamically mix music in real time at runtime?
Is it possible to create and modify some of the mixing effects as shown below, but at runtime?

Hi,

As you are directly loading an audio file and not loading its sample data into a bank it may take some time to load and so the error will be logged. You could try different loading modes to try avoid the error.

This can be done using Automation and Modulation, which allows you to control event parameters.

Hope this helps!

Thank you very much! All my issues have been successfully resolved.

Also I’d like to make some suggestions, I hope you can optimize the FMOD tutorial documentation if you can. Most of the time I can find the name of the parameter I need, but not its corresponding API, which can be very confusing.

1 Like

Hi,

Good to hear that solved it! Could you elaborate on how we could improve our docs? Any improvements we can make are welcome!

Let’s use “Automation and Modulation” as an example:
As you can see below, this is the page you provided me with.

It describes most of the content in great detail, and we can see what it is used for. But I can’t find any description of the API.
I know that FMOD is an audio middleware in order to allow audio engineers and programmers to do their jobs, but for an indie game developer, this makes it exponentially harder to find.
In order to be able to modify values at runtime, I had to go searching for the API specifically, as in the example above, and it was only through a dedicated search that I found some clues within the post.
https://qa.fmod.com/t/control-panner-on-master-bus-through-unity/20325


I realized that I needed to use the “setParameterByName” function to modify the value.

The introduction page of this function is very detailed, but if you do not go to understand, who will know what “Name” refers to the “Parameter name”? I know it has to pass parameters, but what should it pass? I don’t know.
For another example, there are options below it, such as “setParameterByID”.


We see that it needs to pass two parameters, but we don’t know what they are, so we click on the link after it to jump to it.

The explanation is good: “First half of the ID.”, “Second half of the ID.”.
What do they stand for? I don’t know.

These are my personal opinions, and perhaps I have not yet mastered the correct way to read the documentation, but these are some of the problems I encountered in this event.

1 Like

Hi,

That’s brilliant! There is a bit of assumed knowledge when using the API docs, however, I can pass on your suggestions.

Thank you again. Please do not hesitate to ask if I can assist with anything else.

1 Like