How can I sort of loop a piece of music?

I have a song but it doesnt loop perfectly.

To loop perfectly it has to be played like so:


It starts normally, I can just call to play it from an audio source

But then I need a 2nd audio source? To start playing the song again after 1min has passed.

And at the same time fade out the currently playing track.

And after this process it should continue to loop forever using the same logic.

What is the best way to code this? What is the best way to tell the current time of the song so I know when to trigger the effects?

Is there a feature out of the box in unity for this? I think that just checkmarking “loop” doesn’t work for this case?

I know I can schedule tracks to play but then to loop infinitely I dont see how I could do that.

What would be the best way to code this? Thanks!

Is it necessary to have it fade? Or do you just want to loop a specific section?

Loop a specific section

using UnityEngine;

public class AudioLooper : MonoBehaviour
{
    public AudioSource audioSource;
    public float loopStart;
    public float loopEnd;

    private void Start()
    {
        // Set the initial playback position to the loop start position
        audioSource.time = loopStart;
    }

    private void Update()
    {
        // Check if the audio clip has reached the end of the loop section
        if (audioSource.time >= loopEnd)
        {
            // Set the time to the start of the loop section
            audioSource.time = loopStart;
        }
    }
}
1 Like

This is a lot easier if you just record the music to include the blending, but sometimes you can’t do that.

Once you can detect the current time (audioSource.time), you can calculate the desired volume at that time. Mathf.InverseLerp interpolates but clamps the result to 0~1, so it’s easy:

1f - Mathf.InverseLerp(fadeStartTime, clipEndTime, audioSource.time);```

The problem is the overlapping nature of your crossfade. I think you need two AudioSource to do that, one for the "current" loop which begins to fade, and one for the "next" loop which starts to play at the beginning of the other's fade. Then you can reuse the first AudioSource as the next-next one.
1 Like

thanks, I have the music but when i try to create a good loop in premiere somehow the sound is getting screwed on the export

I guess Im not a sound guy

If I was just doing this for a movie I could easily do it since I just have to duplicate the effect for the duration, but for a game I need it to be able to run infinitely.

Anyway if I embed the loop into the music the beginning of the song is no longer a clean start

I don’t mind having many audio sources, thanks for the code for fading

I do need to fade the end otherwise it doesnt sound clean.

Using update to check for the time, I think gives me a clue of when to create the extra audio source thx

Keep in mind certain formats cannot be looped perfectly seamlessly, such as MP3.

https://sound.stackexchange.com/questions/25846/is-it-possible-to-loop-mp3-without-gaps

1 Like

What file do you use for game audio?

I use MP3 if I just need music, which I never loop seamlessly. I just let it fade, then restart.

If it has to loop seamlessly I use WAV, such as for engine thrusters and whatnot.

thats exactly what was happening to me then, the exported file was coming out with an empty frame on the tail end no matter what, thanks for the heads up

1 Like

Oh, it occurred to me I already had this class lying around. It can use one or two AudioSources, and ping-pong them with a list of clips to be selected randomly. If using two AudioSources (such as with one on a child object of the other), it will do the crossfade just as I suggested above. If using only one AudioSource, it just plays them using PlayOneShot, so the clips need their own ramp-in.

    public class AmbientSound: MonoBehaviour
    {
        public AudioClip[] clips;
        public AudioSource one;
        public AudioSource two;
        public float overlap = 2f;
        [Range(0f, 1f)] public float volume = 1f;

        private float time = 0f;
        private float transition = 0f;

        // [ReadOnly]
        public AudioClip playing = null;

        void Start()
        {
            if (one == null)
                one = GetComponentInChildren<AudioSource>();
            if (two == one)
                two = null;
        }

        void Update()
        {
            if (one == null)
                return;

            if (clips == null || clips.Length == 0)
                return;

            time += Time.deltaTime;

            // are we ready to start a clip?
            if (one != null && playing == null)
            {
                playing = clips[Random.Range(0, clips.Length)];
                time = 0f;
                if (two == null)
                    one.PlayOneShot(playing);
                else
                    { one.Stop(); one.clip = playing; one.time = 0f; one.Play(); }
                transition = Mathf.Max(0.001f, playing.length - overlap);
                //Debug.Log($"chose {playing} will transition in {transition}");
            }

            // ramp in first source's volume if we can, otherwise hold
            //
            if (two == null)
                one.volume = volume;
            else
                one.volume = volume * Mathf.InverseLerp(0f, overlap, one.time);

            // if we have two sources, ramp out second source's volume
            if (two != null && two.clip != null)
            {
                two.volume = volume * (1f - Mathf.InverseLerp(
                    two.clip.length-overlap, two.clip.length, two.time));

                if (two.volume <= 0f || two.time >= two.clip.length)
                {
                    //Debug.Log($"stopped {two.clip}");
                    two.Stop();
                    two.clip = null;
                }
            }

            // approaching end of clip in one, prepare for a choice
            if (time >= transition)
            {
                playing = null;
                //Debug.Log($"open to new choices");
                if (two != null)
                {
                    Assert.IsTrue(two.clip == null);
                    AudioSource swap = two;
                    two = one;
                    one = swap;
                }
            }

        }
1 Like

It is an industry-standard practice to actually bake all the fading needed, into the audio file itself. Even if it wasn’t recorded like that, it’s always possible to edit it to be seamlessly loopable. You must copy/paste the “fade out” and overlay it on top of the beginning of the audio file. Then, when it loops back, it will behave exactly the same as shown in your picture.

You will also need two separate files. 1 is the first file to play. 2 is the file to play on all subsequent loops. 1 is the normal song but stops exactly at the “fade out” tail. 2 is the same thing but the “fade out” tail is overlaid onto the beginning. (If you don’t care that the beginning of the first loop will include the fade out tail, because it’s very quiet and barely noticeable, then you really only need file #2).

Then, all the game logic has to do is make sure the audio file loops completely seamlessly. There is no crossfading or special logic needed.

Surprisingly a lot of people don’t know this. They even did it totally wrong in the video game “Outward” and there was an audible glitch every time it looped. I had to make a mod to fix it.

Above, I agreed that it’s preferable to do so. It’s always possible, but not always feasible. If you’re trying to emulate cross-fades between randomly selected variations, the number of pre-recorded cross-fade snippets you need to make and manage increases at an exponential rate. A-fades-into-B, A-fades-into-C, C-fades-into-B, etc.

While a team the size of Nintendo’s Super Mario Wonder may be able to stitch together all those assets and choose to do so because major parts of the gameplay is designed around adaptive music middleware, a much smaller team (or solo dev) would much rather say “here’s the five different bits of audio, please pick from them randomly and cross-fade on two audio channels.”