AudioMixerSnapshot transitions independent of timescale


One of the great new features of Unity 5 is the overhauled audio system, including the fancy new mixer and audio mixer snapshots. As a sound designer, this was one of the major selling points that pushed me to upgrade from 4.6. In particular, the AudioMixerSnapshot class lets you define a bunch of settings (for example submixer settings, filter effect values, etc) and transition between them – either instantly or over time. For example, one classic and emotionally-effective use is to low-pass filter your music when in a pause menu or item selection screen that takes you "out" of the action.

The only problem is that many people set Time.timescale to 0 when pausing their game, and this actually prevents the AudioMixerSnapshot.TransitionTo() function from transitioning smoothly. I was tired of waiting for a native solution to this, so I wrote a coroutine in my AudioManager class to handle snapshot transitions, and this is not affected by Time.timescale. Here's the sample code, easily adaptable to your own class:

private Coroutine transitionCoroutine;
private AudioMixerSnapshot endSnapshot;
 
public void TransitionSnapshots(AudioMixerSnapshot fromSnapshot, AudioMixerSnapshot toSnapshot, float transitionTime)
{
        EndTransition();
        transitionCoroutine = StartCoroutine(TransitionSnapshotsCoroutine(fromSnapshot, toSnapshot, transitionTime));
}
 
IEnumerator TransitionSnapshotsCoroutine(AudioMixerSnapshot fromSnapshot, AudioMixerSnapshot toSnapshot, float transitionTime)
{
        // transition values
        int steps = 20;
        float timeStep = (transitionTime / (float)steps);
        float transitionPercentage = 0.0f;
        float startTime = 0f;
       
        // set up snapshots
        endSnapshot = toSnapshot;
        AudioMixerSnapshot[] snapshots = new AudioMixerSnapshot[] {fromSnapshot, toSnapshot};
        float[] weights = new float[2];
       
        // stepped-transition
        for (int i = 0; i < steps; i++)
        {
                transitionPercentage = ((float)i) / steps;
                weights[0] = 1.0f - transitionPercentage;
                weights[1] = transitionPercentage;
                audioMixer.TransitionToSnapshots(snapshots, weights, 0f);
               
                // this is required because WaitForSeconds doesn't work when Time.timescale == 0
                startTime = Time.realtimeSinceStartup;
                while(Time.realtimeSinceStartup < (startTime + timeStep))
                {
                        yield return null;
                }
        }
       
        // finalize
        EndTransition();
}
 
void EndTransition()
{
        if ( (transitionCoroutine == null) || (endSnapshot == null) )
        {
                return;
        }
       
        StopCoroutine(transitionCoroutine);
        endSnapshot.TransitionTo(0f);
}