Preload Sounds in SpriteKit - ios

I was watching a tutorial on preloading sounds in SpriteKit to avoid the delay and frame rate drop when first playing the sound.This is the way they said to use the AVAudioPlayer to preload the sounds using the prepareToPlay() method:
import AVFoundation
override func didMoveToView(view: SKView) {
do {
let sounds = ["sound1", "sound2"]
for sound in sounds {
let audioPlayer = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(sound ofType: "mp3")!))
audioPlayer.prepareToPlay()
}
}
catch {
}
}
And then playing the sound using an SKAction like this:
self.runAction(SKAction.playSoundFileNamed("sound1.mp3", waitForCompletetion: false)
How does this actually preload the sounds? Is there some reference to the actual sound file in memory when you do the prepareToPlay() method? It seems like I would have to use the AVAudioPlayer or the audioPlayer variable since that's what I used the prepareToPlay() method on instead of just referencing the sound file from the SKAction.

Related

What is the best way to play a sound with the least amount of delay possible, in Swift/Xcode?

I am experiencing a slight delay between UIButton press and sound output (it seems to average about 0.05 seconds). I am currently implementing AVAudioPlayer. Is there a best way to play a sound instantaneously? I scoured the Internet but can't find a concrete answer.
For reference, my (simplified) code is as follows:
// Get sound url
let soundURL = URL(fileURLWithPath: Bundle.main.path(forResource: file, ofType: type)!)
// Audio Player
var audioPlayer: AVAudioPlayer!
do {
// Create audio player
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
}
catch let errMsg as NSError {
print("AVAudioPlayer error: \(errMsg.localizedDescription)")
}
// Prepare audio player to play
audioPlayer.prepareToPlay()
//...
#IBAction func btnPressed(_ sender: UIButton) {
audioPlayer.play()
}

AVAudioPlayer audio breaking and volume increases on its own

I am calling this function playSound() in viewWillAppear, the audio starts playing and then suddenly volume increases with a minor break in playback.
here is my code
func playSound() {
let url = Bundle.main.url(forResource: "music", withExtension: "mp3")!
do {
try self.player = AVAudioPlayer(contentsOf: url)
player?.prepareToPlay()
player?.numberOfLoops = -1
player?.play()
} catch let error as NSError {
KBLog.log(object: error.description)
}}
Edit 1 :
I am using this code for an iPad app, so ipad only have speaker, no earpiece, if this has something to do with the issue.
The problem was i was playing this audio and at the same time i was initialising my publisher view for starting the video call, thus an instance of AVPlayer was initialised to take audio input i.e. two instances of AVPlayer were getting initialised causing the break in playback.

iOS Swift AVAudioPlayer playAtTime method doesn't work

My code is very simple like this:
import UIKit
import AVFoundation
class VC: UIViewController {
var player:AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let url = NSURL.fileURLWithPath(NSBundle.mainBundle().pathForResource("audioName", ofType: "mp3")!)
do{
try player = AVAudioPlayer(contentsOfURL: url)
print("duration", player.duration)// duration 200
player.prepareToPlay()
player.playAtTime(50.0)
}catch{
}
}
}
when player.play() is called, the audio can play normally.
I don't know why the playAtTime function doesn't work.
Please help!
playAtTime plays the sound at a time in the future, specified relative to the device's current time. So the time parameter needs to be the current device time plus your required delay (in seconds):
player.playAtTime(player.currentDeviceTime + 50)
player.currentTime will be 0 when you initialise the player so this isn't the property to use.
Apple doc is here
func playAtTime(time: NSTimeInterval) -> Bool
This function is meant to play the sound some time in the future, based on and greater than deviceCurrentTime, i would suggest to try:
player.playAtTime(player.currentTime + 50.0)

Adding sound effects in Spritekit

I'm making a Flappy Bird-type game, and I want to add a sound effect for when a coin is collected. This is what I have:
import SpriteKit
import AVFoundation
class GameScene: SKScene {
var coinSound = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("Super Mario Bros.- Coin Sound Effect", ofType: "mp3")!)
var audioPlayer = AVAudioPlayer()
Later in my didMoveToView:
audioPlayer = AVAudioPlayer(contentsOfURL: coinSound, error: nil)
audioPlayer.prepareToPlay()
For the second part (in my didMoveToView), the first line keeps showing an error:
Call can throw, but it is not marked with 'try' and the error is not handled
How do I fix this?
I know you already have marked an answer but if you re read this question or for future readers its easier to use SKActions to play a short 1 time sound.
AVFoundation is better used for background music.
e.g
class GameScene: SKScene {
// add as class property so sound is preloaded and always ready
let coinSound = SKAction.playSoundFileNamed("NAMEOFSOUNDFILE", waitForCompletion: false)
}
and than when you need to play the sound simply say this
run(coinSound)
Hope this helps
audioPlayer = AVAudioPlayer(contentsOfURL: coinSound, error: nil)
This is no longer the way that errors are handled in Swift. You must use the do-try-catch syntax.
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: coinSound)
audioPlayer.prepareToPlay()
}
catch let error {
// handle error
}
Alternatively, if you don't care to write any error handling code, you can make your audioPlayer variable optional and write a failable try?:
audioPlayer = try? AVAudioPlayer(contentsOfURL: coinSound)
audioPlayer?.prepareToPlay()
This is more closely equivalent to the code you're attempting where you pass nil for the error parameter.
Or if crashing is the appropriate behavior when an error occurs, you could take this approach:
guard let audioPlayer = try? AVAudioPlayer(contentsOfURL: coinSound) else {
fatalError("Failed to initialize the audio player with asset: \(coinSound)")
}
audioPlayer.prepareToPlay()
self.audioPlayer = audioPlayer
Swift 3.0+
For the above guard let example from #nhgrif, this Swift 3.0+ way of doing it:
var coinSound = NSURL(fileURLWithPath:Bundle.main.path(forResource: "Super Mario Bros.- Coin Sound Effect", ofType: "mp3")!)
var audioPlayer = AVAudioPlayer()
func playCoinSound(){
guard let soundToPlay = try? AVAudioPlayer(contentsOf: coinSound as URL) else {
fatalError("Failed to initialize the audio player with asset: \(coinSound)")
}
soundToPlay.prepareToPlay()
// soundToPlay.play() // NO IDEA WHY THIS DOES NOT WORK!!!???
self.audioPlayer = soundToPlay
self.audioPlayer.play() // But this does work... please let me know why???
}
playCoinSound()

Play music according to scene

I am trying to play theme music in continuous loop according to the scene in Sprite Kit. So far I am doing this:
var backgroundMusicPlayer = AVAudioPlayer();
func playBackgroundMusic() -> () {
var error: NSError?
let backgroundMusicURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Home", ofType: "wav")!)
backgroundMusicPlayer = AVAudioPlayer(contentsOfURL: backgroundMusicURL, error: &error)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
}
And it is working. But now I want to play a different theme music in another theme. According to this, I have to use AVQueuePlayer instead of AVAudioPlayer. But AVQueuePlayer doesn't have methods like stop() or number of loops.
Any idea how to change sounds when switching to different scenes?

Resources