Playing Sounds Automatically when going to a new view controller - ios

I am trying to make a simple reading flash card ios app for esl students using swift.
How can I take what I know about using buttons to play sounds, and have the sounds play automatically when a new page/view is loaded up on the phone? It seems really simple, but I can't figure it out.
This is what I have for the button
var error: NSERROR?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.preparetoPlay()
audioPlayer.play()
Again this works for the button, but I figure it has something to do with the last 2 lines.
For example: After hitting start there is a picture of a Computer. How I have it now is when you click the computer, a voice says the word, and spells it.
I want it to be really intrinsic in case "Click here" doesn't resonate with the class (these are real esl students!) so I would like to have it automatically play.
Thanks!

Place your code inside the viewDidLoad() method of each view controller as such:
override func viewDidLoad() {
var error: NSERROR?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.preparetoPlay()
audioPlayer.play()
}

Related

Cannot play audio file without app crashing on device running ios 13

I have an app being used by people to receive orders with it needing to make a continuous sound until staff attend to it. It was working for two months then just started crashing a lot. For whatever reason, it runs fine on an iPad but not on iPhones running a recent operating system.
When this bit of code gets called it crashes:
guard let path = Bundle.main.path(forResource: "alert.mp3", ofType: nil) else { return }
let url = URL(fileURLWithPath: path)
do {
self.alertSoundEffect = try AVAudioPlayer(contentsOf: url)
} catch let err {
print("err: \(err)")
}
DispatchQueue.main.async {
self.alertSoundEffect.numberOfLoops = -1
self.alertSoundEffect.prepareToPlay()
self.alertSoundEffect.play()
}
The fix online to declare the alertSoundEffect variable like this:
private var alertSoundEffect : AVAudioPlayer!
has not worked at all.
I tried moving everything but the line:
self.alertSoundEffect.play()
to viewDidLoad as I thought maybe that code couldn't get called more than once, but it didn't help.
Specifically, the compiler highlights this line when it crashes:
self.alertSoundEffect = try AVAudioPlayer(contentsOf: url)
I tried using try AVAudioPlayer where it takes a Data object as a parameter or with including the type of audio file to be played, but that did not change anything.
When I try the AVAudioPlayer's delegate and declare it like this:
self.alertSoundEffect.delegate = self
right before the first lines of code I shared above Xcode highlights this line instead when it reliably crashes.
What else should I try?
I suppose your path is wrong.
Try this:
guard let path = Bundle.main.path(forResource: "alert", ofType: "mp3") else { return }
Also, if your audio file is short, like less than 30s, then try not to call self.alertSoundEffect.prepareToPlay(). Just call self.alertSoundEffect.play() right away.
Since iOS 13, this was causing a bug in my app, since I have notification sounds which are 3-10 seconds long.
If you initialise your AVAudioPlayer like var wrongMusicPlayer: AVAudioPlayer = AVAudioPlayer() OR wrongMusicPlayer = AVAudioPlayer() in any method then please remove it and just Declare like var wrongMusicPlayer: AVAudioPlayer!.
iOS 13.1 Crash in AVAudio Player

AVAudioPlayer sound not playing

