Fmod Event Macros
/Here is an overview at how you can control each event in fmod, what the limitations are and what you can achieve by programming your own systems on top.
I’m going to cover a bit of the same content you can read about on the Fmod documentation but I will also be adding my own comments from the perspective of someone using them in the real world.
Persistent
By default, Fmod events stop after they don’t have anything else to play. This notion of “anything else to play” includes any timeline markers or regions. Once the event reaches the last item on the timeline it will stop by itself.
If you want to avoid this, you can make the event persistent which will mean that it will only stop when explicitly told. I usually use this when I’m not playing the event based on the timeline but based on parameters values. Just remember that you need to stop the event later somehow.
Pitch
This is a general pitch control for the whole event which works on top of the instruments pitch values. Remember that both this pitch control and the one on instruments changes the playback speed, so the length of the sound will be altered.
Having a macro control is useful when you want to control multiple instruments within an event. I often use it together with modulation, usually an envelope, to make the event’s pitch raise when played and drop when stopped, which is very handy to create power on and power off type sounds programmatically.
Doppler & Doppler Scale
Fmod has a built-in doppler effect. As you probably know, this effect occurs when there is a difference between the emitter and listener speeds. If you turn this option on, Fmod automatically calculates how this relative speed would affect the event’s pitch. The scale property allows to exaggerate or reduce the effect, allowing for fine tuning for each individual event.
All this sounds great but if you are using Fmod with Unity, it is quite possible that this is not going to work out of the box. The problem is about how Unity gets this speed data. By default, the speed is automatically fed to Fmod through the object’s rigidbody so in principle you need one of these for doppler to work by default.
But the real problem is that you also need the object to move via the Unity physics engine for the speed to be passed correctly. That’s a big hurdle since most often, game objects are not moved solely by physics, it is common to change their location directly using their transform, via animation or code.
So you will usually find that your fmod event attached to a game object, even with a rigidbody, won’t have any doppler effect at all. The same applies, by the way, to the built-in speed parameters. To fix this, what we essentially need to do is use the event instance to transmit the velocity manually by code. This can be achieved with EventInstance::set3DAttributes. The supplied velocity needs to be measured in metres per second, although this can be changed as a general Fmod engine setting.
So I haven’t been able to use the Fmod built-in doppler system yet but it is something I want to add to my Fmod implementation soon. See my article about how to get doppler working in Unity.
Some thoughts about Instances and Voices
Before continuing, I wanted to highlight the difference between these two concepts. To play any event in Fmod, we need to instantiate its description. Each of these instances is like an incarnation of the description. This is a bit similar to how classes work in programming: you instantiate a non static class so you can use it.
So each of these instances is independent of each other for the most part. When are these instances created? If you are using the StudioEventEmitter class in Unity, a look at the code reveals that it creates a new instance every time we play a given event from the emitter. But there is an important consideration: this is only the case if the event itself is a One Shot. Fmod defines a One Shot as an event that would stop by itself, which usually means that there is no loop or sustain point.
In my opinion this behaviour is a bit limiting. Sometimes, I want to send parameters to events that happen to be One Shots, for example. And there is another fundamental problem. You generally want to be economical with the amount of instances you use since each of them consumes resources. Creating new instances everytime you play a One Shot seems like a good default behaviour, think about a shotgun firing: you don't want to restart the instance each time you shoot since you would miss its tail. But at the same time, this means that any emitter which is prone to play very frequently, is going to create a huge amount of instances which will remain alive until they finish playing.
So my recommendation, and what I have actually done myself is to code your own emitter classes which create a pool of instances to use in sequence. Sometimes, I do want to just restart the same instance again and again if the sound is short, simple and not very important like a broken cable producing sparks.
In conclusion: instances are created so we can play Fmod events and ideally you should create your own emitter classes if you want to have more flexibility in terms of how to control them.
Voices are simpler. Each instance uses one voice per instrument. So if you just have a single instrument, that event is only going to use one voice. Is important to remember that even if you are playing only a few event instances, you may be using too many voices if your events contain many different instruments.
Max instances
This value limits the amount of instances in total that can play at the same time. At first, I thought that this value meant something like ”Event Polyphony” but this is only kind of true if you use the vanilla event emitter AND your event is a one shot, so it can be a bit deceiving if you don't know what's going on under the hood.
In my case, since I have my own emitter classes, I don't really use this value much, since my own code controls the amount of instances created in a more flexible way.
Stealing
Let's say you set your max instances to be 3. What happens after you play a fourth instance while the other three are still playing? This is where you define that behaviour. Let´s have a look at the options:
Oldest: Stop the instance that started the longest time ago. This is useful for very frequent events.
Furthest: Stop the instance that is the furthest from the listener.
Quietest: Stop the least audible instance.
Virtualize: Works like the quietest but the instance is not stopped but virtualized. This means that it produces no audio but its timeline is still running. This is most useful for environmental SFXs, with many emitters on the same scene.
None: No stealing, so the new instances just don´t play until an existing instance stops.
Cooldown
This option allows you to have a minimum time between instances. This is very useful to prevent spamming. In general, I think this setting is nice to have as a safety net but if you have to use it, there is probably something wrong with the way audio is played in code, so fixing that first should be the first approach.
Priority
This is not the instance priority but the voice priority, which means this value would affect each of the event´s instruments, instead of the event as a whole. This priority comes into play when we reach the game´s engine voice limit. Once this happens, Fmod will start to steal voices: higher priority voices will be able to steal lower priority voices. The “Highest” priority will never be stolen.
Min and Max Distance
Now these values live on the event level instead of on the spatializer which I think is a very good change. Between 0 and the min distance, the event will always play at its full level. From the min to the max, volume will drop to 0 following the spatializer curve.
Having these values on the event level allows us to use a built-in parameter, normalized distance, to be able to do distance based automation in a very flexible way. I use this to create bespoke attenuation curves that I can then save as presets.