play multiple AVAudioPlayer at the same time asynchronously? - swift

I have a background AVAudioPlayer who normally works just fine. Now I have some event that triggers an asynchron action with a delay, here I want to play another sound while the other should keep playing. So I make a new one:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
guard let urlStr = Bundle.main.path(forResource: "File1", ofType: ".mp3") else { fatalError() }
self.player = try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: urlStr), fileTypeHint: "mp3")
self.player?.play()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
guard let urlStr = Bundle.main.path(forResource: "File2", ofType: ".mp3") else { fatalError() }
let p = try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: urlStr), fileTypeHint: "mp3")
p?.play()
}
So the first one runs normally (Dispatch only to show that it's working also async). The second one runs as well, meaning no error. However, I only hear the first one...

Why do you create a new player inside you async call? I had similar problems and it should work if you declare a new var outside your immediate async-scope.

Related

AVFoundation bugs, No sound when I run audioPlayer.play()

I have a little problem with AVFoundation, when I launch a sound there is nothing that comes out. On the other hand it is enough to add a timer that displays the currentTime of the sound in question and everything works correctly. could you help me? Thanks.
code that does not work:
do {
let path = Bundle.main.path(forResource: "sound1", ofType: "mp3")!
let url = URL(fileURLWithPath: path)
let audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.volume = 1.0
audioPlayer.play()
} catch {
assertionFailure("Failed crating audio player: \(error).")
return
}
just add the timer and everything will work:
do {
let path = Bundle.main.path(forResource: "sound1", ofType: "mp3")!
let url = URL(fileURLWithPath: path)
let audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.volume = 1.0
audioPlayer.play()
print("le son est en cours de lecture !")
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (timer) in
print(audioPlayer.currentTime)
}
} catch {
assertionFailure("Failed crating audio player: \(error).")
return
}
This worked for me
instead of
let audioPlayer = try AVAudioPlayer(contentsOf: url)
change to
audioPlayer = try AVAudioPlayer(contentsOf: url)

How do you fix the "found nil while unwrapping optional value" error when trying to play sound?

I'm making a function to play sound
func playSound(soundName: String) {
let url = Bundle.main.url(forResource: soundName, withExtension: "wav")
player = try! AVAudioPlayer(contentsOf: url!)
player.play()
}
Then call this function in an IBAction that contains all my buttons
#IBAction func buttonPiano(_ sender: UIButton) {
playSound(soundName: String(sender.currentTitle!))
sender.backgroundColor = UIColor.white
sender.alpha = 0.3
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300), execute: {
sender.backgroundColor = UIColor.systemBackground
sender.alpha = 1
})
}
Running the app I can do. But whenever you press a button, it crashes and gives me this error:
Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/administrator/Desktop/Xcode Projects/pianoButtons/pianoButtons/ViewController.swift, line 37
The optional value seems to be url! from my sound function.
I've tried all I could, but no luck. How do I avoid this error and play the sound without crashes?
Make sure that your soundName.wave file is shown inside the copy bundle resources. You can find that by clicking on your project > selecting your target > Build Phases > Copy Bundle Resources. If you do not see it there, click the plus button to add it.
var soundPlayer: AVAudioPlayer?
func playSentSound() {
DispatchQueue.main.async{
let path = Bundle.main.path(forResource: "soundName.mp3", ofType: nil)!
let url = URL(fileURLWithPath: path)
do {
self.soundPlayer = try AVAudioPlayer(contentsOf: url)
print("Playing")
self.soundPlayer?.play()
} catch {
// couldn't load file :(
print("Cant Load File")
}
}
}
Your code is essentially calling this
try! AVAudioPlayer(contentsOf: Bundle.main.url(forResource: String(button.currentTitle!), withExtension: "wav")!)
Each ! is a potential crash. This is when they would occur
The button may not have a current title at the time the action is triggered.
The main bundle may not have a resource with that name/extension
The audio player may not be able to play the contents of that file
In your specific case it seems like it is failing at 2. The nicer way to handle this is like so
if let url = Bundle.main.url(forResource: soundName, withExtension: "wav") {
player = try! AVAudioPlayer(contentsOf: url)
} else {
print("No resouce named \(soundName).wav")
}
First of all your app will just not play a sound instead of crashing, second you will get a helpful log message which might show you why the resource isn't being found.
Ideally all of your ! should be replaced with similar constructs, to log errors or perform some fallback action instead of crashing.

