Doppler effect FMODUnity Integration not working

Hi,
I have doppler turned on (and set to 500% for testing purposes) in the relevant event macros in my FMOD Studio project but the effect is missing when I run my unity game. How do you enable the effect? I have found zero documentation on the topic and no answers in these forums.
Thanks - Brian

To use the doppler effect, you need to have a rigidbody on your event emitter. It uses the velocity of the rigidbody.

You are right, there doesn’t seem to be much documentation on it at the moment, we will look to rectify this. The only real info we have on it at the moment is here: http://www.fmod.org/docs/content/generated/FMOD_System_Set3DSettings.html

Ok thanks - I gave it a try but it does not work. I think the issue is that I am using Rigidbody2D and Collider2D in my game as it is a 2D game. I tried changing the rigidbody variable to a rigidbody2D variable in the event emitter script but ended up with lots of errors - any suggestions?

Ah yes, that is a problem. Our utilities don’t deal with Rigidbody2D’s at the moment.
This will be fixed in a public release at the end of December, in the meantime I will upload the changes to github soon: https://github.com/fmod/UnityIntegration

Ok - Looking forward to the fix. - b

For Doppler in Unity - In general, it would be help to add some sort of public Doppler variable in the FmodUnity event emitter editor panel that requires the user to place the gameobject’s Rigidbody or Rigidbody2D on to it in order to be activated. Either that or a simple embedded note or waring would do - b

The Rigidbody2D fix is now up on github.

Thanks Cameron for the quick fix - I will let you know how I get on.

Hi Cameron, I skipped the github fix and waited on the public release Version: 1.09.01 - however, nothing has changed - I have a Rigidbody2D on my emitter object and and doppler set to on and to 500% (for testing) for the fmod Studio sound the emitter uses. Still no doppler effect? Am I missing something? - b

I’m not having any issues getting it to work.
These are the steps I took:

  • Create new Unity project and add FMOD integration.
  • Add cube to scene, remove 3D collider and add Rigidbody2D.
  • Move the cube high above the Main Camera (add Studio listener to camera).
  • Add emitter to cube and chose a sound.
  • In Studio make sure the event has doppler enabled, rebuild banks.
  • Hit play in Unity.
  • As the cube falls past the player you can hear the doppler.

If that is working but not when you move your player(listener) it may be because the doppler relies on velocity from the rigidbody.

Hi Cameron,
Yep that works but unfortunately if you move an object via transform.Translate or you add an animation say a looping up or down animation to the exact same object the doppler effect completely fails to work. Unless you are using free falling objects with gravity, doppler is not working. I even have a cube in the project that drops from + 100 to -100 on the y axis that has a strong noticeable doppler effect when the animation is disable and allowed to free fall and no doppler effect whatsoever (or a very broken version) when animation is enabled (the animation is of equivalent speed and direction as the free falling scenario). In my game the cars use transform.Translate and doppler does not work there either. What next? Again they all have rigid bodies and correct studio settings etc.

  • Brian

I will add that you can set gravity to 1 in the rigidbody while the up/down animation is playing and you get a glitchy/clicky broken versions of the doppler shift effect. I try all this with a standard non2D rigid body and all the same issues exist.

By moving an object via the Transform, you are basically just telling the object it is in a new location now, not that it has traveled from one to another. Velocity is only used when applying forces to a rigidbody (eg. rigidbody2D.AddForce()). You can check this by printing the objects rigidbody2D.velocity to the debug console.

