1
votes

I've checked other responses on AVAudioPlayer crashing with .stop() but they don't directly address my question - I imagine I'm missing something simple.

My AVAudioPlayer is a class property and I initialize it when declaring it:

var audioPlayer = AVAudioPlayer()

My playSound code is as follows:

func playSound(soundName: String, audioPlayer: inout AVAudioPlayer) {
    // Can we load in the file soundName?
    if let sound = NSDataAsset(name: soundName) {
        // check if sound.data is a sound file
        do {
            try audioPlayer = AVAudioPlayer(data: sound.data)
            audioPlayer.play()
        } catch {
            // if sound.data is not a valid audio file
            print("ERROR: data in \(soundName) couldn't be played as a sound.")
        }
    } else {
        // if reading in the NSDataAsset didn't work, tell the user / report the error.
        print("ERROR: file \(soundName) didn't load")
    }
}

I have a switch control on screen to stop playing. This works great when I've already played a sound, and it properly cuts off any playing sound.

@IBAction func soundSwitchPressed(_ sender: UISwitch) {
        if !soundSwitch.isOn {
            audioPlayer.stop()
        }
}

(I also check the switch's isOn flag anywhere in my code before calling playSound above)

BUT sometimes the user might want to switch off sound playing, and executing: audioPlayer.stop() will crash the code with nothing printed to the Xcode 9 console & this in the error: Thread 1: EXC_BAD_ACCESS (code=1, address=0x48)

audioPlayer isn't nil - false is returned if, after crash, I use lldb:

po audioPlayer == nil

Also: If I play a sound, then stop playing by pressing the sound switch (works fine), and I turn on sound play but DON'T play a sound, then click again, it executes .stop() when nothing is playing, but will not crash. It only crashes under the condition of a sound never playing at all.

I know I can set a simple flag and only execute .stop() if a sound has been played at least once with the audio player, but I'm hoping to get a better understanding of why an AVAudioPlayer initialized in its declaration will crash with .stop(), but won't crash if .stop() is called after successful plays, EVEN if all playing has ceased on its own (no looping). And I expect the answer will point me to a test I'm overlooking in docs, etc.

Hope that was clear - thanks!

1
you have double initialization of audioPlayer once when declaring property and second when just before calling play methodTomasz Czyżak

1 Answers

1
votes

As mentioned in previous comment "you have double initialization of audioPlayer once when declaring property and second when just before calling play method"

Proposed fixes:

  1. audioPlayer as optional:

    var audioPlayer: AVAudioPlayer?
    
  2. call stop only if player is playing

    @IBAction func soundSwitchPressed(_ sender: UISwitch) {
        if !soundSwitch.isOn && audioPlayer?.isPlaying
            audioPlayer?.stop()
        }
    }