Audio not playing after button is clicked - ios

I was following this tutorial on YouTub: https://www.youtube.com/watch?v=qC6DzF_ACpQ&t=714s , and at 12.18 when he clicks the button, it plays audio. I followed all the exact same steps as he did in the video and it is not working for me.
I am pretty new to coding in swift so I did not try anything else other than restarting the whole project again, but it did not work.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var audioPlayer: AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func aTapped(_ sender: Any) {
let url = Bundle.main.url(forResource: "a", withExtension: "mp3")
// Make sure that weve got the url, otherwise abord
guard url != nil else{
return
}
do{
audioPlayer = try AVAudioPlayer(contentsOf: url!)
audioPlayer?.play()
}
catch{
print("error")
}
}
I just want the audio to play when I click the button assigned to the code, but when I click the button, no audio plays at all.
Please Help.

Related

Getting error code without crashes after playing sound in Swift 5

I am very new to swift and programming so in this udemy project I decided to make it without looking to instruction video. So when I open up simulator everything works perfect but I get an error in console and as a perfectionist I want to get rid of it.
My Code Follows:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func keyPressed(_ sender: Any) {
let cSoundUrl = NSURL(fileURLWithPath: Bundle.main.path(forResource: "C", ofType: "wav")!)
do {
audioPlayer = try AVAudioPlayer(contentsOf: cSoundUrl as URL)
}catch{
print("Error was there for C!")
}
audioPlayer.play()
}
And the error follows as this;
2021-07-14 22:03:49.248871+0300 Xylophone[40027:5196447] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x60000214c320> F8BB1C28-BAE8-11D6-9C31-00039315CD46
As I said I am in a beginner state I don't even understand what does AddInstanceForFactory means.
Thanks for any help in advance!

Play music if not playing, but don't if it is already playing (Xcode,Swift4)

I got a question about the AVFoundation and AVAudioPlayer.
In my project I have a view where you start in and a second on where the action happens. When I start the app I told the first view to start playing my background music and it has to continue when the user is in the second view. But whenever you come back to the first view it starts the song again, even though it is already playing. So I end up with a lot of echos.
How can I prevent that from happening?
Here is my code that I have yet:
import UIKit
import AVFoundation
class StartViewController: UIViewController {
var backgroundPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
//background music file
let sound = Bundle.main.path(forResource: "background_music", ofType: "mp3")
do {
backgroundPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: sound!))
}
catch {
print(error)
}
playBackgorundMusic()
}
func playBackgorundMusic() {
if backgroundPlayer.isPlaying {
//do nothing
} else {
backgroundPlayer.play()
}
}
}
You should make a singleton class for Music player to maintain state like play, pause, stop etc.
class MusicPlayerManager{
static let shared = MusicPlayerManager()
var backgroundPlayer = AVAudioPlayer()
init(){}
func playMusic(){
if backgroundPlayer.isPlaying {
//do nothing
} else {
backgroundPlayer.play()
}
}
}

How do I perform segue AFTER the sound has stopped playing

