Introduction to the FMOD API
/These are some notes and pointers about using the FMOD API, particularly if you (like me) don’t have a lot of previous programming experence. I won’t tell you anything that is not already in the official documentation but take this a condensed reference to use when coding or just to get familiar with the API.
Getting started
You can find the official documentation on the FMOD website. In case you don’t know, API stands for Application Programming Interface and is pretty much a bridge between two different computer environments like two different pieces of software. In this case, the FMOD API serves as the bridge between a game engine like Unity which scripts are written in C# and the FMOD engine itself.
The FMOD API can speak C, C#, C++ & JavaScript although not all functions are available in all languages. I will give C# and Unity examples here but these can be easily translated into other languages and engines.
Do you even need to use the API?
It depends. Out of the box, FMOD can be used on Unity or Unreal without much code work. You will be able to load banks and play audio events with just the components that come with the plugin but as soon as you need to do more complex things, you will probably need to use the API, at least in a few areas.
Studio vs Core API
Before diving in, you will see that FMOD differentiates between Studio & Core API. Most likely, you will just use the Studio API since it talks to Fmod Studio making things much easier for us sound designers.
The Core API, on the other hand, is completely standalone, allowing to integrate the fmod engine in a game without even using Fmod Studio. This is aimed for more bespoke integrations and deeper functionality that in most cases is not really needed but it is good to know it is there as an option.
So for now on, I will focus on the Studio API since is the one I’ve used myself.
Studio API Structure
On a high level, the studio API uses a series of concepts and classes that we should famliarize ourselves with. The main “brain” is the class RuntimeManager which will create and initialize an Studio::System instance which, in turn, will be the class we will use to call engine wide functions like setting global parameters.
By default, you don’t need to do anything for this to work as the fmod implementation in Unity or Unreal will get his ready for you. Studio::Bank can be then used to operate on the different banks themselves while Studio::Bus and Studio::VCA will allow you to get and set data on the mixer elements.
The concept of events in FMOD Studio is mapped to an Studio::EventDescription so if you want to do anything related to individual events that is what you will use. But keep in mind that you can have many instances of an event, so the API allow us to affect each of these through Studio::EventInstance. To be able to do this, you will need to cache a reference to each instance you play if you want to have full control of them. Usually, we don’t need to cache one-shots instances since we don’t need to affect them once played.
Also, remember that snapshots, when played, are also considered event instances so you can have all the same functionality for them and I would personally recommend creating a dedicated snapshot manager to keep track of them.
With that basic structure out of the way, let’s see what the API offers.
Studio API: Types
FMOD defines a bunch of types which will be very useful to get information about the audio engine. This are just a few commonly used types but there are many more.
Studio API: System
These calls allow you to talk to fmod in a general way, that would affect the whole fmod audio engine. Here are some of the most useful functions with some examples:
Function | Example | Description |
---|---|---|
Load Bank | FMODUnity.RuntimeManager.LoadBank(bank, bool); | Where "bank" is simply a string with the bank name. Make the bool true if you want to pre-load the sample data. I'm not sure why this can be done with just the RuntimeManager without using StudioSystem. |
Unload Bank | FMODUnity.RuntimeManager.UnloadBank(bank); | |
Get Bank | FMODUnity.RuntimeManager.StudioSystem.getBank(path, out bank); | Use this to grab a reference to a specific bank. "path" is the bank name (a string) and you will get a "Bank" struct type. |
Get Bank Count | FMODUnity.RuntimeManager.StudioSystem.getBankCount(out int count); | Get the number of banks already loaded. |
Get Bank List | FMODUnity.RuntimeManager.StudioSystem.getBankList(out Bank[] array); | Get an array of the type "Bank". Use with the one above if you need to get a reference to all loaded banks. |
Get Listener Attributes | FMODUnity.RuntimeManager.StudioSystem.getListenerAttributes(int listener, out FMOD.ATTRIBUTES_3D attributes, out FMOD.VECTOR attenuationposition); | Use this to get a reference to the listener position which can be useful to calculate distances to emitters. The default listener is in index 0. There is also a set version of this function.. |
Get a Bus Reference | FMODUnity.RuntimeManager.StudioSystem.getBus(path, out Bus bus); | Get a bus reference so you can then use it. "path" is the bus string name. "bus:/" will always be the name of the master bus. You can also find a similar function for VCAs. |
Get an event description | FMODUnity.RuntimeManager.StudioSystem.getEvent(path, out EventDescription _event); | This is very handy if you want to do something with an specific event. As usual, "path" is the string name. |
Set a global parameter | FMODUnity.RuntimeManager.StudioSystem.setParameterByName(name, value, ignoreseekspeed); | Sets a value to a global parameter where "name" is a tring with the name of the parameter, "value" is a float and "ignoreseekspeed" is a bool. |
Studio API: Bank
We won’t be able to do much until we have our banks loaded. Keep in mind that the master bank always needs to be loaded as this contains mixer information like buses, vcas or snapshots. Other than that, all other banks can be loaded one by one as needed. I talked about how to get a reference to a particular bank above, let’s now see what you can do with that reference:
Function | Example | Description |
---|---|---|
Get loading state | Studio.Bank.getLoadingState(out LOADING_STATE state); | We can check if a bank has finished loading before doing anything with it. |
Load Sample data | Studio.Bank.loadSampleData(); | This will load all the audio files themselves into memory before any audio is played. Use this if the events are very time sensitive or you want to trade CPU for memory. There is also an unload method. |
Unload bank | Studio.Bank.unload(); | Use this if you are sure you don't need the events contained on this bank anymore so we can save some memory. |
Get the event count | Studio.Bank.getEventCount(out int count); | Check how many events the bank has. |
Get event list | Studio.Bank.getEventList(out EventDescription[] array); | Use with the above method to get an arrray of event descriptions in case you then want to do something with them. |
Studio API: EventDescription
You can find here all functions related to each specific event set in FMOD Studio. Before you use this, remember that you need to grab a reference to the event description using the method described in the section above so you can only do this once the bank containing the event is fully loaded. Once you have it, you can get some very usefu information from it. Here are some examples:
Function | Example | Description |
---|---|---|
Create Instance | FMOD.Studio.EventDescription.createInstance(out EventInstance instance); | Use this to create an instance which you must do before you play the event. It can be a good idea to cache the instance for use later. |
Load Sample Data | FMOD.Studio.EventDescription.loadSampleData(); | This is very handy if you want to load the sample data for an event before you play it. Particularly useful for time sensitive events. |
Is 3D | FMOD.Studio.EventDescription.is3D(out bool is3D); | Check if the event is 3D whcih would be determined by the event having an spatializer. Nice one if you create your own emitter or player. |
Is Valid | FMOD.Studio.EventDescription.isValid(); | Use this one to make sure the event is not null and can be used. |
Is Snapshot | FMOD.Studio.EventDescription.isSnapshot(out bool snaphot); | Check if a particular description is a snaphot. |
Studio API: EventInstance
As mentioned before, we need to create an event instance in order to be able to play it or just do anything with them. Once this is done, we can have full control of the instance. Here are some useful methods:
Function | Example | Description |
---|---|---|
Play Instance | FMOD.Studio.EventInstance.start(); | Just play the instance. Basically, this will start the timeline, as set in fmod studio. |
Stop Instance | FMOD.Studio.EventInstance.stop(FMOD.Studio.STOP_MODE); | Stops an instance. As you can see, we need to pass a stop mode which will determine if we respect any fades set in studio. |
Release Instance | FMOD.Studio.EventInstance.release(); | This will tell the fmod engine that this instance can be deleted from memory. Use this after stopping an instance if you don't need it anymore. Important to prevent memory leaks. |
Get playback state | FMOD.Studio.EventInstance.getPlaybackState(out FMOD.Studio.PLAYBACK_STATE state) | Use this to know the current state of the instance. Very useful if we want to check if the instance is playing before trying to stop it. |
Set 3D Attributes | FMOD.Studio.EventInstance.set3DAttributes(RuntimeUtils.To3DAttributes(position)); | Sets the instance to a particular 3D position. Used to play audio in some specific place. |
Attach to game object | RuntimeManager.AttachInstanceToGameObject(instance, gameObject.transform, rigidBody); | Use this for 3D events if, instead of playing in an specific place, you want to attach the sound to a game object. |
Studio API: Bus & VCA
We can interact with the game’s mix by getting and setting values on buses and vcas. Check the system section to see how we can get a reference of a particular bus or vca but basically, we would need to know its name (as it was names in FMOD studio) as a string value so it could be a good idea to keep a reference of these somewhere in your code.
Let’s see a few examples of things we can do with buses and vcas:
Function | Example | Description |
---|---|---|
Set Bus volume | Studio.Bus.setVolume(float volume); | Sets a particular volume for this bus. There is also a get method. |
Stop all events | Studio.Bus.stopAllEvents(STOP_MODE mode); | Stops all the events within a bus. Useful if you want to make sure all audio within a particular bus stops when unloading a level, for example. Notice that we need to pass the stop mode we wish to use. |
Pause all events | Studio.Bus.setPaused(bool pause); | This will pause or unpause all the events contained in a bus. Use this for pause menus, for example. |
Set VCA volume | Studio.VCA.setVolume(float volume); | Set volume on a VCA. Nice for the audio level settings. |
In conclusion
I think if you understand how all the above work, that would be a very good start. Check out the FMOD reference docs for many useful examples and dont’ hesitate to drop me a line if you have any questions, I know some of this can be obscure until you use it for a while.