How to get spatializer min/max distance override in code (FMOD 2.02.03)

Hi,

We are using FMOD 2.02.03 in Unity 2020.2.7. Even though FMOD now allows you to set Min and Max distance on the Event, our sound designer still likes to set it on the spatializer, as he uses presets extensively.

I need to get the max distance that applies to an event in code, but am having trouble getting the override max distance specified on the spatializer.

If I call getMinMaxDistance() on either the EventDescription or EventInstance, it returns the value specified on the event. If I call getProperty() to get EVENT_PROPERTY.MAXIMUM_DISTANCE, it returns -1 (indicating to use the distance from the EventDescription).

Because the Min/Max distance is overridden in the spatializer, these returned values are not useful to me. How can I get the override values in code?

Thanks,
Evan

The best way I can think of would be to get the underlying Spatializer DSP object and get the parameters from there. This would be the process:

  • Get event instance’s channel group
  • Search through the channel group for a Spatializer
  • Search through the Spatializer for the “3D Min Distance” and “3D Max Distance” parameter values

The event instance would need to be playing for it to work in this manner though. Here’s a rough example:

    void GetSpatializerMinMaxDistance(FMOD.Studio.EventInstance eventInstance, out float spatializerMin, out float spatializerMax)
    {
        spatializerMin = -1;
        spatializerMax = -1;

        eventInstance.getChannelGroup(out FMOD.ChannelGroup cg);
        cg.getNumDSPs(out int numDSPs);

        for(int i = 0; i < numDSPs; ++i)
        {
            cg.getDSP(i, out FMOD.DSP dsp);

            dsp.getInfo(out string dspName, out uint version, out int channels, out int configwidth, out int configheight);

            if (dspName.Contains("FMOD Pan"))
            {
                dsp.getNumParameters(out int numParams);

                FMOD.DSP_PARAMETER_DESC desc;
                for (int j = 0; j < numParams; ++j)
                {
                    dsp.getParameterInfo(j, out desc);

                    string paramName = System.Text.Encoding.UTF8.GetString(desc.name);

                    if (paramName.Contains("3D Min Distance"))
                    {
                        dsp.getParameterFloat(j, out spatializerMin);
                    }
                    else if (paramName.Contains("3D Max Distance"))
                    {
                        dsp.getParameterFloat(j, out spatializerMax);
                    }
                }
            }
        }
    }

The fact that the event instance would need to be playing is a problem. Would the event instance have to actually be playing, or just instantiated?

We have significant customizations in the Unity integration code to improve performance. One of our optimizations is to not even attempt to play event instances that are out of range.

I know that the newer versions of FMOD for Unity provide a checkbox to do the same thing; when I tried that, it seemed to also only look at the event min/max distances, not the spatializer distances.

Is there any way to set event distances in a preset? If our sound designer can make a slight adjustment to his workflow, that would work, too.

It would need to be playing- it requires a channel and that is only assigned after play is called.
In the case of setting event distances in a preset, you could add automation to the spatializer’s min and max distances and access the values using parameters. Otherwise, if this is more of a workflow thing about setting default event min and max distances your sound designer could create a Default Event and use the event macro Min & Max Distance properties.

Unfortunately, a default event won’t work for our case.

We are making a racing game. We have various events for exhaust, engine, skid, etc, for each different type of vehicle or surface. We want the max distance to be different for each class of sound (exhaust, engine, skid), but the same within each class for every vehicle. So all car exhaust events have the same max distance, all car engine events have the same max distance, etc.

Additionally, we need to be able to tune the max distance for each class, for both audio effect and for performance.

Presets have worked well for this; all engine sounds have the “Engine Spatializer” preset on them, exhausts have the “Exhaust Spatializer” preset, etc. Then we can adjust the min/max distance on the presets and have the change propagate to all of the proper events.

Default events don’t maintain any sort of linkage to their derived events, so that wouldn’t allow us to alter the min/max distance for an entire class to tune things. And having to manually go to every event to change its min/max distance settings would be both extremely time-consuming and error-prone.

I’m not sure I see how using automation on the spatializer’s min/max would work here. Could you elaborate on that?

I may need to resort to making a script that, when selected, goes through every event and applies the spatializer min/max to the event min/max. We never change the min/max distance, so we at least don’t need to worry about that. That is clunky, though, to require manually clicking something to copy the distances… is there anywhere to hook into via scripting, that could make that execute automatically?

I think automation can achieve the broad-stroke behaviour you are looking for and give you access to the spatializer values iniside Unity:

  • Create parameters for each min and max value, i.e “Engine Max Range”, “Engine Min Range”, “Exhaust Max Range” etc

  • Add automation curves to your spatializers, using these new parameters

  • Tweak the parameter values in FMOD studio

  • When your sound designer is happy with them, right click on the parameter and click “Set as Initial Value”. Here is a screenshot of such a setup for clarity:

  • Build banks and open in Unity

  • Lookup the initial value in Unity using:

emitter = GetComponent<FMODUnity.StudioEventEmitter>();

var eventDescription = FMODUnity.RuntimeManager.GetEventDescription(emitter.EventReference);

if (eventDescription.isValid())
{
    FMOD.Studio.PARAMETER_DESCRIPTION param;
    eventDescription.getParameterDescriptionByName("Engine Max Range", out param);
    Debug.Log(String.Format("Engine Max Range: {0}", param.defaultvalue));
}

We do have a JS scripting API inside FMOD Studio, it should give you the ability to modify event macro values to match spatializer values. We have some example scripts in “C:\Program Files\FMOD SoundSystem\FMOD Studio 2.02.03\scripts” to get you started if you would rather go that way.

Hi Jeff,

Sorry for the delay in responding, I had some other tasks come up. I’ve implemented this solution, but there is another issue I have run into: the “Min & Max Distance” values on the spatializer are implemented on a logarithmic scale, but I don’t know of a way to create a logarithmic automation curve (aside from manually plotting one out, which may not be precise, is error-prone, and is extremely tedious).

This means that the parameter won’t have a 1:1 relationship with the actual max distance parameter on the spatializer, so FMOD and I will be using different values, which is no good.

Any thoughts?

Apologies for the oversight there, you are right the non-linearity of the spatializer makes it impractical to use parameters effectively for this purpose.
In that case, I think a script might be the best way to go.
Here is a simple script for replacing the event macro min and max distances with the min and max distances on the spatializer in the master track. Run it before building and it should be a good workaround. Place it in your FMOD plugins directory and it should be a good starting point for you:

studio.menu.addMenuItem({ name: "Transfer Event Min Max",
    isEnabled: function() { var event = studio.window.browserCurrent(); return event && event.isOfExactType("Event"); },
    execute: function() {

        function transferSpatializerMinMaxValues(event)
        {
            var effects = event.masterTrack.mixerGroup.effectChain.effects;

            effects.forEach(function(effect) {
                if(effect.entity == "SpatialiserEffect") {
                    event.automatableProperties.minimumDistance = effect.minimumDistance;
                    event.automatableProperties.maximumDistance = effect.maximumDistance;
                    return;
                }
            });

        }

        var events = studio.project.model.Event.findInstances();
        events.forEach(function(event) {
            transferSpatializerMinMaxValues(event);
        });

    },
});

Then you can just read the min and max values from the event description as usual:

emitter = GetComponent<FMODUnity.StudioEventEmitter>();

var eventDescription = FMODUnity.RuntimeManager.GetEventDescription(emitter.EventReference);
eventDescription.getMinMaxDistance(out float min, out float max);
Debug.Log(String.Format("Min: {0}, Max: {1}", min, max));