Vladislav Kulikov
Vladislav Kulikov

Reputation: 465

How to preload SCNAudioSource to prevent a lag at the first sound playback?

There shouldn't be lags, because I set up and preload each sound in viewDidLoad(), before playing.

class GameViewController: UIViewController {

    private var sounds = [String: SCNAudioSource]()

    override func viewDidLoad() {
        super.viewDidLoad()

        loadAllSounds()
    }

}

extension GameViewController {

    private func loadSound(name: String, path: String) {
        if let sound = SCNAudioSource(fileNamed: path) {
            sound.isPositional = false
            sound.volume = 0.8
            sound.shouldStream = false

            // Loads audio data from the source and prepares it for playing
            sound.load()

            sounds[name] = sound
        }
    }

    private func loadAllSounds() {
        loadSound(name: "Whoosh", path: "whoosh.wav")
        loadSound(name: "Pop", path: "pop.wav")
        loadSound(name: "Slide", path: "slide.wav")
    }

}

For playing sounds, I have a function playSound(). This function is called depending on the events in the game. For example, when a player jumps. So, lag occurs only the first time each sound is played.

private func playSound(withName name: String, atNode node: SCNNode) {
    let playAudio = SCNAction.playAudio(sounds[name]!, waitForCompletion: false)
    node.runAction(playAudio)
}

Looks like SCNAudioSource.load() doesn't load or prepare sounds. Either I'm doing something wrong.

I'd be happy to have any help. Thank you!

Upvotes: 2

Views: 418

Answers (1)

Daywalker
Daywalker

Reputation: 31

I had the same problem when working with SCNAction.playAudio() in SceneKit. After days of playing with my sound file (changing codec, bitrate, etc.) I analyzed the issue using Instruments and concluded, that SceneKit will only initialize its Audio-Engine when you tell it to play a sound. I'm aware that this is not a great solution, but I could not figure out a more elegant one:

static func prepareSoundEngine(rootNode: SCNNode) {
    let sound = SCNAudioSource(named: "init.mp3")!
    sound.load()
    let player = SCNAudioPlayer(source: sound)

    player.didFinishPlayback = {
        rootNode.removeAudioPlayer(player)
    }

    rootNode.addAudioPlayer(player)
}

'init.mp3' is just a one second long completely empty sound file. Check out this GitHub page to download such a file: https://github.com/anars/blank-audio

I then call this function as soon as my scene is visible and every SCNAction.playSound() will play without lag.

Upvotes: 3

Related Questions