0

I have one main AVAudioPlayerNode that plays constantly. And there are other players that are put in the queue and play when the main player reaches a certain point. These AVAudioPlayerNode`s should be synchronized to a millisecond. Sometimes 4-10 pieces can be started at a time.

How its works - I store lastRenderTime of the main player on point when need start all scheduled players and then start all needed player with player.start(at: lastRenderTime)

Usually, it's working well and without any latency between sounds.

But, I got some latency on this case:

  1. Running on OLD devices(iPad 2, iPhone 5 etc)
  2. ONLY when MANY players the first time after application startup (For example on iPhone 7 I run 8 players in same time after the application is running and I hear desynchronization between sounds), but all players are running with same player.start(at: lastRenderTime) time.

All player actions I run async with custom concurrent queue.

Spend 3 days on trying fix this. I would like for any pieces of advice.

Some code: Here is how I starting main player -

self.scheduleBuffer(audioFileBuffer, at: nil, options: [], completionHandler: {
    lastRenderTime = lastRenderTime + audioBufferLength //Schematic showing logic that after each loop I calculate current render time value.

    if self.isStopPressed == false { //Run fileEndCompletion only when player seek to end.
        fileEndCompletionHandler() //here is callback that running all scheduled players.
        if loop { // Recursive play this file again.
            self.playAudioBuffer(audioFileBuffer: audioFileBuffer, loop: loop, fileEndCompletionHandler: fileEndCompletionHandler)
        }
    }
})

Example of code that running all scheduled player.

AudioEnginePlayer.sharedInstance.audioQueue.async {
    if !self.isPlaying {
        self.volume = 1.0
        self.audioFile.framePosition = frameCount.startingFrame
        self.prepare(withFrameCount: frameCount.frameCount)
        self.play(at: AudioEnginePlayer.sharedInstance.loopMsLastStartRenderTime) //Calculated lastRenderTime
    }

    self.scheduleSegment(self.audioFile, startingFrame: frameCount.startingFrame, frameCount: frameCount.frameCount, at: nil) {
        //WARNING: COMPLETION HANDLER RUNNING EARLY THEN FILE IS COMPLETED

        if self.isStopPressed == false { //Run fileEndCompletion only when player seek to end.
            fileEndCompletionHandler()
            if loop {
                self.playAudioFile(loop: loop, startTime: 0, fileEndCompletionHandler: fileEndCompletionHandler)
            }
        }
    }
}
Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159
shshsh
  • 51
  • 4
  • 1
    isn't `lastRenderTime` already in the past? do you have some sample code that demonstrates the problem? – Rhythmic Fistman Aug 31 '18 at 19:49
  • @RhythmicFistman Yes. `lastRenderTime` is always in the past. It allows all player to be synchronized. Also added some code. – shshsh Sep 01 '18 at 13:18

0 Answers0