Skip to content

Commit

Permalink
Fix #232 Allow media playback to work when device locked or muted
Browse files Browse the repository at this point in the history
Added support for AVAudioSession to allow media files to continue playing when device is locked or muted. This is a change in behavior from 1.0 where playback would pause when the device was locked or could not be heard when the device was muted.
Recording behavior is not changed.  Recording will occur if the mute button is set and will continue recording if the device is locked during recording.
Sending the app to the background will pause playing or recording and resume when the app is returned to the foreground (depending upon the length of the audio).  If you want to stop playing or recording when the app enters the background listen for the pause event and call your media object's stop() or stopRecord() functions.
  • Loading branch information
becka11y committed Sep 30, 2011
1 parent 709842f commit 8821a61
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 17 deletions.
3 changes: 3 additions & 0 deletions PhoneGapLib/Classes/Sound.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,18 @@ typedef NSUInteger MediaMsg;
@interface PGSound : PGPlugin <AVAudioPlayerDelegate, AVAudioRecorderDelegate>
{
NSMutableDictionary* soundCache;
AVAudioSession* avSession;
}
@property (nonatomic, retain) NSMutableDictionary* soundCache;
@property (nonatomic, retain) AVAudioSession* avSession;

- (void) play:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) pause:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) stop:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) release:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) getCurrentPosition:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) prepare:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (BOOL) hasAudioSession;

// helper methods
- (PGAudioFile*) audioFileForResource:(NSString*) resourcePath withId: (NSString*)mediaId;
Expand Down
98 changes: 81 additions & 17 deletions PhoneGapLib/Classes/Sound.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

@implementation PGSound

@synthesize soundCache;
@synthesize soundCache, avSession;
/*
// Maps a url to the original resource path
- (NSString*) resourceForUrl:(NSURL*)url
Expand Down Expand Up @@ -117,6 +117,23 @@ - (PGAudioFile*) audioFileForResource:(NSString*) resourcePath withId: (NSString
}
return audioFile;
}
// returns whether or not audioSession is available - creates it if necessary
- (BOOL) hasAudioSession
{
BOOL bSession = YES;
if (!self.avSession) {
NSError* error = nil;

self.avSession = [AVAudioSession sharedInstance];
if (error) {
// is not fatal if can't get AVAudioSession , just log the error
NSLog(@"error creating audio session: %@", [[error userInfo] description]);
self.avSession = nil;
bSession = NO;
}
}
return bSession;
}

- (void) play:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
Expand Down Expand Up @@ -165,7 +182,7 @@ - (void) play:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
audioFile.player.numberOfLoops = numberOfLoops;
[audioFile.player play];
} */
// error creating the player
// error creating the session or player
jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"PhoneGap.Media.onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_NONE_SUPPORTED];
[super writeJavascript:jsString];
}
Expand All @@ -176,19 +193,33 @@ - (void) play:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
- (BOOL) prepareToPlay: (PGAudioFile*) audioFile withId: (NSString*) mediaId
{
BOOL bError = NO;
NSError* error = nil;
// create the player
NSURL* resourceURL = audioFile.resourceURL;
if ([resourceURL isFileURL]) {
audioFile.player = [[[ AudioPlayer alloc ] initWithContentsOfURL:resourceURL error:&error] autorelease];
} else {
NSData* data = [NSData dataWithContentsOfURL:resourceURL];
audioFile.player = [[[ AudioPlayer alloc ] initWithData:data error:&error] autorelease];
NSError* playerError = nil;
// get the audioSession and set the category to allow Playing when device is locked or ring/silent switch engaged
if ([self hasAudioSession]) {
NSError* err = nil;
[self.avSession setCategory:AVAudioSessionCategoryPlayback error:nil];
if (![self.avSession setActive: YES error: &err]){
// other audio with higher priority that does not allow mixing could cause this to fail
NSLog(@"Unable to play audio: %@", [err localizedFailureReason]);
bError = YES;
}
}

if (error != nil) {
NSLog(@"Failed to initialize AVAudioPlayer: %@\n", error);
if (!bError) {
// create the player
NSURL* resourceURL = audioFile.resourceURL;
if ([resourceURL isFileURL]) {
audioFile.player = [[[ AudioPlayer alloc ] initWithContentsOfURL:resourceURL error:&playerError] autorelease];
} else {
NSData* data = [NSData dataWithContentsOfURL:resourceURL];
audioFile.player = [[[ AudioPlayer alloc ] initWithData:data error:&playerError] autorelease];
}
}
if (playerError != nil) {
NSLog(@"Failed to initialize AVAudioPlayer: %@\n", [playerError localizedFailureReason]);
audioFile.player = nil;
if (self.avSession) {
[self.avSession setActive:NO error:nil];
}
bError = YES;
} else {
audioFile.player.mediaId = mediaId;
Expand Down Expand Up @@ -330,6 +361,10 @@ - (void) release:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)optio
if(audioFile.recorder && [audioFile.recorder isRecording]){
[audioFile.recorder stop];
}
if (self.avSession) {
[self.avSession setActive:NO error: nil];
self.avSession = nil;
}
[[self soundCache] removeObjectForKey: mediaId];
NSLog(@"Media with id %@ released", mediaId);
}
Expand Down Expand Up @@ -377,12 +412,27 @@ - (void) startAudioRecord:(NSMutableArray*)arguments withDict:(NSMutableDictiona
[audioFile.recorder stop];
audioFile.recorder = nil;
}
// create a new recorder for each start record
audioFile.recorder = [[[AudioRecorder alloc] initWithURL:audioFile.resourceURL settings:nil error:&error] autorelease];

// get the audioSession and set the category to allow recording when device is locked or ring/silent switch engaged
if ([self hasAudioSession]) {
[self.avSession setCategory:AVAudioSessionCategoryRecord error:nil];
if (![self.avSession setActive: YES error: &error]){
// other audio with higher priority that does not allow mixing could cause this to fail
NSLog(@"Unable to record audio: %@", [error localizedFailureReason]);
jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"PhoneGap.Media.onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_ABORTED];
[super writeJavascript:jsString];
return;
}
}

// create a new recorder for each start record
audioFile.recorder = [[[AudioRecorder alloc] initWithURL:audioFile.resourceURL settings:nil error:&error] autorelease];

if (error != nil) {
NSLog(@"Failed to initialize AVAudioRecorder: %@\n", error);
audioFile.recorder = nil;
if (self.avSession) {
[self.avSession setActive:NO error:nil];
}
jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"PhoneGap.Media.onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_ABORTED];

} else {
Expand Down Expand Up @@ -436,6 +486,9 @@ - (void)audioRecorderDidFinishRecording:(AVAudioRecorder*)recorder successfully:
} else {
jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"PhoneGap.Media.onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_DECODE];
}
if (self.avSession) {
[self.avSession setActive:NO error:nil];
}
[super writeJavascript:jsString];

}
Expand All @@ -457,6 +510,9 @@ - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer*)player successfully:(BOOL)fl
jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"PhoneGap.Media.onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_DECODE];

}
if (self.avSession) {
[self.avSession setActive:NO error:nil];
}
[super writeJavascript: jsString];

}
Expand All @@ -465,10 +521,18 @@ - (void) onMemoryWarning
{
[[self soundCache] removeAllObjects];
[self setSoundCache: nil];
[self setAvSession: nil];

[super onMemoryWarning];
}

- (void) dealloc
{
[[self soundCache] removeAllObjects];
[self setSoundCache: nil];
[self setAvSession: nil];

[super dealloc];
}
@end

@implementation PGAudioFile
Expand Down

0 comments on commit 8821a61

Please sign in to comment.