how to stop AVAudioPlayer securely - iphone

i have an AVAudioPlayer with two buttons : Play and Stop.
The play button is hidden when playing, and vice versa.
The problem is that a crash occurs when i attempt to push the Stop button, just at the end of the playing, at the moment when the method "audioPlayerDidFinishPlaying" is invoked.
Indeed, this method release the AVAudioPlayer, and when i push the stop button (which release the object too...) it crashes.
But still, i test if the player is nil before releasing it manually, so why does it crashes and how to avoid this ?
My code :
in the .h :
#private AVAudioPlayer* monPlayer;
in the .m :
-(IBAction)playSound{
NSString *path = nil;
if(path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"sounds/%d", idoiseau] ofType:#"m4a"])
{
monPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
monPlayer.delegate = self;
[monPlayer play];
[buttonPlay setEnabled:NO];
[buttonStop setEnabled:YES];
}
else return;
}
-(IBAction)stopSound{
if(!monPlayer) return;
[monPlayer stop];
[monPlayer release];
monPlayer = nil;
[buttonPlay setEnabled:YES];
[buttonStop setEnabled:NO];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
[player release];
player = nil;
[buttonPlay setEnabled:YES];
[buttonStop setEnabled:NO];
}
edit : just replace the local variable "player" by the instance variable "monPlayer" in the delegate method, and it works. Thanks to hotpaw2 :-)

Your code seems to be setting the local parameter player to nil instead of the object instance variable monPlayer.

Related

How to prevent AVAudioPlayer from playing before a view is displayed

