Alex Merlin
Alex Merlin

Reputation: 451

How to loop background music without lag/delay when song repeats (seamless)

I am trying to make a game with background music looping in it. I made the song file in Adobe Audition (which is similar to audacity) and when I play it in a loop in Adobe Audition it loops how I want it.

When I play it in Xcode however, it has a lag in between the loops. I am using AVFoundations for the sound playing.

I have searched everywhere but I can't find the solution for the problem.

Is there any way you can loop audio files without there being any lags in between ? (I believe its called "seamless looping" )

here is the code:

class GameScene: SKScene {
...// Other Code

var ButtonAudio = URL(fileURLWithPath: Bundle.main.path(forResource: "Gamescene(new)", ofType: "mp3")!)
var ButtonAudioPlayer = AVAudioPlayer()

... //Other Code

}

And When I Call it:

 override func didMove(to view: SKView) {

    ...//Code

    ButtonAudioPlayer = try! AVAudioPlayer(contentsOf: ButtonAudio, fileTypeHint: nil)
    ButtonAudioPlayer.numberOfLoops = -1
    ButtonAudioPlayer.prepareToPlay()
    ButtonAudioPlayer.play()

    ...//More Code
    }

Can someone help me with this issue ?

Thank you in advance!

Upvotes: 1

Views: 1607

Answers (2)

Vergiliy
Vergiliy

Reputation: 1356

The solution proposed by @dave234 only works in iOS > 10. Since I needed to make seamless playback in iOS > 9, I did something different:

  1. Instead of AVPlayer, I created AVQueuePlayer and immediately added two identical melodies to the queue.
  2. Next, I made a listener to the penultimate melody.
  3. When the listener was triggered, I added another similar record to the queue after the last one.

In fact, in order to avoid delay, I always play the penultimate record.

My code:

var player: AVQueuePlayer?

override func viewDidLoad() {
    super.viewDidLoad()

    if let path = Bundle.main.path(forResource: "music_file", ofType: "mp3") {
        player = createPlayer(url: URL(fileURLWithPath: path))
    }
}

func createPlayer(url: URL) -> AVQueuePlayer {
    let player = AVQueuePlayer(items: [AVPlayerItem(url: url), AVPlayerItem(url: url)])
    loopPlayer(playerItem: player.items()[player.items().count - 2])
    return player
}

func loopPlayer(playerItem: AVPlayerItem) {
    NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: playerItem, queue: .main) { _ in
        if let player = self.player, let url = (playerItem.asset as? AVURLAsset)?.url {
            player.insert(AVPlayerItem(url: url), after: player.items()[player.items().count - 1])
            self.loopPlayer(playerItem: player.items()[player.items().count - 2])
        }
    }
}

Upvotes: 0

dave234
dave234

Reputation: 4955

You can use AVPlayerLooper and AVQueuePlayer to do this.

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var queuePlayer = AVQueuePlayer()
    var playerLooper: AVPlayerLooper?

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let url = Bundle.main.url(forResource: "Gamescene(new)", withExtension: "mp3") else { return }
        let playerItem = AVPlayerItem(asset: AVAsset(url: url))
        playerLooper = AVPlayerLooper(player: queuePlayer, templateItem: playerItem)
        queuePlayer.play()
    }
}

Upvotes: 4

Related Questions