AVAudioPlayer does not think it is playing when it is - swift

I call the superPlay function to start audio playback a little while later I call the superPlay function again to stop the playback. This does not work though because player.isPlaying is false even knowing the sound is clearly playing in an infinite loop. I have no idea why please help!
func superPlay(timeInterval: TimeInterval, soundName: String) {
do {
alarmSound = soundName
//set up audio session
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker, .duckOthers])
try AVAudioSession.sharedInstance().setActive(true)
let url = Bundle.main.url(forResource: alarmSound, withExtension: "mp3")
player = try! AVAudioPlayer(contentsOf: url!)
player.numberOfLoops = -1
//Start AVAudioPlayer
print(timeInterval)
print(time)
let playbackDelay = timeInterval // must be ≥ 0
if player.isPlaying {
player.stop()
} else {
player.play(atTime: player.deviceCurrentTime + playbackDelay) //time is a TimeInterval after which the audio will start
}
}
catch {
print(error)
}
}
I have spent a couple more days debugging this now. What is happening is the original play is being assigned to a specific AudioPlayer ID for example the print is: "Optional(<AVAudioPlayer: 0x600002802280>)"
When I call the function again to stop the play the AVAudioPlayer is assigned a different ID therefore it does not find that the old player is still playing and moves forward with playing a new sound on top of the old sound.
I am not sure how to store the AVAudioPlayer ID and then call the function so that it checks the store Player for if it is ".isPlaying"??

This happen because you initialize it before stopping current audio. Try this way:-
func superPlay(timeInterval: TimeInterval, soundName: String) {
// If audio is playing already then stop it.
if let audioPlayer = player {
if player.isplaying {
player.stop()
}
}
// Initilize audio player object with new sound
do {
alarmSound = soundName
//set up audio session
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker, .duckOthers])
try AVAudioSession.sharedInstance().setActive(true)
let url = Bundle.main.url(forResource: alarmSound, withExtension: "mp3")
player = try! AVAudioPlayer(contentsOf: url!)
player.numberOfLoops = -1
//Start AVAudioPlayer
print(timeInterval)
print(time)
let playbackDelay = timeInterval // must be ≥ 0
player.play(atTime: player.deviceCurrentTime + playbackDelay) //time is a TimeInterval after which the audio will start
}
catch {
print(error)
}
}

Related

How to play sound after each network request is successful?

I want to play a sound after each network request is successful. I have some objects that I want to send to the server using a for loop. In each iteration I'm sending a request to the server and after each request is complete, I want to play a sound.
I've tried this:
for object in objects {
sendObjectToServer(object)
}
func playSound() {
let url = Bundle.main.url(forResource: "sound", withExtension: "mp3")!
let player = try! AVAudioPlayer(url)
player.play()
}
func sendObjectToServer(object: Object) {
URLSession.shared.dataTask(with url: object.url) {(data,response,error) in
DispatchQueue.main.async {
playSound() // doesn't play sound synchronously
}
}
}
Why are you using the main thread for background-related operations. Only update your UI in the Dispatch.main.async code:
Try this on a physical device (tested and worked)
import AVFoundation
for object in objects {
DispatchQueue.global(qos: .background).async {
sendObjectToServer(object)
}
}
var player: AVAudioPlayer?
func playSound() {
guard let url = Bundle.main.url(forResource: "sound", withExtension: "mp3") else { return }
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
/* The following line is required for the player to work on iOS 11. Change the file type accordingly*/
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
/* iOS 10 and earlier require the following line:
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3) */
guard let player = player else { return }
player.play()
} catch let error {
print(error.localizedDescription)
}
}
func sendObjectToServer(object: Object) {
URLSession.shared.dataTask(with url: object.url) {(data,response,error) in
// Custom Logic
playSound()
}
}

Two simultaneous AVAudioSessions

