I'm new to swift and can't seem to figure this out. I am trying to have one button act as both a play button which loops audio and as a pause button if clicked again. I've tried using an if/else statement but I keep getting this error "fatal error: unexpectedly found nil while unwrapping an Optional value"
The code works without the if/else statement, however, in that case it loops indefinitely without a way to pause it.
Any suggestions?
var hihat16: AVAudioPlayer!
#IBAction func hat16(_ sender: Any) {
if hihat16.isPlaying == false {
let path = Bundle.main.path(forResource: "Hat 017-1.aif", ofType:nil)!
let url = URL(fileURLWithPath: path)
do {
let sound = try AVAudioPlayer(contentsOf: url)
hihat16 = sound
sound.play()
hihat16.numberOfLoops = -1
} catch {
// couldn't load file :(
}
}else{ hihat16.stop()}
Picture of the code and error message :
Try this.
Do it in your viewDidLoad
let path = Bundle.main.path(forResource: "Hat 017-1.aif", ofType:nil)!
let url = URL(fileURLWithPath: path)
do {
hihat16 = try AVAudioPlayer(contentsOf: url)
hihat16.numberOfLoops = -1
} catch {}
Then in your #IBAction you can do this.
if !hihat16.isPlaying {
hithat16.play()
}
else{ hithat.stop()}
Related
I'm in my first week of coding.
So far the youtube tutorial app I am making in swift is going perfectly apart from the above error message. Here is the code. I have rewritten this twice now exactly as they specify.
I am trying to set up sounds to play with the card game we are making, the sounds play when cards are shuffled, turned around, matched or incorrectly matched.
The error message is shown for "let soundURL" line of code, "Code after 'return' will never be executed".
Please help?
class SoundManager {
static var audioPlayer:AVAudioPlayer?
enum SoundEffect {
case flip
case shuffle
case match
case nomatch
}
static func playSound(_ effect:SoundEffect) {
var soundFilename = ""
// Determine which sound effect we want to play
//and set the appropriate file name
switch effect {
case .flip:
soundFilename = "cardflip"
case .shuffle:
soundFilename = "cardflip"
case .match:
soundFilename = "dingcorrect"
case.nomatch:
soundFilename = "dingwrong"
}
// Get the path to the sound file inside the bundle
let bundlePath = Bundle.main.path(forResource: soundFilename, ofType: "wav")
guard bundlePath != nil else {
print("Couldn't find sound file \(soundFilename) in the bundle")
return
// Create a URL object from this string path
let soundURL = URL(fileURLWithPath: bundlePath!)
do {
// Create audio player object
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
// Play the sound
audioPlayer?.play()
}
catch {
// Could'nt create audio player object, log the error
print("Could'nt create the audio player object for sound file \(soundFilename)")
}
}
}
}
I believe you want your code to look something like this:
// Get the path to the sound file inside the bundle
let bundlePath = Bundle.main.path(forResource: soundFilename, ofType: "wav")
guard bundlePath != nil else {
print("Couldn't find sound file \(soundFilename) in the bundle")
return
}
// Create a URL object from this string path
let soundURL = URL(fileURLWithPath: bundlePath!)
do {
// Create audio player object
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
// Play the sound
audioPlayer?.play()
}
catch {
// Could'nt create audio player object, log the error
print("Could'nt create the audio player object for sound file \(soundFilename)")
}
If there is a guard statement with return inside its brackets, you do not want to put any code beneath the return and within the same brackets. None of the code below the return will execute. In my example, the following code is outside of the brackets, meaning it will execute so long as there is a value for bundlePath.
Code after 'return' will never be executed
var soundFilename = ""
// Determine which sound effect we want to play
//and set the appropriate file name
switch effect {
case .flip:
soundFilename = "cardflip"
case .shuffle:
soundFilename = "cardflip"
case .match:
soundFilename = "dingcorrect"
case.nomatch:
soundFilename = "dingwrong"
}
// Get the path to the sound file inside the bundle
let bundlePath = Bundle.main.path(forResource: soundFilename, ofType: "wav")
guard bundlePath != nil else {
print("Couldn't find sound file \(soundFilename) in the bundle")
return
}
// Create a URL object from this string path
let soundURL = URL(fileURLWithPath: bundlePath!)
do {
// Create audio player object
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
// Play the sound
audioPlayer?.play()
}
catch {
// Could'nt create audio player object, log the error
print("Could'nt create the audio player object for sound file \(soundFilename)")
}
// }
}
I believe that you have misunderstood the guard syntax as it will execute the code when the condition is correct and will fall in else part when the condition is false. So therefore it will use return in else with no other code , So that the method will be terminated gracefully without giving any crash.
The return keyword is used to return something with the method execution or to exit from the method.
So you need to close the else part with “}” and remove one below catch block. Hope this helps.
I have had success playing sounds by pressing a button, by using the following code. However, I'd like to press a button and have that sound play in a loop/infinitely. How is this acheived? I'd also like to acheive pause/play functionality as well. Thanks in advance.
#IBAction func keyPressed(_ sender: UIB`utton) {
playSound(soundName: sender.currentTitle!)
}
func playSound(soundName: String) { //
let url = Bundle.main.url(forResource: soundName, withExtension: "wav")
player = try! AVAudioPlayer(contentsOf: url!)
player.play()
} // End of Play Sound
By default AVAudioPlayer plays its audio from start to finish then stops, but we can control how many times to make it loop by setting its numberOfLoops property. For example, to make your audio play three times in total, you’d write this:
player.numberOfLoops = 3
if you want the infinite loop then use
player.numberOfLoops = -1
for e.g
func playSound(soundName: String) { //
let url = Bundle.main.url(forResource: soundName, withExtension: "wav")
player = try! AVAudioPlayer(contentsOf: url!)
player.numberOfLoops = -1 // set your count here
player.play()
} // End of Play Sound
var audioPlayer: AVAudioPlayer?
func startBackgroundMusic() {
if let bundle = Bundle.main.path(forResource: "Guru_Nanak_Sahib_Ji", ofType: "mp3") {
let backgroundMusic = NSURL(fileURLWithPath: bundle)
do {
audioPlayer = try AVAudioPlayer(contentsOf:backgroundMusic as URL)
guard let audioPlayer = audioPlayer else { return }
audioPlayer.numberOfLoops = -1 // for infinite times
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print(error)
}
}
}
You can retain the player. Then in the player's completion delegate callback, start playing again. Or stop the player at any time, since you've retained a reference to it.
I am trying to play an MP3 file (works when played via VLC/iTunes) when a button is pressed. Here is my code:
var audioPlayer: AVAudioPlayer!
#IBAction func playEpisode(sender: AnyObject) {
println("now playing")
let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
let data: CDEpisode = fetchedResultsController.objectAtIndexPath(indexPath!) as! CDEpisode
var err: NSError?
let url = NSURL(string: data.localPath)
println("The url is \(url)")
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &err)
if audioPlayer == nil {
if let e = err {
println(e.localizedDescription)
}
}
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
audioPlayer.play()
}
Here is the log:
now playing
The url is Optional(file:///var/mobile/Containers/Data/Application/4747A71E-A63F-4EFC-B2DF-8B361361080B/Documents/serial-s01-e12.mp3)
The operation couldn’t be completed. (OSStatus error 2003334207.)
fatal error: unexpectedly found nil while unwrapping an Optional value
The EXC_BREAKPOINT happens on the audioPlayer.delegate = self.
Other threads on StackoOverflow do not help. Any ideas?
Thanks
Edit: I have tried passing a localURL to contentsOfURL (instead of a CDEpisode object) and it still fails.
This is probably caused by trying to load a file that doesn't exist. If that helps someone adding the call to url.checkResourceIsReachable() will log more descriptive message.
Example code:
do {
let url = URL(fileURLWithPath: dbObject.path)
let isReachable = try url.checkResourceIsReachable()
// ... you can set breaking points after that line, and if you stopped at them it means file exist.
} catch let e {
print("couldnt load file \(e.localizedDescription)")
}
It looks like your trying to unwrap a variable that has a nil value. You should safely unwrap your variables to prevent this.
if let data: CDEpisode = fetchedResultsController.objectAtIndexPath(indexPath!) as! CDEpisode
{
var err: NSError?
let url = NSURL(string: data.localPath)
println("The url is \(url)")
//rest of code
}
You will still need to figure out why it is returning nil but this is a safer way to unwrap variables and prevent crashing as there would need to be more context to resolve that issue.
Some questions to look into:
Are you sure the fetchedResultsController is returning an object at
all?
Are you sure it is of CDEpisode?
try this one
var player: AVAudioPlayer = AVAudioPlayer()
#IBAction func playX(_ sender: Any) {
let urlstring = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3"
let url = URL(string: urlstring)
let data = try! Data(contentsOf: url!)
player = try! AVAudioPlayer(data: data)
player.prepareToPlay()
player.volume = 1.0
player.play()
}
You're checking if audioPlayer is nil but then you go on to use it as if it wasn't anyway. You probably want something like:
if audioPlayer == nil {
if let e = err {
println(e.localizedDescription)
}
} else {
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
audioPlayer.play()
}
And do something to actually handle the error case rather than just printing the error.
In my case I was having the same issue and I found out that I needed to set this before start recording
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
Hope it helps anyone
I also got this problem, after checking up the audio file url, found that it is stored in Cache directory. so audio player probably couldn't find audio file according to your "url".
please ensure, the url path is under Document directory.
As others have said, the audio player couldn't find the file.
This helped me: Document directory path change when rebuild application
Basically, you cannot load files with an absolute reference, as the sandbox environment regenerates the absolute file url each time. So you will need to add a small bit of code (see above) to get the correct urls to use.
I want to play an audio file when a key on my custom keyboard is clicked using swift. I've tried this, however, have had no luck. How can I make this work? Is playing a custom audio sound on a keyboard action possible?
func btnPressed(sender: AnyObject)
{
var btn = sender as UIButton
var stringToInsert=""
var proxy = self.textDocumentProxy as UIKeyInput
switch (btn.tag)
{
case 10:
stringToInsert = "a"
if let path = NSBundle.mainBundle().pathForResource("8bit", ofType: "wav") {
audioPlayer = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: path), fileTypeHint: "wav", error: nil)
if let sound = audioPlayer {
sound.prepareToPlay()
sound.play()
println("hello")
}
}
}
proxy.insertText(stringToInsert)
}
}
I'm creating a game using Swift and SpriteKit in Xcode 6.1 and I can't
get AVAudioPlayer to initialize. I have an SKScene for my menu, and I
want to run music in my "Sounds" folder called "menu.mp3." However, it
crashes every time I run my game. My code is below:
let filePath:NSURL = NSURL(fileURLWithPath: "menu.mp3")!
var er:NSError?
let audioPlayer:AVAudioPlayer = AVAudioPlayer(contentsOfURL: filePath, error: &er)
if (er != nil) {
} else {
audioPlayer.play()
}
I put this in my initializer right after my super.init call. The error I get follows:
fatal error: unexpectedly found nil while unwrapping an Optional value
I've tried a lot of fixes, but I can't figure out what's going wrong.
I've added the AVFoundation framework and imported it to this scene,
but it continues to crash. Is there something wrong with my filePath
that makes my audioPlayer not initialize or something? I've tried
switching to other ways and making different parts optional, but I
can't get it to work. Thanks!
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as NSURL
func preparePlayer(name:String){
var error:NSError?
let fileUrl = documentsUrl.URLByAppendingPathComponent(name)
// or
// let fileUrl = NSBundle.mainBundle().bundleURL.URLByAppendingPathComponent(name)
let audioPlayer = AVAudioPlayer(contentsOfURL: fileUrl, error: &error)
if error == nil {
audioPlayer.play()
}
}
override func viewDidLoad() {
super.viewDidLoad()
preparePlayer("menu.mp3")
}