I have built a simple game in SpriteKit with a sound that plays every time the screen is tapped. It all works fine; however, after a varying period of time the action to play the sound stops and it says the following every time the screen is tapped;
2019-04-19 13:05:46.670138+0200 New Game[2559:608748] SKAction: Error loading sound resource: "engine.mp3"
The game continues to work without the sound but after you complete the level you are on, the game crashes at the next transition as it says;
2019-04-19 13:05:47.044452+0200 New Game[2559:608748] Fatal error: Unexpectedly found nil while unwrapping an Optional value
If I remove the code for the engine sound action, it never crashes and the Optional for GScene! is never nil
If I remove the code for the engine sound action, it never crashes and the Optional is never nil
Sound Action:
//play engine sound
let engineSound = SKAction.playSoundFileNamed("engine.mp3", waitForCompletion: false)
self.run(engineSound)
Transition Code:
GScene?.scaleMode = .aspectFill
let transition = SKTransition.doorsOpenHorizontal(withDuration: 1)
view?.presentScene(GScene!, transition: transition)
Related
I have an WKInterfaceInlineMovie within an WKInterfaceController. The URL of the video is set at some point after the video file is downloaded. Playing works fine besides this problem which is a different story, I think.
Here's the problem. If I keep the screen open, lower my wrist and then raise it again, I can see the same screen and the video starts playing automatically.
It looks very weird and unexpected especially because I have some custom UI (video progress indicator, animated Play/Pause buttons) which is triggered when I manually start the video but it obviously doesn't react on this unwanted automatic video start. If I close the extension with the Crown button, next time I open the app, it again shows the screen with video and start playing automatically. I can even not using the extension for a while and receive a user notification later – while the custom notification UI is displayed, I can hear the video starts playing somewhere below for a short period of time.
When it happens I always receive two messages in console:
<<<< PlayerRemoteXPC >>>> remoteXPCPlaybackItem_NotificationFilter: [0x128b86e0] I/NQB.01 Received kFigPlaybackItemNotification_FirstVideoFrameEnqueued
<<<< PlayerRemoteXPC >>>> remoteXPCItem_handleFirstFrameNotificationLatch: [0x128b86e0] I/NQB.01 Posting kFigPlaybackItemNotification_FirstVideoFrameEnqueued
I have the Autoplay checkmark unchecked in storyboard. I also tried to set setAutoplays(_:) to false programmatically when the outlet is initialized, or later when a URL is set to the movie. All of this makes no difference.
The behavior is the same no matter was the video playing or not when the screen get deactivated. I tried to call pause() on willDisappear() and didDeactivate() – it also doesn't do any difference.
I even tried to call pause() on didAppear() and willActivate() – didn't help either. Curiously enough, these methods are not called when I lower the wrist and raise it again (however, willDisappear() and didDeactivate() are both called.) But perhaps, it's a different story, too.
Using Swift5.2, Xcode11.4 and iOS13.4,
I try to run Audio at a certain time in the future.
Moreover, the App is in background mode (and the App is completely closed).
The Audiosession must be set up to keep the App responsive and the Audio sound shall start at delay-times up to 1 year.
I tried:
A) The Background Mode "Audio, AirPlay and Picture in Picture" is added:
B) The AudioSession and AVAudioPlayer is configured as follows:
import AVKit
var audioPlayer: AVAudioPlayer?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?)
-> Bool {
do {
//set up audio session
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback, mode: .default, options: [.defaultToSpeaker, .duckOthers])
try audioSession.setActive(true)
// Start AVAudioPlayer
try audioPlayer = AVAudioPlayer(contentsOf: alertSound)
audioPlayer!.numberOfLoops = -1 // play endlessly
audioPlayer!.prepareToPlay()
let currentAudioTime = audioPlayer!.deviceCurrentTime
let delayTime: TimeInterval = 20.0 // here as an example, we use 20 seconds delay
audioPlayer!.play(atTime: currentAudioTime + delayTime) // delayTime is the time after which the audio will start
}
catch {
print(error)
}
return true
}
.
There are two problems with the code above:
the session-Category .playback does cause the error Error Domain=NSOSStatusErrorDomain Code=-50 "(null)"
the sound only plays when App is in foreground or in background-with-app-still-alive - but unfortunately not in background-with-app-completely-closed
Here my questions:
Why is session-category .playback not working under iOS13.4 ?
Why is the sound not playing when the app is completely closed ? (background-mode) ???
Is it enough to just set the Background-mode tag "Audio, AirPlay and Picture in Picture" ? Or is there some code needed in order to fully implement background mode ? Is there something missing in code in order to make the sound future-start in a fully closed app ?
As for Question 1:
--> I've found a workaround and set the session-Category to .playAndRecored - after that the error goes away
try audioSession.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker, .duckOthers])
The question still remains: why is .playback not working ? And what is the difference between .playback and .playAndRecord ?
Why is session-category .playback not working under iOS13.4 ?
The .defaultToSpeaker option causes that error when used with .playback. I don't know why. If you remove it, it should work.
Why is the sound not playing when the app is completely closed ? (background-mode) ???
The audio session is killed when the app is killed. If you want to play sounds beyond the lifetime of your app, you need to use remote notifications. See "Push notifications method" in this article on building an alarm clock app:
http://andrewmarinov.com/building-an-alarm-app-on-ios/
Note that there are limitations with this method, mainly that it requires the user to have an internet connection at the alarm time to work. This is the current way to setup an alarm beyond the lifetime of the app, other than sending a simple local notification which has its own limitations on sound playback and is silenced by the hardware silent switch.
There are other methods in that link to keep your app open, but it will notify the user (keeping the mic always on, or subscribing to frequent location updates).
Is it enough to just set the Background-mode tag "Audio, AirPlay and Picture in Picture" ? Or is there some code needed in order to fully implement background mode ? Is there something missing in code in order to make the sound future-start in a fully closed app ?
This background mode keeps your app from being suspended while it has an active audio session. It does not prevent your app from being killed. Once killed, your app can't play any sound until it is opened again.
I am trying to get a background sound file to play in the GameScene.swift file. But whenever I do and call the run.SKAction, the screen turns white and I hear a lot of static until it crashes. When I comment out the run.SKAction the game starts normally. Ive tried changing the formats of the file to WAV, MP3, AAC, CAF. But the same thing happens. I checked to see if I mispelled anything but I did not.
It gives me a message saying: "Message from debugger: Terminated due to memory issue"
IMPORTANT NOTE: the "waitForCompletion is set to TRUE it seems to work fine with a little static at the beginning of the soundtrack then it plays normally. but when "waitForCompletion" is set to FALSE I get the white screen. I Also Sometimes get a message saying:
SKAction: Error loading sound resource: "Entry.m4a"
I tried several different soundtrack files but it always happens.
Here is the beginning of the GameScene():
class GameScene: SKScene {
var SpaceShip2 = SKSpriteNode(imageNamed:"IntroSpaceShip")
var GameSceneSound = SKAction.playSoundFileNamed("BackgroundSound.wav", waitForCompletion: false)
}
Here is the function I used to call the sound:
func playsound(soundVariable: SKAction){
run(SKAction.repeatForever(soundVariable))
}
And here is when I call it:
override func didMove(to view: SKView) {
playsound(soundVariable: GameSceneSound)
}
Can someone please help me!
Thank you in advance!
Since waitForCompletion is set to false, the action is considered to be have been completed immediately when run. Since this is a repeatForever action, the next repetition of the action runs before the the first action of playing the sound has completed. The 2nd repetition runs, immediately causing the 3rd repetition, which causes the 4th one, etc. Eventually you fill up too much memory with all the SKActions and the app is terminated by iOS.
I would suggest having waitForCompletion set to true. That way the 2nd repetition of the action won't start until the 1st action has completed playing the sound.
Hope this helps!
I can't seem to find much information about SKAudioNode. How do i play a sound only once? I do not want to repeat the sound.
What i am trying to accomplish is to play a short laser sound each time a bullet spawns, in spritekit.
Unfortunately, what #KnightOfDragon says is not correct (but I do not have enough reputation to comment).
SKAudioNode has been introduced in iOS 9 and is meant as a replacement for SKAction.playSoundFileNamed(...) as it is much more powerful (You can add it as a child to a SpriteKit SKNode and if the attribute positional is set to true, 3D audio mixing is added automatically).
In order to play a sound once with SKAudioNode use the following code:
if #available(iOS 9, *) {
let pling = SKAudioNode(fileNamed: "pling.wav")
// this is important (or else the scene starts to play the sound in
// an infinite loop right after adding the node to the scene).
pling.autoplayLooped = false
someNode.addChild(pling)
someNode.runAction(SKAction.sequence([
SKAction.waitForDuration(0.5),
SKAction.runBlock {
// this will start playing the pling once.
pling.runAction(SKAction.play())
}
])
}
else {
// do it the old way
}
Update
Have a look here, how to use Actions on audio nodes: SKAction->Audio-Stuff. The documentation says:
Use SKAction playSoundFileNamed:waitForCompletion: only for short incidentals.
Use AVAudioPlayer for long running background music.
This does not mean, that a SKAudioNode should not be used for one-time audio. With playSoundFileNamed you cannot change the volume or pause/stop playback and so on.
if you are trying to do sound effects, you use SKAction.playSoundFileNamed(...) on the sprite that is creating the effect. SKAudioNode is more for having music playing in your game
Example:
//we have ship as an SKSpriteNode
//lets fire laser
ship.runAction(SKAction.playSoundFileNamed("pewpewpew.caf",waitForCompletion:false));
I'm having difficulty pausing the game when I leave and switch back to it.
I'm trying to pause the SKSpriteNode called main, which contains all my sprites, when the view returns from the background. In-game, I can touch the pause button and the game pauses, and the resume button and it resumes.
This is my code:
func didBecomeActive() {
println("didBecomeActive")
main.paused = true
}
The first time this runs is when the app opens for the first time, and everything is paused as it should be. The second time, is when it returns from the background, and suddenly all the animations (SKActions, particles, etc.) start working.
I've confirmed that the method is running, and I've also tried setting main.paused to false and then true, and even self.paused to true. Nothing works.
I'm completely stumped. Anyone know what the issue is here?
Setting self.scene.paused = YES should fix this. I have tried it with a game I am developing and it works fine.
Just set self.scene.paused = YES when the game enters the background, then when it return to the foreground, it should stay paused till you resume it, i.e. set self.scene.paused = NO.