Sprite Kit & playing sound leads to app termination
The problem is AVAudioSession
can't be active while the app enters background. This isn't immediately obvious because Sprite Kit makes no mention that it uses AVAudioSession internally.
The fix is quite simple, and also applies to ObjectAL => set the AVAudioSession
to inactive while the app is in background, and reactivate the audio session when the app enters foreground.
A simplified AppDelegate with this fix looks like so:
#import
...
- (void)applicationWillResignActive:(UIApplication *)application
{
// prevent audio crash
[[AVAudioSession sharedInstance] setActive:NO error:nil];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// prevent audio crash
[[AVAudioSession sharedInstance] setActive:NO error:nil];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// resume audio
[[AVAudioSession sharedInstance] setActive:YES error:nil];
}
PS: this fix will be included in Kobold Kit v7.0.3.
SpriteKit app crashes when playing a sound after re-entering a scene
I was able to fix this issue by creating a strong property for the sound action like this
@property (strong, nonatomic) SKAction *playMySound;
and initialised it like this
self.playMySound = [SKAction playSoundFileNamed:@"mySound.mp3" waitForCompletion:NO];
After that I had no more issues with the sound crashes. In addition it gave me a path for preloading them before they were played.
Action that runs audio file in Sprite Kit stops running after applicationWillResignActive: is called
Running audio with SKAction is not reliable when it comes to interruptions which send your app into the background such as phone calls, pressing the home button, etc...
The solution is to use an AVAudioPlayer instead for anything other than short lived sound effects.
You would have to:
- Only use AVAudioPlayer for all your audio as it is designed and tested to handle interruptions.
- Use NSData as a sound container to be used with AVAudioPlayer (optional).
- Each sound should have its own temporary AVAudioPlayer instance for sound lifetime only.
There is a detailed explanation of how to do this on iKnowSomething.com which deals specifically with:
SpriteKit app using [SKAction playSoundFileNamed:] cause crash on app exit or going into background.
Deactivating AVAudioSession in AppDelegate applicationDidEnterBackground: fails with error (no deactivation in effect): Error Domain=NSOSStatusErrorDomain Code=560030580 "The operation couldn’t be completed. (OSStatus error 560030580.)
SpriteKit app using [SKAction playSoundFileNamed:] on AVAudioSession interruption (e.g. call arrived) cannot be reactivated (silence).
How to implement AVAudioPlayer in place of SpriteKit [SKAction playSoundFileNamed:]?
LearnCocos2D also wrote an excellent answer on the issue of preventing crashes when an app goes into the background and an AVAudioPlayer is being used.
#import
...
- (void)applicationWillResignActive:(UIApplication *)application
{
// prevent audio crash
[[AVAudioSession sharedInstance] setActive:NO error:nil];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// prevent audio crash
[[AVAudioSession sharedInstance] setActive:NO error:nil];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// resume audio
[[AVAudioSession sharedInstance] setActive:YES error:nil];
}
His original answer is here.
Handling interruptions in Sprite Kit - can't get sound effects via [SKAction playSoundFileNamed: to work after interruption
From various discussions this seems to be a bug which can show its head on occasion. The short of it is that if you are experiencing these issues, and it appears you are, it comes down to skipping the SKAction playSoundFileName
and going purely with AVAudioPlayer.
You would have to:
- Only use AVAudioPlayer for all your audio as it is designed and tested to handle interruptions.
- Use NSData as a sound container to be used with AVAudioPlayer
- Each sound should have its own temporary AVAudioPlayer instance for sound lifetime only.
There is a detailed explanation of how to do this on http://iknowsomething.com/ios-sdk-spritekit-sound/
Play sound in Sprite Kit
Delete the file from your project and add it again.
Remember to select
Copy items into destination group...
And select your project as a target (not test).
Playing Motor Sound Effects for iOS SpriteKit Game... AVAudioPlayer vs SKAction vs?
Based on the comment left by LearnCocos2D, I looked into ObjectAL. For those not familiar with ObjectAL
, it is designed to be a simple and intuitive interface to OpenAL
and AVAudioPlayer
. You can download and find more information about it here...
http://kstenerud.github.io/ObjectAL-for-iPhone/index.html
For my audio clip I used 3 to 5 second long .caf audio file of a motor sound. ObjectAL
allowed me to continuously loop and vary the pitch of the audio file. By varying the pitch, I could simulate the motor at different speeds.
Below is a sample of the code...
Two member variables or you can set them as properties...
ALBuffer *_buffer;
ALSource *_source;
Method to initialize the motor sound effect...
- (void)initializeSound
{
// We'll let OALSimpleAudio deal with the device and context.
// Since we're not going to use it for playing effects, don't give it any sources.
[OALSimpleAudio sharedInstance].reservedSources = 0;
_source = [ALSource source];
_buffer = [[OpenALManager sharedInstance] bufferFromFile:@"EngineSound.caf"];
_source.pitch = 0.30; // Start at low pitch for engine idle.
[_source play:_buffer loop:YES];
}
Inside my SKScene
's update method I adjust the pitch according to speed.
- (void)update:(CFTimeInterval)currentTime
{
CGFloat enginePitch;
// Code to calculate desired enginePitch value based on vehicle speed.
_source.pitch = enginePitch;
}
Because of an alleged bug in SpriteKit
, I did have an issue with a gpus_ReturnNotPermittedKillClient
EXC_BAD_ACCESS
error when closing the app. I found the fix here...
https://stackoverflow.com/a/19283721/3148272
Any ideas why I'm getting this error in sprite kit when adding sound?
Your sound file sounds/jump.caf
could not be found by your app. Where and how did you add it to your Xcode project?
Related Topics
When to Use Dequeuereusablecellwithidentifier VS Dequeuereusablecellwithidentifier: Forindexpath
Uitapgesturerecognizer Breaks Uitableview Didselectrowatindexpath
Dealing With Different iOS Device Resolutions in Spritekit
How to Get the Day of the Week With Foundation
In Swift, How to Declare a Variable of a Specific Type That Conforms to One or More Protocols
How to Run App in Simulator: Xcode Beta 6 iOS 8
Nslog on Devices in iOS 10/Xcode 8 Seems to Truncate - Why
Detecting the Call Events in Ios
Firebasestorage: How to Delete Directory
How to Disable the Uitableview Selection
A Complete Solution to Locally Validate an In-App Receipts and Bundle Receipts on iOS 7
Why Uitableviewautomaticdimension Not Working
How to Change the Status Bar Background Color and Text Color on iOS 13
Swift 3.1 Deprecates Initialize(). How to Achieve the Same Thing
Adding Custom Fonts to iOS App Finding Their Real Names
iOS Firebase Push Notifications:How to Give Firebase User's Device Token and Send Notification