Play continuous short sound without lagging UI animation - ios

So here is the story.
I have a timer and timer.setEventHandler renders animation (something like adding 10 circles in the UI every 125ms) when I tap a button. The animation runs smoothly and so far so good.
Now I want to add sound (the sound plays 10mm like a beep but from a mp3 file) every time when the circle is added. I tried 2 approaches:
AudioServicesPlaySystemSound
The Animation is smooth, but the sound is not played evenly. At least the sound playing doesn't lag the UI whatsoever.
Use AVAudioPlay
I prepared the AVAudioPlay instance in a singleton class and every time the animation logic calls play() in a DispatchedQueue to make sure the sound is played in a separate thread. The first time when the button is pressed, the animation is lagged. However, when I press the button again shortly (within 2 seconds) when the animation is done, the animation and sound will be played perfectly. However, if I wait for longer time like 10 seconds since the last sound was played, the animation will be lagged and sound is kind of bursting out.
I am wondering if iOS (I am running iOS 10 on iPad Air 2) actually put AVAudioPlay instances on sleep when it is not active. Unless I press the button within the short time (2 seconds) since the last sound was played, the AVAudioPlay thread will be active all the time.
Any idea?

I figured it out.
Use a timer to play sound at 0 volumn every 1 second forever to keep the audio session alive. It sounds stupid but it works like a charm!

Related

AVAudioRecorder stops after certain time on background

I'm using AVAudioRecorder to record audio on my Swift app. It takes 30 seconds clip, do some things with that clip, deletes the clip and then it starts the recording of 30 seconds on a new clip, and it goes on. The app works fine doing that on the background, I have the audio background mode enabled. The problem that I'm having is that, when it passes some time (probably like 90 minutes or something like that), the recording suddenly stops and it does not continue recording. I handle the interruptions in my app fine, so when the recording stops for some reason, I get a notification telling me that the recording has been stopped; but when it passes about 90 minutes, the recording just stops without giving me any alerts or something.
In fact, I enable the recording with an UISwitch. If the recording stops normally due to some interruption, the switch changes to 'off'. But when the recording stops when about 90 minutes has passed, the switch does not change and it is like the app doesn't get notified about the recording session being canceled.
I do not know if this is due to some time restriction when I use a recording on the background or something like that. I could't find any documentation about this problem, so that's why I'm asking for help here.

remove pop/click when stopping audiounit

When i call AudioOutputUnitStop, I hear an audible pop/click. Is there a way to prevent this? One solution I had in mind was to set a flag as soon as the user pauses. This flag would cause the audio to keep running but it would fade out. Then it would reset the playhead to 0.1s before where the audio was paused. Then, when the user hits play it would fade in in that tenth of a second. Is there an easier way?
EDIT:
I ended up implementing what I suggested and it works fine though I changed the delay to 0.02s to make it less noticeable. I actually don't call AudioOutputUnitStop at all anymore but I just fill the buffer with 0's when the user hits pause. There's less lag this way when starting to play again.

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?

iphone-loading sound files quickly

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

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