I am developing an application where the user can tap multiple hit areas which produces sounds.
But the result is a little laggy, when multiple sounds start at the same time, the sounds are played with an ugly delay.
I am using AVAudioPlayer instances for each sound.
Is there a better way to play sounds and prevent this lag?
Here's the code:
#import "MBImageView.h"
#import <AVFoundation/AVFoundation.h>
#implementation MBImageView
-(void)awakeFromNib
{
NSURL* audioFile = [NSURL fileURLWithPath[[NSBundlemainBundle] pathForResource:#"shaker"
ofType:#"caf"]];
AudioServicesCreateSystemSoundID((CFURLRef)audioFile, &shortSound);
}
- (id)initWithImage:(UIImage *)image{
return self;
}
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
AudioServicesPlaySystemSound(shortSound);
}
#end
Regards.
Most of these sounds (AVAudioPlayer and AudioServices) are playing after your runloop ends. That is, you say play and they get queued for playing, and they don't start playing immediately.
If you want lag free sound, you use Audio Unit:
To provide lowest latency audio,
especially when doing simultaneous
input and output (such as for a VoIP
application), use the I/O unit or the
Voice Processing I/O unit. See “Audio
Unit Support in iPhone OS.”
You may also want to look at Audio Toolbox:
Use the Audio Toolbox framework to
play audio with synchronization
capabilities, access packets of
incoming audio, parse audio streams,
convert audio formats, and record
audio with access to individual
packets. For details, see Audio
Toolbox Framework Reference and the
SpeakHere sample code project.
If they're short sounds that you don't mind loading into memory, the C-based System Sound Services might suit you better.
I've used the SoundEffect class in Audio Toolbox with good results. My short sounds play with no delay.
Also one other thing to consider with audio delays; make sure your audio files has no "whitespace" before the actual sound - I beat my head into a wall once looking for the sound delay, only to find it in the actual audio file itself.
Hope this helps.
Related
Is it possible to play some audio files while my movie is playing. I don't want it to interrupt or pause he movie. Just the audio to play at same time at various predetermined points.
Many Thanks,
-Code
You shouldn't have any problems with it.
If you want to synchronize or delay these players you should use
the code they provide in AvAudioPlayer Class Reference using
playAtTime: method.
Sadly MPMediaPlayback protocol doesn't provide the same method
so exact synchronizing with video is a bit harder task.
EDIT: RoLYroLLs mentions using MPMoviePlayerLoadStateDidChangeNotification to achieve this here and this approach seems promising.
Use two or three Audioplayer at same time . Play that audio when u need it
I am making a music game and when the user presses a note it will produce a sound. The sound naturally needs to play immediately when the user presses, so they can tell whether they are in time with the music. However, it feels as if the sound is lagging, especially when note presses become quicker.
My background .m4a music file is played with AVAudioPlayer. I chose to use this over Cocos Denshion as I have access to the currentTime property. I may be wrong, but I dont think I can access this with CocosDenshion.
I made a .wav file which is extremely short (less than a second). I preload my sound effect on init:
[[SimpleAudioEngine sharedEngine] preloadEffect:#"Assist.wav"];
Then to play the sound effect, in CCTouchesBegan I call:
[[SimpleAudioEngine sharedEngine] playEffect:#"Assist.wav"];
After that it calls my code to determine the users timing and awards points. Any idea why it might be lagging, or a better way to play sound effects in time with music?
EDIT: Ive tried a few things recently with no results. First I tried playing the sounds automatically as they came up to the appropriate time in the song. Still had the lag, so I dont think it is touch events being slow. I also tried 3 different sound libraries.
However, when I ran in the simulator, it seemed to not be laggy. Does anyone have an idea? Im clueless and its a major feature I cant really take out...
you should avoid this code:- [[SimpleAudioEngine sharedEngine] preloadEffect:#"Assist.wav"];
with the start of app you should load your framework SimpleAudioEngine by writing this code :-
//SimpleAudioEngine *palySound; made object in .h file.
palySound=[SimpleAudioEngine sharedEngine];
and whenever you want to play sound you can write: [palySound playEffect:#"Assist.wav"];
I am not sure what you're doing in your SoundEngine, but in my own experience, the best way to not get lag to play a sound is to assign an AVAudioPlayer for each sound file (unless you want to start messing around with AudioQueues).
Here it is an example:
Let's assume that you have an AVAudioPlayer *assistPlayer; in your current view controller.
In your viewDidLoad initialize it with your sound:
NSURL *wavURL = [[NSBundle mainBundle] URLForResource:#"Assist" withExtension:#"wav"];
assistPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:wavURL error:nil];
Then, in your IBAction where you want to play the file, just do:
[assistPlayer play];
You shouldn't get any lag.
Did you try Finch? It claims to play sounds with low latency, and it is also just a wrapper around OpenAL.
Other than that, I'm really not experienced with OpenAL, but can think of two possible reasons for your lag:
The main thread is too busy - Try to offload work from it to other
threads.
Perhaps OpenAL is defined with too large of a buffer, so the pipeline loads the entire sound into the buffer (or a big chunk of it), and only afterwards the playback starts.
I have set up an AVAudioPlayer object in viewDidLoad, as per the Apple guidelines, calling prepareToPlay as the last line in viewDidLoad (i have tried in awakeFromNib also).
When i press my play button, there is a pause, as it would appear to load the file, then it plays.
In audioPlayerDidFinishPlaying, i reload the player with a different sound, and when clicking play for a second time, the file plays instantly.
What would cause the player to lag on the first play?
Thanks.
The delay is due to AVAudioPlayer being initialised. Please see this answer.
The audio system runs on several asynchronous software processes (audio units, OS drivers, etc.) and hardware systems (DMA, DACs, audio amp power supplies, etc.) that never really all completely finish initialization until some sound is actually played all the way out the speakers or earphones.
Here's one method to do that: Create a sound file containing a half second of silence. On app start up, while your app and view controller are still loading, use AVAudioPlayer to play this file of silence. Now when your view finishes loading, AVAudioPlayer should be ready to play subsequent non-silent sounds much faster, since some audio (silence) has already already gone all the way out to the speakers.
What kind of sound are you playing? Alerts, something longer? If alerts, I did go this way and it's much better with lags ...
create system sound with AudioServicesCreateSystemSoundID
play system sound with AudioServicesPlaySystemSound
dispose system sound with AudioServicesDisposeSystemSoundID
... you only need to store SystemSoundID for each sound you would like to play.
I'm using AudioServices to play a sound in my app:
AudioServicesCreateSystemSoundID (soundFileURLRef,&soundFileObject );
and then
AudioServicesPlaySystemSound (soundFileObject);
The sound plays but because I need to play it frequently (its 0.24 seconds long) I get a strange unnatural sounding repetition.
You can hear it at http://testing.lukem.co.uk/Simulator/Simulator.html
The other noise is me adjusting the volume - oops.
Thanks!
If you want the most precise control over timing of a sound's repetition, use Audio Queues or Audio Unit RemoteIO, and count samples between where you start filling the callback buffers with your sound's raw/PCM sample array.
I went with open source openAL library Finch.
I have a performance-intensive iPhone game I would like to add sounds to. There seem to be about three main choices: (1) AVAudioPlayer, (2) Audio Queues and (3) OpenAL. I’d hate to write pages of low-level code just to play a sample, so that I would like to use AVAudioPlayer. The problem is that it seems to kill the performace – I’ve done a simple measuring using CFAbsoluteTimeGetCurrent and the play message seems to take somewhere from 9 to 30 ms to finish. That’s quite miserable, considering that 25 ms == 40 fps.
Of course there is the prepareToPlay method that should speed things up. That’s why I wrote a simple class that keeps several AVAudioPlayers at its disposal, prepares them beforehand and then plays the sample using the prepared player. No cigar, still it takes the ~20 ms I mentioned above.
Such performance is unusable for games, so what do you use to play sounds with a decent performance on iPhone? Am I doing something wrong with the AVAudioPlayer? Do you play sounds with Audio Queues? (I’ve written something akin to AVAudioPlayer before 2.2 came out and I would love to spare that experience.) Do you use OpenAL? If yes, is there a simple way to play sounds with OpenAL, or do you have to write pages of code?
Update: Yes, playing sounds with OpenAL is fairly simple.
AVAudioPlayer is very marginal for game audio. Tackling AudioQueue or OpenAL by adapting one of the examples is definitely the way to go. latency is much more controllable that way.
If you're calling play on the main thread, try running it on a separate thread. What I ended up doing is:
#include <dispatch/dispatch.h>
dispatch_queue_t playQueue = dispatch_queue_create("com.example.playqueue", NULL);
AVAudioPlayer* player = ...
dispatch_async(playQueue, ^{
[player play];
});
which fixed the worst of the framerate stuttering I was experiencing.
I use OpenAL and the classes that came with the CrashLanding sample code. It's worked fine so far to play samples and play looped music all at the same time. I'm currently learning how to release the memory I've allocated for a sound (.wav file) when, for example, I want to play some intro music just once.
Use CocosDenshion – it’s free, easy, and works. It wraps AVAudioPlayer for background tracks and OpenAL for sounds.
Do you want to check the buffering with the implementation you're using? It might be somehow related to the 20ms delay you're experiencing. i.e., try to play around with the buffer size.