iOS AVAudioPlayer double sound playing issue with Xcode 4.2 and ARC - objective-c

Ive been beating my head up against the all trying to figure out a weird sound issue in my program. I will post the code below. As a brief description I have a function that gets passed a variable and it takes that and decides which category of sound it should play, within that function I have multiple sounds of reach category so I use an arc4random statement then run that through a switch statement. What keeps happening is that it will play two sounds from the case statement instead of just one, most times if I call it twice with my button push the second time it will play the sound from the first time and a new sound, other times it plays the same same over the top of each other but with a slight delay. I put in a breakpoint in the switch and when it double plays it only goes through the switch once which is really confusing. One thing to note is right before this I do play another sound but it uses a separate AVAudioplayer and path variable so that shouldn't be an issue as it never plays the second sound as the other sound I'm playing. I'm only calling the function once when I press the button so I'm not sure why it does this. I have tried putting the *path and *avaudioplayer variables inside the function but it won't play at all. Searching here it seems as though arc deallocs it before it gets a chance to play it. I ended up trying to put it at the top of my .m file as a global variable then just set the actual path and play the sound within the switch. The sound will play but it plays twice.. Hopefully someone can help me out. I tried putting the avaudioplayer definition as a property as well and it does the same thing. Here is my code snippet. And thanks in advance ...
in the .m file
// Just below my synthesize statements
NSString *path;
AVAudioPlayer *theSound
// My code that I call when the button is pressed
[self playVoice:#"buyVoice"];
// The playVoice function
- (void)playVoice:(NSString*)voiceNum;
if ([voiceNum isEqualToString:#"buyVoice"]) // Bought an Item Time for a voice
{
// play coin sound
[self coinSound];
// play random buy phrase and coin sound
int phraseNumber = 0;
phraseNumber = arc4random() %(3);
switch (phraseNumber)
{
case 0:
{
path = [[NSBundle mainBundle] pathForResource:#"sndBuy1" ofType:#"m4a"];
theSound =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath: path] error:NULL];
[theSound setNumberOfLoops:0];
[theSound play];
break;
}
case 1:
{
path = [[NSBundle mainBundle] pathForResource:#"sndBuy2" ofType:#"m4a"];
theSound =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath: path] error:NULL];
[theSound setNumberOfLoops:0];
[theSound play];
break;
}
case 2:
{
path = [[NSBundle mainBundle] pathForResource:#"sndBuy3" ofType:#"m4a"];
theSound =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath: path] error:NULL];
[theSound setNumberOfLoops:0];
[theSound play];
break;
}
case 3:
{
path = [[NSBundle mainBundle] pathForResource:#"sndBuy4" ofType:#"m4a"];
theSound =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath: path] error:NULL];
[theSound setNumberOfLoops:0];
[theSound play];
break;
}
}
}

set this code before u play the Audio sound
This will reset the player to the beginning:
[theSound setCurrentTime:0.0];
[theSound play];
try this

Related

Objective-C exception handling