As for animations, Unity have physics based animations on their roadmap (https://unity3d.com/unity/roadmap) but it is still under research.

That makes sense - but I didn’t have this issue with fmod designer for the same game 3 years ago now. It might have been an idea to calculate the velocity based on transform/s positions over time irrespective of rigidbody calculations so that all movement types would work with Doppler as before - or I might be missing some logic here. I will look into a work around but would love to see a more inclusive solution in a future update so that fast moving objects using transform.translate or standard animations would work. Thanks for looking into all this for me - b

<NOTE: I am using FMOD Unity 1.09. I am not sure whether or not this fix is still required for 1.10+>

Hi All,

I know this is a very old post now, but I found myself asking the exact same question yesterday, so thought I’d leave my solution here for others to try.
In order for your events’ doppler effects to work without rigidbodies, all that FMOD really needs is an FMOD.ATTRIBUTES_3D struct describing world-space velocity of the moving object, and the same for the listener.
To achieve this, we can overload some functions in the FMOD C# scripts.

First, create a class that has the same members as a Vector3. In C#, classes are reference types by default. Using a class instead of a struct will simplify our method overloading, and slightly reduce the amount of code we need to re-write. I declared my class in RuntimeUtils.cs:

public class CVector3
{
public float x = 0;
public float y = 0;
public float z = 0;
}

Find RuntimeUtils.To3DAttributes, and overload it so it can take an instance of our class as a parameter. Add the following to RuntimeUtils.cs:

    public static FMOD.ATTRIBUTES_3D To3DAttributes(Transform transform, CVector3 kinematicVelocity)
    {
        FMOD.ATTRIBUTES_3D attributes = transform.To3DAttributes();
        FMOD.VECTOR vel;
        vel.x = kinematicVelocity.x;
        vel.y = kinematicVelocity.y;
        vel.z = kinematicVelocity.z;

        attributes.velocity = vel;

        return attributes;
    }

Next, we are going to expand the AttachedInstance class (found in RuntimeManager.cs) so that it can accommodate our CVector3 reference:

class AttachedInstance
{
public FMOD.Studio.EventInstance instance;
public Transform transform;
public Rigidbody rigidBody;
public Rigidbody2D rigidBody2D;
public CVector3 kinematicVelocity; //<----------Add this line
}

We can now add the following overload of RuntimeManager.AttachInstanceToGameObject:

    public static void AttachInstanceToGameObject(FMOD.Studio.EventInstance instance, Transform transform, CVector3 kinematicVelocity)
    {
        var attachedInstance = new AttachedInstance();
        attachedInstance.transform = transform;
        attachedInstance.instance = instance;
        attachedInstance.rigidBody2D = null;
        attachedInstance.rigidBody = null;
        attachedInstance.kinematicVelocity = kinematicVelocity;
        Instance.attachedInstances.Add(attachedInstance);
    }

In RuntimeManager.cs, locate the Update() method. About half way down you will see this loop:

             for (int i = 0; i < attachedInstances.Count; i++)
             //...etc

At the bottom of the for loop, change this…

                if (attachedInstances[i].rigidBody)
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform, attachedInstances[i].rigidBody));
                }
                else
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform, attachedInstances[i].rigidBody2D));
                }

To this…

                if (attachedInstances[i].rigidBody)
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform, attachedInstances[i].rigidBody));
                }
                else if (attachedInstances[i].rigidBody2D)
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform, attachedInstances[i].rigidBody2D));
                }
                else if (attachedInstances[i].kinematicVelocity != null)
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform,attachedInstances[i].kinematicVelocity));
                }

Here we are saying that our Doppler effect can fall back to kinematic velocity if there are no Rigidbodies associated with that instance.

We have sorted out everything we need to convert our CVector3 data into an FMOD.ATTRIBUTES_3D struct. However, we still need to fill in the kinematic velocity each frame.
To do this we need to make some changes to StudioEventEmitter.cs.

First, add and initialise the following class members to StudioEventEmitter:

CVector3 kinematicVelocity = null;
Vector3 positionLastFrame = Vector3.zero;

Next, locate the Play() method and find this piece of code:

            if (is3D)
            {
                var rigidBody = GetComponent<Rigidbody>();
                var transform = GetComponent<Transform>();
                instance.set3DAttributes(RuntimeUtils.To3DAttributes(gameObject, rigidBody));
                RuntimeManager.AttachInstanceToGameObject(instance, transform, rigidBody);
            }

