Reputation: 27384
I want to play background music in my game (ideally i'd like it to fade in / out but I want it playing first)
I have looked at various answers on SO and my code seems to look correct but for some reason the file isnt playing, in fact it crashes (with no useful output) on the AVAudioPlayer *player
line. If I continue it crashes again on the [player play]
line but error
is 0
.
Updated... again
GameViewController.h
@property (nonatomic, retain) AVAudioPlayer *player;
GameViewController.m
@synthesize player; // the player object
ViewDidLoad:
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource: @"bgtrack" ofType: @"mp3"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error: nil]; // breaks here
[player prepareToPlay];
I am including
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
Updates:
As per comments I tried copying / pasting the code directly from the Apple Dev center. I am using the latest XCode 4 and iOS 6.1 simulator.
When it crashes I get no information bar the fact it hits a breakpoint. The file is included in the bundle
Upvotes: 1
Views: 473
Reputation: 1148
Promote the player ivar to a strong property and log the [error description]
if player is nil.
If it is crashing something definitely is wrong. Did you add an exception breakpoint?
Upvotes: 0
Reputation: 29886
You need to store a strong reference to player
(perhaps in an ivar of your view controller or something). If you just put that code into the -viewDidLoad
method or something without making a strong reference to it, what'll happen is that the player will be deallocated before it has a chance to start playing. For example:
@interface MyViewController : UIViewController
@end
@implementation MyViewController
{
AVAudioPlayer* player;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"YourMP3FileName" ofType:@"mp3"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSError *error;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:&error];
player.numberOfLoops = -1; //infinite
player.currentTime = 139;// 2:19;
[player play];
}
@end
That works fine for me, but if player
is not an ivar of the view controller I get no sound.
Alternately, I whipped up the following class that will work as a "one-shot" AVAudioPlayer. This obviously presents a problem for cases where numberOfLoops
is -1 (infinite) since that means it will never stop playing or go away, but it might make things slightly easier in the case where you want something to play once and then go away without needing to keep a reference to it around.
OneShotAVAudioPlayer.h
#import <AVFoundation/AVFoundation.h>
@interface OneShotAVAudioPlayer : AVAudioPlayer
@end
OneShotAVAudioPlayer.m
#import "OneShotAVAudioPlayer.h"
#import <AVFoundation/AVFoundation.h>
#import <objc/runtime.h>
@interface OneShotAVAudioPlayer () <AVAudioPlayerDelegate>
@property(weak) id<AVAudioPlayerDelegate> p_exogenousDelegate;
@end
@implementation OneShotAVAudioPlayer
static void * const OneShotAVAudioPlayerKey = (void*)&OneShotAVAudioPlayerKey;
- (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError
{
if (self = [super initWithContentsOfURL:url error:outError])
{
// Retain ourself
objc_setAssociatedObject(self, OneShotAVAudioPlayerKey, self, OBJC_ASSOCIATION_RETAIN);
[super setDelegate: self];
}
return self;
}
- (id)initWithData:(NSData *)data error:(NSError **)outError;
{
if (self = [super initWithData:data error:outError])
{
// Retain ourself
objc_setAssociatedObject(self, OneShotAVAudioPlayerKey, self, OBJC_ASSOCIATION_RETAIN);
[super setDelegate: self];
}
return self;
}
- (void)setDelegate:(id<AVAudioPlayerDelegate>)delegate
{
self.p_exogenousDelegate = delegate;
}
- (id<AVAudioPlayerDelegate>)delegate
{
return self.p_exogenousDelegate;
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
@try
{
if ([self.p_exogenousDelegate respondsToSelector: _cmd])
[self.p_exogenousDelegate audioPlayerDidFinishPlaying:player successfully:flag];
}
@finally
{
// Make a strong ref so we stay alive through the scope of this function
typeof(self) keepAlive = self;
// Give up the self retain
objc_setAssociatedObject(keepAlive, OneShotAVAudioPlayerKey, nil, OBJC_ASSOCIATION_RETAIN);
// Push in the "real" (outside) delegate, cause our job is done here.
[super setDelegate: self.p_exogenousDelegate];
}
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
@try
{
if ([self.p_exogenousDelegate respondsToSelector: _cmd])
[self.p_exogenousDelegate audioPlayerDecodeErrorDidOccur:player error:error];
}
@finally
{
// Make a strong ref so we stay alive through the scope of this function
typeof(self) keepAlive = self;
// Give up the self retain
objc_setAssociatedObject(keepAlive, OneShotAVAudioPlayerKey, nil, OBJC_ASSOCIATION_RETAIN);
// Push in the "real" (outside) delegate, cause our job is done here.
[super setDelegate: self.p_exogenousDelegate];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
BOOL retVal = [super respondsToSelector: aSelector];
if (!retVal)
{
struct objc_method_description method = protocol_getMethodDescription(@protocol(AVAudioPlayerDelegate), aSelector, YES, YES);
if (method.name)
{
retVal = [self.p_exogenousDelegate respondsToSelector: aSelector];
}
}
return retVal;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
id retVal = [super forwardingTargetForSelector:aSelector];
if (!retVal)
{
struct objc_method_description method = protocol_getMethodDescription(@protocol(AVAudioPlayerDelegate), aSelector, YES, YES);
if (method.name && [self.p_exogenousDelegate respondsToSelector: aSelector])
{
retVal = self.p_exogenousDelegate;
}
}
return retVal;
}
- (void)setNumberOfLoops:(NSInteger)numberOfLoops
{
if (numberOfLoops < 0)
{
NSLog(@"Warning! You have set an infinite loop count for an instance of %@ (%p). This means the instance will effectively be leaked.", NSStringFromClass([self class]), self);
}
[super setNumberOfLoops: numberOfLoops];
}
@end
Posted here as a gist.
Upvotes: 3