Search code examples
c#unity-game-engineaudio-source

Unity Lerping the volume of an AudioSource to 0.01 works but lerping it back to 1.0 causes the source to break


I unfortunately hit a roadblock with this audio fading function I've made. It takes in a source, duration and a new clip. It is supposed to fade out the initial clip, replace the clip and lerp the volume back to 1.

public static IEnumerator FadeSwitchAudio(AudioSource audioSource, float duration, int clip)
{
    float firstTimer = 0;
    float secondTimer = 0;
    float start = audioSource.volume;

    // Fade audio out, works as intended 
    while (firstTimer < duration)
    {
        firstTimer += Time.deltaTime;
        audioSource.volume = Mathf.Lerp(start, 0.01f, firstTimer / duration);
        yield return null;
    }

    audioSource.clip = GameObject.FindWithTag("AudioManager").GetComponent<AudioManager>().clips[clip];

    // Fade audio back in, volume jumps immediately to 1 and audiosource stops having output
    yield return new WaitForSeconds(0.1f);
    while (secondTimer < duration)
    {
        secondTimer += Time.deltaTime;
        audioSource.volume = Mathf.Lerp(start, 1.0f, secondTimer / duration);
        yield return null;
    }
}

I have tried a lot of different timer functions, however, it seems that increasing the volume of an audiosource always breaks it. Is there a sure way to slowly increase the audio of a source without the source completely losing output? Here is a gif of the current behaviour in the inspector. Please excuse the low resolution but you can see the behaviour.

Unity Audio Lerping Issue

Thank you for your time!


Solution

  • Two issues here:

    • Only changing the AudioSource.clip doesn't do anything.

      Assigning clip with a new audio clip does not instantly change the clip that is being played

      You additionally need to start playing that new clip using AudioSource.Play.

    • Also as a new start for the second loop you do not want to use the original AudioSource.volume you have stored at the top of the Corouinte but rather the current one after fading out.

    So just a little bit redactored I would use e.g.

    private const float FADED_OUT_VOLUME = 0.01f;
    
    public static IEnumerator FadeSwitchAudio(AudioSource audioSource, float duration, int clip)
    {
        var originalVolume = audioSource.volume;
    
        // I prefer using for loops over while to eliminate the danger of infinite loops
        // and the need for "external" variables
        // I personally also find this better to read and maintain
        for(var timePassed = 0f; timePassed < duration; timePassed += Time.deltaTime)
        {
            audioSource.volume = Mathf.Lerp(originalVolume, FADED_OUT_VOLUME, timePassed / duration);
    
            yield return null;
        }
    
        // To be sure to end with clean values
        audioSource.volume = FADED_OUT_VOLUME;
    
        // If there is only one instance of `AudioManager` in your scene this is more efficient
        // in general you should fetch that AudioManager reference only ONCE and re-use it
        audioSource.clip = FindObjectOfType<AudioManager>().clips[clip];
        //audioSource.clip = GameObject.FindWithTag("AudioManager").GetComponent<AudioManager>().clips[clip];
    
        yield return new WaitForSeconds(0.1f);
    
        // Actually start playing the new clip
        audioSource.Play();
    
        for(var timePassed = 0f; timePassed < duration; timePassed += Time.deltaTime)
        {
            audioSource.volume = Mathf.Lerp(FADED_OUT_VOLUME, originalVolume, timePassed / duration);
    
            yield return null;
        }
    
        audioSource.volume = 1f;
    }