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:
- Running on OLD devices(iPad 2, iPhone 5 etc)
- 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)
}
}
}
}