Change it to this:

            if (is3D)
            {
                var rigidBody = GetComponent<Rigidbody>();
                var transform = GetComponent<Transform>();
                
                if (!rigidBody)
                {
                    kinematicVelocity = new CVector3();
                    instance.set3DAttributes(RuntimeUtils.To3DAttributes(this.transform, kinematicVelocity));
                    RuntimeManager.AttachInstanceToGameObject(instance, transform, kinematicVelocity);
                }
                else
                {
                    instance.set3DAttributes(RuntimeUtils.To3DAttributes(gameObject, rigidBody));
                    RuntimeManager.AttachInstanceToGameObject(instance, transform, rigidBody);
                }
            }

…StudioEventEmitter will now send kinematic data if a Rigidbody is not found.

Next, add the following method to StudioEventEmitter.cs:

    void setKinematicVelocity()
    {
        //Get current velocity
        Vector3 currentVel;
        currentVel.x = kinematicVelocity.x;
        currentVel.y = kinematicVelocity.y;
        currentVel.z = kinematicVelocity.z;

        //Update to new velocity
        currentVel = Vector3.Lerp(currentVel, (this.transform.position - positionLastFrame) / Time.deltaTime, Time.deltaTime * 15); //A very short lerp prevents jitter

        //Reassign to CVector3 object
        kinematicVelocity.x = currentVel.x;
        kinematicVelocity.y = currentVel.y;
        kinematicVelocity.z = currentVel.z;

        //Store world position for next frame
        positionLastFrame= this.transform.position;
    }

Call the above method it in Update(), but ONLY if kinematicVelocity is non-null. If it is null, this indicates that a rigidbody was found, and kinematicVelocity should be ignored.

void Update()
{
if (kinematicVelocity != null)
{
setKinematicVelocity();
}
}

We’re almost done. However, Doppler effect should be calculated from the point of the listener, who may also be moving. We therefore need to apply similar changes to the StudioListener.cs script.

First, add the two members, and setKinematicVelocity() method, in the exact same manner as with StudioEventEmitter.cs:

public CVector3 kinematicVelocity = null;
Vector3 positionLastFrame;

    void setKinematicVelocity()
    {
        //Get current velocity
        Vector3 currentVel;
        currentVel.x = kinematicVelocity.x;
        currentVel.y = kinematicVelocity.y;
        currentVel.z = kinematicVelocity.z;

        //Update to new velocity
        currentVel = Vector3.Lerp(currentVel, (this.transform.position - positionLastFrame) / Time.deltaTime, Time.deltaTime * 15);

        //Reassign to CVector3 object
        kinematicVelocity.x = currentVel.x;
        kinematicVelocity.y = currentVel.y;
        kinematicVelocity.z = currentVel.z;

        //Store world position for next frame
        positionLastFrame = this.transform.position;
    }

…Call setKinematicVelocity() in the Update method for StudioListener, again checking for nulls:

void Update()
{
if (kinematicVelocity != null)
{
setKinematicVelocity();
}
//…etc
}

In the OnEnable() method, we are going to create a CVector3 IF there are no rigidbodies on the StudioListener game object:

    void OnEnable()
    {
        RuntimeUtils.EnforceLibraryOrder();
        rigidBody = gameObject.GetComponent<Rigidbody>();
        rigidBody2D = gameObject.GetComponent<Rigidbody2D>();

     //<-----Add this part here--------->
        if (!rigidBody && !rigidBody2D)
        {
            kinematicVelocity = new CVector3();
        }
     //<---------------------------------->

         //...etc
    }

In RuntimeManager.cs, we need to add one last overload. That is to let SetListenerLocation() work with CVector3 objects. Add this overload to RuntimeManager.cs:

    public static void SetListenerLocation(int listenerIndex, GameObject gameObject, CVector3 kinematicVelocity)
    {
        Instance.studioSystem.setListenerAttributes(listenerIndex, RuntimeUtils.To3DAttributes(gameObject.transform, kinematicVelocity));
    }

