Stopping the background music when starting a game - ios

I have background music which starts when the app is launched in GameViewController.swift using the following code:
class GameViewController: UIViewController {
// VARIABLES
var backgroundMusicPlayer : AVAudioPlayer!
// AUDIO PLAYER
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
var error : NSError? = nil
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: url!)
} catch let error1 as NSError {
error = error1
backgroundMusicPlayer = nil
}
if backgroundMusicPlayer == nil {
print("Could not create audio player: \(error!)")
return
}
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
}
func stopBackgroundMusic() {
backgroundMusicPlayer.stop()
}
override func viewDidLoad() {
super.viewDidLoad()
playBackgroundMusic("MainTheme.mp3")
<< Various irrelevant code >>
}
Because this is run in the viewController, it persists through changing scenes on the menu (i.e. opening the "shop" scene) and creates a seamless track. When I click the "Play" button on the menu scene I want the music to then stop, and transition to the game. I have the stopBackgroundMusic() method in the GameViewController but I don't know how to call it on on the menu scene. IN THE MENU SCENE I tried this:
// TOUCH
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch?
let touchLocation = touch!.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
if touchedNode.name == "startGame" {
GameViewController.stopBackgroundMusic()
let transitionType = SKTransition.fadeWithDuration(2)
let viewSize = self.view?.bounds.size
let scene = GameScene(size: viewSize!)
self.view?.presentScene(scene, transition: transitionType)
}
}
But I get an error saying I'm missing parameter #1 in call for stopBackgroundMusic() which shouldn't require any parameters. Am I calling this method wrong? Thanks!

You are referring to your class by using GameViewController but your function is at the object instance level.
If you declare the variable and function at the class level, your code in the touchesBegan function should work fine.
static var backgroundMusicPlayer : AVAudioPlayer!
class func playBackgroundMusic(filename: String) ...
class func stopBackgroundMusic()
override func viewDidLoad() {
super.viewDidLoad()
GameViewController.playBackgroundMusic("MainTheme.mp3")
<< Various irrelevant code >>
}

Related

How to pause background music in game using AVAudioPlayer for multiple scenes

So I have a background music looping in the background in my GameViewController. The pause music button is available in the GameScene where a user can mute or unmute the game music.
I have two global variables:
var muteButton = SKSpriteNode(imageNamed: "pause")
var mute: Bool = false
Inside my GameScene I've added, things work like they are suppose to (the print responses are triggered).
class GameScene: SKScene{
override func didMove(to view: SKView){
...
muteButton.position = CGPoint(x: self.size.width*0.2, y: self.size.height*0.90)
muteButton.name = "Mute Button"
muteButton.zPosition = 10
self.addChild(muteButton)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
if nodeITapped.name == "Mute Button"{
if mute == false {
print("music will now turn OFF")
mute = true
}
else{
print("music will now turn ON")
mute = false
}
}
}
}
}
I suspect the mute variable is only being called once in the GameViewController viewDidLoad, and thus the if statement is being checked only once. Since I have multiple senses connected that all need to have music playing, the best place for me to put the backgroundAudio would be here.
In my GameViewController:
class GameViewController: UIViewController{
var backgroundAudio = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
// Background Audio plays throughout the game
let filePath = Bundle.main.path(forResource: "Track1",ofType:"mp3")
let audioNS_URL = NSURL(fileURLWithPath: filePath!)
if mute == false{
do{ backgroundAudio = try AVAudioPlayer(contentsOf: audioNS_URL as URL)}
catch { return print("No Audio Found")}
// audio will loop forever
backgroundAudio.numberOfLoops = -1
backgroundAudio.play()
}
else{
backgroundAudio.pause()
}
}
}
Add an observer inside GameViewController:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(switchBackgroundAudio), name: NSNotification.Name.init("switchBackgroundAudio"), object: nil)
//... other stuff here ...//
}
then add a function for switching the sound on/off:
#objc func switchBackgroundAudio() {
if mute == false {
backgroundAudio.play()
} else {
backgroundAudio.pause()
}
}
finally whenever inside your GameScene you touch the button, you may launch an event:
if nodeITapped.name == "Mute Button"{
//... stuff here ...//
NotificationCenter.default.post(Notification(name: NSNotification.Name("switchBackgroundAudio")))
}

Music Stops playing after scene change SKAudioNode Xcode 8