Hi there I have an a UIView named HomeViewController that use UINavigationController. On a click of a button, I push another view using the following code:
gameView = [[GameViewControler alloc] init];
[[self navigationController] pushViewController:gameView animated:YES];
[gameView release];
In GameViewController's viewDidLoad, I use the following code to play two audio file one after another:
//handle sound stuff
if([[GameStore defaultStore] currentIndex] == 0)
{
if(![audioPlayer isPlaying])
{
NSString *findPath = [[NSBundle mainBundle] pathForResource:#"findtheword" ofType:#"aiff"];
if(findPath)
{
NSURL *findURL = [NSURL fileURLWithPath:findPath];
NSError *findError;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:findURL error:&findError];
if(audioPlayer == nil)
{
NSLog([findError description]);
}
else{
[audioPlayer play];
}
}
}
}
while ([audioPlayer isPlaying]) {
//do nothing and wait so the sound is not overlap
}
if(![audioPlayer isPlaying] || ([audioPlayer isPlaying] && override)){
Word *currentWord = [[GameStore defaultStore] currentWord];
NSString *soundPath = [[NSBundle mainBundle] pathForResource:[currentWord fileName]
ofType:[currentWord fileType]];
// If this file is actually in the bundle...
if (soundPath) {
// Create a file URL with this path
NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
if (audioPlayer == nil)
NSLog([error description]);
else
[audioPlayer play];
}
[FlurryAnalytics logEvent:#"GameViewController - playSound"];
}
else{
[FlurryAnalytics logEvent:#"GameViewController - playSound is still playing"];
}
I can't figure out why the first audio file started to play while the screen is still on HomeViewController even though it is being called from GameViewController's viewDidLoad.
If I remove the following code:
while ([audioPlayer isPlaying]) {
//do nothing and wait so the sound is not overlap
}
The first audio file started to play when GameViewController is loaded but the 2nd audio file overlap.
Thank you very much.
Does the audio file start to play after you click some button or table view cell in HomeViewController? As in, it starts to play while the GameViewController's view is being animated from right to left? If so, then that's because -viewDidLoad is called before the view is actually presented. What you need to do is instead place your view logic in -viewDidAppear:. That will only be called when the view is finished animating and is completely visible.
You could move the code from viewDidLoad to viewDidAppear:.

Touchesbegan always fire when using touchesmoved?

I'm new to iPhone programing.
I'm trying to do a simple window with a cat doing two sounds. When you click at the cat icon it should do "miaau", and when wou drag over window (stroke) it should make "mrrrr".
It works, but always when I try to make cat mrrrrr function TouchesBegan fires and cat also do "miaaau".
What to do to make the interface recognize I want only stroke cat, not to touch it for doing the first option, "miaau"?
I suggest to add a NSTimer to touchesBegan method with small time interval (say 0.1 sec):
BOOL tap_event = NO; //ivar declared in header
-(void) touchesBegan:... {
tap_event = YES;
[NSTimer scheduledTimerWithTimeInterval: 0.1 target: self selector: #selector(checkTap:) userInfo: nil repeats: NO];
}
-(void) checkTap:(NSTimer*) t {
if( tap_event ) //miauu here
tap_event = NO;
}
-(void) touchesMoved:... {
tap_event = NO;
//mrrrr here
}
Or as an option check docs for UIGestureRecognizers
This may help you
When does a touchesBegan become a touchesMoved?
touchesBegin & touchesMove Xcode Obj C Question
Max' solution is correct. It will definitely work. I am having an alternative, see this approach.
Make your player object in an .h file then allocate it in viewDidLoad and release in dealloc.
-(void) touchesBegan:... {
//Play the miauu sound.
}
-(void) touchesMoved:... {
[self.yourPlayer stop];
//Play mrrrr.
}
Edit
.h file,
AVAudioPlayer *avPlayer1;
AVAudioPlayer *avPlayer2;
.m file
-(void) touchesBegan:... {
NSString *path = [[NSBundle mainBundle] pathForResource:#"cat" ofType:#"wav"];
avPlayer1 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[avPlayer1 play];
}
-(void) touchesMoved:... {
[avPlayer1 stop];
NSString *path = [[NSBundle mainBundle] pathForResource:#"cat" ofType:#"wav"];
avPlayer2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[avPlayer2 play];
}
-(void)dealloc
{
[avPlayer1 release];
[avPlayer2 release];
[super dealloc];
}

AVaudioPlayer regarding song overlapping

I have using AVAudioPlayer in my app but the problem is that when i play the song from list it will played while before stopping the song i am again goes to song list then select another song then both song will start playing simultaneously so please tell me the code which will terminate my first song.
I use flag variable for initializing the song file---
fileURL = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:#" Om Namo" ofType:#"aac"]];
self._player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
if (self._player)
{
_fileName.text = [NSString stringWithFormat: #"%# (%d ch.)", [[self._player.url relativePath] lastPathComponent], self._player.numberOfChannels, nil];
[self updateViewForPlayerInfo];
[self updateViewForPlayerState];
}
[fileURL release];
break;
Play and push function---
- (void)startPlayback
{
if ([self._player play])
{
//[self stop];
[self updateViewForPlayerState];
self._player.delegate = self;
}
else
NSLog(#"Could not play %#\n", self._player.url);
}
- (void)pausePlayback
{
[self._player pause];
[self updateViewForPlayerState];
}
I also make stop function---
-(void)stop
{
[_player release];
self._player=nil;
}
Please help me .
Is correct my stop function.
and where i put our stop function or any other solution to fix this problem...
I can think of 3 things :
1) You can make code look like code by putting 4 spaces infront of each line. The reason you've gt no answers so far is probably because your question is almost unreadable ;)
2) In your stop method you might want to try this :
- (void) stop {
[_player stop];
self._player = nil;
}
3) Apple highly discourage using an underscore in front of member names. I've never run into problems but I'd rename _player to player.

off a sound effect with NSUserDefaults

i try to off a sound effect on my app
play method is ' [myMusic play];
-(void)viewDidLoad {
BOOL soundIsOff = [defaults boolForKey:#"sound_off"];
//the problem is here
//xcode compiler doesn't copile this code
[myMusic play] = soundIsOff;
}
sound code :
'
///sound effect
NSString * musicSonati = [[NSBundle mainBundle] pathForResource:#"sound" ofType:#"wav"];
myMusic = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:musicSonati] error:NULL];
myMusic.delegate = self;
myMusic.numberOfLoops = 0;
Shake API Code :
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.subtype == UIEventSubtypeMotionShake)
//Play sound :
[myMusic play] ;
}
}
Looks like you are trying to assign a value to a method call. That is why you are getting the compiler error:
[myMusic play] = soundIsOff //This does not work.
You probably want something like this:
if(!soundIsOff) {
[myMusic play];
}
The question is unclear however, so this is a guess at this point.
I am confused by the question, but if what you want is to stop the sound, then you just need:
[myMusic stop];
Like the others, I can only guess at what you want to do here. Perhaps you are trying to do this:
soundIsOff ? [music stop] : [music play];

Can you see anything wrong in this code?

NSString *path = [[NSBundle mainBundle] pathForResource:[arraySub objectAtIndex:indexPathHere.row] ofType:#"mp3"];
NSURL *file = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:file error:nil];
self.player = myPlayer;
[player prepareToPlay];
[player setDelegate:self];
[self.player play];
NSTimeInterval lenghtMusic = player.duration;
if (player.currentTime == lenghtMusic) {
NSLog(#"It worked");
[tableThing deselectRowAtIndexPath:indexPathHere animated:YES];
[myPlayer autorelease];
[file autorelease];
}
Can you see anything wrong?
Apparently, the "if"-statement never gets called...
If I read your code correctly you are trying to find out when the player has finished playing and then you want to deselect the current rode and release the player. Your problem is that you start the player and then immediately checks where it is currently playing. Unless you have an almost 0 length file the currentTime and length will NEVER be equal.
If that is what you are trying to do you should use the AVAudioPlayerDelegate Protocol and especially the:
– audioPlayerDidFinishPlaying:successfully:
method that is called when the player is finished.
So you need to change your class that controls the player by editing the .h file to use the AVAudioPlayerDelegate Protocol. Obviously keep extending whatever you were before.
#interface YourClass <AVAudioPlayerDelegate>
#end
In your .m file:
Then when you have created and assigned your player instance:
[player setDelegate:self];
In the .m file also add the method
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
// Your success code goes here
}
You are comparing double's with ==, which is almost always false due to round-off errors.
Compare with a range e.g.
if (fabs(player.currentTime - lenghtMusic) < 0.0001) {
...
}
I'm a noob iPhone dev, but are you sure that player.currentTime is a NSTimeInterval? That looks like the most obvious candidate.
Also, I'd log out what your player.currentTime is and what lenghtMusic is before your if and that'll probably let you know what's going on.