Why does audio get out of sync with multiple loops playing?

Hi,

Can anyone explain why this code sort of works, but then gets out of sync if I keep stopping and starting loops?

I have 3 audio loop sources loaded as WAVs and set to Compressed (MPEG) and Compressed in Memory and Gapless Looping ON (though Gapless looping off also seems to be gapless?).

I am clicking/tapping on 3 objects which represent each of the loops. I want to stop (or mute) them individually and then clicking again should restart that loop at the same point in time as the other playing loops continue playing.

I’ve tried using .time but that does the same thing - I read that .timeSamples is more precise so I assumed this would work. In this code, I stop the loops if they are playing, and if not I see which one is playing and set it’s timeSamples to that one and play it. If none are playing I reset timeSamples to 0 and restart (that is the only way to get them back in sync if they get out of sync).

So with this pretty simple code, what is happening with this when they do get out of sync? Is it the Debug statements? I’ve tried this in Editor and iPad and it gets out of sync on both, but usually only if I tap fast turning them on and off quickly.

Just assume melody, bass, and drums are AudioSources and my CheckObject function fires when one of the corresponding gameobjects of these is tapped (This snippet below also uses FingerGestures and is in an OnTap method for a FingerGestureRecognizer – is FingerGestures slowing this down?)

Any help getting audio loops to start at EXACTLY where the other playing one is would be appreciated! Thanks! :slight_smile:

    if( CheckObject( source.Position, melodyObject ) )
	{
		Debug.Log ("melody clicked");
		if(melody.isPlaying)
		{
			melody.Stop();
		}
		else
		{
			if(bass.isPlaying)
			{
				melody.timeSamples = bass.timeSamples;
				melody.Play();
			}
			else
			{
				if(drums.isPlaying)
				{
					melody.timeSamples = drums.timeSamples;
					melody.Play();
				}
				else
				{
					melody.timeSamples = 0;
					bass.timeSamples = 0;
					drums.timeSamples = 0;
					melody.Play();
					bass.Play();
					drums.Play();
				}
			}
		}
		
	}
	if( CheckObject( source.Position, bassObject ) )
	{
		Debug.Log ("bass clicked");
		if(bass.isPlaying)
		{
			bass.Stop();
		}
		else
		{
			if(melody.isPlaying)
			{
				bass.timeSamples = melody.timeSamples;
				bass.Play();
			}
			else
			{
				if(drums.isPlaying)
				{
					bass.timeSamples = drums.timeSamples;
					bass.Play();
				}
				else
				{
					melody.timeSamples = 0;
					bass.timeSamples = 0;
					drums.timeSamples = 0;
					melody.Play();
					bass.Play();
					drums.Play();
				}
			}
		}			
	}
	if( CheckObject( source.Position, drumsObject ) )
	{
		Debug.Log ("drums clicked");
		if(drums.isPlaying)
		{
			drums.Stop();
		}
		else
		{
			if(bass.isPlaying)
			{
				drums.timeSamples = bass.timeSamples;
				drums.Play();
			}
			else
			{
				if(melody.isPlaying)
				{
					drums.timeSamples = melody.timeSamples;
					drums.Play();
				}
				else
				{
					melody.timeSamples = 0;
					bass.timeSamples = 0;
					drums.timeSamples = 0;
					melody.Play();
					bass.Play();
					drums.Play();
				}
			}
		}

If you want sample level accuracy, do not compress.

Your code should work then. If not, you could try an offset:

sourceToSync.timeSamples = (playingSource.timeSamples+offset)%playingSource.clip.samples;
sourceToSync.Play(offset);

I use this a lot to start playing an audioSource on the beat! for example, at 60 bpm, 44.1 khz, one beat is 44100 samples:

beatSamples = 44100*(60/bpm);

offset = beatSamples - playingSource.timeSamples%beatSamples;
sourceToPlayOnTheNextBeat.timeSamples = (playingSource.timeSamples+offset)%playingSource.clip.samples;
 sourceToPlayOnTheNextBeat.Play(offset);