iphone-loading sound files quickly - ios

I have a space shooter game I am making. I want to play a sound every time a user hits the button fire. This is a short .wav file of about half a second in length. Also, when the user dies, I have an explosion .mp3 file to play. Since the fire button can be clicked very rapidly, I need to play sounds quickly. I used some avaudio player code previously where I make a new audioplayer every time I need to fire, but this crashes occasionally if I fire very quickly. Any suggestions on ways to play sound files very quickly? (including some example code in you answer would be great)

if you using MPMediaPlayback you can try prepareToPlay

Related

AVAudioPlayer not playing after a while when app is in background

I am making an alarm clock app. To play the alarm sound at the appropriate time, I use myAudioPlayer.play(atTime: myAudioPlayer.deviceCurrentTime + secondsUntilAlarm). This way, even if the app is in the background, the audio player plays the alarm sound at the appropriate time.
Note: I got this idea from a different SO answer, which unfortunately I can't seem to find right now.
However, what I've noticed is that alarms are being played correctly if the secondUntilAlarm value is relatively soon, like maybe 20 minutes or less (converted to seconds of course since that's what the method requires). However, if it's longer than that, the sound does not play. Is there something I'm missing with how this method works in the background? Could the app be entering some sort of suspended state or something that disables the audio player from triggering the playback?
Any suggestions or comments would be appreciated--thanks!
So I've actually determined that it does not matter if the app enters the suspended state (in fact, this is expected). I've identified that the reason the alarm sound does not play sometimes because I open another app with audio that deactivates my app's audio session (therefore, it has nothing to do with the time the alarm is set for). To fix this, all I had to do was ensure I set .mixWithOthers for my app's audio session. That way, other audio sessions from other apps don't deactivate mine!

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.

iOS AVAudioPlayer play sound again

Hey I have a couple of AVAudioPlayers containing one sound each. If I press the same button a couple of times, it should repeat the sound from the beginning. If I press another button afterwards, the running sound shall be stopped in order to "make room" for the new one.
The code I am using for that:
-(void) plays:(int)p{ // p is the index of the sound being triggered
if([players[p] isPlaying])
{ // setting the time back to 0 makes
players[p].currentTime = 0.0; // the player automatically play again
}
else
{
[players[p] play]; // if not playing, start playing
}
if(last!=p)
{ // if the last sound is different from the current
[players[last] stop]; // stop the last one
players[last].currentTime = 0.0;} // put its position back to 0
last=p; // set the 'last' variable
}
However, hitting the same button again ends up in a little delay (maybe 20ms) in which no sound is heard. This is the time, the AVAudioPlayer seems to need to "rewind" the track in order to play it again. One Idea to get around this would be to create multiple objects of AVAudioPlayer for each sound but that'd make some awful code! Any ideas on how to make this process quicker?
Thanks, Alex
EDIT: playing 2 different sounds works perfectly fine, I can't hear any delay in between them as I prepareToPlay all the sounds beforehand.
I know how to eliminate the 20ms gap, but first consider if you want to.
Imagine if you jumped immediately from the mid-point of the sound file to the beginning with no gap at all. Better yet, download Audacity and hear how it sounds. Because of the discontinuity, you are going to get an unpleasant crackling or pop sound. Perhaps that 1 fiftieth of a second of silence actually sounds better than immediately restarting.
If you want an uninterrupted audio stream, you're going to have to put away the easy AVAudioPlayer interface and build an AUGraph. Of course, this means learning a complex audio interface and learning all about audio formats. And then you have to figure out what data you're going to stuff into your audio stream.
How would you make your loop sounds nice? You might try fading out at the touch point, and then fading back in. Or you could search for the zero crossings at the beginning and end of your loop (zero crossings are the place where the value of your sound wave is 0. They happen all the time in a mono output, but might be harder to find if your output is stereo.) In the end will this sound nicer than the 20 ms of silence? Get out Audacity and experiment with looping before you enter the long road of learning the AUGraph interace.
To the same song, how about stopping it, then prepareToPlay and play?

Use javascript to detect if a youtube video is having trouble loading and playing

Is it possible, using javascript, to detect if an embedded youtube video pauses playback in order to let the video buffer? I know that there are events that fire when the user presses pause, but I'm looking for an event that fires when the video pauses due to a slow connection. I'm creating a web application where it's important to have the video play through smoothly. If the video pauses due to a slow connection, I want to detect that.
Use this code player.getPlayerState():Number it seems like you are allowed to ask the player what status it is in so this may help you
https://developers.google.com/youtube/js_api_reference
There is also a state 'buffering' being fired when the player needs to buffer more data..in that case the video stops. I guess also 'error' state might be of help.
The solution I worked out is just to use the javascript API's onStateChange callback (https://developers.google.com/youtube/js_api_reference) to detect when the player is started for the first time and when it finishes playing at the end. When the player is started, I grab the current time. When it finishes, it sees how much time has elapsed. In my application, the user cannot pause the video, so comparing the elapsed time to the video length indicates if it paused for loading.

MPMusicPlayerController: combine iPodMusicPlayer and applicationMusicPlayer to prevent continuing to next track

I have a bit of a strange problem. I have a music app that uses the [MPMusicPlayerController iPodMusicPlayer]. Everything is fine, notifications are fired for track changes and changes in playback state.
I have one screen where the user needs to review one single song, I don't want him to go on to the next song in his queue. Since there is no delegate method for when a track WILL change (only DID change), to prevent the music player from continuing to the next track I use a new [MPMusicPlayerController applicationMusicPlayer], give it iPodMusicPlayer's currently playing song and all is well. No new tracks to continue to, and I'm not touching the original iPodMusicPlayer queue so in theory, when I close this screen and use the iPodMusicPlayer again, all should be perfectly fine.
However, when the user is done on this screen and closes it, iPodMusicPlayer is now suddenly broken, notifications are not called and when I put the app to the background, music stops playing, causing me to believe that iPodMusicPlayer is now actually applicationMusicPlayer.
Okay so my question is basically: I need a way to prevent the music player to continue on to the next track in the queue. Switching to applicationMusicPlayer with one track seems to break stuff, as explained above. What's the best solution?
EDIT: because this might be a bit difficult to understand, I created a small project to show the problem: https://github.com/kevinrenskers/MPMusicPlayerControllerTest. Open the app while music is playing, see that the play button behaves correctly. Now open the popup, close it again and the play button is broken.
I found a solution to my problem: set the repeatMode to MPMusicRepeatModeOne and then catch the MPMusicPlayerControllerNowPlayingItemDidChangeNotification notification. You can stop the playback and you never continue to the next track. Once I'm done with the second screen I reset the repeatMode to the original value.

Resources