Galen
Galen

Reputation: 53

How to stop multiple AVAudioPlayers from playing in swift?

I am very new to swift and I was a novice with Objective-C. Recently I started a soundboard app that plays many sounds on 3 different view controllers. For the apps next update I wanted to make it so it could play multiple sounds simultaneously and I did this by creating an individual audioPlayer for each button. So for each view controllers I would have 9 instances of AVAudioPlayers (One for each button) and when each button was pressed it would play one of them EX: audioPlayer8 would belong to the hitmarkerButton. I succesfully did this but now I want a way for the users to press a button to "reset" the sounds in the app. So for each view controller I already created an IBAction called stopPlayingSounds that should "reset" or stop playing all sounds from all view controllers. This is my first time asking a question here so please tell me if there is anything else I should include. Thank you.

Also each of the examples included below are just one of up to 9 examples for each view controller. PS - Each view controller is a page.

The "Sound Stopper" Button:

    @IBAction func stopPlayingSounds(sender: AnyObject) {

    }

An example of one of the buttons:

    @IBAction func hitmarkerButton(sender: AnyObject) {
    audioPlayer8 = AVAudioPlayer(contentsOfURL: hitmarkerSound, error: nil)
    audioPlayer8.prepareToPlay()
    audioPlayer8.play()
}

Example of one of the declarations of the AVAudioPlayers:

var audioPlayer8 = AVAudioPlayer()

And lastly, an example of the creation of the actual sound:

    var hitmarkerSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("hitmarker", ofType: "mp3")!)

Upvotes: 1

Views: 3323

Answers (2)

hawtakshun
hawtakshun

Reputation: 89

I struggled with this issue for weeks (overlaid audio from every stream that has been loaded). I eventually discovered that every let statement creates a new instance of AVPlayer, so the multi-threaded OS plays them all simultaneously. I solved my problem by creating a single AVPlayer at the class level with:

var vidPlayer:AVPlayer = AVPlayer() 

and then just reassigned vidPlayer to new content each time with:

vidPlayer = AVPlayer(URL: url) //where url was defined previously

The player restarts with the new content and only the most current audio stream.

Upvotes: 3

Brian
Brian

Reputation: 46

I think you just need to initialize your variables somewhere other than inside the button press methods, so you can see those variables after the method is over. I always heard it "what happens in a method stays in a method..."

So for example I'd put the declarations at the top of your view controller, and stuff them in an array:

var myAudioPlayers = [AVAudioPlayer] //remember arrays start at 0 and go up to n-1
myAudioPlayers[0] = AVAudioPlayer(contentsOfURL: sound1, error: nil)
...
myAudioPlayers[8] = AVAudioPlayer(contentsOfURL: sound8, error: nil)

Then you'd have

@IBAction func hitmarkerButton(sender: AnyObject) {
    audioPlayer[7].prepareToPlay()
    audioPlayer[7].play()
}

And you can use the array to quickly do:

@IBAction func stopPlayingSounds(sender: AnyObject) {
    foreach (ap in myAudioPlayers)
    ap.stop()
}

Upvotes: 3

Related Questions