Swift SpriteKit playing audio in the background - ios

I have an app which needs to play audio in the background...
Is this possible using Swift and SpriteKit with SKActions...
Or is it possible another way..
A nudge in the right direction would be very helpful.

SKAction is really easy to use with sounds, but sometimes you might want to do more.
In that case, you would want to use AVAudioPlayerinstead of it.
In order to not write your own "player", I suggest you to use an existing one. Here is one I've already used (SKTAudio) : https://github.com/raywenderlich/SKTUtils/blob/master/SKTUtils/SKTAudio.swift
Here is how to use it :
// For background audio (playing continuously)
SKTAudio.sharedInstance().playBackgroundMusic("yourBackgroundMusic.mp3") // Start the music
SKTAudio.sharedInstance().pauseBackgroundMusic() // Pause the music
SKTAudio.sharedInstance().resumeBackgroundMusic() // Resume the music
// For short sounds
SKTAudio.sharedInstance().playSoundEffect("sound.wav") // Play the sound once
As you can see, you'll be able to either play short sound (as you might already have done with SKAction) and even background music that will play in loop as you're looking for.

After trying out a few things I stumbled upon a reasonable solution. For playing music in a loop you can set up an SKAudioNode and set the AutoPlayedLoop = true. Then call the "play music" method in when Scene loads.
Now, if you want a sound effect you can use an SKAudioNode for that as well. I found that the sound effect started repeating and so to counteract that I simply created a play-stop sequence. Essentially, every time the sound effect was triggered it would run this sequence, hence, it only sounded once, as intended. There was a cut off to the sound effect however, so I created a simple colour shade method for about 1 second and added this to the sequence. Now the sequence produced was play-shade-stop. Every time the sound effect ran this sequence the dormant shade action would give it a 1 second lag and allow it to play fully and then it would stop as intended. Based on the sound effect you can use the dormant shade action to compensate as required, sometimes longer, sometimes shorter.
Well, here is the code. It's pretty straightforward and you don't need to create a whole AVPlayer class for it.
func playerhitfx() {
let fx:SKAudioNode = SKAudioNode(fileNamed: "playerfx")
let play: SKAction = SKAction.play()
let stop: SKAction = SKAction.stop()
let shade: SKAction = SKAction.colorize(with: UIColor.clear, colorBlendFactor: 1, duration: 1)
let volume: SKAction = SKAction.changeVolume(to: 3, duration: 0)
let seq: SKAction = SKAction.sequence([play,shade,stop])
fx.run(seq)
fx.run(volume)
self.addChild(fx)
}
Furthermore, you can adjust the volume properties having all audio as AudioNodes, to create a good audio mix between the music in the background and the sound effects and also to pronounce certain other effects over others. Hope this helps.
By the way, you can't cast an SKAudioNode into an AVAudioNode, just to bare in mind.

Related

Swift: How to play short sounds longer

I'm working on a SpriteKit project using Swift and I wanted to play my 1 sec long sound longer. For instance, the sound file is "beep" - I want it to be extended to something like "beeeep" or "beeeeeeeeep". I'm tried several options to play a sound file, but I can't find a way to extend it.
Currently, I'm using SKAction to play sound:
var beep = SKAction.playSoundFileNamed("beep.caf", waitForCompletion: false)
runAction(beep)
Anyone knows how to do this?
AVAudioPlayer has a rate property which lets you play at half to double speed. If half works, you can use that.
If that does not work, you can use AVComposition to create a composite track from your sound file. You can select portions of the sound file and stitch them together anyway you like.

Playing a sound while drawing a line with Sprite Kit

I am looking into making a drawing app with Sprite Kit in iOS, with either swift or objective C.
There is a tutorial here that shows how to draw a line with sprite kit. This is great, but for my app I want more. I want the app to play sound effects while the line is being drawn. The tone of the sound will depend on the speed at which the user is drawing the line. The faster the user moves their finger, the higher pitch the sound is. All i have found regarding playing sounds in sprite kit is background music and playing a single sound. Can someone point me in the right direction to accomplish my goal?
You'll probably want to have a chromatic set of samples, ranging from the lowest to the highest possible tone you want to play, at a very short duration (maybe 0.25 seconds? You'll need to experiment, but they will all have to be the same duration).
Let's say you have 30 different samples, line-0.wav .. line-29.wav. Then all you need to do is calculate the velocity of the user's drag appropriately, and use some function to map the possible range of drag velocity to the integer range of sample indices. As long as the drag is in progress, play the appropriate sound repeatedly.

Playing a sequence of audio clips in the background after a delay

I would like to schedule a series of local audio tracks to play in sequence while my app is backgrounded or the device is locked, and after a delay. Furthermore, each track should only play a fixed duration sample. For example:
User presses a button in my app.
User locks device.
After x minutes, tracks A, B, and C each play for y seconds in sequence.
How might I accomplish this?
My current Best Hope is to schedule these in an AVPlayerQueue since if I set up the queue correctly the background audio should then 'just work'. But I don't see any way to set a duration for each AVPlayerItem. I also don't know how to set an initial delay, though I would consider using looping a silent audio clip if that is the only obstacle.
If you use AudioKit, something like will be easy to accomplish. Have a look at AKClipPlayer or AKPlayer. Both classes support scheduling and start/end time.

Playing Motor Sound Effects for iOS SpriteKit Game... AVAudioPlayer vs SKAction vs?

I am developing a driving game using SpriteKit and am having trouble with engine sound effects.
I want to have two different engine sounds. One for when the throttle button is being pressed and one for when the throttle button is not being pressed. One of the two sounds will be playing constantly while the game is going.
What is the best approach? Should my sound files be extremely short (0.10 seconds or less) and looped or should they be fairly long and just turned on and off? Should I use SKAction to play the sounds or AVAudioPlayer or something else? I have tried using AVAudioPlayer but every time I pause and play the player (switching the throttle on or off), the frame rate of the game momentarily drops. Any help is appreciated!
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
I would suggest using AVAudioPlayer. You need to have it in property and not create every time you want to play something.
SKAction is bad in a way that you can't stop or cancel it (even by removing action).

Create a crossfader with AvAudioPlayer

How I can create a crossfader between two sound track with AVAudioplayer?
I must use AVAudiomix? But... how I should use?
AVAudioPlayer does not support using an AVAudioMix. You could try to fade out the playing yourself by directly setting the playback volume but getting the timing right between two AVAudioPlayers will be difficult as AVAudioPlayer is known to have very high and unpredictable latency when starting to play.
One way to accomplish this is to use AVPlayer and AVPlayerItem and AVComposition. You can setup the composition to overlay the two audio files by your desired amount and setup the AVAudioMix fade out the first and fade in the second. This method will let you have precise control over when the audio files play relative to one another.

Resources