I'm trying to figure out how exception handling works in Objective-C. So I'm forcing an exception by performing a selector (boom) which does not exist. When I run it, the application crushes, the exception is not handled properly. Can somebody show me the proper way of handling exceptions in objective-c?
#try{
[self performSelector:#selector(boom)];
}
#catch (NSException *ex) {
NSLog(#"Error");
}
... And can somebody also show me how to handle exception for the code below. I'm using one instance variable for four different sound effects. After playing, I set the variable to nil. For some reason, the code below crushes the app at times, so I will like to handle that exception. Thanks.
- (void) playSound:(NSUInteger) number withDelay:(NSTimeInterval) delay
{
if(sound == nil)
{
NSURL* url;
switch (number)
{
case 1: url = [[NSBundle mainBundle] URLForResource:#"Shuffle" withExtension:#"caf"]; break;
case 3: url = [[NSBundle mainBundle] URLForResource:#"Clap" withExtension:#"caf"]; break;
case 4: url = [[NSBundle mainBundle] URLForResource:#"Glass" withExtension:#"caf"]; break;
case 5: url = [[NSBundle mainBundle] URLForResource:#"Knock" withExtension:#"caf"]; break;
default: break;
}
if (url != nil)
{
sound = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[sound setCurrentTime:0];
[sound prepareToPlay];
[sound setVolume:1.0];
[sound performSelector:#selector(play) withObject:nil afterDelay: delay];
}
}
}
Exception Handling will not solve your problem here,
If you call - (void) playSound:(NSUInteger) number withDelay:(NSTimeInterval) delay from button press events. Multiple button presses would lead to this kind of crashes. Because you are using the same AVAudioPlayer variable *sound and that same object is used to play sounds after delays.A call from other button press could be initiating the sound player, while another one tries to play the sound.
If these are short sound clips (less than 30sec), Without using AVAudioPlayer , you better use AudioServicesPlaySystemSound,
You can write a method to delay the start of play, This will let you play multiple sound clips at the same time without any problem.
NSString *path = [[NSBundle mainBundle] pathForResource:#"Shuffle" ofType:#"caf"];
NSURL *url = [NSURL fileURLWithPath:path];
SystemSoundID soundFileObject;
AudioServicesCreateSystemSoundID ((__bridge_retained CFURLRef) url, &soundFileObject);
AudioServicesPlaySystemSound (soundFileObject);

NSString set from a url doesn't read?

I am having trouble getting my NSString to read my string that I have set. The play action is hooked to a UIButton OnTouchDown if it matters.
- (IBAction)play {
if (audioPlayer.playing == NO) {
urlSong = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/k.mp3", [[NSBundle mainBundle] resourcePath]]];
[audioPlayer release];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:urlSong error:&error];
audioPlayer.numberOfLoops = 0;
//if i set this line to stringSong = #"k"; then comment out the next line everything works fine
stringSong = [[urlSong lastPathComponent] stringByDeletingPathExtension];
label.text = stringSong;
AVAudioSession *audiosession = [AVAudioSession sharedInstance];
[audiosession setCategory:AVAudioSessionCategoryAmbient error:nil];
[audioPlayer play];
[start setTitle:#"Stop" forState:UIControlStateNormal];
}
else
if ([stringSong isEqualToString:#"k"]){
label.text = #"audio stopped";
[audioPlayer stop];
}
}
When I run the code on my iPhone Xcode returns
EXC_BAD_ACCESS
on this line
if ([stringSong isEqualToString:#"k"]){
I know this is due to me trying to set the string from the url because when I plainly set my string to 'k' the code executes just fine.
If anybody is wondering the overall point of this, I am trying to have my audio buttons set to stop all other audio when this button is pressed and start the audio assigned to this button which in this case is 'k.mp3'. But if this audio is already playing just to stop playing all audio. Thank you.
You aren't retaining stringSong.
This line:
stringSong = [[urlSong lastPathComponent] stringByDeletingPathExtension];
Should be:
stringSong = [[[urlSong lastPathComponent] stringByDeletingPathExtension] retain];
At some point this needs to be released.

AVAudioPlayer Won't Play Repeatedly

I have a relatively simple app that plays a sound using AVAudioPlayer, like this:
NSURL *file3 = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/ball_bounce.aiff", [[NSBundle mainBundle] resourcePath]]];
player3 = [[AVAudioPlayer alloc] initWithContentsOfURL:file3 error:nil];
[player3 prepareToPlay];
to later be called to play, like this:
[player3 play];
But this sound is called in multiple places (collisions with a bouncing ball), many times. Sometimes, the ball hits two things in quick enough succession where the sound from the first bounce is still playing when the ball collides a second time. This second bounce then does not produce a sound. How can I make it play a sound every time it collides, even if it involves paying a sound that overlaps an already playing sound?
Any help is greatly appreciated.
One way to solve this problem would to be create a new instance of AVAudioPlayer if the previous one is already playing:
if([player3 isPlaying]) {
AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[player3 url]];
[newPlayer setDelegate:self];
[newPlayer play];
}
Then, in the delegate method audioPlayerDidFinishPlaying:successfully:, you should send the player the -release message:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
if(player != player3) {
[player release];
}
}
This is just the way it has to be, as you do need a new source buffer for each concurrent sound playing. :(
It is an old thread but maybe someone can benefit from this answer. Creating AVAudioPlayer repeatedly can be a burden for the app and can creates lags. In this case you can use SystemSound.
NSString *pathToMySound = [[NSBundle mainBundle] pathForResource:#"yourMusic" ofType:#"mp3"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)([NSURL fileURLWithPath: pathToMySound]), &soundID);
AudioServicesPlaySystemSound(soundID);
Downside of this solution, you can't adjust the volume.

Help on Objective C? My app keeps crashing?

I am creating an app for the Iphone/Itouch, but I keep on running into a couple of major leaks that just crash the app. In my game, right when I press play (from the home screen)It goes to another page that has the game on it. But, right after it appears, ind the console I get a Warning: Memory level=1. What could be happening? Here is my ViewDidLoad Method:
-(void)viewDidLoad {
[super viewDidLoad];
array = [[NSMutableArray alloc] initWithCapacity:100];
bulletArray = [[NSMutableArray alloc] initWithCapacity:100];
pos = CGPointMake(0.0,-5.0);
NSString *path = [[NSBundle mainBundle] pathForResource:#"GloriousMorning" ofType:#"mp3"];
AVAudioPlayer *theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
//float effects_Volume = [[NSUserDefaults standardUserDefaults] floatForKey:#"effectsVolume"];
//theAudio.volume = effects_Volume;
[theAudio play];
}
And also, a second question, since my game is a shooting game, the user presses a button titled "Fire". But, every time I test my app on a device, It crashes when I press the fire button. Here is my code for the fire button.
-(IBAction)Fire {
NSString *path = [[NSBundle mainBundle] pathForResource:#"gunShot" ofType:#"mp3"];
AVAudioPlayer *theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
//float effects_Volume = [[NSUserDefaults standardUserDefaults] floatForKey:#"effectsVolume"];
//theAudio.volume = effects_Volume;
[theAudio play];
//IBOutlet UIImageView *newBullet = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Bullet.png"]];
UIImageView *newBullet = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Bullet.png"]];
newBullet.frame = CGRectMake(0, 0, 10.0, 10.0);
newBullet.center = CGPointMake(239.0, 236.0);
[bulletArray addObject:newBullet];
[self.view addSubview:newBullet];
}
First, I create a sound. Then, I place a bullet right where the gun is currently located, and add it to an array so that every .01 of a second, in another bit of code, I can run through the array and check every bullet to detect collision.
Please tell me what I am doing wrong. Thanks!!!
The error when I click the Fire Button that makes the app crash is this:
GDB: Data Formatters temporarily unavailable, will retry after a 'continue'(unknown error loading shared library "
And Also I think I am making a huge leak when I try to play the audio, at least that's what someone told me. (If that is the case, please tell me how to fix it)
You alloc'd theAudio so you need to release it when you are done. Might be better to make that a ivar so you don't have to set up and tear down the audio every time they press the fire button.

problem in playing next song in the avaudioplayer

my delegate method looks like this. after the first song is played it goes into this method and plays the second song , however when the second song is done playing it stops. it does not go into the delegate method.i need to play all the songs continuously. i am not sure, why. can someone help me.
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)p successfully:(BOOL)flag
{
if (flag == NO)
NSLog(#"Playback finished unsuccessfully");
else
{
//[player stop];
index++;
NSLog(#"%d",index);
path=[[NSBundle mainBundle] pathForResource:[songlist objectAtIndex:index] ofType:#"mp3"];
[player initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[songlabel2 setTitle:[songlist objectAtIndex:index]];
[endtime setText:[NSString stringWithFormat:#"%.2f",[player duration]/100]];
[player play];
}
}
Since you’re not defining a local variable called player, I’m assuming that player is an instance variable that was used to play the first song, and you’ve created it with
player = [[AVAudioPlayer alloc] initWithContentsOfURL:firstSongURL error:&error];
or something similar, and you’ve set the delegate. In your audioPlayerDidFinishPlaying: successfully: method, you have
[player initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
which means you’re sending -initWithContentsOfURL:error: again to the same instance. Do not call an initialiser twice on a given object. The results are unpredictable and there is potential for memory leaking. You should release the previous instance of AVAudioPlayer, e.g.
[player release];
and then create a new instance with +alloc and -initWithContentsOfURL:error: again, just like you did with the first song, and set the appropriate delegate:
player = [[AVAudioPlayer alloc] initWithContentsOfURL:secondSongURL error:&error];
player.delegate = self;
[player play];