Audio cutting off when playing multiple sounds sounds in a row. Anyone have a fix?

Hello, i have made a game for fun and i have come across a problem i can’t seem to fix. When i, for example shoot my automatic gun which shoots (using raycasts) about 10 rounds per second i only seem to hear the last shot that was fired. It is the same with the semi-automatic pistol, if i shoot once and then again, the audio from the last shot gets cancelled out. This gives a very unsatisfying sound which i very much would like to fix

You can create a list of audiosources.

You can initialize it to the number of audiosources you will probably require (how many concurrent gunshot sounds do you need to play at the same time)

Then when you want to play a gunshot sound you find an audiosource in the list that isn’t already playing, and you use it to play the gunshot sound.

If all the audiosources in the list are already playing and you need to play another gunshot sound, you add a new audiosource component to the gameobject, add it to your list, and use it to play the gunshot sound.

Your list grows as you need it to. Initializing the list at the start is just so that you don’t have to add like 10 in a short space of time because the player used the machine gun or whatever. Adding components isn’t that cheap. Once they are added, they are fine.

public class AudioPoolExample : MonoBehaviour
{
    public AudioClip GunShotClip;
    public int InitialAudioSourcePoolSize = 3;
    private List<AudioSource> audioSourcePool = new();

    void Start()
    {
        for (int i = 0; i < InitialAudioSourcePoolSize; i++)
        {
            _ = AddNewSourceToPool();
        }
    }

    private AudioSource AddNewSourceToPool()
    {
        AudioSource newSource = gameObject.AddComponent<AudioSource>();
        newSource.playOnAwake = false;
        audioSourcePool.Add(newSource);
        return newSource;
    }

    private AudioSource GetAvailablePoolSource()
    {
        //Fetch the first source in the pool that is not currently playing anything
        foreach (var source in audioSourcePool)
        {
            if (!source.isPlaying)
            {
                return source;
            }
        }

        //No unused sources. Create and fetch a new source
        return AddNewSourceToPool();
    }


    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Mouse0))
        {
            PlayClip(GunShotClip);
        }
    }

    public void PlayClip(AudioClip clip)
    {
        AudioSource source = GetAvailablePoolSource();
        source.clip = clip;
        source.Play();
    }
}

Depending on the tail of the sound and the frequency with which it is playing, you might not actually want ALL the sounds to play concurrently and to completion, as that might not sound good. Might muddy up your mix and it might use up too many voices in your audio, or just use too many audiosource components for your liking.

You may only want to play perhaps 2 or 3 concurrent sounds for example.
In that case you would have a target concurrent sound cap.

If when you play a sound your concurrent sounds go over that cap, you would then find the playing source that is closest to finishing playing, and you would quickly fade its volume to zero and stop it.

This is much less jarring than what unity does if you try to play audio with an audiosource that is already playing, where it just cuts it off. This also allows you to have some concurrency without getting muddy and using too many audiosources.

The basic concept would look something like this…

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AudioPoolExample : MonoBehaviour
{
    public AudioClip GunShotClip;
    public int InitialAudioSourcePoolSize = 3;
    public int TargetConcurrentSoundCap = 3;
    public int FadeOutSpeed = 10;
    private List<AudioSource> audioSourcePool = new();

    void Start()
    {
        for (int i = 0; i < InitialAudioSourcePoolSize; i++)
        {
            _ = AddNewSourceToPool();
        }
    }


    private AudioSource AddNewSourceToPool()
    {
        AudioSource newSource = gameObject.AddComponent<AudioSource>();
        newSource.playOnAwake = false;
        audioSourcePool.Add(newSource);
        return newSource;
    }

    private AudioSource GetAvailablePoolSource()
    {
        //Fetch the first source in the pool that is not currently playing anything
        foreach (var source in audioSourcePool)
        {
            if (!source.isPlaying)
            {
                return source;
            }
        }

        //No unused sources. Create and fetch a new source
        return AddNewSourceToPool();
    }


    public int PlayingPoolSourcesCount
    {
        get
        {
            int playingSourcesCount = 0;

            foreach (var source in audioSourcePool)
            {
                if (source.isPlaying)
                {
                    playingSourcesCount++;
                }
            }

            return playingSourcesCount;
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Mouse0))
        {
            PlayClip(GunShotClip);
        }
    }

    public void PlayClip(AudioClip clip)
    {
        AudioSource availableSource = GetAvailablePoolSource();
        availableSource.clip = clip;
        availableSource.Play();


        //Do we need to fade out any sources?
        if (PlayingPoolSourcesCount > TargetConcurrentSoundCap)
        {
            //Find the source that is closest to finishing
            float maxSourceTime = 0;
            AudioSource sourceClosestToFinishing = null;
            foreach (var source in audioSourcePool)
            {
                if (source.time > maxSourceTime)
                {
                    maxSourceTime = source.time;
                    sourceClosestToFinishing = source;
                }
            }

            StartCoroutine(FadeStopResetAudioSource(sourceClosestToFinishing));
        }
    }

    public IEnumerator FadeStopResetAudioSource(AudioSource source)
    {
        //temporarily remove it from the pool so that we don't pick it up in a subsequent check
        //and inadvertently start another fade coroutine
        audioSourcePool.Remove(source);

        //fade out
        while (!(Mathf.Approximately(source.volume, 0)))
        {
            source.volume -= FadeOutSpeed * Time.deltaTime;
            yield return null;
        }

        //stop and reset the volume
        source.Stop();
        source.volume = 1;

        //throw it back into the pool
        audioSourcePool.Add(source);
    }
}

I haven’t added pitch variation, or thought about random clips from a list of gunshots or anything. That would just get in the way of the main concept.

Hey, thanks for a soloution, i’ll try to see if i can add it to the game.