In iOS 8/Xcode 6 I had a function that included a sound effect. It no longer works in iOS 9 after changing the code multiple times. This is what I've tried:
Original:
let bangSoundEffect = SKAction.playSoundFileNamed("Bang.mp3", waitForCompletion: false)
runAction(bangSoundEffect)
Other attempt:
self.runAction(SKAction.playSoundFileNamed("Bang.mp3", waitForCompletion: false))
Also:
func playRocketExplosionSound(filename: String) {
let url = NSBundle.mainBundle().URLForResource(
filename, withExtension: nil)
if (url == nil) {
print("Could not find file: \(filename)")
return }
var error: NSError? = nil
do {
backgroundMusicPlayer =
try AVAudioPlayer(contentsOfURL: url!)
} catch let error1 as NSError {
error = error1
backgroundMusicPlayer = nil
}
if backgroundMusicPlayer == nil {
print("Could not create audio player: \(error!)")
return}
backgroundMusicPlayer.numberOfLoops = 1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play() }
playRocketExplosionSound("Bang.mp3")
I'm pulling my hair out. I'm using the same code in a different scene for another sound effect and it works fine!! What's going wrong?
I've noticed that the sound effect begins to play sometimes in the simulator, however it doesn't complete and throws this error:
2015-09-24 19:12:14.554 APPNAME[4982:270835] 19:12:14.553 ERROR: 177: timed out after 0.012s (735 736); mMajorChangePending=0
It doesn't work at all on actual devices.
What is the problem? :'(
Possible problem with MP3 file
The problem is most likely connected with the MP3 file you're using. The code works for other sounds, this suggests that the MP3 file might be corrupted and AVAudioPlayer fails with decoding it. You can try re-encode this file and see if the problem persists. Or, even better, converting it to WAV.
Using WAVs
General rule of the thumb when creating short sound effects for games, is to use WAV unless you really feel you need the trim the fat.
Top-notch games are going for top-of-the-line production quality, so they record and produce assets uncompressed 24bit/48kHz. Titles with slightly lesser ambitions might record and produce in 16/44.1, which is the official standard for CD quality audio.
This has at least two benefits. One is that the sound has a better quality. Second one, the CPU does not have to decode the file to play it.
Corrupt data file | AVAudioPlayer out of scope
1. Corrupt data file
This will ensure you have found the file:
var backgroundMusicPlayer: AVAudioPlayer? = nil
if let url = Bundle.main.url(
forResource: "Bang", withExtension: "mp3") {
do {
try backgroundMusicPlayer = AVAudioPlayer(contentsOf: url)
backgroundMusicPlayer!.play()
} catch {}
}
return nil
2. AVAudioPlayer out of scope
The variable retaining backgroundMusicPlayer must not go out of scope before play() has completed and returns. This is generally achieved by using a class variable:
var backgroundMusicPlayer: AVAudioPlayer? = nil
Don't do this: the following sound will play for, at best, outOfScopeDelay due to the local scope of var audioPlayer.
let outOfScopeDelay = 0.5
do {
var audioPlayer:AVAudioPlayer! // Incorrectly scoped variable
try audioPlayer = AVAudioPlayer(contentsOf: audioRecorder.url)
audioPlayer.play()
Thread.sleep(forTimeInterval: outOfScopeDelay)
} catch {}
► Find this solution on GitHub and additional details on Swift Recipes.
try this:
dispatch_async(dispatch_get_main_queue(), {
(self.playRocketExplosionSound("Bang.mp3")
})
it's no longer safe to play audio in child thread under iOS 9.

AVAudioPlayer Error in Swift?

So I'm trying to play a random audio track from an array when a certain view loads. Everything's fine, but when I go to load that view, I get an error. Here is the class code of the View Controller:
class SoundGameViewController: UIViewController {
var levelOneSounds : [NSURL]!
var levelOneAudioPlayer: AVAudioPlayer! = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
var error : NSError?
levelOneSounds = NSBundle.mainBundle().URLsForResourcesWithExtension("mp3", subdirectory: "Level 1") as! [NSURL]
var randomSound = levelOneSounds[Int(arc4random_uniform(UInt32(levelOneSounds.count)))]
levelOneAudioPlayer = AVAudioPlayer(contentsOfURL: randomSound, error: &error)
if error != nil { println(error!) }
levelOneAudioPlayer.play()
}
The error shows up on the last line, levelOneAudioPlayer.play(). This is the error:
Thread 1: EXC_BAD_INSTRUCTION . . . (code = exc_1386_INVOP, subcode = 0 x 0
However, with one of the sounds, it actually ends up working and plays the sound when the view loads. But with all the other sounds, the view doesn't load at all and I get the error. Sometimes, the view does load but I get no sound and no error. Here is an image of the folders I hold all of the sounds in: http://i.stack.imgur.com/JBdGU.png
I have tried numerous times asking questions on Stack Overflow and research about it, but I just can't seem to get it. I have also looked for many tutorials on debugging, but I guess I'm not experienced enough to really understand what I need to do in this situation. I'm 13, and I don't have Apple Developer for much longer, and this is becoming really bothersome. I'll go as far as sending someone the project if they can fix this error.
I'm new to programming, so I'm sorry if I made an obvious mistake. Thanks!

XCode 7 Swift - Call can throw, it it is not markedwith 'try' and the error is not handled

I'm trying to play music on my iOS application when I click a button and I already know how to do it but this time when I insert the line; audioPlayer = AVAudioPlayer(contentsOfURL: audioPlayerURL, error: nil)it will give me an option to change errorto fileTypeHintwhich I do but then I get the following error; `Call can throw, it it is not markedwith 'try' and the error is not handled'.
I don't really understand because in Xcode 6 I used the word error in that line of code and I had no issues with that and playing music was working. There must be a different or similar way of writing that line since I've upgraded to Xcode 7 Beta 3 but I've searched everywhere to see what's changed.
Would anybody know anything about it?
Error handling has changed in Swift 2.0. Use the following try-catch code to resolve the error:
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: audioFileUrl,
fileTypeHint: AVFileTypeMPEGLayer3)
} catch let error as NSError {
print("AV Sound Error: \(error.localizedDescription)")
}
I had the same issue, after piecing together some information this is what I came up with and it works in Swift 2.0.
override func viewWillLayoutSubviews() {
let bgMusicURL:NSURL = NSBundle.mainBundle().URLForResource("bgmusic", withExtension: "mp3")!
backgroundMusicPlayer = try! AVAudioPlayer(contentsOfURL: bgMusicURL)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
}
Make sure to Import AVFoundation
and also add this in your controller
var backgroundMusicPlayer:AVAudioPlayer = AVAudioPlayer()