I have an app that uses WebRTC for voice chat.
After a lot of troubleshooting I came to the conclusion that whenever I receive a stream, the WebRTC will set the AVAudioSession shared instance to the following:
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat, options: .allowBluetooth)
I want to play an audio file (about a second long) and if I simply load it and do player.play(), it won't play. If I try to set the category to .playback or anything that would make sense for an audio file, the stream's audio will stop playing.
I also tried to create a new AVAudioSession instance but once the audio file plays, the stream's audio will stop.
Is there a way for me to get the audio file to play without affecting the stream's audio and its configuration?
Here's the full code
private func playTapSound() {
guard let url = Bundle.main.url(forResource: App.AudioFile.emojiTapSound, withExtension: "mp3") else { return }
do {
let audioSesson = AVAudioSession()
try audioSesson.setCategory(.playback, mode: .default)
try audioSesson.setActive(true)
audioPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
guard let player = audioPlayer else { return }
player.play()
} catch let error {
LogManager.shared.print("Error - playing emoji tap audio -", error.localizedDescription, type: [.Error, .Audio])
}
// var chime1URL: NSURL?
// var chime1ID: SystemSoundID = 0
// let filePath = Bundle.main.path(forResource: "emojiTapSound", ofType: "wav")
// chime1URL = NSURL(fileURLWithPath: filePath!)
// AudioServicesCreateSystemSoundID(chime1URL!, &chime1ID)
// AudioServicesPlaySystemSound(0)
}
I also tried using the AudioToolBox but for some reason it only played on the simulator. I verified that it's not my phone's settings by trying one of Apple's preloaded sounds and it worked.

AVAudioPlayer object not stable in swift

I have a global variable audioPlayer. Every time user click on "Buzz" button, it call this function:
static func playBuzz() {
if audioPlayer == nil {
let url = Bundle.main.path(forResource: "6", ofType: "wav")!
let contentURL = URL.init(fileURLWithPath: url)
do {
audioPlayer = try AVAudioPlayer(contentsOf: contentURL)
audioPlayer.volume = 1.0
} catch {
}
}
print("play buzz ")
DispatchQueue.global().async {
audioPlayer.prepareToPlay()
audioPlayer.play()
}
}
}
But when I call this, it play sound very randomly. Sometimes it play sound, sometimes it don't play sound. How can I resolve this problem?
The answer is when audioPlaying is play, I call play it won't process. My audio player have 1 second duration.
So to fix this, I put more lines:
if audioPlayer.isPlaying {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { //Make sure to call play() after audioPlayer finish playing.
audioPlayer.prepareToPlay()
audioPlayer.play()
return
}
}

How to change default notification sound with Firebase?

I can get notification data with
let userInfo = notification.request.content.userInfo
But I don't know how to use it when my app is on background. The data I receive from Firebase is like this
%# [AnyHashable("google.c.a.c_l"): DEFGH, AnyHashable("sound"): alert.aiff, AnyHashable("google.c.a.e"): 1, AnyHashable("google.c.a.ts"): 1515722730, AnyHashable("google.c.a.udt"): 0, AnyHashable("gcm.notification.sound2"): default, AnyHashable("gcm.n.e"): 1, AnyHashable("aps"): {
alert = ABC;
badge = 1;
sound = default;
}, AnyHashable("google.c.a.c_id"): xxxxxx, AnyHashable("gcm.message_id"): 0:xx%xx]
Easy APNs Provider plays custom sound just fine though.
just disable sound from fireBase console and when your notification recieved use custom sound and call this method when your notification recieved
import AVFoundation
var player: AVAudioPlayer?
func playSound() {
guard let url = Bundle.main.url(forResource: "soundName", withExtension: "mp3") else { return }
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(contentsOf: url)
guard let player = player else { return }
player.play()
} catch let error {
print(error.localizedDescription)
}
}

Play a sound upon collecting coin in Swift SKAction

I have a code in Swift which plays a bird game. A bird collects eggs. I want to play a coin sound upon bird touching the egg. I tried code below but it's not playing upon contact. How can I accomplish it?
do {
// Preperation
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch _ {
}
// Play the sound
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: alertSound)
} catch _{
}
audioPlayer.prepareToPlay()
audioPlayer.play()
Create a path to the sound file
let path = NSBundle.mainBundle().pathForResource("(sound name)", ofType: (type) )!
Url for the path
var url = NSURL()
Variable to hold sound
var soundEffect: AVAudioPlayer!
Then when you want to play the sound
url = NSURL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOfURL: url)
soundEffect = sound
sound.play()
} catch {
}