AVAudioPlayer and AVAudioRecorder: Delegate Methods not Called - iphone

My delegate methods audioRecorderDidFinishRecording and audioPlayerDidFinishPlaying are not being called. Those methods should trigger a 'stopanimation` method that stops an animation after recording is finished.
I have placed a call call to the stopanimation method at the end of audioPlayerDidFinishPlaying.
Here is the relevant code where the delegate is assigned:
VoiceEditor.h
#interface VoiceEditor : UIViewController <UITextFieldDelegate, AVAudioRecorderDelegate, AVAudioPlayerDelegate>{
}
VoiceEditor.m
- (void)record{
recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:pathString] settings:recordSettings error:nil];
[recorder setDelegate:self];
[recorder record];
recording = YES;
[pauseButton setImage:[UIImage imageNamed:#"stop.png"] forState:UIControlStateNormal];
[pauseButton setEnabled:YES];
[playButton setEnabled:NO];
[recordButton setEnabled:NO];
[self beginAnimation];
}
- (void)play{
player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:pathString] error:nil];
double seconds=[player duration];
NSLog(#"%f",seconds);
[player setDelegate:self];
[player play];
playing = YES;
[recordButton setEnabled:NO];
[pauseButton setEnabled:YES];
[playButton setEnabled:NO];
[self beginAnimation];
}

Your code setting the delegate looks fine.
Are you assuming that the delegate methods are not called only because the animation does not stop or have you tried to log/breakpoint the methods directly? If you haven't tested directly do so before working on the assumption that they aren't.
If you have confirmed they are not being called the, most likely, your animation is tying up the self object such that it cannot respond to the AV delegate methods.
Comment out the animation and just put a log in the AV delegate methods to see if they are called.

Try to pass the error parameter to be sure your recorder object is being created properly.
NSError *err=nil;
recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:pathString] settings:recordSettings error:&err];
if(!recorder){
NSLog(#"recorder: %# %d %#", [err domain], [err code], [[err userInfo] description]);
}

Check the current AVAudioSession category. If the category is not set to AVAudioSessionRecord or AVAudioSessionPlayAndRecord, the recorder won't work and none of its delegate methods will be called. The AVAudioSession category can be changed with the setCategory:error: method. For example:
NSError *sessionCategoryError = nil;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
BOOL success = [audioSession setCategory: AVAudioSessionCategoryPlayAndRecord
error: &sessionCategoryError];

Related

iOS - Code only works when I insert a breakpoint

Update
I'm starting to figure this out. The AVAudioRecorder session gets activated and I get mic level readings for a few seconds. Then the async video code completes, the camera view displays, and I stop getting readings. It seems like the video is killing the audio session.
What's strange is that the code works on iOS 7 and won't work on iOS 6. Any ideas how to get around this. Is it a limitation on iOS 6?
I'm getting sound levels via the mic and I can only get them when I place a breakpoint on the code that sets up my audio recorder object. If I run without pausing at that point for a few seconds it can't get the levels. This code is in viewDidLoad and is and I can place the breakpoint on one of those first 3 lines:
_recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
[_recorder prepareToRecord];
_recorder.meteringEnabled = YES;
_levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.03 target: self selector: #selector(levelTimerCallback) userInfo: nil repeats: YES];
I get my levels in the levelTimerCallback method.
This code goes in viewDidAppear:
NSError *error;
if (_recorder) {
[_recorder record];
} else
NSLog(#"Error: %#", [error description]);
Edit:
levelTimerCallback code:
[_recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [_recorder peakPowerForChannel:0]));
_lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * _lowPassResults;
NSLog(#"Average input: %f Peak input: %f", [_recorder averagePowerForChannel:0], [_recorder peakPowerForChannel:0]);
Async code run above recorder code:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[[self captureManager] session] startRunning];
});
Camera code:
if ([self captureManager] == nil) {
AVCamCaptureManager *manager = [[AVCamCaptureManager alloc] init];
[self setCaptureManager:manager];
[[self captureManager] setDelegate:self];
if ([[self captureManager] setupSession]) {
// Create video preview layer and add it to the UI
AVCaptureVideoPreviewLayer *newCaptureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[[self captureManager] session]];
UIView *view = [self videoPreviewView];
CALayer *viewLayer = [view layer];
[viewLayer setMasksToBounds:YES];
CGRect bounds = [view bounds];
[newCaptureVideoPreviewLayer setFrame:bounds];
if ([[newCaptureVideoPreviewLayer connection] isVideoOrientationSupported]) {
[[newCaptureVideoPreviewLayer connection] setVideoOrientation:AVCaptureVideoOrientationPortrait];
}
[newCaptureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[viewLayer insertSublayer:newCaptureVideoPreviewLayer below:[[viewLayer sublayers] objectAtIndex:0]];
[self setCaptureVideoPreviewLayer:newCaptureVideoPreviewLayer];
// Start the session. This is done asychronously since -startRunning doesn't return until the session is running.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[[self captureManager] session] startRunning];
});
[self addObserver:self forKeyPath:#"captureManager.videoInput.device.focusMode" options:NSKeyValueObservingOptionNew context:AVCamFocusModeObserverContext];
// Add a single tap gesture to focus on the point tapped, then lock focus
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapToAutoFocus:)];
[singleTap setDelegate:self];
[singleTap setNumberOfTapsRequired:1];
[view addGestureRecognizer:singleTap];
// Add a double tap gesture to reset the focus mode to continuous auto focus
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapToContinouslyAutoFocus:)];
[doubleTap setDelegate:self];
[doubleTap setNumberOfTapsRequired:2];
[singleTap requireGestureRecognizerToFail:doubleTap];
[view addGestureRecognizer:doubleTap];
}
}
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setActive: YES error: nil];
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryRecord error: nil];
NSError *error;
_recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
I believe you should call _levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.03 target: self selector: #selector(levelTimerCallback) userInfo: nil repeats: YES];
at the end of viewDidAppear instead, and nil it out and invalidate it in viewDidDisappear. Having a timer firing from viewDidLoad does not make sense when you have the other code in viewDidAppear. You may also want to check using the main thread instead of the background thread for [[[self captureManager] session] startRunning];.

