AVAudioPlayer loops are not seamless

Viewed 290

I am creating and playing an AVAudioPlayer as the following:

playerOne = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: path))
playerOne.numberOfLoops = -1
playerOne.prepareToPlay()

I am playing an AAC file. I am using

playerOne.play(atTime: startTime)

to schedule a play in future and sync multiple AVAudioPlayers.

All works fine but my problem is when sounds loop they go out of sync, this is due to loops not being seamless.

What happens here is that due to aac decoders I believe that there is an exra small silence is added to the decoded audio data which causes the sync between audio players to be lost. I expected this loop to be perfect with 0 gap between looping from end to beginning.

How may I achieve seamless looping with AVAudioPlayer?

2 Answers

Using AVAudioEngine will give you a lot of flexibility but it's overhead if you don't need anything else but sync your tracks.

In this case you can try to use a single player with AVComposition containing all your tracks, something like this:

func generateComposition(urls: [URL]) throws -> AVComposition {
    let composition = AVMutableComposition()
    let audioTracks = urls
        .map(AVAsset.init(url:))
        .flatMap { $0.tracks(withMediaType: .audio) }
    
    for audioTrack in audioTracks {
        guard
            let compositionTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
        else { continue }
        
        try compositionTrack.insertTimeRange(
            audioTrack.timeRange,
            of: audioTrack,
            at: .zero
        )
    }
    return composition
}

And play it using AVPlayer:

AVPlayer(playerItem: AVPlayerItem(asset: composition))

due to loops not being seamless.

I'm not sure what you mean by "not seamless", but if the problem is that you are experiencing latency, that's pretty much natural with AVAudioPlayer. If the goal is to loop with minimal latency, use AVAudioEngine.

Related