Start-Loop-End AudioClip player

Hi Everyone,

I’m currently working on a space exploration game where players pilot a space ship arround a procedurally generated solar system.

The last few days I’ve been working making the sound effects for the game. In lots of scenarios I have sound effects that have three components - a start sound which plays first, a looping sound that plays as long as an action is happening, and an end sound that plays last.

An example of this is when the player turns on a cooling pump on their ship - there is a short audio clip of the pump spinning up and valves opening, then a looping clip of the pump running, and then an ending clip of the valves closing and the pump spinning down.

I have been trying to achieve this using Coroutines and I’m getting some weird behaviour. The core code that makes things happen is below. There are two public methods for starting and stopping the sound, and a private coroutine function that handles the timing.

The weird behaviour I’m seeing (or rather hearing) is the start clip plays OK, then the loop clip plays for about a second (seems variable) and then stops. The End clip is then played as expected as soon as the EndPlaying() method is called.

public void StartPlaying()
    {
        playing = true;
        StartCoroutine(play());
    }

    public void EndPlaying()
    {
        playing = false;
    }

    private IEnumerator play()
    {
        source.clip = StartClip;
        source.loop = false;
        source.Play();

        yield return new WaitForSeconds(source.clip.length);

        source.clip = LoopClip;
        source.loop = true;
        source.Play();

        while (playing) yield return new WaitForEndOfFrame();

        source.clip = EndClip;
        source.loop = false;
        source.Play();

        yield return new WaitForSeconds(source.clip.length);

        source.Stop();
        source.clip = null;
    }

The audio clips used for the loop segment are all longer than the sound that plays, and otherwise sound plays fine.

I’m a little concerned about the line:

        while (playing) yield return new WaitForEndOfFrame();

which just feels like a really sloppy way of delaying until the EndPlaying() method is called.

Here’s the full MonoBehaviour class:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(AudioSource))]
public class StartLoopEndPlayer : MonoBehaviour
{

    public AudioClip StartClip;
    public AudioClip LoopClip;
    public AudioClip EndClip;

    private AudioSource source;
    private bool playing;

    // Use this for initialization
    void Start()
    {
        source = this.GetComponent<AudioSource>();

        StartCoroutine(test());
    }

    public void StartPlaying()
    {
        playing = true;
        StartCoroutine(play());
    }

    public void EndPlaying()
    {
        playing = false;
    }

    private IEnumerator test()
    {
        int i = 1;
        while (true)
        {
            yield return new WaitForSeconds(2);

            StartPlaying();

            yield return new WaitForSeconds(i);

            EndPlaying();

            if (i > 10) i = 1;
            else i++;
        }
    }

    private IEnumerator play()
    {
        source.clip = StartClip;
        source.loop = false;
        source.Play();

        yield return new WaitForSeconds(source.clip.length);

        source.clip = LoopClip;
        source.loop = true;
        source.Play();

        while (playing) yield return new WaitForEndOfFrame();

        source.clip = EndClip;
        source.loop = false;
        source.Play();

        yield return new WaitForSeconds(source.clip.length);

        source.Stop();
        source.clip = null;
    }
}

Did you ever figure this out?