Get AVAudioPlayer to play multiple sounds at a time

I am trying to get some sound files to play on an instance of AVAudioPlayer, but when one sound plays, the other stops. I can’t get more than one sound to play at the same time. Here is my code:

import AVFoundation class GSAudio{ static var instance: GSAudio! var soundFileNameURL: NSURL = NSURL() var soundFileName = "" var soundPlay = AVAudioPlayer() func playSound (soundFile: String){ GSAudio.instance = self soundFileName = soundFile soundFileNameURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(soundFileName, ofType: "aif", inDirectory:"Sounds")!) do{ try soundPlay = AVAudioPlayer(contentsOfURL: soundFileNameURL) } catch { print("Could not play sound file!") } soundPlay.prepareToPlay() soundPlay.play () } } 

Can someone help me by telling me how to get more than one audio file to play at a time? Any help is much appreciated.

Thanks a lot, Kai

+7
ios swift avfoundation avaudioplayer
source share
3 answers

The reason the audio stops is because you only have one AVAudioPlayer, so when you ask the class to play another sound, you are currently replacing the old instance with a new instance of AVAudioPlayer. You rewrite it basically.

You can create two instances of the GSAudio class, and then call playSound for each of them, or make the class a universal audio manager that uses a dictionary of audio planners.

I prefer the latter option because it allows you to use cleaner code and is also more efficient. You can check if you really made a player for sound, instead of creating a new player, for example.

In any case, I remade my class for you to play several sounds at once. It can also play the same sound over itself (it does not replace the previous sound instance) Hope this helps!

The class is singleton, so to access the class, use:

 GSAudio.sharedInstance 

For example, to play the sound you are calling:

 GSAudio.sharedInstance.playSound("AudioFileName") 

and play several sounds at once:

 GSAudio.sharedInstance.playSounds("AudioFileName1", "AudioFileName2") 

or you can load sounds in an array somewhere and call the playSounds function, which takes an array:

 let sounds = ["AudioFileName1", "AudioFileName2"] GSAudio.sharedInstance.playSounds(sounds) 

I also added the playSounds function, which allows you to delay every sound played in cascading format. So:

  let soundFileNames = ["SoundFileName1", "SoundFileName2", "SoundFileName3"] GSAudio.sharedInstance.playSounds(soundFileNames, withDelay: 1.0) 

will play sound2 a second after sound1, then sound3 will play a second after sound2, etc.

Here is the class:

 class GSAudio: NSObject, AVAudioPlayerDelegate { static let sharedInstance = GSAudio() private override init() {} var players = [NSURL:AVAudioPlayer]() var duplicatePlayers = [AVAudioPlayer]() func playSound (soundFileName: String){ let soundFileNameURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(soundFileName, ofType: "aif", inDirectory:"Sounds")!) if let player = players[soundFileNameURL] { //player for sound has been found if player.playing == false { //player is not in use, so use that one player.prepareToPlay() player.play() } else { // player is in use, create a new, duplicate, player and use that instead let duplicatePlayer = try! AVAudioPlayer(contentsOfURL: soundFileNameURL) //use 'try!' because we know the URL worked before. duplicatePlayer.delegate = self //assign delegate for duplicatePlayer so delegate can remove the duplicate once it stopped playing duplicatePlayers.append(duplicatePlayer) //add duplicate to array so it doesn't get removed from memory before finishing duplicatePlayer.prepareToPlay() duplicatePlayer.play() } } else { //player has not been found, create a new player with the URL if possible do{ let player = try AVAudioPlayer(contentsOfURL: soundFileNameURL) players[soundFileNameURL] = player player.prepareToPlay() player.play() } catch { print("Could not play sound file!") } } } func playSounds(soundFileNames: [String]){ for soundFileName in soundFileNames { playSound(soundFileName) } } func playSounds(soundFileNames: String...){ for soundFileName in soundFileNames { playSound(soundFileName) } } func playSounds(soundFileNames: [String], withDelay: Double) { //withDelay is in seconds for (index, soundFileName) in soundFileNames.enumerate() { let delay = withDelay*Double(index) let _ = NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(playSoundNotification(_:)), userInfo: ["fileName":soundFileName], repeats: false) } } func playSoundNotification(notification: NSNotification) { if let soundFileName = notification.userInfo?["fileName"] as? String { playSound(soundFileName) } } func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) { duplicatePlayers.removeAtIndex(duplicatePlayers.indexOf(player)!) //Remove the duplicate player once it is done } } 
+10
source share

I created a helper library that makes it easy to play sounds in Swift. It creates multiple instances of AVAudioPlayer to simultaneously play the same sound multiple times. You can download it from Github or import using Cocoapods.

Here is the link: SwiftySound

Use is as simple as it can be:

 Sound.play(file: "sound.mp3") 
+1
source share

All answers are published on code pages; it doesn't have to be so complicated.

 // Create a new player for the sound; it doesn't matter which sound file this is let soundPlayer = try AVAudioPlayer( contentsOf: url ) soundPlayer.numberOfLoops = 0 soundPlayer.volume = 1 soundPlayer.play() soundPlayers.append( soundPlayer ) // In an timer based loop or other callback such as display link, prune out players that are done, thus deallocating them checkSfx: for player in soundPlayers { if player.isPlaying { continue } else { if let index = soundPlayers.index(of: player) { soundPlayers.remove(at: index) break checkSfx } } } 
+1
source share

All Articles