AVAudioPlayer behaviour handling issue

I have been trying to learn AVAudioPlayer and it's behaviour.
So to start with I had written
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
player.numberOfLoops = -1;
[player play];
It did worked well as long as application did not enter background. So I searched again and found that I need to add entry in plist and also have written following code
NSError *activationError = nil;
if([[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&activationError] == NO)
NSLog(#"*** %# ***", activationError);
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
Now my sample application plays sound in background as well. Then I encountered another problem. If I put sleep before all this processing, take my application in background during the sleep and start playing music from some other app, in this case my application is not able to play sound. It is probably because it does not get the audio session which some other music application has taken.
Questions
1. I understand that there is a way to detect if any other sound is playing, but I could not find how do I stop it. There are methods for stopping standard app sound like iPod, but is there any generic method to stop the sound from any other application.
2. [player play]; gives NO as result which is failure. How do I find what failure has caused. I have implemented audioPlayerDecodeErrorDidOccur:error: method, but it is never called.
Please share your knowledge on this. I have already gone through most of the stack overflow questions regarding the same, but did not find anything useful to this particular problem.
-(void)loadPlayer
{
NSURL *audioFileLocationURL = [NSURL URLWithString:[[NSBundle mainBundle] URLForResource:#"01-YedhiYedhi[www.AtoZmp3.in]" withExtension:#"mp3"]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileLocationURL error:&error];
[audioPlayer setNumberOfLoops:-1];
if (error)
{
NSLog(#"%#", [error localizedDescription]);
[[self volumeControl] setEnabled:NO];
[[self playPauseButton] setEnabled:NO];
[[self alertLabel] setText:#"Unable to load file"];
[[self alertLabel] setHidden:NO];
}
else
{
[[self alertLabel] setText:[NSString stringWithFormat:#"%# has loaded", str]];
[[self alertLabel] setHidden:NO];
//Make sure the system follows our playback status
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
//Load the audio into memory
[audioPlayer prepareToPlay];
}
}
if (!self.audioPlayer.playing) {
[self playAudio];
} else if (self.audioPlayer.playing) {
[self pauseAudio];
}
}
- (void)playAudio {
[audioPlayer play];
}

AVAudioRecorder won't record IF movie was previously recorded & played

My iPhone app uses "AVAudioRecorder" to make voice recordings. It also uses "UIImagePickerController" to record movies and "MPMoviePlayerController" to play movies.
Everything works fine until I do all three things in a row:
Record a movie using UIImagePickerController
Play back the recorded movie using MPMoviePlayerController
Try to make a voice recording using AVAudioRecorder
When I call AVAudioRecorder's "record" method in step 3, it returns NO indicating failure, but giving no hints as to why (come on Apple!) AVAudioRecorder's audioRecorderEncodeErrorDidOccur delegate method is never called and I receive no other errors when setting up the recorder.
My first guess was that the movie recording/playing was modifying the shared instance of "AVAudioSession" in such a way that it prevented the audio recorder from working. However, I'm manually setting AVAudioSession's category property to "AVAudioSessionCategoryRecord" and I make the audio session active before trying to record.
Here's my method for creating the recorder:
- (void)createAudioRecorder
{
NSError *error = nil;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryRecord error:&error];
if (error)
...
[audioSession setActive:YES error:&error];
if (error)
...
NSMutableDictionary *settings = [[NSMutableDictionary alloc] init];
// General Audio Format Settings
[settings setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[settings setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[settings setValue:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
// Encoder Settings
[settings setValue:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey];
[settings setValue:[NSNumber numberWithInt:96] forKey:AVEncoderBitRateKey];
[settings setValue:[NSNumber numberWithInt:16] forKey:AVEncoderBitDepthHintKey];
// Write the audio to a temporary file
NSURL *tempURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:#"Recording.m4a"]];
audioRecorder = [[AVAudioRecorder alloc] initWithURL:tempURL settings:settings error:&error];
if (error)
...
audioRecorder.delegate = self;
if ([audioRecorder prepareToRecord] == NO)
NSLog(#"Recorder fails to prepare!");
[settings release];
}
And here's my method to start recording:
- (void)startRecording
{
if (!audioRecorder)
[self createAudioRecorder];
NSError *error = nil;
[[AVAudioSession sharedInstance] setActive:YES error:&error];
if (error)
...
BOOL recording = [audioRecorder record];
if (!recording)
NSLog(#"Recording won't start!");
}
Has anyone run into this problem before?
I was having the same issue. Before I fixed the issue, my recording/playback code was like this:
Start Recording Function
- (BOOL) startRecording {
#try {
NSDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey, [NSNumber numberWithFloat: 44100.0], AVSampleRateKey, [NSNumber numberWithInt: 1], AVNumberOfChannelsKey, [NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey, nil];
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *soundFilePath = [documentsPath stringByAppendingPathComponent:#"recording.caf"];
if(audioRecorder != nil) {
[audioRecorder stop];
[audioRecorder release];
audioRecorder = nil;
}
NSError *err = nil;
audioRecorder = [[AVAudioRecorder alloc] initWithURL:soundFileURL settings:recordSetting error:&err];
[soundFileURL release];
[recordSetting release];
if(!audioRecorder || err){
NSLog(#"recorder initWithURL: %# %d %#", [err domain], [err code], [[err userInfo] description]);
return NO;
}
[audioRecorder peakPowerForChannel:8];
[audioRecorder updateMeters];
audioRecorder.meteringEnabled = YES;
[audioRecorder record];
}
#catch (NSException * e) {
return NO;
}
recording = YES;
return YES;
}
Stop Recording Function
- (BOOL) stopRecording {
#try {
[audioRecorder stop];
[audioRecorder release];
audioRecorder = nil;
recording = NO;
}
#catch (NSException * e) {
return NO;
}
return YES;
}
Start Playing Function
- (BOOL) startPlaying {
#try {
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *soundFilePath = [documentsPath stringByAppendingPathComponent:#"recording.caf"]; NSURL * soundFileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
NSError *err = nil;
if (audioPlayer) {
[audioPlayer release];
audioPlayer = nil;
}
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error: &err];
[soundFileURL release];
if (!audioPlayer || err) {
NSLog(#"recorder: %# %d %#", [err domain], [err code], [[err userInfo] description]);
return NO;
}
[audioPlayer prepareToPlay];
[audioPlayer setDelegate: self];
[audioPlayer play];
playing = YES;
}
#catch (NSException * e) {
return NO;
}
return YES;
}
Stop Playing Function
- (BOOL) stopPlaying {
#try {
[audioPlayer stop];
[audioPlayer release];
audioPlayer = nil;
playing = NO;
}
#catch (NSException * e) {
return NO;
}
return YES;
}
I fixed the recording issue after playing a captured video, the code is as follows:
- (BOOL) startRecording {
#try {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryRecord error:nil];
// rest of the recording code is the same .
}
- (BOOL) stopRecording {
#try {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
// rest of the code is the same
}
- (BOOL) startPlaying {
#try {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
// rest of the code is the same.
}
- (BOOL) stopPlaying {
// There is no change in this function
}
Had the same problem. My Solution was to STOP the movie before starting the recording session. My code was similar to rmomin's code.
I've been having the same problem. I use AVPlayer to play compositions (previous recordings I've used AVAudioRecord for). However, I found that once I've used AVPlayer I could no longer use AVAudioRecorder. After some searching, I discovered that so long as AVPlayer is instantiated in memory and has been played at least once (which is usually what you do immediately after instantiating it) AVAudioRecorder will not record. However, once AVPlayer is dealloc'd, AVAudioRecorder is then free to record again. It appears that AVPlayer holds on to some kind of connection that AVAudioRecorder needs, and it's greedy...it won't let it go until you pry it from it's cold dead hands.
This is the solution I've found. Some people claim that instantiating AVPlayer takes too much time to keep breaking down and setting back up. However, this is not true. Instantiating AVPlayer is actually quite trivial. So also is instantiating AVPlayerItem. What isn't trivial is loading up AVAsset (or any of it's subclasses). You really only want to do that once. They key is to use this sequence:
Load up AVAsset (for example, if you're loading from a file, use AVURLAsset directly or add it to a AVMutableComposition and use that) and keep a reference to it. Don't let it go until you're done with it. Loading it is what takes all the time.
Once you're ready to play: instantiate AVPlayerItem with your asset, then AVPlayer with the AVPlayerItem and play it. Don't keep a reference to AVPlayerItem, AVPlayer will keep a reference to it and you can't reuse it with another player anyway.
Once it's done playing, immediately destroy AVPlayer...release it, set its var to nil, whatever you need to do. **
Now you can record. AVPlayer doesn't exist, so AVAudioRecorder is free to do its thing.
When you're ready to play again, re-instantiate AVPlayerItem with the asset you've already loaded & AVPlayer. Again, this is trivial. The asset has already been loaded so there shouldn't be a delay.
** Note that destroying AVPlayer may take more than just releasing it and setting its var to nil. Most likely, you've also added a periodic time observer to keep track of the play progress. When you do this, you receive back an opaque object you're supposed to hold on to. If you don't remove this item from the player AND release it/set it to nil, AVPlayer will not dealloc. It appears that Apple creates an intentional retain cycle you must break manually. So before you destroy AVPlayer you need to (example):
[_player removeTimeObserver:_playerObserver];
[_playerObserver release]; //Only if you're not using ARC
_playerObserver = nil;
As a side note, you may also have set up NSNotifications (I use one to determine when the player has completed playing)...don't forget to remove those as well.
I had the same problem in Monotouch and adjusted rmomins answer for Monotouch.
changed
avrecorder.Record();
to
NSError error;
var avsession = AVAudioSession.SharedInstance();
avsession.SetCategory(AVAudioSession.CategoryRecord,out error);
avrecorder.Record();
Works like a charm.
I had the same problem to record and play. To fix the problem I recall AVAudioSession in the "record" and "play" function.
This can may be help to person who has this problem on the device and not on the simulator!
I got the same problem. Finally I found out that ARC has released my recorder. So, you must declare the recorder in your .h file, i.e. AVAudioRecord *recorder;. Put other things to .m will work as normal.

AVAudioPlayer crash after playing from an AVAudioRecorder

I've got a button the user tap to start recording and tap again to stop. When it stop I want the recorded voice 'echo' back so the user can hear what was recorded. This works fine the first time. If I hit the button for the third time, it starts a new recording and when I hit stop it crashes with EXC_BAD_ACCESS.
- (IBAction) readToMeTapped {
if(recording)
{
recording = NO;
[readToMeButton setTitle:#"Stop Recording" forState: UIControlStateNormal ];
NSMutableDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
// Create a new dated file
NSDate *now = [NSDate dateWithTimeIntervalSinceNow:0];
NSString *caldate = [now description];
recordedTmpFile = [NSURL fileURLWithPath:[[NSString stringWithFormat:#"%#/%#.caf", DOCUMENTS_FOLDER, caldate] retain]];
error = nil;
recorder = [[ AVAudioRecorder alloc] initWithURL:recordedTmpFile settings:recordSetting error:&error];
[recordSetting release];
if(!recorder){
NSLog(#"recorder: %# %d %#", [error domain], [error code], [[error userInfo] description]);
UIAlertView *alert =
[[UIAlertView alloc] initWithTitle: #"Warning"
message: [error localizedDescription]
delegate: nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
NSLog(#"Using File called: %#",recordedTmpFile);
//Setup the recorder to use this file and record to it.
[recorder setDelegate:self];
[recorder prepareToRecord];
[recorder recordForDuration:(NSTimeInterval) 5]; //recording for a limited time
}
else
{ // it crashes the second time it gets here!
recording = YES;
NSLog(#"Recording YES Using File called: %#",recordedTmpFile);
[readToMeButton setTitle:#"Start Recording" forState:UIControlStateNormal ];
[recorder stop]; //Stop the recorder.
//playback recording
AVAudioPlayer * newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:recordedTmpFile error:&error];
[recordedTmpFile release];
self.aPlayer = newPlayer;
[newPlayer release];
[aPlayer setDelegate:self];
[aPlayer prepareToPlay];
[aPlayer play];
}
}
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)sender successfully:(BOOL)flag {
NSLog (#"audioRecorderDidFinishRecording:successfully:");
[recorder release];
recorder = nil;
}
Checking the debugger, it flags the error here
#synthesize aPlayer, recorder;
This is the part I don't understand. I thought it may have something to do with releasing memory but I've been careful. Have I missed something?
After working on it for a while I stumbled on this debugging tip. It showed me that AVAudioPlayer was deallocated the second time around, causing the crash. So the Delegate must have done the clean up? I checked this SO thread and it is suggestion that the Delegate does not deallocate. However, if I remove the line
[newPlayer release];
My program works! After reading this SO thread, I believe my issue is that I should implement AVAudioPlayerDelegate's - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag method and release the audio player there, after the sound is done playing. I had done it for AVAudioRecorder.
Did you make sure that #property (retain) does not include nonatomic? I'm thinking that some of the code that accesses those properties may be doing so from another thread. Also, you should always access those properties via their getters by using self.recorder and self.player, in order to make sure they are accessed within their respective locks.

AVAudioPlayer works once

Trying to create a playlist of music files that are NOT part of the user's iphone library so I'm using AVAudioPlayer and creating the playlist functionality myself. It works on the first pass (meaning the first song is played). When the first song finishes and it goes to play the 2nd AVAudioPlayer crashes in prepareToPlay.
- (void) create {
Song *song;
NSArray *parts;
NSString *path;
NSString *name;
NSError *err;
if (currentIndex > [queue count])
currentIndex = 0;
if (currentIndex < 0)
currentIndex = ([queue count] - 1);
song = (Song *) [queue objectAtIndex:currentIndex];
name = song.fileName;
parts = [name componentsSeparatedByString:#"."];
path = [[NSBundle mainBundle] pathForResource:[parts objectAtIndex:0] ofType:[parts objectAtIndex:1]];
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
[[AVAudioSession sharedInstance] setDelegate: self];
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &err];
[[AVAudioSession sharedInstance] setActive: YES error: &err];
appPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: url error: &err];
[url release];
[parts release];
[appPlayer prepareToPlay];
[appPlayer setVolume: 1.0];
[appPlayer setDelegate: self];
}
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL) flag {
[appPlayer stop];
[appPlayer release];
appPlayer = nil;
[self next];
}
The call to [self next] just increases currentIndex and calls create again.
The only other thing (I know, the infamous "only other thing" statement) is that this is all going on inside a Singleton. There's lots of things that could cause the music to start and stop playing, change the queue, etc so I thought it would be best to wrap it all up in one spot.
Any thoughts?
I was able to track this down myself. I re-wrote the whole section to have multiple AVAudioPlayer objects but still had issues. Turns out the [parts release] line was the issue, it was causing an over-release situation in the autorelease routine.
One of the reason of having exception in prepareToPlay method is enabled "All Exceptions" breakpoint. Go to Breakpoint Navigator and disable it.