I want to play some simple sound effects when people press certain buttons in my app and I've tried several things, but I always get a latency that makes the sound seem out of sync.
I've followed the tutorials here, so I've tried the build in Audio Services, but that had a latency and I've tried the AVAudioPlayer, but that had a latency as well, even though I used "prepareToPlay".
Do I really have to install a big and messy library like Finch to get a simple sound effect with no latency in my simple app?
Hope you can clarify things for me!
UPDATE
Code for Audio Services:
NSURL *soundURL = [[NSBundle mainBundle] URLForResource:#"PlopsX4"
withExtension:#"aif"];
AudioServicesCreateSystemSoundID((CFURLRef)soundURL, &sound1);
AudioServicesPlaySystemSound(sound1);
Code for AVAudioPlayer in viewDidLoad:
NSURL *soundURL = [[NSBundle mainBundle] URLForResource:#"PlopsX4"
withExtension:#"aif"];
self.avSound = [[AVAudioPlayer alloc]
initWithContentsOfURL:soundURL error:nil];
[self.avSound prepareToPlay]
Code for AVAudioPlayer in method where I want to play the file:
[self.avSound play];
The way I got this to work (and I think I got the idea from another post on SO) was to create an empty 1 second .wav file and "play" it at initialization. After that, there was no delay when I called the other sound effects.
Something like this in my sound player object
SystemSoundID blID;
SystemSoundID cID;
+(void)doInit
{
if (spinitialized){
AudioServicesPlaySystemSound (blID);
return;
}
NSString *str = [[NSBundle mainBundle] pathForResource:#"ding.wav" ofType:#""];
NSURL *uref = [NSURL fileURLWithPath:str];
OSStatus error = AudioServicesCreateSystemSoundID ((CFURLRef)uref, &cID);
if (error) NSLog(#"SoundPlayer doInit Error is %ld",error);
str = [[NSBundle mainBundle] pathForResource:#"blank.wav" ofType:#""];
uref = [NSURL fileURLWithPath:str];
error = AudioServicesCreateSystemSoundID ((CFURLRef)uref, &blID);
if (error) NSLog(#"SoundPlayer doInit Error is %ld",error);
AudioServicesPlaySystemSound (blID);
spinitialized = YES;
}
Then I could just play the "ding" without any delay using:
AudioServicesPlaySystemSound (cid);
SOLUTION
I ended up doing something similar as Mike above. I ended up playing the sound with the sound volume down in viewDidLoad:
NSURL *soundURL = [[NSBundle mainBundle] URLForResource:#"PlopsX4"
withExtension:#"aif"];
self.plops = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:nil];
[self.plops setVolume:0.0];
[self.plops play];
And then in the function where I play it I do:
[self.plops setVolume:1.0];
[self.plops play];
One thing I found that seems to help is, upon entry to the screen where the sound MIGHT be played, to play a fraction of a second of a sound. This seems to "prime" the sound mechanism -- allocating internal objects, paging in stuff, etc.
(I observe that this is similar to Mike M's approach.)
Related
First time i am capturing video in my App, but when i start capturing i want that button click sound as native application. I have searched a lot and came to know that it is a system sound. Can anyone please tell me how can i add that sound in my application when i start capturing video and when i stop capturing.
Currently i am using AVAudioPlayer to play sound as shown below
NSURL *url1 = [[NSBundle mainBundle] URLForResource:str withExtension:#"wav"];
_startAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url1 error:nil];
_startAudioPlayer.delegate= self;
[_startAudioPlayer prepareToPlay];
[_startAudioPlayer play];
Thanks in advance.....
Just import AudioToolbox.framework.
Create the URL for the source audio file
NSURL *tapSound = [[NSBundle mainBundle] URLForResource: #"tap"
withExtension: #"aif"];
// Store the URL as a CFURLRef instance
self.soundFileURLRef = (CFURLRef) [tapSound retain];
// Create a system sound object representing the sound file.
AudioServicesCreateSystemSoundID (
soundFileURLRef,
&soundFileObject
);
Play sound
AudioServicesPlayAlertSound (soundFileObject);
Here is a nice tutorial link you can follow this
When you want to start capturing video at that time call this bellow method like this...
[self playSound];
use my this bellow method for play sound...
-(void)playSound
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"adriantnt_release_click" ofType:#"mp3"];
AVAudioPlayer* theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate=self;
[theAudio play];
}
first store any mp3 or any audio file in your project folder For Ex: i add adriantnt_release_click.mp3 file.
Also add AVAudioPlayerDelegate in your .h file and add AudioToolbox.framework in your project..
I am trying to add a tap-sound to a UIButton to play when it is pushed. So far, I've tried two methods, but none worked out quite as well as I hoped.
I have tried using simple SystemSounds, but they are an ugly solution, as the user have no (or rather very limited) control over the audio volume.
I also tried AVAudioPlayer in the following form (called in the ViewController's init method):
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient
error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[AVAudioSession sharedInstance] setDelegate:self];
NSString *filePath = [[NSBundle mainBundle]
pathForResource: #"button_pushed"
ofType: #"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: filePath];
self.buttonPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
error: nil];
[buttonPlayer prepareToPlay];
[buttonPlayer setDelegate: self];
This second method seemed to work a lot better than the first one; especially when I called sound with the following statement:
if (self.clickPlayer.playing) {
[self.clickPlayer stop];
self.clickPlayer.currentTime = 0;
[self.clickPlayer play];
}
else {
[self.clickPlayer play];
}
The above ensured that I got a very fast response when I pushed the button repeatedly. Fast response time, by the way, is essential in my app, and unfortunately, this is where this second methods fails.
If I let the application idle for about 10 seconds, loading the first sound takes a noticeable delay (then, it becomes fast and responsive again). Not only that, it also delays the button from being pushed with it. While this delay is very tiny (probably the fraction of a second), unfortunately it does effect the usability of my application. It is as if the sound was becoming deallocated automatically and needed to be initialized or put into the memory every time I did not use it for a while.
Is there a workaround to always keep the sound available and avoid the delay or another, possibly simple, method to implement button sounds?
A few more info:
1. I use ARC
2. I do not use IB
3. Currently, there are no audio delegate methods implemented
4. The ability to play audio in the background from other applications is kind of important
5. The delay occurred on an iPhone 5; so, I can only imagine how long it would take on less powerful devices...
I managed to solve this problem by creating a "blank" player that continuously fires off a tiny .wav file with no sound, therefore keeping the AVPlayer alive. This is probably not the most elegant solution, but it does work, while it does not seem to affect the performance of the program. Here's what you need to do:
Create a silent .wav file of 0.1 seconds long (the smaller the better).
Initialize a player for it:
NSString *filePath = [[NSBundle mainBundle] pathForResource: #"silence"
ofType: #"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: filePath];
silencePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
error: nil];
[silencePlayer prepareToPlay];
[silencePlayer setDelegate: self];
Then make it fire every second or so:
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
target:self
selector:#selector(repeatSilence)
userInfo:nil
repeats:YES];
Finally, create the called method (- (void) repeatSilence { } ) to play the sound:
if (self.silencePlayer.playing) {
[self.silencePlayer stop];
self.silencePlayer.currentTime = 0;
[self.silencePlayer play];
}
else {
[self.silencePlayer play];
}
Not entirely sure, but wouldn't it go faster if you make an NSData of the path and change [[AVAudioPlayer alloc] initWithContentsOfURL to [[AVAudioPlayer alloc] initWithData?
I have problems with playing sounds on my iPhone programmatically - i don't understand the explanation in Xcode's Documentation about AvAudioPlayer.
Could someone else please give me a code snippet, or better, explain it to me?
Edit what I exactly want to do is playing a sound in an infinite loop, starting when the viewdidload, on app exit the music should stop (not playing in background).
It is for a game I am trying to develop
Actually every method i am using is giving back the same error: (log excerpt)
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSURL initFileURLWithPath:]: nil string parameter'
edit end
Thanks
Here is a very simple way to play short sounds that you have added to your bundle:
NSString *soundPath = [[NSBundle mainBundle] pathForResource:#"somesound" ofType:#"aif"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath: soundPath], &soundID);
AudioServicesPlaySystemSound (soundID);
Here is more documentation on System Sound Services Reference
Edit: since you don't want to simply play a short sounds this might be better:
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"mySound" ofType:#"mp3"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
player.numberOfLoops = -1; //infinite
[player play];
See the AVAudioPlayer Class Reference for more information.
Based on your error, your path to the sound file is not valid one (NSURL returns nil for not valid paths).
-(IBAction)playSound{ AVAudioPlayer *myExampleSound;
NSString *myExamplePath = [[NSBundle mainBundle] pathForResource:#"myaudiofile" ofType:#"caf"];
myExampleSound =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:myExamplePath] error:NULL];
myExampleSound.delegate = self;
[myExampleSound play];
}
I want to play a beep sound when a button is clicked. I had used the above code. But it is taking some delay in playing the sound.
Anyone please help.
There are two sources of the delay. The first one is bigger and can be eliminated using the prepareToPlay method of AVAudioPlayer. This means you have to declare myExampleSound as a class variable and initialize it some time before you are going to need it (and of course call the prepareToPlay after initialization):
- (void) viewDidLoadOrSomethingLikeThat
{
NSString *myExamplePath = [[NSBundle mainBundle]
pathForResource:#"myaudiofile" ofType:#"caf"];
myExampleSound =[[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:myExamplePath] error:NULL];
myExampleSound.delegate = self;
[myExampleSound prepareToPlay];
}
- (IBAction) playSound {
[myExampleSound play];
}
This should take the lag down to about 20 milliseconds, which is probably fine for your needs. If not, you’ll have to abandon AVAudioPlayer and switch to some other way of playing the sounds (like the Finch sound engine).
See also my own question about lags in AVAudioPlayer.
AudioServicesPlaySystemSound is an option. Tutorial here, sample code here.
Considering the code:
soundFilePath = [[NSBundle mainBundle] pathForResource: #"Sound" ofType: #"wav"];
fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
avPlayerNextLevel = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL error: nil];
avPlayerNextLevel1.volume = volume ;
[soundFilePath release];
[fileURL release];
To play the sound I then do
if([avPlayerNextLevel prepareToPlay]){
[avPlayerNextLevel play];
}
I do this many times within my game. Sometimes on the simulator , the sound stops playing. And when sometimes I detect memory leaks with AvAudioPlayer.
Does everything look alright with my code ?
You'll need to release the instance of AVAudioPlayer that you created. Since the above code sets the delegate to self, just implement the following method and release there:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
NSLog(#"Audio finished playing.");
[player release];
}
Why are you releaseing a non-owning reference (soundFilePath)?
You don't need to manually prepareToPlay if you're going to invoke play right after.
Quoting the documentation for play:
Discussion
Calling this method implicitly calls the prepareToPlay method if the audio player is not already prepared to play.
Personally, I do this:
NSURL *soundUrl = [NSURL fileURLWithPath:
[[NSBundle mainBundle] pathForResource:track
ofType:nil]];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl
error:nil];
[audioPlayer setDelegate:self];
[audioPlayer play];
Where audioPlayer is an ivar and track is an lvar of type NSString* which I obtained earlier from wherever I configure the next song to play.
I've been working on something similar but I was using a slightly different piece of code. Unfortunately, I got memory leaks, therefore I decided to give a try to the code you posted. Same result: memory leaks again when I invoke [audioPlayer play];
I can't really figure out what's going on. Are you guys aware of any memory leaks when AVAudioPlayer is involved?