I have 2 sound effects and i can't stop them , however my background music works perfect.
My sound effects are in a different class - They Are in GameScene I run them from there.
How i can stop my sound effects correct?
For example: i have CatchObjectSound and i created the functions that i need. but still i can hear the sound effect when I'm clicking on the button.
What i want to do?
I want to stop my sound effects.
SKTAudio Class :
import UIKit
import AVFoundation
public class Singleton {
// Initialize
public var backgroundMusicPlayer: AVAudioPlayer?
public var soundEffectCatchObject: AVAudioPlayer?
//public var soundEffectGameOver: AVAudioPlayer?
//public var soundEffectClickedButton: AVAudioPlayer?
public class func sharedInstance() -> Singleton {
return SingletonInstance
}
// ---------------------------------------------
// Play Background Music
// ---------------------------------------------
func playBackgroundMusic() {
let url = NSBundle.mainBundle().URLForResource("Unity.mp3", withExtension: nil)
guard let newURL = url else {
print("Could not find file: \("Unity.mp3")")
return
}
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: newURL)
backgroundMusicPlayer!.numberOfLoops = -1
backgroundMusicPlayer!.prepareToPlay()
backgroundMusicPlayer!.play()
} catch let error as NSError {
print(error.description)
}
}
func pauseBackgroundMusic() {
backgroundMusicPlayer!.pause()
}
func resumeBackgroundMusic() {
backgroundMusicPlayer!.play()
}
// ---------------------------------------------
// Play Sound Effect Catch Object
// ---------------------------------------------
func playSoundEffectCatchObject() {
let url = NSBundle.mainBundle().URLForResource("Magical Echo Spell 03.wav", withExtension: nil)
guard let newURL = url else {
print("Could not find file: \("Magical Echo Spell 03.wav")")
return
}
do {
let error: NSError? = nil
soundEffectCatchObject = try! AVAudioPlayer(contentsOfURL: newURL)
if let player = soundEffectCatchObject {
player.numberOfLoops = 0
player.prepareToPlay()
player.play()
} else {
print("Could not create audio player: \(error!)")
}
}
func pauseSoundEffectCatchObject() {
if let player = soundEffectCatchObject {
if player.playing {
player.pause()
}
}
}
func resumeSoundEffectCatchObject() {
if let player = soundEffectCatchObject {
if player.playing {
player.play()
}
}
}
}
}
let SingletonInstance = Singleton()
I have button that pause/resume in main scene.
the button works perfectly , but i can't stop my sound effect, i tried.
Main Scene
if(SoundOnOff.containsPoint(location)) {
if (Singleton.sharedInstance().backgroundMusicPlayer?.playing == true) {
Singleton.sharedInstance().pauseBackgroundMusic()
Singleton.sharedInstance().pauseSoundEffectCatchObject()
self.SoundOnOff.texture = SKTexture(imageNamed:"MusicOff.png")
} else {
Singleton.sharedInstance().resumeBackgroundMusic()
Singleton.sharedInstance().resumeSoundEffectCatchObject()
self.SoundOnOff.texture = SKTexture(imageNamed:"MusicOn.png")
}
}
So create a sound playing singleton. Give it methods to start and stop the 2 sound effects. Since it's a singleton you can call it from anywhere in your program.
Related
I have an app in which I want to have a single audio player, and the ability to switch out what audio clips are in the player. Currently using AKAudioPlayer and replace(file: audioFile).
I have the following class that gets created on the view controller loading:
class AudioFilePlayer {
var songFile = Bundle.main
var player: AKAudioPlayer!
func play(file: String, type: String) {
var audioFile: AKAudioFile!
let song = songFile.path(forResource: file, ofType: type)
do {
let url = URL(string: song!)
audioFile = try AKAudioFile(forReading: url!)
} catch {
AKLog(error)
}
do {
player = try AKAudioPlayer(file: audioFile)
} catch {
AKLog(error)
}
}
func rePlay(file: String, type: String) {
var audioFile: AKAudioFile!
let song = songFile.path(forResource: file, ofType: type)
do {
let url = URL(string: song!)
audioFile = try AKAudioFile(forReading: url!)
} catch {
AKLog(error)
}
do {
try player.replace(file: audioFile)
} catch {
AKLog(error)
}
}
func pause(){
player.pause()
}
}
Once the app starts, I have the following code to set up the AK signal chain and create a player with an audio file, and I immediately pause it:
audioFilePlayer.play(file: "Breathing_01", type: ".mp3")
audioFilePlayer.player.looping = false
AudioKit.output = audioFilePlayer.player
do {
try AudioKit.start()
} catch {
AKLog("AudioKit did not start!")
}
audioFilePlayer.player.play()
audioFilePlayer.pause()
Elsewhere in the app, I have the following code to replace the audio file used in the player:
self.audioFilePlayer.player.pause()
self.audioFilePlayer.rePlay(file: "Breathing_01", type: "mp3")
self.audioFilePlayer.player.play()
When I run the app and initiate the process of trying to replace the file, I see this log:
2020-04-05 17:32:13.674413-0700 Mindful[24081:4439478] [general] AKAudioPlayer.swift:replace(file:):397:AKAudioPlayer -> File with "Breathing_01.mp3" Reloaded (AKAudioPlayer.swift:replace(file:):397)
2020-04-05 17:32:13.686119-0700 Mindful[24081:4439478] [general] AKAudioPlayer.swift:startTime:171:AKAudioPlayer.startTime = 0.0, startingFrame: 0 (AKAudioPlayer.swift:startTime:171)
2020-04-05 17:32:14.282632-0700 Mindful[24081:4439478] [general] AKAudioPlayer.swift:updatePCMBuffer():570:read 13359773 frames into buffer (AKAudioPlayer.swift:updatePCMBuffer():570)
But no audio output at all. When setting breakpoints, I can confirm that my player is playing, but have no audio.
Any help appreciated!
I had the same issue. Doing the following fixed it:
try player.replace(file: audioFile)
DispatchQueue.main.async {
self.player.play(from: 0)
}
I used the function slowPlay to create an action that will play background music. I tried to do the reverse in stop to stop the music. But the music does not stop.
I just want to have a button that plays the music and stops the music. I'm using the code:
#IBAction func play(_ sender: Any) {
slowPlay
}
#IBAction func stop(_ sender: Any) {
slowStop
}
func slowPlay() {
do {
let ap = Bundle.main.path(forResource: "slowslow", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: ap!) as URL)
} catch {
////
}
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayback)
} catch {
}
player?.play()
}
func slowStop() {
do {
let ap = Bundle.main.path(forResource: "slowslow", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: ap!) as URL)
} catch {
////
}
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayback)
} catch {
}
player?.stop()
}
If you look at your code example you are creating a new instance of the player for both play and stop.
You need to create a class variable for your player, something like:
var player: AVAudioPlayer?
This will allow you to stop like this:
func slowStop() {
player?.stop()
}
Create a single instance for player like blow
var player: AVAudioPlayer?
func slowPlay() {
do {
let ap = Bundle.main.path(forResource: "slowslow", ofType: "mp3")
self.player = try AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: ap!) as URL)
} catch {
////
}
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayback)
} catch {
//catch error here
}
self.player?.play()
}
func slowStop() {
self.player?.stop()
}
You can add validation to check whether player is valid or not before performing any action.
So I have a list of audio files and when a cell is pressed it plays the audio. This works fine but I am not able to pause it by clicking the same cell again.
My code for the table view:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let a = indexPath.section;
let b = indexPath.row;
var path = NSBundle.mainBundle().pathForResource(musicArray[b], ofType: "mp3")
var error:NSError?
do{
audioPlayer = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: path!))
}
catch
{
print("Something bad happened")
}
if (audioPlayer.playing)
{
audioPlayer.pause();
}
else
{
audioPlayer.play();
}
}
musicArray has the titles of the audio files
You're creating a new AVAudioPlayer every time you select the cell. You need to keep a reference to the previous one and tell that audio player to pause.
You can create a singleton object.
import AVFoundation
class SomeAudioManager: NSObject, AVAudioPlayerDelegate
{
class var sharedInstance: SomeAudioManager {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: SomeAudioManager? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = SomeAudioManager()
}
return Static.instance!
}
func audioView(songname: String,format: String) {
let audioPlayer: AVAudioPlayer
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(songname, ofType:format)!), fileTypeHint: AVFileTypeMPEGLayer3)
audioPlayer.delegate = self;
audioPlayer.play()
} catch {
// error
}
}
}
It will be alive through the whole app.
How to implement AVAudioPlayer Inside Singleton Method?
You can just create a singleton of the player so that you will always know if it's playing or not.
Playing a sound is so verbose in code and would like to create an extension of AVAudioSession if possible. The way I'm doing it is assigning an object to a variable, but need help/advise on how to set up this function so it's reusable and optimized.
Here's what I have:
func playSound(name: String, extension: String = "mp3") {
let sound = NSBundle.mainBundle().URLForResource(name, withExtension: extension)
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
audioPlayer = try AVAudioPlayer(contentsOfURL: sound!)
audioPlayer?.prepareToPlay()
audioPlayer?.play()
} catch { }
}
I think I have to create the audioPlayer variable outside of this function, otherwise I had a hard time playing anything. Maybe it can be self contained? I'm hoping to use it something like this:
AVAudioSession.sharedInstance().play("bebop")
Taking the code straight from your example I see two options:
1) Is there any particular reason why you want to make it as an extension of AVAudioSession? If not, just make your own service!
class AudioPlayerService {
static let sharedInstance = AudioPlayerService()
var audioPlayer: AVAudioPlayer?
func playSound(name: String, extension: String = "mp3") {
let sound = NSBundle.mainBundle().URLForResource(name, withExtension: extension)
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
audioPlayer = try AVAudioPlayer(contentsOfURL: sound!)
audioPlayer?.prepareToPlay()
audioPlayer?.play()
} catch { }
}
}
2) If you do need to make it as an extension of AVAudioSession, then take a look at associated objects
extension AVAudioSession {
private struct AssociatedKeys {
static var AudioPlayerTag = "AudioPlayerTag"
}
var audioPlayer: AVAudioPlayer? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.AudioPlayerTag) as? AVAudioPlayer
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.AudioPlayerTag,
newValue as AVAudioPlayer?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
func playSound(name: String, extension: String = "mp3") {
let sound = NSBundle.mainBundle().URLForResource(name, withExtension: extension)
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
audioPlayer = try AVAudioPlayer(contentsOfURL: sound!)
audioPlayer?.prepareToPlay()
audioPlayer?.play()
} catch { }
}
}
This is the best way I have found to add sound
Filename is shootMissile.wav
func shootMissileSound() {
if let soundURL = NSBundle.mainBundle().URLForResource("shootMissile", withExtension: "wav") {
var mySound: SystemSoundID = 0
AudioServicesCreateSystemSoundID(soundURL, &mySound)
AudioServicesPlaySystemSound(mySound);
}
}
I have a game which consists of 3 view controllers:
viewController
settingViewController
GameViewController
I have set the background music and played it in the viewController class
var backgroundMusic : AVAudioPlayer!
func setUpSounds(){
//button sound
if let buttonSoundPath = NSBundle.mainBundle().pathForResource("buttonClick", ofType: "mp3") {
let buttonSoundURL = NSURL(fileURLWithPath: buttonSoundPath)
do {
try buttonSound = AVAudioPlayer(contentsOfURL: buttonSoundURL)
}catch {
print("could not setup button sound")
}
buttonSound.volume = 0.5
}
//background sound
if let backgroundMusicPath = NSBundle.mainBundle().pathForResource("BackgroundMusic", ofType: "mp3") {
let backgroundMusicURL = NSURL(fileURLWithPath: backgroundMusicPath)
do {
try backgroundMusic = AVAudioPlayer(contentsOfURL: backgroundMusicURL)
}catch {
print("could not setup background music")
}
backgroundMusic.volume = 0.2
/*
set any negative integer value to loop the sound
indefinitely until you call the stop method
*/
backgroundMusic.numberOfLoops = -1
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.setUpSounds()
self.playBackgroundSound()
// Do any additional setup after loading the view, typically from a nib.
}
when I move to the settingViewController then back again to the viewController the sound replay and overlap the old played music.
What is the solution of this problem?
You have to check if the AVAudioPlayer is playing a music before call playBackgroundSound.
You can also put your SoundManager as a singleton, so you can manipulate it, from other parts of the app.
class SoundManager{
static var backgroundMusicSharedInstance = AVAudioPlayer?
}
In the View
func setUpSounds(){
//background sound
if SoundManager.backgroundMusicSharedInstance == nil {
if let backgroundMusicPath = NSBundle.mainBundle().pathForResource("BackgroundMusic", ofType: "mp3") {
let backgroundMusicURL = NSURL(fileURLWithPath: backgroundMusicPath)
do {
try SoundManager.backgroundMusicSharedInstance = AVAudioPlayer(contentsOfURL: backgroundMusicURL)
}catch {
print("could not setup background music")
}
SoundManager.backgroundMusicSharedInstance!.volume = 0.2
/*
set any negative integer value to loop the sound
indefinitely until you call the stop method
*/
SoundManager.backgroundMusicSharedInstance!.numberOfLoops = -1
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.setUpSounds()
if SoundManager.backgroundMusicSharedInstance!.playing == false{
self.playBackgroundSound()
}
}