Device is restarting during audio play AVPlayer - iphone

I have an app for iPhone and iPad that plays an audio stream using AVPlayer, I am using the same player of the Apple Sample StitchedStreamPlayer, but I made some changes to play music instead of video.
When I run the app, I can listen for some few seconds and then, the device restarts and following error is displayed:
Terminating in response to SpringBoard's termination.
(when I am running using xcode on the device it plays some minutes, but when I unplug the device and run the app again the app crashes)
I am using the iPhone 4 and an iPad mini for testing, none of them are Jailbroken and booth are iOS 6.
The code is quite big, but here is some parts:
header:
#interface NewPlayer : NSObject <AVAudioSessionDelegate>
#property (strong) AVPlayer *player;
#property (strong) AVPlayerItem *playerItem;
some important methods of Implementation
-(void)play:(NSString *)audio
{
/* Has the user entered a audio URL? */
NSURL *audioUrl = [NSURL URLWithString:audio];
if ([audioUrl scheme]) /* Sanity check on the URL. */
{
/*
Create an asset for inspection of a resource referenced by a given URL.
Load the values for the asset keys "tracks", "playable".
*/
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:audioUrl options:nil];
NSArray *requestedKeys = [NSArray arrayWithObjects:kTracksKey, kPlayableKey, nil];
/* Tells the asset to load the values of any of the specified keys that are not already loaded. */
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
^{
dispatch_async( dispatch_get_main_queue(),
^{
/* IMPORTANT: Must dispatch to main queue in order to operate on the AVPlayer and AVPlayerItem. */
[self prepareToPlayAsset:asset withKeys:requestedKeys];
});
}];
}
}
- (void)prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys
{
/* Make sure that the value of each key has loaded successfully. */
for (NSString *thisKey in requestedKeys)
{
NSError *error = nil;
AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
if (keyStatus == AVKeyValueStatusFailed)
{
[self assetFailedToPrepareForPlayback:error];
return;
}
/* If you are also implementing the use of -[AVAsset cancelLoading], add your code here to bail
out properly in the case of cancellation. */
}
/* Use the AVAsset playable property to detect whether the asset can be played. */
if (!asset.playable)
{
/* Generate an error describing the failure. */
NSString *localizedDescription = NSLocalizedString(#"Item cannot be played", #"Item cannot be played description");
NSString *localizedFailureReason = NSLocalizedString(#"The assets tracks were loaded, but could not be made playable.", #"Item cannot be played failure reason");
NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
localizedDescription, NSLocalizedDescriptionKey,
localizedFailureReason, NSLocalizedFailureReasonErrorKey,
nil];
NSError *assetCannotBePlayedError = [NSError errorWithDomain:#"StitchedStreamPlayer" code:0 userInfo:errorDict];
/* Display the error to the user. */
[self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
return;
}
/* At this point we're ready to set up for playback of the asset. */
/* Stop observing our prior AVPlayerItem, if we have one. */
if (self.playerItem)
{
/* Remove existing player item key value observers and notifications. */
[self.playerItem removeObserver:self forKeyPath:kStatusKey];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.playerItem];
}
/* Create a new instance of AVPlayerItem from the now successfully loaded AVAsset. */
self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
/* Observe the player item "status" key to determine when it is ready to play. */
[self.playerItem addObserver:self
forKeyPath:kStatusKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:MyStreamingAudioViewControllerPlayerItemStatusObserverContext];
/* When the player item has played to its end time we'll toggle
the movie controller Pause button to be the Play button */
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.playerItem];
/* Create new player, if we don't already have one. */
if (![self player])
{
/* Get a new AVPlayer initialized to play the specified player item. */
[self setPlayer:[AVPlayer playerWithPlayerItem:self.playerItem]];
/* Observe the AVPlayer "currentItem" property to find out when any
AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did
occur.*/
[self.player addObserver:self
forKeyPath:kCurrentItemKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:MyStreamingAudioViewControllerCurrentItemObservationContext];
}
/* Make our new AVPlayerItem the AVPlayer's current item. */
if (self.player.currentItem != self.playerItem)
{
/* Replace the player item with a new player item. The item replacement occurs
asynchronously; observe the currentItem property to find out when the
replacement will/did occur*/
[[self player] replaceCurrentItemWithPlayerItem:self.playerItem];
[self syncPlayPauseButtons];
}
}
- (void)observeValueForKeyPath:(NSString*) path
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context
{
/* AVPlayerItem "status" property value observer. */
if (context == MyStreamingAudioViewControllerPlayerItemStatusObserverContext)
{
[self syncPlayPauseButtons];
AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
switch (status)
{
/* Indicates that the status of the player is not yet known because
it has not tried to load new media resources for playback */
case AVPlayerStatusUnknown:
{
NSLog(#"desconhecido");
}
break;
case AVPlayerStatusReadyToPlay:
{
/* Once the AVPlayerItem becomes ready to play, i.e.
[playerItem status] == AVPlayerItemStatusReadyToPlay,
its duration can be fetched from the item. */
NSLog(#"ready to play");
[player play];
[self.delegate tocandoMusica];
}
break;
case AVPlayerStatusFailed:
{
AVPlayerItem *thePlayerItem = (AVPlayerItem *)object;
[self assetFailedToPrepareForPlayback:thePlayerItem.error];
NSLog(#"falhou");
[self.delegate acabouMusica];
}
break;
}
}
/* AVPlayer "rate" property value observer. */
else if (context == MyStreamingAudioViewControllerRateObservationContext)
{
//[self syncPlayPauseButtons];
}
/* AVPlayer "currentItem" property observer.
Called when the AVPlayer replaceCurrentItemWithPlayerItem:
replacement will/did occur. */
else if (context == MyStreamingAudioViewControllerCurrentItemObservationContext)
{
AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
/* New player item null? */
if (newPlayerItem == (id)[NSNull null])
{
//[self disablePlayerButtons];
//[self disableScrubber];
}
else /* Replacement of player currentItem has occurred */
{
/* Specifies that the player should preserve the video’s aspect ratio and
fit the video within the layer’s bounds. */
[self syncPlayPauseButtons];
}
}
/* Observe the AVPlayer "currentItem.timedMetadata" property to parse the media stream
timed metadata. */
else if (context == MyStreamingAudioViewControllerTimedMetadataObserverContext)
{
//NSArray* array = [[player currentItem] timedMetadata];
//for (AVMetadataItem *metadataItem in array)
//{
//}
}
else
{
[super observeValueForKeyPath:path ofObject:object change:change context:context];
}
return;
}
If you want to take a deep look, just take a look on StitchedStreamPlayer Sample, I have no idea. I have looked at:
Failed to play audio file using AVPlayer in iPhone
memory leak in AudioToolbox library AVAudioPlayer
AudioToolBox leak in iOS6?
and many others..
I have tried to forget all this implementation and use just
player = [AVPlayer playerWithURL:[NSURL URLWithString:url]];
[player play];
but it crashes!
Some idea?
EDITED
I Have tried the MPMoviePlayerController but the same happened, the music started and then the device restarted.
This is the code I have used:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:NULL];
player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:[[arrRadios objectAtIndex:indexPath.row] objectForKey:#"url"]]];
[player play];

Related

How does my app recognize that a song finished?

I´m working on my first app. It plays an mp3 song. Everything works fine, but I need the app to recognize that the sound finished, then change the button “pause” image to the “play” one. I can already do that, but just when the button is pressed, not when song ends.
Another issue is that when the song restarts after stop->play, it doesn´t continue playing at the previous volume setting (got a slider). The player starts at maximum volume. I can change the volume slider position, but I am not able to make the app respect the set volume when restarts.
Any help will be very appreciated!
Thnx a lot.
When using "AVAudioPlayerDelegate protocol delegate", please do not forget to set delegate (for example) as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"33fr" ofType:#"mp3"];
NSURL *fileURL = [NSURL fileURLWithPath:path];
NSError *error = nil;
myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
if (error) {
NSLog(#"error = %#", error);
return;
}
[myPlayer setDelegate:self]; <<=== HERE!
}
I uploaded sample code here. Please check it. ->
http://yahoo.jp/box/X7Hxfk
I have not done much of Objective-C but it looks like you need to implement a delegate.
I quote: "You implement a delegate to handle interruptions (such as an incoming phone call) and to update the user interface when a sound has finished playing."
I am just reading the AVAudioPlayer Class Reference here:
https://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVAudioPlayerClassReference/Reference/Reference.html#//apple_ref/doc/uid/TP40008067
you can use the AVAudioPlayerDelegate protocol delegate
After the audio player is finished playing the song (if it does so
successfully), the audio PlayerDidFinishPlaying:successfully: delegate
method will be called in the delegate object of the audio player. We
can implement this method like below (this method is defined in the
AVAudioPlayerDelegate protocol):
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
if (flag)
{
NSLog(#"Audio player stopped correctly.");
}
else
{ NSLog(#"Audio player did not stop correctly.");
}
if ([player isEqual:self.audioPlayer])
{ self.audioPlayer = nil;
}
else
{ /* This is not the player */ }
}
in .h file add delegate like below code
#interface yourViewController : UIViewController <AVAudioPlayerDelegate>

AVPlayerLayer goes blank after several presentModalView/dismissModalView calls

I have a video playing app which displays nothing in the AVPlayerLayer after repeatedly presenting and hiding the modal view which contains it. If I dismiss the modal view when this happens, the next load usually displays fine (?!!). The black screen issue happens roughly 20% of the time.
I build an AVMutableComposition to make the AVPlayerItem, but this bug happens even if there's only a single sample involved.
The issue can also be reproduced with a lot of app switching and turning music on and off. I do include music controls in my app (along with a simple view which displays the currently playing iTunes track).
This only happens on iOS 4. It used to happen on iOS 5 as well, but when I started recycling the view which contains the AVPlayerLayer, it worked fine. The only things I don't recycle are the AVPlayer and the relevant AVPlayerItem.
Here's how I load the assets and build a player:
- (void)loadAssetsFromFiles:(id)sender {
NSLog(#"loadAssetsFromFiles called");
assert ([assetURL2clipID count] > 0);
self.isWaitingToLoadAssets = NO;
composition = [AVMutableComposition new];
videoComposition = [AVMutableVideoComposition new];
[self releaseAssets];
//We're going to add this asset to a composition, so we'll need to have random access available
//WARNING: This can cause slow initial loading, so consider loading files later and as needed.
NSDictionary *assetOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
//iterate through the asset urls we know we need to load
for (NSURL *fileURL in [assetURL2clipID allKeys])
{
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:assetOptions];
assert(asset);
//index assets by clipID
[assets setObject:asset forKey:[assetURL2clipID objectForKey:fileURL]];
NSString *tracksKey = #"tracks";
[asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:tracksKey] completionHandler:
^{
NSLog(#"an asset completed loading values for keys.");
NSLog(#"Tracks loaded:");
[asset.tracks enumerateObjectsUsingBlock:
^(AVAssetTrack *obj, NSUInteger index, BOOL *stop)
{
NSLog(#"\n mediaType: %#\n trackID: %d\n", obj.mediaType, obj.trackID);
}];
NSArray *metadata = [asset commonMetadata];
for ( AVMetadataItem* item in metadata ) {
NSString *key = [item commonKey];
NSString *value = [item stringValue];
NSLog(#" metadata key = %#, value = %#", key, value);
}
if (!viewIsActive)
{
NSLog(#"An asset finished loading while the player view was inactive! Did you make sure cancelLoading called on this asset?");
}
// Completion handler block.
NSError *error = nil;
AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey error:&error];
if (status == AVKeyValueStatusLoaded && error == nil) {
//if we've loaded all of our assets, it's time to build the composition and prepare the player!
loadedAssets++;
if (loadedAssets == [assets count])
{
CGSize videoSize = [asset naturalSize];
//every video composition needs these set
videoComposition.renderSize = videoSize;
videoComposition.frameDuration = CMTimeMake(1, 30); // 30 fps. TODO: Set this to the framerate of one of the assets
//using the assets we've already got
[self buildCompositions];
self.playerItem = [AVPlayerItem playerItemWithAsset:composition];
self.playerItem.videoComposition = videoComposition;
//TODO: Adding observer stuff should be on the main thread to prevent a partial notification from happening
[playerItem addObserver:self forKeyPath:#"status"
options:0 context:&ItemStatusContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:playerItem];
self.player = [AVPlayer playerWithPlayerItem:playerItem];
[playerView setPlayer:player];
[self.player addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionNew context:nil];
self.isObservingPlayerStatus = YES;
}
} else if (error != nil) {
// Deal with the error appropriately.
NSLog(#"WARNING: An asset's tracks were not loaded, so the composition cannot be completed. Error:\n%#\nstatus of asset: %d", [error localizedDescription], status);
}
else
{
//There was no error but we don't know what the problem was.
NSLog(#"WARNING: An asset's tracks were not loaded, so the composition cannot be completed. No error was reported.\nstatus of asset: %d", status);
}
}];
}
}
That [self buildCompositions] function you see builds an AVVideoComposition to do opacity ramps, but I tried bypassing it and get the same problem.
When profiling the program, CoreAnimation reports a framerate of ~45 FPS when everything is working correctly, and 0-4 FPS when the blank screen rears its presumably ugly head.
This guy seems to have had a similar problem, but for me recycling the views really only fixed things for iOS 5:
Playing many different videos on iphone using AVPlayer

How to resume a recording using AVAudioRecorder?

I am writing an application that uses the AVAudioRecorder class. It works great except for when a phone call comes in. I am handling this per apple's guidelines of using the AVAudioRecorderDelegate methods
– (void) audioRecorderBeginInterruption:
– (void) audioRecorderEndInterruption:
It works great until the interruption ends and I attempt to "resume" the recording by calling the record method again (per the documentation). However it does not resume my recording but instead throws out the old one and starts up an entirely new one in its place. I have not been able to find a solution to this problem, if anyone has figured this out, or if it is a bug with apple's AVAudioRecorder please let me know. I really hope I do not have to write this using AudioQueues.
thanks
Looks like its a bug with apple's API. Great fun....
This was the response we received from a support ticket.
"The behavior you described is a bug and unfortunately there's nothing in the API that you can change to work around to actually append to the original recording. The interruption is resulting in capturing only the audio recorded after the interruption. You could try and stop the recording after the interruption then creating a new file after which would at least not cause the user to loose any information, but the result would be two separate files.
Please file a bug report at for this issue since bugs filed by external developers are critical when iOS engineering is evaluating critical features of fixes to address. It's easily reproducible but if you have a test app you can include please do, iOS engineering like having apps that show the bug directly.
"
My solution was:
Start record on temp file
Watch for AVAudioSessionInterruptionNotificatio
On AVAudioSessionInterruptionTypeBegan - stop the recording.
On AVAudioSessionInterruptionTypeEnded - Start new recording.
When the user stops - Marge the files.
Full Code
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(audioSessionInterruptionNotification:)
name:AVAudioSessionInterruptionNotification
object:audioSession];
-(void)audioSessionInterruptionNotification:(NSNotification*)notification {
NSString* seccReason = #"";
//Check the type of notification, especially if you are sending multiple AVAudioSession events here
NSLog(#"Interruption notification name %#", notification.name);
NSError *err = noErr;
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
seccReason = #"Interruption notification received";
//Check to see if it was a Begin interruption
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
seccReason = #"Interruption began";
NSLog(#"Interruption notification name %# audio pause", notification.name);
dispatch_time_t restartTime = dispatch_time(DISPATCH_TIME_NOW,
0.01 * NSEC_PER_SEC);
dispatch_after(restartTime, dispatch_get_global_queue(0, 0), ^{
AVAudioRecorder *recorder = [[self recorderPool] objectForKey:lastRecID];
if (recorder) {
if(recorder.isRecording) {
[recorder stop];
NSLog(#"Interruption notification name Pauseing recording %#", lastRecID);
} else {
NSLog(#"Interruption notification name Already Paused %#", lastRecID);
}
}else {
NSLog(#"Interruption notification name recording %# not found", lastRecID);
}
NSLog(#"Interruption notification Pauseing recording status %d",recorder.isRecording);
});
} else if([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]){
seccReason = #"Interruption ended!";
NSLog(#"Interruption notification name %# audio resume", notification.name);
//Start New Recording
dispatch_time_t restartTime = dispatch_time(DISPATCH_TIME_NOW,
0.1 * NSEC_PER_SEC);
dispatch_after(restartTime, dispatch_get_global_queue(0, 0), ^{
AVAudioRecorder *recorder = [[self recorderPool] objectForKey:lastRecID];
NSLog(#"Interruption notification Resumeing recording status %d",recorder.isRecording);
if (recorder) {
if(!recorder.isRecording) {
NSString *filePath = [[self orgFileNames] objectForKey:lastRecID];
NSArray * fileNames =[[self fileNames] objectForKey:lastRecID];
NSString *tmpFileName = [self gnrTempFileName:filePath AndNumber:fileNames.count];
[[[self fileNames] objectForKey:lastRecID] addObject:tmpFileName];
NSURL *url = [NSURL fileURLWithPath:tmpFileName];
NSError *error = nil;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:&error];
if (![recorder record]) {
NSLog(#"Interruption notification Error Resumeing recording %#",tempRecorder);
return;
}
[[self recorderPool] setObject:recorder forKey:lastRecID];
NSLog(#"Interruption notification nameResumeing recording %#",lastRecID);
}else {
NSLog(#"Interruption notification Already Recording %d",recorder.isRecording);
}
}else {
NSLog(#"Interruption notification name recording %# not found",lastRecID);
}
});
}
}
}
You will try by using this piece of code
-(IBAction)pauseandplay:(id)sender
{
BOOL status= [player isPlaying];
if(status)
{
[pauseplay setImage:[UIImage imageNamed:#"play.png"]];
[player pause];
}
else
{
[pauseplay setImage:[UIImage imageNamed:#"icon-pause.png"]];
[player play];
updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(updateCurrentTime) userInfo:player repeats:YES];
}
}

didSelectRowAtIndexPath - access tapCount or similar

I'd like to control an instance of AVAudioPlayer from a UITableView item in a didSelectRowAtIndexPath instance.
First touch of the row item triggers 'play' of the AVAudioPlayer. Second touch of the row item triggers 'stop' of the AVAudioPlayer.
I can make the 'play' work but can't get the 'stop' to work. Also, subsequent touches of the row item starts another thread of the audio in the background.
What's the best way to ensure 1 tap starts the audio and a 2nd tap stops it?
Code samples - this method preps audio file and AVAudioPlayer for use:
- (void)makeReadyAudio {
NSString *path = [[NSBundle mainBundle] pathForResource:#"Murderers" ofType:#"mp3"];
NSURL *url = [NSURL fileURLWithPath:path];
NSError *error;
musicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
[musicPlayer prepareToPlay];
}
This block will start & stop the AVAudioPlayer within a case statement in the didSelectRowAtIndexPath section:
case 7: {
//touch to start audio sound playing, touch again to stop playing
[self makeReadyAudio];
if ([musicPlayer isPlaying]) {
[musicPlayer stop];
NSLog(#"musicPlayer tested to be playing, so stop it.");
} else {
[musicPlayer play];
NSLog(#"musicPlayer tested to be *not* playing, so play it.");
}
break;
}
How about:
- (void) didSelectRowAtIndexPath:
{
if (player.isPlaying) {
[player stop];
} else {
[player start];
}
}
In other words, keep the player around and see what it is doing.
The problem is that you call makeReadyAudio every single time the row is tapped. You need to check if you have already done that. For example by doing something like:
if (musicPlayer == nil) {
[self makeReadyAudio];
}

playing background audio on iphone

How can I play a background audio while my application is running?
Thanks.
Okay. This is a solution for background sound on iOS4 & iOS5 (definitely works up to iOS 5.0.1), and I have tested it only with AVPlayer. It should probably work for MPMusicPlayerController too.
Required frameworks:
AVFoundation.framework
AudioToolbox.framework
In your Info.plist, for the key UIBackgroundModes, add audio.
In MyAppDelegate.h:
reference <AVFoundation/AVFoundation.h> & <AudioToolbox/AudioToolbox.h>
implement the protocol AVAudioSessionDelegate:
#interface MyAppDelegate : NSObject <UIApplicationDelegate, AVAudioSessionDelegate>
define a method ensureAudio:
// Ensures the audio routes are setup correctly
- (BOOL) ensureAudio;
In MyAppDelegate.m:
implement the ensureAudio method:
- (BOOL) ensureAudio
{
// Registers this class as the delegate of the audio session (to get background sound)
[[AVAudioSession sharedInstance] setDelegate: self];
// Set category
NSError *categoryError = nil;
if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&categoryError]) {
NSLog(#"Audio session category could not be set");
return NO;
}
// Activate session
NSError *activationError = nil;
if (![[AVAudioSession sharedInstance] setActive: YES error: &activationError]) {
NSLog(#"Audio session could not be activated");
return NO;
}
// Allow the audio to mix with other apps (necessary for background sound)
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
return YES;
}
in the application:didFinishLaunchingWithOptions: method, before you assign the root view controller, run [self ensureAudio]:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Configure audio session
[self ensureAudio];
// Add the navigation controller's view to the window and display.
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
implement the AVAudioSessionDelegate methods like this:
#pragma mark - AVAudioSessionDelegate
- (void) beginInterruption
{
}
- (void) endInterruption
{
// Sometimes the audio session will be reset/stopped by an interruption
[self ensureAudio];
}
- (void) inputIsAvailableChanged:(BOOL)isInputAvailable
{
}
ensure that your app continues to run in the background. You can use the ol' [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler] if you want, but I think there are better ways.
play the actual audio (note I'm using ARC, that's why there are no release calls):
NSURL * file = [[NSBundle mainBundle] URLForResource:#"beep" withExtension:#"aif"];
AVURLAsset * asset = [[AVURLAsset alloc] initWithURL:file options:nil];
AVPlayerItem * item = [[AVPlayerItem alloc] initWithAsset:asset];
__block AVPlayer * player = [[AVPlayer alloc]initWithPlayerItem:item];
__block id finishObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
object:player.currentItem
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
[[NSNotificationCenter defaultCenter] removeObserver:finishObserver];
// Reference the 'player' variable so ARC doesn't release it until it's
// finished playing.
player = nil;
}];
// Trigger asynchronous load
[asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:#"tracks"] completionHandler:^{
// Start playing the beep (watch out - we're not on the main thread here)!
[player play];
}];
And it shooooooooooooould work!
If you are using your app also for recording - then don't forget to change setCategory to AVAudioSessionCategoryPlayAndRecord. In other case you won't be able to record
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error:&setCategoryErr];