- Home /
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! :)
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();
}
}
}
$$anonymous$$aybe latency variance on play?
A workaround could be just to mute the audio sources that should not be heard and let them keep playing.
Thanks for responding. How can I get around latency variance on play? Is there any way to programmatically deter$$anonymous$$e that so I can add time to the amount before playing and have it be device independent?
The muting is a good idea changing volumes, however I need to be able to have far more than 3 audio tracks that are playable (but only 3 at a time) so that means I couldn't have 30 playing and 27 muted performance-wise so that solution wont help unfortunately.
I just tried adding this to see what the system thinks the .timeSamples are for each audio source. But what is weird is they sound out of sync when the debug prints that they are in sync?
void Start()
{
melody.Play();
bass.Play();
drums.Play();
InvokeRepeating("PrintSamples", 2F, 2F);
}
void PrintSamples()
{
Debug.Log("melody = "+ melody.timeSamples + "\n bass = " +bass.timeSamples + "\n drums = "+ drums.timeSamples+"\n -----------");
}
As I write in my answer, try not compressing the wavs!
And are you 100% sure each track is of the same, sample accurate, length?
I just noticed something else. This whole out of sync problem seems to only occur (on both PC, $$anonymous$$ac & iPad) whenever the AudioSources are set to "Compressed ($$anonymous$$PEG)" if I set them all to "Native (WAV)" this sync problem goes away - but that means that memory usage is higher - Anyone have any thoughts on all of this or what the best way forward is? thanks :)
Answer by gregzo · Apr 09, 2012 at 10:36 AM
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);
Thanks for your reply. I was worried about memory usage etc - if I have 30+ loops (with only 3 playing at a time). When I first posted this question I didn't realize that "compressed" doesn't seem to report the correct timeSamples to set the playhead to. It seems that only works when they are Native (WAV).
I probably can't use "Load Into $$anonymous$$emory" with them? I know with 3 loaded into memory it works fine as above as long as I don't set them to "compressed". Do you think "Stream From Disc" will work on mobiles if I have 30 or so loops to choose from?
Thanks also for your offset code - yes I am 100% sure they are all identical lengths and sample rates.
I'll try your code for starting on the beat, that will sound better I expect than just starting instantly. :)
I have 49 10s loops (mono) in my iPad app, all set to Load into $$anonymous$$emory. No problem, even on an iPad 1...
It depends how much ram you need for the rest of your game, I guess. I haven't tried stream from disc, because my app is an instrument and I need quick access to all 49 clips!
Great. Thanks for mentioneing that- it's good to know that will work even on iPad 1!
You're welcome. Check my posts here and on the forum in case you need help audio wise: I was a total noob 6 months ago, and am now a proud noob+! Unity 3.5 is great for audio if you know how to work with it. The new GetData and SetData functions, in particular, can be used effectively to solve a lot of synching problems.