Finally, StudioListener.cs will also have a method called SetListenerLocation(). Remove it, and replace it with this version:

    void SetListenerLocation()
    {
        if (rigidBody)
        {              
            RuntimeManager.SetListenerLocation(ListenerNumber, gameObject, rigidBody);
        }
        else if (rigidBody2D)
        {
            RuntimeManager.SetListenerLocation(ListenerNumber, gameObject, rigidBody2D);
        }
        else
        {
            RuntimeManager.SetListenerLocation(ListenerNumber, gameObject, kinematicVelocity);
        }
    }

You should now have made all the adjustments to the FMOD Unity wrapper to allow you to supply your events with kinematic velocity.

If the above changes don’t work, I’ve likely missed something out… I’ve checked it over as much as I can right now, but my FMOD Unity install is heavily modified and I was trying to leave out other stuff that made no sense in this context, while still trying to remember all the steps required to do this.

Leave a reply if it helps you. Also leave a reply if it doesn’t, along with any errors, and I’ll fix it asap!

In any case, hopefully you understand what I’m doing here, and if so, you can make your own modifications depending on your needs.

Happy programming
Geoff

I wrote this simple but very useful script to get the Doppler effect with no rigidbody attached to the game object.

using UnityEngine;
using FMOD.Studio;
using FMODUnity;


namespace Fiftytwo
{
    public class FmodDoppler : MonoBehaviour
    {
        [EventRef, SerializeField] private string _event;

        private EventInstance _eventInstance;
        private Vector3 _previousPosition;


        private void Awake ()
        {
            _eventInstance = RuntimeManager.CreateInstance( _event );
            _previousPosition = transform.position;
        }

        private void FixedUpdate ()
        {
            var newPosition = transform.position;

            var move = newPosition - _previousPosition;
            var velocity = move / Time.fixedDeltaTime;

            var attributes = transform.To3DAttributes();
            attributes.velocity = velocity.ToFMODVector();

            _eventInstance.set3DAttributes( attributes );

            _previousPosition = newPosition;
        }

        private void OnEnable ()
        {
            _eventInstance.start();
        }

        private void OnDisable ()
        {
            _eventInstance.stop( FMOD.Studio.STOP_MODE.ALLOWFADEOUT );
        }

        private void OnDestroy ()
        {
            _eventInstance.release();
        }
    }
}

It would be so nice to get the Doppler work in Fmod using just a transform position. It’s not obvious that it needs the rigidbody. In many cases it’s even impossible to have a rigidbody.

1 Like

@fiftytwo & @GeoffAngus I had forgotten and/or given up on a solution for this issue - Thanks so much for all the work you put in here, I will try it out and I’m sure it has benefited others with the same problem. Cheers - b

Also, for those who came across this issue, you’ve better have a listener supporting Doppler as well as objects.

using UnityEngine;
using FMOD.Studio;
using FMODUnity;


namespace Fiftytwo
{
    public class FmodDopplerListener : StudioListener
    {
        private Vector3 _previousPosition;


        private void Awake ()
        {
            _previousPosition = transform.position;
        }

        private void Update ()
        {
            if (ListenerNumber >= 0 && ListenerNumber < FMOD.CONSTANTS.MAX_LISTENERS)
            {
                SetListenerLocation();
            }
        }

        void SetListenerLocation()
        {
            var newPosition = transform.position;

            var move = newPosition - _previousPosition;
            var velocity = move / Time.deltaTime;

            var attributes = transform.To3DAttributes();
            attributes.velocity = velocity.ToFMODVector();
            
            RuntimeManager.StudioSystem.setListenerAttributes(0, attributes);

            _previousPosition = newPosition;
        }
    }
}

Another vote for this. As a non-programmer it’s quite hard for me to come out with workarounds if the feature is not readily available on FMOD. I’ve been working in many little 2D games and in most of them I thought at least once about putting a doppler effect in one of the elements, but with the confusion plus lack of documentation, I always had to discard the idea just thinking that Fmod simply does not support doppler for 2D objects. Is there a feature request already in place somewhere?