How can I pause SCNAudioPlayer playback in ARKit? - ios

I'm trying to implement a play/pause button for positional audio attached to a node in an ARKit scene. I want the user to be able to pause the audio, then resume it from the paused point at a later time. So removeAudioPlayer() is no good, as this resets the audio back to the start when it is reattached.
The only pause method associated with SCNAudioPlayers that I can find is buried deep down in:
myAudioPlayer.audioNode?.engine?.pause()
But this doesn't seem to have any effect. Strangely
myAudioPlayer.audioNode?.engine?.stop()
Does stop the audio from playing, but then you can't start it again afterwards!
My code for creating the audioPlayer is as follows:
audioSource = SCNAudioSource(fileNamed: "audio.caf")!
audioSource.loops = false
audioSource.isPositional = true
audioSource.shouldStream = false
audioSource.load()
// then a bit later...
audioPlayer = SCNAudioPlayer(source: audioSource)
myARNode.addAudioPlayer(audioPlayer)
Notice that no 'play' method is needed. The audio just starts playing as soon as you attach it to the node. And the lack of straight play() and pause() methods as top-level properties of SCNAudioPlayer makes me wonder if it's possible at all. This seems slightly crazy however, as it's a pretty rudimentary aspect of working with audio.
Does anyone know if this can be done?
Thanks in advance.

To stop audio engine use 2 sec delay. Pause doesn't work here (I'm using Xcode 12.2).
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
player.audioNode?.engine?.stop()
}

Related

Best way to play silence using AVAudioPlayer on iOS

I found myself in a situation where I need to simulate audio playback to trick OS controls and MPNowPlayingInfoCenter into thinking that an audio is being played. This is because I am building a player that plays multiple audio tracks, with pauses in-between creating one, continuous "audio" track. I have already everything setup inside the app itself, and the lock screen controls are working correctly but the only problem I am facing is while the actual audio stops and a pause is being "played", the lock screen info center stops the timer, and it only continues with showing correct time and overall state once another audio track starts playing.
Here is the example of my audio track built from audio files and pause items:
let items: [AudioItem] = [
.audio("part-1.mp3"),
.pause(duration: 5), // value of type: TimeInterval
.audio("part-2.mp3"),
.pause(duration: 3),
... // the list goes on
]
then in my custom player, once AVAudioPlayer finishes its job with current item, I get the next one from the array and play either a .pause with a scheduled Timer or another .audio with AVAudioPlayer.
extension Player: AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
playNextItem()
}
}
And here lies the problem, once the AVAudioPlayer stops, the Now Playing info center automatically stops too, even tho I keep feeding it fresh nowPlayingInfo. Then when it hits another .audio item, it resumes correctly and shows current time, etc.
And here lies the question
how do I trick the MPNowPlayingInfoCenter into thinking that audio is being played while I "play" my .pause item?
I realise that it may still not be clear, what I am trying to achieve but I am happy to share more insight if needed. Thanks!
Some solutions I am currently thinking about:
A. Keeping 1s long empty audio track that would play on loop for as long as the pause is needed to play.
B. Creating programatically empty audio track with appropriate lenght and playing it instead of using Timer for keeping track of pause duration/progress and relying completely on AVAudioPlayer for both .audio and .pause items. Not sure this is possible though.
C. Maybe there is a way to tell the MPNowPlayingInfoCenter that the audio keeps playing without the need of using AVAudioPlayer but some API I am not familiar with?
AVAudioPlayer is probably the wrong tool here. You want AVAudioPlayerNode, which is slightly lower-level. Create an AVAudioEngine, and attach an AVAudioPlayerNode. You can then call scheduleFile(_:at:completionHandler:) to play the audio at the times you want.
Much of the Apple documentation on AVAudioEngine appears broken right this moment, but the links hopefully will be available again shortly in the links for Audio Engine Building Blocks. (If it stays down and you have trouble finding docs, leave a comment and I'll hunt down the WWDC videos and other tutorials on using AVAudioEngine. It's not particularly difficult for simple problems.)
If you know in advance how you want to compose these items (and it looks like you may), see also AVMutableComposition, which lets you glue together assets very efficiently, including adding empty segments of silence. See Media Composition and Editing for the various tools in that space.