I have made a custom LaunchScreen in the Main.Storyboard to make it seem that a sound is actually coming from the LaunchScreen. The sound works fine and the segue to the next view controller as well. The only problem is that the segue happens before the sound has stopped playing. I would like the sound to complete before making the segue. Logically, it should work since the performSegue is directly after the .play(). But it seems that the two happens simultaneously. Here's my code:
super.viewDidLoad()
//PLAY SOUND CLIP//
let musicFile = Bundle.main.path(forResource: "fanfare", ofType: ".mp3")
do {
try musicSound = AVAudioPlayer(contentsOf: URL (fileURLWithPath: musicFile!))
} catch { print("ERROR PLAYING MUSIC")}
musicSound.play() //WORKS
//
DispatchQueue.main.async() {
self.performSegue(withIdentifier: "myLaunchSegue", sender: self) //WORKS
}
I have tried to add:
perform(Selector(("showNavController")), with: self, afterDelay: 3)
where "showNavController" simply is the segue:
func showNavController() {
performSegue(withIdentifier: "myLaunchSegue", sender: nil)
}
but the program crashes with the error "uncaught exception.... ....unrecognized selector sent to instance"
I have also tried to add a boolean to keep the program from progressing until the sound has played, but didn't get it to work. Any ideas?
//////////////////////////
Update:
Trying Russels answer, but have a few questions. Setting AVAudioPlayer as delegate, does that mean setting it next to the class like this:
class MyLaunchViewController: UIViewController, AVAudioPlayer { ...
Also, how do I call the function audioPlayerDidFinishPlaying? Like so:
audioPlayerDidFinishPlaying(musicSound, successfully: true)
I'll post the whole code block. Makes it easier to understand...
import UIKit
import AVFoundation //FOR SOUND
class MyLaunchViewController: UIViewController, AVAudioPlayer {
var musicSound: AVAudioPlayer = AVAudioPlayer() //FOR SOUND
override func viewDidLoad() {
super.viewDidLoad()
//PLAY SOUND CLIP//
let musicFile = Bundle.main.path(forResource: "fanfare", ofType: ".mp3")
do {
try musicSound = AVAudioPlayer(contentsOf: URL (fileURLWithPath: musicFile!))
} catch { print("ERROR PLAYING MUSIC")}
musicSound.play() //WORKS
//
audioPlayerDidFinishPlaying(musicSound, successfully: true)
}
optional func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
DispatchQueue.main.async() {
self.performSegue(withIdentifier: "myLaunchSegue", sender: self)
}
}
I get an error when writing AVAudioPlayer in the class (perhaps I misunderstood what I was supposed to do?). It says I have multiple inheritances. Also, it doesn't want me to set the new function as optional, as its only for protocol members. Finally, If I correct the errors and run the program, the next segue runs before the sound has finished playing... :( sad panda.
You need to make your LaunchScreen an AVAudioPlayerDelegate, and then use the audioPlayerDidFinishPlaying callback. Here's all you need in the first controller
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioPlayerDelegate
{
var musicSound: AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
//PLAY SOUND CLIP//
let musicFile = Bundle.main.path(forResource: "sound", ofType: ".wav")
do {
try musicSound = AVAudioPlayer(contentsOf: URL (fileURLWithPath: musicFile!))
} catch { print("ERROR PLAYING MUSIC")}
musicSound?.delegate = self
musicSound!.play() //WORKS
print("Next line after play")
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
DispatchQueue.main.async() {
print("audioPlayerDidFinishPlaying")
self.performSegue(withIdentifier: "myLaunchSegue", sender: self)
}
}
}
You can get more details here https://developer.apple.com/documentation/avfoundation/avaudioplayerdelegate/1389160-audioplayerdidfinishplaying

Thread1:EXC_BAD_ACCESS(code=1, address = 0X48)

I'm new in Swift. I am trying to Run this code but got an error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x48) a.
Error appears here:
self.player.play()
Can anyone help in this issue?
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
do {
if let audioPath = Bundle.main.path(forResource: "music", ofType: "mp3") {
try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPath))
}
} catch {
print("ERROR")
}
self.player.play()
}
What you doing with your code is:
Creating an instance of AVAudioPlayer without any data
Trying to create a new instance with some data from music.mp3 file
Playing it
If the audioPath is not correct, then the player is not correctly created: application will use the one without valid data, leading to a crash during playback.
Your code can be written making the player optional, which helps preventing the crash:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
player = initializePlayer()
player?.play()
}
private func initializePlayer() -> AVAudioPlayer? {
guard let path = Bundle.main.path(forResource: "music", ofType: "mp3") else {
return nil
}
return try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
}
}
Some changes performed to the code:
player is now optional: there is no need to initialize it twice. Plus if the initialization goes well you can use it without problems, otherwise your app won't crash.
The play method is now in an optional chain
I've moved the initialization of the player in a separate private methods, to maintain the code readable. Feel free to change it, it is just a suggestion.
I've got it. The issue is that the file isn't being coping to my app bundle. To fix it:
Click your project
Click your target
Select Build Phases
Expand Copy Bundle Resources
Click '+' and select your file.
Thanks Denis Hennessy

Swift - AVAudioPlayer not playing sound

I have a sound called speeding.mp3 and I am trying to make a AVAudioPayer instance with it by doing this in the root of the class:
let speedingAudioPlayer = try! AVAudioPlayer(contentsOfURL: NSBundle.mainBundle().URLForResource("speeding", withExtension: "mp3")!)
Then when I run the app it throws an error but I don't know why this is happening. The speeding.mp3 file is in the Bundle Resources drop down in Build Phases so it should find it fine. If I try to print out the URL in the simulator and paste it into Safari it loads and plays the file like normal. I am importing AVFoundation into the class and the framework into the project.
Use this code snippet to debug your error,
if let URL = NSBundle.mainBundle().URLForResource("speeding", withExtension: "mp3") {
do {
let speedingAudioPlayer = try AVAudioPlayer(contentsOfURL: URL)
} catch let error as NSError {
print(error.localizedDescription)
}
} else {
print("No such file exist")
}
I found another question where the var is placed outside the class because otherwise it would be deallocated by the phone.
AVAudioPlayer Object Sound Not Playing - Swift
import UIKit
import AVFoundation
class CustomAudioPlayer {
var audioPlayer : AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
playSound("sound_file_name")
}
func playSound(soundName: String)
{
let alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(soundName, ofType: "mp3")!)
do{
audioPlayer = try AVAudioPlayer(contentsOfURL:alertSound)
audioPlayer.prepareToPlay()
audioPlayer.play()
}catch {
print("Error getting the audio file")
}
}
}
Try this code for playing the sound.

Resources