9

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? :'(

SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
JGrn84
  • 750
  • 1
  • 9
  • 26
  • I have provided the full function that's causing the problem here: http://stackoverflow.com/questions/32723431/skaction-playsoundfilenamed-not-working-in-ios-9 – JGrn84 Sep 24 '15 at 18:25
  • Don't test in the simulator? – matt Sep 24 '15 at 18:27
  • It doesn't work at all on actual devices. – JGrn84 Sep 24 '15 at 18:30
  • 1
    It is difficult to say. Usually in videogames, for those kind of sounds, you want to avoid any lossy compression format because they take time to decode. Why don't you try to make the "bang" sound into a WAV? I have never seen a videogame with MP3s for short sounds. I am pretty sure that Apple can hardware accelerate the decoding of one audio track but might have issues with multiple at the same time (WAV files don't need decoding). If that does not help, then the issue is somewhere else, maybe SK kills sounds in some circumstances. – Eppilo Nov 11 '15 at 20:35
  • If the code works for other sounds, then I would look at the resource itself. Delete it and re-add it, make sure you Copy items into destination group's folder. – john elemans Nov 13 '15 at 19:40
  • According to other comments and answers, it appears that the mp3 you are using is at fault – SwiftArchitect Nov 18 '15 at 20:29

3 Answers3

8

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.

Community
  • 1
  • 1
Rafał Sroka
  • 39,540
  • 23
  • 113
  • 143
  • I'm not the OP, but I did add the bounty...I'm getting my mp3 as NSData from an external web service, so getting an occasional corrupt file is not out of the question. I do not have the option to switch formats. Any thoughts on how that relates to the error message "ERROR: 177: timed out after 0.012s (735 736); mMajorChangePending=0"? – picciano Nov 18 '15 at 14:42
  • I upvoted this answer. Even if the file format is not the problem, the correct way of debugging is to try this route with one example. This specific error is not documented anywhere, as far as I can tell, but it seems to be connected to the act of playing a sound with any API whatsoever. I think different routes must be explored and this is an obvious one in video game programming (sound engines have limited channels as well as a well documented Apple limitation: audio decoding is limited to one track at a time). – Eppilo Nov 18 '15 at 17:14
4

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.

SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
  • That will just catch the error, not identify the cause, correct? I'm looking for an explanation of the error "ERROR: 177: timed out after 0.012s (735 736); mMajorChangePending=0" and how to prevent it more so than just catching it. Thanks! – picciano Nov 18 '15 at 14:44
  • Have you verified that you do not incorrectly scoped `AVAudioPlayer`? See http://stackoverflow.com/a/32827915/218152 – SwiftArchitect Mar 02 '16 at 22:15
3

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.

Allen
  • 6,505
  • 16
  • 19
  • 1
    Interesting, could you provide a link to documentation that backs this up? Thanks! – picciano Nov 18 '15 at 14:39
  • the problem is APPLE really shy of their issue, so we all just try and resolve the problem from experience, there has no documentation available for the problem. – Allen Nov 19 '15 at 16:58
  • 1
    I beg to differ. It **is** possible to play in the background. You just need to keep the `AVAudioPlayer` reference long enough for the sound to complete. – SwiftArchitect Mar 02 '16 at 22:49
  • 1
    I agree with @SwiftArchitect. The AVAudioPlayer's play method is asynchronous, but seems to work fine from a background thread. – jk7 Jun 09 '16 at 18:55