First time audio playing getting slow my Sprite Kit game?

I'm new with Sprite Kit, I have a short sound effect to play in a function. I have tried AVFoundation and SKAudioNode like below in that function. But I discovered It triggers some stuck for my SKActions. How Can I solve this playing problem. It looks waiting for completion or something different?
let audioNode = SKAudioNode(fileNamed: "catch")
audioNode.autoplayLooped = false
self.addChild(audioNode)
let playAction = SKAction.play()
audioNode.run(playAction)
There is another action for playing sound file,
you can use
SKAction.playSoundFileNamed("catch", waitForCompletion: false)
instead of SKAction.play().
There is a complete explanation of what waitForCompletion is doing in Apple Documentation

SpriteKit background music not looping forever

I'm making a simple SpriteKit game with two scenes, and I want the background music to loop unconditionally through both scenes. Right now, I'm using
if soundIsPlaying == false {
runAction(SKAction.repeatActionForever(backgroundMusicEffect), withKey: "backgroundMusic")
soundIsPlaying = true
}
in my menu scene where backgroundMusicEffect is a global variable
let backgroundMusicEffect = SKAction.playSoundFileNamed("content/divertimentoK131.mp3", waitForCompletion: true)
When I play my game, the music never loops. It always stops after one play. If I remove the if-else statement, the music plays over itself every time I reenter the menu.
Is there a better way to play background music? What am I doing wrong?
I believe that the way you are doing it, after the first time looping, every iteration after is "complete" since the sound is finished. You would need to create a new SKAction instance every time if you want to get this to loop.
This of course is a bad idea, since playSoundFileNamed is designed to only play a sound file once with as little over head as possible.
As #Alessandro Omano has commented, use SKAudioNode to get sound playing in a loop. This is my preferred way of doing in, but you limit yourself to >= iOS 9 users.
If you have to support iOS 8 users (At this point I would say why bother) then you need to look into the AVFoundation sections of the libs to get audio going, or use OpenAL.

Audio Start Delay For The First Time - ios Swift

I am creating an application with five buttons. When I click on each button each audio will play. It is working. Now my problem is when I click first button audio play with 1 second delay(App stuck for 1 second) and play. Next time clicks a button audio play without any delay. What could be the issue here?
I am using the following code to play an audio
var currentAudio = try? AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("sample_audio", ofType: "mp3")!));
currentAudio!.stop()
currentAudio!.currentTime = 0
currentAudio!.play();
Please someone help me to finds this issue.
You could use AVAudioPlayer's .prepareToPlay() method to preload the player's buffers, it will increase AVAudioPlayer's performance (faster start).
The idea is to prepare the player some time before actually playing it:
currentAudio?.prepareToPlay()
then later, in your play function, it will start immediately:
currentAudio?.play()

AVFoundation AVAudioPlayer stop is pausing - Objective-C

It's fairly hard to show code for this as it is not noticeable in the code. I have some actions like -(IBAction)PlaySound1:(id)sender; and -(IBAction)PlaySound2:(id)sender; and audio files. I have the sounds working all fine. I also have -(void)Stop;. When Play sound 1 is pressed it calls the method Stop and stops sound 2. and the same with sound 2.
The problem I am encountering is when you play sound 1 and stop sound 2, when you play sound 2 it starts from where it left off.
Inside my (void) I have [PlaySound1 stop] //and [PlaySound2 stop]
If anyone could help that would be great. If something was unclear, just comment on this and I'll try and explain it better.
Thanks :)
Stopping an AVAudioPlayer does not reset the play head.
The stop method does not reset the value of the currentTime property to 0. In other words, if you call stop during playback and then call play, playback resumes at the point where it left off.
To resume playback from the start set the currentTime property to 0.0 before calling play.
example -
playSound2.currentTime = 0.0;
[playSound2 play];

Resources