how do I set my Audio Session Default Behavior to Playback in swift?

My app has a "Click" sound functionality. I used the
import AVFoundation
then the following function to run the "Click" sound:
var audioPlayer = AVAudioPlayer()
func playSound() {
var soundPath = NSBundle.mainBundle().pathForResource("tick", ofType: "wav")
var soundURL = NSURL.fileURLWithPath(soundPath!)
self.audioPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
self.audioPlayer.play()
}
Now if the user is running a music player, my app causes the music player to stop. I read about the Audio Session Default Behavior in the documentation, but I don't know how to apply it.
Can you please help?
Thank you!
If you are wondering the syntax for swift 2, here it is:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: .DuckOthers)
} catch {
print("AVAudioSession cannot be set: \(error)")
}
Depending on what you want the app to behave, i.e, how your app's sound effect or music should interact with other app's background audio session, you might need to tweak both the audio session category and categoryOption.
If you just want to play the sound effect, like "tick" sound, then, AVAudioSessionCategoryAmbient and DuckOthers should be used respectively, for example:
let audioSession = AVAudioSession.sharedInstance()
var error: NSErrorPointer = nil
audioSession.setCategory(AVAudioSessionCategoryAmbient, withOptions: .DuckOthers, error: error)
However, I suppose you are actually trying to play a sound effect, in this case, the AudioServices API is a more suitable choice. You can check func AudioServicesPlaySystemSound(inSystemSoundID: SystemSoundID) in AudioToolbox framework for more details.
Another common scenario. If you want to have your app to play audio exclusively, even if there're other app's playing the music in the background, you need to set the category to AVAudioSessionCategorySoloAmbient, for example:
let audioSession = AVAudioSession.sharedInstance()
var error: NSErrorPointer = nil
audioSession.setCategory(AVAudioSessionCategorySoloAmbient, error: error)
I hope you've got what you're looking for.

Resources