I recently implemented sounds into my game. Every time the user touches the screen, an arrow is shot, and a sound is played. I am using the following code to play the sound:
- (void)arrowShoot {
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/arrow.mp3", [[NSBundle mainBundle] resourcePath]]];
arrowShootPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
if (arrowShootPlayer != nil) {
[arrowShootPlayer play];
}
}
The reason I was allocating memory to the arrowShootPlayer every time is because the user can tap the screen rapidly, and I want the sounds to overlap. Otherwise, the sound only plays once the previous one has finished playing.
Anyway, I am getting this error:
<com.apple.main-thread> shm_open failed: "AppleAudioQueue.36.5755" (23) flags=0x2 errno=24
Once I get this error, the sounds stop being played. Everything else is fine, just the arrow sounds stop playing.
Is there a better way of playing 'overlapping' sounds like this on the iPhone, or is there a way around this error??
Thanks
if it is the same audio always, use the playing from memory buffer instead of opening the files. Declare some local buffer:
NSData* data;
then in your method:
if(!data) {
data = [NSData dataWithContentsOfURL:url options:NSDataReadingMappedIfSafe error:&err];
}
arrowShootPlayer = [[AVAudioPlayer alloc] initWithData:data error:nil];
if (arrowShootPlayer) {
[arrowShootPlayer play];
}
Related
I have an app that uses an avaudio player for two things. One of them is to play an explosion sound when a uiimageview collision is detected, and the other is to play a lazer sound when a button is pressed. I declared the audioplayer in the .h class, and I call it each time the button is clicked by doing this:
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"/lazer"
ofType:#"mp3"]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
[audioPlayer prepareToPlay];
}
[audioPlayer play];
This works fine, but after many uses of the game, the audio will stop play when i hit the button, and when a collision is detected, the game crashes. Here is my crash log:
2013-09-18 18:09:19.618 BattleShip[506:907] 18:09:19.617 shm_open failed: "AppleAudioQueue.41.2619" (23) flags=0x2 errno=24 (lldb)
Suggestions? Could there be something to do with repeatedly creating an audio player? Alternatives maybe?
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 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.)
I'm working on a game, that at first seem too run fine, but.......
after it runs a long time the sound first stops to work,
[[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error]; returns a null and a little while later when I try to set a view equal to a new image, by view.image=, I get a error in my console that says to many files open,
ImageIO: CGImageRead_mapData 'open' failed '/Users/deno/Library/Application Support/iPhone Simulator/5.1/Applications/5A0C777C-70D1-4AA0-8D7B-04A1F734C932/cPetCakes.app/twinkles04fin32.png'
error = 24 (Too many open files)
right now I creat a new I'm creating a new AVAudioPlayer object each time a sound is played. Should I be re-using the object or some how freeing it??? Since I was not using a alloc, I was thinking that the object would be freed automatcly???
Code for sound
-(void) PlaySound: (NSString *) name
{
NSURL *url;
if ( [name compare:#"chasdog"]==0)
url = [[NSBundle mainBundle]
URLForResource:name
withExtension:#"wav"];
else
url = [[NSBundle mainBundle]
URLForResource:name
withExtension:#"mp3"];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.volume=1.0;
audioPlayer.numberOfLoops= 0;
[audioPlayer play];
audioPlayer.delegate = self;
}
code that no longer loads in new images
self.image=gPetCakeB[gGame->mPetCakeId];
In the player delegate it's needed to release the player once it's stopped playing:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
[player release];
}
How can I play a custom, looping sound (from my app, while my app is open) while the iPhone is sleeping?
I'm using this code to play a audio file:
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"sample_name" ofType:#"wav"];
NSData *sampleData = [[NSData alloc] initWithContentsOfFile:soundFilePath];
NSError *audioError = nil;
// Set up the audio player
testAudioPlayer = [[AVAudioPlayer alloc] initWithData:sampleData error:&audioError];
[sampleData release];
if(audioError != nil) {
NSLog(#"An audio error occurred: \"%#\"", audioError);
}
else {
[testAudioPlayer setNumberOfLoops: -1];
[testAudioPlayer play];
}
But it doesn't play if the iPhone is sleeping.
Pandora has this ability, along with other similar apps.
If you are playing a sound when the phone goes to sleep, you will continue to run in the background as Pandora and others do.
If you are not playing a sound when the user puts the device to sleep, you will be suspended.
There's a reference in this answer to the apple documentation for this.
Play sound with screen turned off / don't let iPhone go to sleep
Try registering for a local notification.