2 AudioFormatReaderSources out of sync 1-2 seconds


#1

Hi, I’m working on a multifile audio player plugin (playing multiple files in parallel).
It works fine when starting the playback from 0 position or going back to 0.

A strange effect happens when I use e.g. a slider to change the position.
Suddenly the 2 files run out of sync 1-2 seconds! (always, on every position change except going to 0).
(of course with 1 file it works fine)

To debug, I displayed the time of both tracks with getNextReadPosition() in a timer callback in the UI, it is always exactly in sync, but audio output is not!
I also ensured that the position change only happens within the audio callback (although this could not explain 1-2 seconds), so i.e. the UI only requests a position change.
I also locked the position change with a critical section as below.

What I do is:

  • create a BufferingAudioReader for each file (64k buffer)
  • create a AudioFormatReaderSource from it
  • call prepareToPlay(getBlockSize(), getSampleRate());
  • subsequently call getNextAudioBlock in processBlock
  • at some point of time within processBlock change all read positions of all AudioFormatReaderSource stored in a std::vector like this
void AudioPlayerPlugin::setAllReadPositions(int64 pos) {
	const ScopedLock sl(posLock);
	for (const auto& source: audioSources)
		source->setNextReadPosition(pos);
}

Any idea what is happening here?
Am I getting something wrong?
Thanks for any hint! :slight_smile:


#2

can you set a breakpoint in processBlock and watch how that read position changes during a slider change?


#3

One thing to note is that you absolutely can’t call setNextReadPosition from your audio process callback - that method could block for an unlimited time, so will glitch. You need to always read from your files on a background thread and get the data to your audio thread via a fifo of some kind.

(That could be why your readers are out of sync - maybe the first one blocks for a second while it seeks)


#4

@jules Sorry, I thought in another (old) post I read that you recommended exactly the opposite. But I must have got you wrong. Cannot find that old post now to verify why.

So thanks for the hints, I’ll work on it again now!


#5

Finally working on this again…
I went back to what I had before: setting setNextReadPosition outside audio callback and beforehand stopping all Readers. It did not change anything.

Then I found out that by chance I had 1 WAV and 1 MP3 file.
I did some more tests and I found out that even if you play the same MP3 file twice and set the same read position, they can differ up to ~2 seconds!

So I think setNextReadPosition for MP3 reader sources does not work correctly!
Could that be possible?


#6

Yeah, seeking inside MP3s is a bit hit-and-miss. Really the only way to reproduce the same decoding is to read it from the start. That’s why in a lot of DAWs they’ll always decompress it to a WAV before playing it.


#7

OK, sounds reasonable. So for now I’ll avoid MP3 if I need exact positioning.

Only thing I wonder: why is it not reproducible?
Seeking same position in same MP3 gets different results each time by up to ~2 seconds…


#8

Well, if you’re seeking from a different thread than the audio thread, obviously the timing won’t be repeatable.


#9

Hmm… I’m first setting a status Paused similar to AudioPlayer example, then I set both BufferingAudioReaders (playing the same file) to the same position and set status to Starting->Playing.
Even if something goes wrong here, they should differ by 1-2x audio buffer size only and not by up to ~2 seconds…
If you are interested, I could try to write a simple reproducible test case (if not, I’ll save my time and just try to avoid MP3).


#10

Doesn’t sound like anything we’d be interested in debugging - just sounds like a side-effect of the way you’re calling things. Syncing streams is non-trivial, you can’t just do it by making start/stop calls on audio players.


#11

Ah, OK, good to know, thanks! It seems to work for WAV files though.

I assumed that if I start all sources at the same position and then always call getNextAudioBlock in a loop for all of them, they should stay in sync?
(you suggested something similar, but back in 2009, so it might be outdated Synchronising audio tracks)

Or is there any built-in sync mechanism in Juce? So far I didn’t find any…