I have implemented background music in my game using AVFoundation. However, when leaving the GameScene the music continues playing. I know that in viewControllers you can use viewWillDisappear but I can't find anything similar for GameScenes.
var audioPlayer = AVAudioPlayer()
let audioPath = Bundle.main.path(forResource: "electroDrive", ofType: "wav")
//.......//
//.......//
do {
try audioPlayer = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPath!))
}
catch {
// process error
}
audioPlayer.play()
//Code to go to other scene
if viewController != nil {
self.viewController?.performSegue(withIdentifier: "push", sender: viewController)
}
Stop the AVAudioPlayer before segueing to the next Scene
Insert this before your segue code
audioPlayer.pause()
Related
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()
}
An application writes. Music plays on the background of the application. And I want to stop and resume this music from every page of the application
But after pressing the music stop button on one page, the buttons on the other pages do not change.
And when I go back to the first page, the music starts all over again.
import Foundation
import AVFoundation
class Music {
static var audioPlayer : AVAudioPlayer?
enum SoundEffect {
case background
}
static func playSound(effect : SoundEffect) {
var soundFileName = ""
switch effect {
case.background:
soundFileName = "background"
}
let bundlePath = Bundle.main.path(forResource: soundFileName, ofType: "wav")
guard bundlePath != nil else {
print("Couldn't find sound file \(soundFileName) in the bundle")
return
}
do {
let soudURL = URL(fileURLWithPath: bundlePath!)
audioPlayer = try AVAudioPlayer(contentsOf: soudURL)
audioPlayer!.numberOfLoops = -1
audioPlayer?.prepareToPlay()
audioPlayer?.play()
} catch {
}
}
}
Al first check AVAudioPlayer is already initialize or not and after that check player is playing or stop.
if let audioPlayer = Music.audioPlayer, audioPlayer.playing {
audioPlayer.stop()
}
I am using xcode 9 and swift 4 for my app. In my app i have music playing in the viewDidLoad. When i exit the view controller to go to another View, it continues to play like it should. How ever, when i return to that view controller the song starts to play again. This song is overlapping the song that first loaded. Do you guys have any ideas on how to stop this from happening?
do
{
let audioPath = Bundle.main.path(forResource: "APP4", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
}
catch
{
//catch error
}
let session = AVAudioSession.sharedInstance()
do
{
try session.setCategory(AVAudioSessionCategoryPlayback)
}
catch
{
}
player.numberOfLoops = -1
player.play()
It starts playing again, because your viewDidLoad is called again, which asks it to play it again. A simplest fix would be to keep a static bool variable to keep track if you have already made this call.
static var isMusicPlaying: Bool = false
In your viewDidLoad, you can put code before the code that calls the play.
guard !isMusicPlaying else {
return
}
isMusicPlaying = true
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()
In my game I added a button to the GameScene and played music:
let url = NSBundle.mainBundle().URLForResource("Happy Background Music", withExtension: "mp3")
let bgMusic = AVAudioPlayer(contentsOfURL: url, error: nil)
#IBAction func SoundOnOff(sender: UIButton) {
bgMusic.numberOfLoops = -1
bgMusic.play()
}
and the music played, but I want to add an if so when the button is pressed the music will stop. What if should I write?
Change your code with this,
let url = NSBundle.mainBundle().URLForResource("Happy Background Music", withExtension: "mp3")
do{
let bgMusic = try AVAudioPlayer(contentsOfURL: url!)
}catch{
print(error)
}
#IBAction func SoundOnOff(sender: UIButton) {
if bgMusic.playing {
bgMusic.stop()
} else {
self.bgMusic.play()
}
}
Edit: Description
playing- Property
-A Boolean value that indicates whether the audio player is playing (true) or not (false). (read-only)
AVAudioPlayer has a built-in property playing, which you can use to decide whether to play or pause the music:
#IBAction func SoundOnOff(sender: UIButton) {
if bgMusic.playing {
bgMusic.pause()
} else {
bgMusic.numberOfLoops = -1
bgMusic.play()
}
}
Just seen that this is only available in iOS9 and later. So if you're targeting iOS9 then this is possible.
If you're using SpriteKit to write your game I would recommend using the SKAudioNode class to play your BGMusic.
let url = NSBundle.mainBundle().URLForResource("Happy Background Music", withExtension: "mp3")
let audioNode = SKAudioNode(URL:url)
audioNode.positional = false
audioNode.autoplayLooped = true
self.addChild(audioNode)
To pause the music you create an SKAction ...
let pauseAction = SKAction.pause()
audioNode.runAction(pauseAction)
You can resume it also but just trying to find the code for that...