Create a function from code

How do I create a function from this code in swift3?
I have a button which is pressed then plays this sound "push"
How can it be simplified when there are lots of buttons? I don't want to add all codes to every button.
var myAudio = AVAudioPlayer()
// Add sound
do {
try myAudio = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath:
Bundle.main.path(forResource: "push", ofType: "mp3")!) as URL)
} catch {
NSLog("No file!")
}
//call the sound
myAudio.play()
I made this change
func play(name : String){
do {
try myAudio = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath:
Bundle.main.path(forResource: name, ofType: "mp3")!) as URL)
} catch {
NSLog("No file!")
}
}
#IBAction func buttonFirst(_ sender: UIButton) {
play(name: "push")
myAudio.play()
}
#IBAction func buttonSecond(_ sender: UIButton) {
play(name: "second")
myAudio.play()
}
I got this output:
2017-07-25 16:13:23.270349+0100 sound[1728:933024] [aqme] 254: AQDefaultDevice (173): skipping input stream 0 0 0x0
Is that a problem?
I think you forgot the prepare
var audioPlayer = AVAudioPlayer()
let sound = URL(fileURLWithPath: Bundle.main.path(forResource: "sound", ofType: "mp3")!)
try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try! AVAudioSession.sharedInstance().setActive(true)
try! audioPlayer = AVAudioPlayer(contentsOf: sound)
audioPlayer.prepareToPlay()
audioPlayer.play()
You can use it in the following way
var myAudio : AVAudioPlayer?
func playSound(){
let path = Bundle.main.path(forResource: "push", ofType:"mp3")!
let url = URL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOf: url)
self.myAudio = sound
sound.numberOfLoops = 1
sound.prepareToPlay()
sound.play()
} catch {
print("error loading file")
// couldn't load file :(
}
}
Furthermore you can use SwiftySound that lets you play sounds easily in Swift 3.
for example
Sound.play(file: "push.mp3")

decode morse code by character swift

I am trying to make a morse code converter in a swift playground. I got the conversion to work, but I need to make the code "speak" with AVFoundation. How can I decode the morse code string to play the short beep for every '.' and the long beep for every '-'?
Here's my code so far:
func speakTheCode(message: String) {
var speaker = AVAudioPlayer()
let longBeep = URL(fileURLWithPath: Bundle.main.path(forResource: "beep_long", ofType: "mp3")!)
let shortBeep = URL(fileURLWithPath: Bundle.main.path(forResource: "beep_short", ofType: "mp3")!)
try! speaker = AVAudioPlayer(contentsOf: longBeep)
try! speaker = AVAudioPlayer(contentsOf: shortBeep)
speaker.prepareToPlay()
}
Just try to decode the string to the correspondingly audio.
func speakTheCode(message: String) {
var audioItems: [AVPlayerItem] = []
guard let longPath = Bundle.main.path(forResource: "beep_long", ofType: "mp3"),
let shortPath = Bundle.main.path(forResource: "beep_short", ofType: "mp3") else {
print("Path is not availabel")
return
}
let longBeep = AVPlayerItem(url: URL(fileURLWithPath: longPath))
let shortBeep = AVPlayerItem(url: URL(fileURLWithPath: shortPath))
for character in message.characters {
if character == Character("-") {
audioItems.append(longBeep)
} else if character == Character(".") {
audioItems.append(shortBeep)
}
}
let player = AVQueuePlayer(items: audioItems)
player.play()
}
speakTheCode(message: "..--..")

Game crashing when i run this function?

It's meant to be sound whenever the player collects a coin, but instead as soon as the character touches the coin the game freezes and exits.
func playTap() {
if let url = Bundle.main.url(forResource: "tap", withExtension: "caf"){
tap = try AVAudioPlayer(contentsOf: url)
guard let tap = tap else { return }
tap.prepareToPlay()
tap.play()
} else {
print(error.localizedDescription)
}
}
I'm guessing your code is crashing at since you are trying to FORCE UNWRAP a null optional with the ! at the end of the line.
let url = Bundle.main.url(forResource: "tap", withExtension: "caf")!
Try
if let url = Bundle.main.url(forResource: "tap", withExtension: "caf"){
//Code here
} else {
}
or
guard let url = Bundle.main.url(forResource: "tap", withExtension: "caf") else {}
EDIT
Spell check key values!!