So I have been trying to get this audio file to play through all scenes of the game but it cuts off after the MenuScene. I call the MenuScene from GameViewController
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'MenuScene.sks'
if let scene = SKScene(fileNamed: "MenuScene") {
// Set the scale mode to scale to fit the window
Then in the MenuScene I start the music by doing this.
class MenuScene: SKScene {
var starfield:SKEmitterNode!
var newGameButtonNode:SKSpriteNode!
var difficultyButtonNode:SKSpriteNode!
var difficultyLabelNode:SKLabelNode!
var backgroundMusic:SKAudioNode!
override func didMove(to view: SKView) {
let backgroundMusic = SKAudioNode(fileNamed: "Snap.mp3")
backgroundMusic.autoplayLooped = true
addChild(backgroundMusic)
The music starts playing. Then I have in the touchesBegan () if the GameScene button is touched it will load that scene. I made the transition really long to find out when the music stops and it plays through the entire transition but when the GameScene is loaded it stops.
if nodesArray.first?.name == "newGameButton" {
let transition = SKTransition.fade(withDuration: 5.0)
run(SKAction.wait(forDuration: 5.0), completion: {
self.backgroundMusic = SKAudioNode(fileNamed: "Snap.mp3")
self.backgroundMusic.autoplayLooped = true
self.addChild(self.backgroundMusic)
})
let gameScene = GameScene(size: self.size)
self.view!.presentScene(gameScene, transition: transition)
I want it to continue playing throughout the game and on the GameOver scene and loop over forever.
Make sure your in main SKScene and
You can do something like this:
self.runAction(SKAction.playSoundFileNamed("YourFileName.extension", waitForCompletion: false))
Repeat Music:
SKAction.repeatForever(SKAction.playSoundFileNamed("YourFileName.extension", waitForCompletion: true))
Your music will keep on playing..
With AVFoundation
One of my code I have done.
import AVFoundation
var backgroundMusicPlayer: AVAudioPlayer!
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(
filename, withExtension: nil)
if (url == nil) {
print("Could not find file: \(filename)")
return
}
var error: NSError? = nil
backgroundMusicPlayer =
AVAudioPlayer(contentsOfURL: url, error: &error)
if backgroundMusicPlayer == nil {
print("Could not create audio player: \(error!)")
return
}
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
}
import SpriteKit

Create a random audio sound generator

Thanks for replying.
I am trying to make a program when I press a button once, two random sounds will play. I can get the random sounds to play if I press the button, but I am looking that if I press the button, the random sound will play differently each time.
I can paste the sounds together to hear them in the sequence I want them, but I would like swift to generate the sounds.
I thought of the AVqueplayer to make it as a playlist. I was thinking this can be like a pair of dice in an analogy. For example, if I were to throw the dice down, the random sounds will occur.
I am still a newbie, and tried to figure this out on my own, because it seemed so simple, but I am out of options now.
Here is what I got so far. This will play a random sound when I press the button each time.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer = AVAudioPlayer()
var sounds = ["sound1", "sound2", "sound3"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if event!.subtype == UIEventSubtype.motionShake {
let randomNumber = Int(arc4random_uniform(UInt32(sounds.count)))
let fileLocation = Bundle.main.path(forResource: sounds[randomNumber], ofType: "mp3")
var error: NSError? = nil
do { try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: fileLocation!))
player.play()
} catch {}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Using the same code, and adding a second random number with matching file location will allow the sounds to play back to back, both being random:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer = AVAudioPlayer()
var sounds = ["sound1", "sound2", "sound3"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if event!.subtype == UIEventSubtype.motionShake {
let randomNumber1 = Int(arc4random_uniform(UInt32(sounds.count)))
let randomNumber2 = Int(arc4random_uniform(UInt32(sounds.count)))
let fileLocation1 = Bundle.main.path(forResource: sounds[randomNumber1], ofType: "mp3")
let fileLocation2 = Bundle.main.path(forResource: sounds[randomNumber2], ofType: "mp3")
//var error: NSError? = nil
do {
try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: fileLocation1!))
player.play()
try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: fileLocation2!))
player.play()
} catch {}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

AVFoundation: prevent background music playing twice when returning to first screen

At the moment I have a home screen which plays background music using AVFoundation. If you click play the music stops (which is what I want).
If you move to the instructions screen the music continues (which I want), however when you click to return to home screen the background music continues (which I want), but a new track starts over the top.
In my mind what I ideally need is an if statement which prevents the music from restarting when I return to the home screen (if it is already playing). I have scoured the internet but I can't find any suggestions that will work.
This is the what I am currently working with,
class firstPageViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
backgroundMusic = self.setupAudioPlayerWithFile("background", type:"mp3")
backgroundMusic.volume = 0.3
backgroundMusic.numberOfLoops = -1
backgroundMusic.play()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
var backgroundMusic = AVAudioPlayer()
func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer {
//1
var path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
var url = NSURL.fileURLWithPath(path!)
//2
var error: NSError?
//3
var audioPlayer:AVAudioPlayer?
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
//4
return audioPlayer!
}
#IBAction func startGame(sender: UIButton) {
backgroundMusic.stop()
}
#IBAction func instructionsButton(sender: UIButton) {
}
var isMusicPlaying = 0
class firstPageViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
backgroundMusic = self.setupAudioPlayerWithFile("background", type:"mp3")
if isMusicPlaying == 0 {
backgroundMusic.volume = 0.3
backgroundMusic.numberOfLoops = -1
backgroundMusic.play()
}
if backgroundMusic.playing == true {
isMusicPlaying = 1
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
var backgroundMusic = AVAudioPlayer()
func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer {
//1
var path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
var url = NSURL.fileURLWithPath(path!)
//2
var error: NSError?
//3
var audioPlayer:AVAudioPlayer?
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
//4
return audioPlayer!
}

Remote Control event in iOS with Swift

Trying to figure out how to read the Apple headphone's volume buttons to use as a trigger for the camera shutter (as the Apple Camera app does).
From the documentation on Remote Control Events,
Remote Control Received With Event, and this git repo, I've pieced together that I'll probably need an AVAudioPlayer object, .beginReceivingRemoteControlEvents(), and remoteControlReceivedWithEvent, along with making this view canBecomeFirstResponder() return true.
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioPlayerDelegate {
var player: AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
var session: AVAudioSession = AVAudioSession.sharedInstance()
session.setActive(true, error: nil)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
println("viewDidAppear worked...")
self.becomeFirstResponder()
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func remoteControlReceivedWithEvent(event: UIEvent) {
let rc = event.subtype
println("does this work? \(rc.rawValue)")
//takePicture()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I expected to get "does this work" when hitting the volume buttons on the headphones, instead I just see it adjust the headphone volume like normal. So I must be missing something, maybe with a delegate or AVSession?
I cross-posted this on r/swift, where I was told it probably requires playing audio (quoted straight from the documentation).
So while this isn't the ideal solution, it works for my own private use.
import UIKit
import AVFoundation
import MediaPlayer
class ViewController: UIViewController, AVAudioPlayerDelegate {
var testPlayer: AVAudioPlayer? = nil
func loadSound(filename: NSString) -> AVAudioPlayer {
let url = NSBundle.mainBundle().URLForResource(filename as String, withExtension: "caf")
var error: NSError? = nil
let player = AVAudioPlayer(contentsOfURL: url, error: &error)
if error != nil {
println("Error loading \(url): \(error?.localizedDescription)")
} else {
player.prepareToPlay()
}
return player
}
override func viewDidLoad() {
super.viewDidLoad()
self.testPlayer = self.loadSound("silence")
self.testPlayer?.numberOfLoops = -1
self.testPlayer?.play()
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.becomeFirstResponder()
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
}
override func remoteControlReceivedWithEvent(event: UIEvent) {
let rc = event.subtype
println("rc.rawValue: \(rc.rawValue)")
// take photo
}
}
I noticed that in Apple's camera app, the +/- volume buttons trigger the camera, and the microphone button pauses/plays any audio running in another app, but in this implementation the volume buttons still control the volume (and any audio has been paused when the app is launched).
An rc.rawValue: 103 corresponds to a single click of the microphone button, a double click returns 104, and a triple click returns 105, and then sometimes bumping a couple at a time returns a 108 or 109.
Based on Cody's answer but updated for 2019 (Swift 5)
import UIKit
import AVFoundation
import MediaPlayer
class ViewController: UIViewController, AVAudioPlayerDelegate {
var myPlayer: AVAudioPlayer? = nil
func loadSound(filename: NSString) -> AVAudioPlayer? {
let url = Bundle.main.url(forResource: filename as String, withExtension: "mp3")
do {
let player = try AVAudioPlayer(contentsOf: url ?? URL(fileURLWithPath: ""))
player.prepareToPlay()
return player
}
catch {
print("Error : \(error)")
return nil
}
}
override func viewDidLoad() {
super.viewDidLoad()
guard let testPlayer = loadSound(filename: "silence") else {
print("Not able to load the sound")
return
}
testPlayer.delegate = self
testPlayer.volume = 0.8
testPlayer.numberOfLoops = -1
myPlayer = testPlayer
myPlayer?.play()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.becomeFirstResponder()
UIApplication.shared.beginReceivingRemoteControlEvents()
}
override func remoteControlReceived(with event: UIEvent?) {
let rc = event?.subtype
print("rc.rawValue: \(rc?.rawValue)")
// Do your thing
}
}

Resources