123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916 |
- /*
- Copyright (c) 2010 Steve Oldmeadow
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-
- $Id$
- */
- #import "audio/ios/CDAudioManager.h"
- NSString * const kCDN_AudioManagerInitialised = @"kCDN_AudioManagerInitialised";
- //NSOperation object used to asynchronously initialise
- @implementation CDAsynchInitialiser
- -(void) main {
- [super main];
- [CDAudioManager sharedManager];
- }
- @end
- @implementation CDLongAudioSource
- @synthesize audioSourcePlayer, audioSourceFilePath, delegate, backgroundMusic, paused;
- -(id) init {
- if ((self = [super init])) {
- state = kLAS_Init;
- volume = 1.0f;
- mute = NO;
- enabled_ = YES;
- paused = NO;
- stopped = NO;
- }
- return self;
- }
- -(void) dealloc {
- CDLOGINFO(@"Denshion::CDLongAudioSource - deallocating %@", self);
- [audioSourcePlayer release];
- [audioSourceFilePath release];
- [super dealloc];
- }
- -(void) load:(NSString*) filePath {
- //We have already loaded a file previously, check if we are being asked to load the same file
- if (state == kLAS_Init || ![filePath isEqualToString:audioSourceFilePath]) {
- CDLOGINFO(@"Denshion::CDLongAudioSource - Loading new audio source %@",filePath);
- //New file
- if (state != kLAS_Init) {
- [audioSourceFilePath release];//Release old file path
- [audioSourcePlayer release];//Release old AVAudioPlayer, they can't be reused
- }
- audioSourceFilePath = [filePath copy];
- NSError *error = nil;
- NSString *path = [CDUtilities fullPathFromRelativePath:audioSourceFilePath];
- audioSourcePlayer = [(AVAudioPlayer*)[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
- if (error == nil) {
- [audioSourcePlayer prepareToPlay];
- audioSourcePlayer.delegate = self;
- if (delegate && [delegate respondsToSelector:@selector(cdAudioSourceFileDidChange:)]) {
- //Tell our delegate the file has changed
- [delegate cdAudioSourceFileDidChange:self];
- }
- } else {
- CDLOG(@"Denshion::CDLongAudioSource - Error initialising audio player: %@",error);
- }
- } else {
- //Same file - just return it to a consistent state
- [self pause];
- [self rewind];
- }
- audioSourcePlayer.volume = volume;
- audioSourcePlayer.numberOfLoops = numberOfLoops;
- state = kLAS_Loaded;
- }
- -(void) play {
- if (enabled_) {
- systemPaused = NO;
- paused = NO;
- stopped = NO;
- [audioSourcePlayer play];
- } else {
- CDLOGINFO(@"Denshion::CDLongAudioSource long audio source didn't play because it is disabled");
- }
- }
- -(void) stop {
- paused = NO;
- stopped = YES;
- [audioSourcePlayer stop];
- }
- -(void) pause {
- paused = YES;
- [audioSourcePlayer pause];
- }
- -(void) rewind {
- paused = NO;
- [audioSourcePlayer setCurrentTime:0];
- [audioSourcePlayer play];
- stopped = NO;
- }
- -(void) resume {
- if (!stopped) {
- paused = NO;
- [audioSourcePlayer play];
- }
- }
- -(BOOL) isPlaying {
- if (state != kLAS_Init) {
- return [audioSourcePlayer isPlaying];
- } else {
- return NO;
- }
- }
- -(void) setVolume:(float) newVolume
- {
- volume = newVolume;
- if (state != kLAS_Init && !mute) {
- audioSourcePlayer.volume = newVolume;
- }
- }
- -(float) volume
- {
- return volume;
- }
- #pragma mark Audio Interrupt Protocol
- -(BOOL) mute
- {
- return mute;
- }
- -(void) setMute:(BOOL) muteValue
- {
- if (mute != muteValue) {
- if (mute) {
- //Turn sound back on
- audioSourcePlayer.volume = volume;
- } else {
- audioSourcePlayer.volume = 0.0f;
- }
- mute = muteValue;
- }
- }
- -(BOOL) enabled
- {
- return enabled_;
- }
- -(void) setEnabled:(BOOL)enabledValue
- {
- if (enabledValue != enabled_) {
- enabled_ = enabledValue;
- if (!enabled_) {
- //"Stop" the sounds
- [self pause];
- [self rewind];
- }
- }
- }
- -(NSInteger) numberOfLoops {
- return numberOfLoops;
- }
- -(void) setNumberOfLoops:(NSInteger) loopCount
- {
- audioSourcePlayer.numberOfLoops = loopCount;
- numberOfLoops = loopCount;
- }
- - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
- CDLOGINFO(@"Denshion::CDLongAudioSource - audio player finished");
- #if TARGET_IPHONE_SIMULATOR
- CDLOGINFO(@"Denshion::CDLongAudioSource - workaround for OpenAL clobbered audio issue");
- //This is a workaround for an issue in all simulators (tested to 3.1.2). Problem is
- //that OpenAL audio playback is clobbered when an AVAudioPlayer stops. Workaround
- //is to keep the player playing on an endless loop with 0 volume and then when
- //it is played again reset the volume and set loop count appropriately.
- //NB: this workaround is not foolproof but it is good enough for most situations.
- player.numberOfLoops = -1;
- player.volume = 0;
- [player play];
- #endif
- if (delegate && [delegate respondsToSelector:@selector(cdAudioSourceDidFinishPlaying:)]) {
- [delegate cdAudioSourceDidFinishPlaying:self];
- }
- }
- -(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player {
- CDLOGINFO(@"Denshion::CDLongAudioSource - audio player interrupted");
- }
- -(void)audioPlayerEndInterruption:(AVAudioPlayer *)player {
- CDLOGINFO(@"Denshion::CDLongAudioSource - audio player resumed");
- if (self.backgroundMusic) {
- //Check if background music can play as rules may have changed during
- //the interruption. This is to address a specific issue in 4.x when
- //fast task switching
- if([CDAudioManager sharedManager].willPlayBackgroundMusic) {
- [player play];
- }
- } else {
- [player play];
- }
- }
- @end
- @interface CDAudioManager (PrivateMethods)
- -(BOOL) audioSessionSetActive:(BOOL) active;
- -(BOOL) audioSessionSetCategory:(NSString*) category;
- -(void) badAlContextHandler;
- @end
- @implementation CDAudioManager
- #define BACKGROUND_MUSIC_CHANNEL kASC_Left
- @synthesize soundEngine, willPlayBackgroundMusic;
- static CDAudioManager *sharedManager;
- static tAudioManagerState _sharedManagerState = kAMStateUninitialised;
- static tAudioManagerMode configuredMode;
- static BOOL configured = FALSE;
- -(BOOL) audioSessionSetActive:(BOOL) active {
- NSError *activationError = nil;
- if ([[AVAudioSession sharedInstance] setActive:active error:&activationError]) {
- _audioSessionActive = active;
- CDLOGINFO(@"Denshion::CDAudioManager - Audio session set active %i succeeded", active);
- return YES;
- } else {
- //Failed
- CDLOG(@"Denshion::CDAudioManager - Audio session set active %i failed with error %@", active, activationError);
- return NO;
- }
- }
- -(BOOL) audioSessionSetCategory:(NSString*) category {
- NSError *categoryError = nil;
- if ([[AVAudioSession sharedInstance] setCategory:category error:&categoryError]) {
- CDLOGINFO(@"Denshion::CDAudioManager - Audio session set category %@ succeeded", category);
- return YES;
- } else {
- //Failed
- CDLOG(@"Denshion::CDAudioManager - Audio session set category %@ failed with error %@", category, categoryError);
- return NO;
- }
- }
- // Init
- + (CDAudioManager *) sharedManager
- {
- @synchronized(self) {
- if (!sharedManager) {
- if (!configured) {
- //Set defaults here
- configuredMode = kAMM_FxPlusMusicIfNoOtherAudio;
- }
- sharedManager = [[CDAudioManager alloc] init:configuredMode];
- _sharedManagerState = kAMStateInitialised;//This is only really relevant when using asynchronous initialisation
- [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_AudioManagerInitialised object:nil];
- }
- }
- return sharedManager;
- }
- + (tAudioManagerState) sharedManagerState {
- return _sharedManagerState;
- }
- /**
- * Call this to set up audio manager asynchronously. Initialisation is finished when sharedManagerState == kAMStateInitialised
- */
- + (void) initAsynchronously: (tAudioManagerMode) mode {
- @synchronized(self) {
- if (_sharedManagerState == kAMStateUninitialised) {
- _sharedManagerState = kAMStateInitialising;
- [CDAudioManager configure:mode];
- CDAsynchInitialiser *initOp = [[[CDAsynchInitialiser alloc] init] autorelease];
- NSOperationQueue *opQ = [[[NSOperationQueue alloc] init] autorelease];
- [opQ addOperation:initOp];
- }
- }
- }
- + (id) alloc
- {
- @synchronized(self) {
- NSAssert(sharedManager == nil, @"Attempted to allocate a second instance of a singleton.");
- return [super alloc];
- }
- return nil;
- }
- /*
- * Call this method before accessing the shared manager in order to configure the shared audio manager
- */
- + (void) configure: (tAudioManagerMode) mode {
- configuredMode = mode;
- configured = TRUE;
- }
- -(BOOL) isOtherAudioPlaying
- {
- // AudioSessionGetProperty removed from tvOS 9.1
- #if defined(CC_TARGET_OS_TVOS)
- return false;
- #else
- UInt32 isPlaying = 0;
- UInt32 varSize = sizeof(isPlaying);
- AudioSessionGetProperty (kAudioSessionProperty_OtherAudioIsPlaying, &varSize, &isPlaying);
- return (isPlaying != 0);
- #endif
- }
- -(void) setMode:(tAudioManagerMode) mode {
- _mode = mode;
- switch (_mode) {
-
- case kAMM_FxOnly:
- //Share audio with other app
- CDLOGINFO(@"Denshion::CDAudioManager - Audio will be shared");
- //_audioSessionCategory = kAudioSessionCategory_AmbientSound;
- _audioSessionCategory = AVAudioSessionCategoryAmbient;
- willPlayBackgroundMusic = NO;
- break;
-
- case kAMM_FxPlusMusic:
- //Use audio exclusively - if other audio is playing it will be stopped
- CDLOGINFO(@"Denshion::CDAudioManager - Audio will be exclusive");
- //_audioSessionCategory = kAudioSessionCategory_SoloAmbientSound;
- _audioSessionCategory = AVAudioSessionCategorySoloAmbient;
- willPlayBackgroundMusic = YES;
- break;
-
- case kAMM_MediaPlayback:
- //Use audio exclusively, ignore mute switch and sleep
- CDLOGINFO(@"Denshion::CDAudioManager - Media playback mode, audio will be exclusive");
- //_audioSessionCategory = kAudioSessionCategory_MediaPlayback;
- _audioSessionCategory = AVAudioSessionCategoryPlayback;
- willPlayBackgroundMusic = YES;
- break;
-
- case kAMM_PlayAndRecord:
- //Use audio exclusively, ignore mute switch and sleep, has inputs and outputs
- CDLOGINFO(@"Denshion::CDAudioManager - Play and record mode, audio will be exclusive");
- //_audioSessionCategory = kAudioSessionCategory_PlayAndRecord;
- _audioSessionCategory = AVAudioSessionCategoryPlayAndRecord;
- willPlayBackgroundMusic = YES;
- break;
-
- default:
- //kAudioManagerFxPlusMusicIfNoOtherAudio
- if ([self isOtherAudioPlaying]) {
- CDLOGINFO(@"Denshion::CDAudioManager - Other audio is playing audio will be shared");
- //_audioSessionCategory = kAudioSessionCategory_AmbientSound;
- _audioSessionCategory = AVAudioSessionCategoryAmbient;
- willPlayBackgroundMusic = NO;
- } else {
- CDLOGINFO(@"Denshion::CDAudioManager - Other audio is not playing audio will be exclusive");
- //_audioSessionCategory = kAudioSessionCategory_SoloAmbientSound;
- _audioSessionCategory = AVAudioSessionCategorySoloAmbient;
- willPlayBackgroundMusic = YES;
- }
-
- break;
- }
-
- [self audioSessionSetCategory:_audioSessionCategory];
-
- }
- /**
- * This method is used to work around various bugs introduced in 4.x OS versions. In some circumstances the
- * audio session is interrupted but never resumed, this results in the loss of OpenAL audio when following
- * standard practices. If we detect this situation then we will attempt to resume the audio session ourselves.
- * Known triggers: lock the device then unlock it (iOS 4.2 gm), playback a song using MPMediaPlayer (iOS 4.0)
- */
- - (void) badAlContextHandler {
- if (_interrupted && alcGetCurrentContext() == NULL) {
- CDLOG(@"Denshion::CDAudioManager - bad OpenAL context detected, attempting to resume audio session");
- [self audioSessionResumed];
- }
- }
- - (id) init: (tAudioManagerMode) mode {
- if ((self = [super init])) {
- // 'delegate' not supported on tvOS
- #if !defined(CC_TARGET_OS_TVOS)
- //Initialise the audio session
- AVAudioSession* session = [AVAudioSession sharedInstance];
- session.delegate = self;
- #endif
-
- _mode = mode;
- backgroundMusicCompletionSelector = nil;
- _isObservingAppEvents = FALSE;
- _mute = NO;
- _resigned = NO;
- _interrupted = NO;
- enabled_ = YES;
- _audioSessionActive = NO;
- [self setMode:mode];
- soundEngine = [[CDSoundEngine alloc] init];
-
- //Set up audioSource channels
- audioSourceChannels = [[NSMutableArray alloc] init];
- CDLongAudioSource *leftChannel = [[CDLongAudioSource alloc] init];
- leftChannel.backgroundMusic = YES;
- CDLongAudioSource *rightChannel = [[CDLongAudioSource alloc] init];
- rightChannel.backgroundMusic = NO;
- [audioSourceChannels insertObject:leftChannel atIndex:kASC_Left];
- [audioSourceChannels insertObject:rightChannel atIndex:kASC_Right];
- [leftChannel release];
- [rightChannel release];
- //Used to support legacy APIs
- backgroundMusic = [self audioSourceForChannel:BACKGROUND_MUSIC_CHANNEL];
- backgroundMusic.delegate = self;
-
- //Add handler for bad al context messages, these are posted by the sound engine.
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(badAlContextHandler) name:kCDN_BadAlContext object:nil];
- }
- return self;
- }
- -(void) dealloc {
- CDLOGINFO(@"Denshion::CDAudioManager - deallocating");
- [self stopBackgroundMusic];
- [soundEngine release];
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [self audioSessionSetActive:NO];
- [audioSourceChannels release];
- [super dealloc];
- }
- /** Retrieves the audio source for the specified channel */
- -(CDLongAudioSource*) audioSourceForChannel:(tAudioSourceChannel) channel
- {
- return (CDLongAudioSource*)[audioSourceChannels objectAtIndex:channel];
- }
- /** Loads the data from the specified file path to the channel's audio source */
- -(CDLongAudioSource*) audioSourceLoad:(NSString*) filePath channel:(tAudioSourceChannel) channel
- {
- CDLongAudioSource *audioSource = [self audioSourceForChannel:channel];
- if (audioSource) {
- [audioSource load:filePath];
- }
- return audioSource;
- }
- -(BOOL) isBackgroundMusicPlaying {
- return [self.backgroundMusic isPlaying];
- }
- //NB: originally I tried using a route change listener and intended to store the current route,
- //however, on a 3gs running 3.1.2 no route change is generated when the user switches the
- //ringer mute switch to off (i.e. enables sound) therefore polling is the only reliable way to
- //determine ringer switch state
- -(BOOL) isDeviceMuted {
- #if TARGET_IPHONE_SIMULATOR || defined(CC_TARGET_OS_TVOS)
- //Calling audio route stuff on the simulator causes problems
- return NO;
- #else
- CFStringRef newAudioRoute;
- UInt32 propertySize = sizeof (CFStringRef);
-
- AudioSessionGetProperty (
- kAudioSessionProperty_AudioRoute,
- &propertySize,
- &newAudioRoute
- );
-
- if (newAudioRoute == NULL) {
- //Don't expect this to happen but playing safe otherwise a null in the CFStringCompare will cause a crash
- return YES;
- } else {
- CFComparisonResult newDeviceIsMuted = CFStringCompare (
- newAudioRoute,
- (CFStringRef) @"",
- 0
- );
-
- return (newDeviceIsMuted == kCFCompareEqualTo);
- }
- #endif
- }
- #pragma mark Audio Interrupt Protocol
- -(BOOL) mute {
- return _mute;
- }
- -(void) setMute:(BOOL) muteValue {
- if (muteValue != _mute) {
- _mute = muteValue;
- [soundEngine setMute:muteValue];
- for( CDLongAudioSource *audioSource in audioSourceChannels) {
- audioSource.mute = muteValue;
- }
- }
- }
- -(BOOL) enabled {
- return enabled_;
- }
- -(void) setEnabled:(BOOL) enabledValue {
- if (enabledValue != enabled_) {
- enabled_ = enabledValue;
- [soundEngine setEnabled:enabled_];
- for( CDLongAudioSource *audioSource in audioSourceChannels) {
- audioSource.enabled = enabled_;
- }
- }
- }
- -(CDLongAudioSource*) backgroundMusic
- {
- return backgroundMusic;
- }
- //Load background music ready for playing
- -(void) preloadBackgroundMusic:(NSString*) filePath
- {
- [self.backgroundMusic load:filePath];
- }
- -(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop
- {
- [self.backgroundMusic load:filePath];
- if (loop) {
- [self.backgroundMusic setNumberOfLoops:-1];
- } else {
- [self.backgroundMusic setNumberOfLoops:0];
- }
- if (!willPlayBackgroundMusic || _mute) {
- CDLOGINFO(@"Denshion::CDAudioManager - play bgm aborted because audio is not exclusive or sound is muted");
- return;
- }
- [self.backgroundMusic play];
- }
- -(void) stopBackgroundMusic
- {
- [self.backgroundMusic stop];
- }
- -(void) pauseBackgroundMusic
- {
- [self.backgroundMusic pause];
- }
- -(void) resumeBackgroundMusic
- {
- if (!willPlayBackgroundMusic || _mute) {
- CDLOGINFO(@"Denshion::CDAudioManager - resume bgm aborted because audio is not exclusive or sound is muted");
- return;
- }
-
- if (![self.backgroundMusic paused]) {
- return;
- }
-
- [self.backgroundMusic resume];
- }
- -(void) rewindBackgroundMusic
- {
- [self.backgroundMusic rewind];
- }
- -(void) setBackgroundMusicCompletionListener:(id) listener selector:(SEL) selector {
- backgroundMusicCompletionListener = listener;
- backgroundMusicCompletionSelector = selector;
- }
- /*
- * Call this method to have the audio manager automatically handle application resign and
- * become active. Pass a tAudioManagerResignBehavior to indicate the desired behavior
- * for resigning and becoming active again.
- *
- * If autohandle is YES then the applicationWillResignActive and applicationDidBecomActive
- * methods are automatically called, otherwise you must call them yourself at the appropriate time.
- *
- * Based on idea of Dominique Bongard
- */
- -(void) setResignBehavior:(tAudioManagerResignBehavior) resignBehavior autoHandle:(BOOL) autoHandle {
- if (!_isObservingAppEvents && autoHandle) {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:@"UIApplicationWillResignActiveNotification" object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:@"UIApplicationDidBecomeActiveNotification" object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:@"UIApplicationWillTerminateNotification" object:nil];
- _isObservingAppEvents = TRUE;
- }
- _resignBehavior = resignBehavior;
- }
- - (void) applicationWillResignActive {
- _resigned = YES;
-
- //Set the audio session to one that allows sharing so that other audio won't be clobbered on resume
- [self audioSessionSetCategory:AVAudioSessionCategoryAmbient];
-
- switch (_resignBehavior) {
-
- case kAMRBStopPlay:
-
- for( CDLongAudioSource *audioSource in audioSourceChannels) {
- if (!audioSource->systemPaused) {
- if (audioSource.isPlaying) {
- audioSource->systemPaused = YES;
- audioSource->systemPauseLocation = audioSource.audioSourcePlayer.currentTime;
- [audioSource pause];
- } else {
- //Music is either paused or stopped, if it is paused it will be restarted
- //by OS so we will stop it.
- audioSource->systemPaused = NO;
- [audioSource stop];
- }
- }
- }
- break;
-
- case kAMRBStop:
- //Stop music regardless of whether it is playing or not because if it was paused
- //then the OS would resume it
- for( CDLongAudioSource *audioSource in audioSourceChannels) {
- [audioSource stop];
- }
-
- default:
- break;
-
- }
- CDLOGINFO(@"Denshion::CDAudioManager - handled resign active");
- }
- //Called when application resigns active only if setResignBehavior has been called
- - (void) applicationWillResignActive:(NSNotification *) notification
- {
- [self applicationWillResignActive];
- }
- - (void) applicationDidBecomeActive {
-
- if (_resigned) {
- _resigned = NO;
- //Reset the mode incase something changed with audio while we were inactive
- [self setMode:_mode];
- switch (_resignBehavior) {
-
- case kAMRBStopPlay:
-
- //Music had been stopped but stop maintains current time
- //so playing again will continue from where music was before resign active.
- //We check if music can be played because while we were inactive the user might have
- //done something that should force music to not play such as starting a track in the iPod
- if (self.willPlayBackgroundMusic) {
- for( CDLongAudioSource *audioSource in audioSourceChannels) {
- if (audioSource->systemPaused) {
- [audioSource resume];
- audioSource->systemPaused = NO;
- }
- }
- }
- break;
-
- default:
- break;
-
- }
- CDLOGINFO(@"Denshion::CDAudioManager - audio manager handled become active");
- }
- }
- //Called when application becomes active only if setResignBehavior has been called
- - (void) applicationDidBecomeActive:(NSNotification *) notification
- {
- [self applicationDidBecomeActive];
- }
- //Called when application terminates only if setResignBehavior has been called
- - (void) applicationWillTerminate:(NSNotification *) notification
- {
- CDLOGINFO(@"Denshion::CDAudioManager - audio manager handling terminate");
- [self stopBackgroundMusic];
- }
- /** The audio source completed playing */
- - (void) cdAudioSourceDidFinishPlaying:(CDLongAudioSource *) audioSource {
- CDLOGINFO(@"Denshion::CDAudioManager - audio manager got told background music finished");
- if (backgroundMusicCompletionSelector != nil) {
- [backgroundMusicCompletionListener performSelector:backgroundMusicCompletionSelector];
- }
- }
- -(void) beginInterruption {
- CDLOGINFO(@"Denshion::CDAudioManager - begin interruption");
- [self audioSessionInterrupted];
- }
- -(void) endInterruption {
- CDLOGINFO(@"Denshion::CDAudioManager - end interruption");
- [self audioSessionResumed];
- }
- #if __CC_PLATFORM_IOS >= 40000
- -(void) endInterruptionWithFlags:(NSUInteger)flags {
- CDLOGINFO(@"Denshion::CDAudioManager - interruption ended with flags %i",flags);
- if (flags == AVAudioSessionInterruptionFlags_ShouldResume) {
- [self audioSessionResumed];
- }
- }
- #endif
- -(void)audioSessionInterrupted
- {
- if (!_interrupted) {
- CDLOGINFO(@"Denshion::CDAudioManager - Audio session interrupted");
- _interrupted = YES;
- // Deactivate the current audio session
- [self audioSessionSetActive:NO];
-
- if (alcGetCurrentContext() != NULL) {
- CDLOGINFO(@"Denshion::CDAudioManager - Setting OpenAL context to NULL");
- ALenum error = AL_NO_ERROR;
- // set the current context to NULL will 'shutdown' openAL
- alcMakeContextCurrent(NULL);
-
- if((error = alGetError()) != AL_NO_ERROR) {
- CDLOG(@"Denshion::CDAudioManager - Error making context current %x\n", error);
- }
- #pragma unused(error)
- }
- }
- }
- -(void)audioSessionResumed
- {
- if (_interrupted) {
- CDLOGINFO(@"Denshion::CDAudioManager - Audio session resumed");
- _interrupted = NO;
-
- BOOL activationResult = NO;
- // Reactivate the current audio session
- activationResult = [self audioSessionSetActive:YES];
-
- //This code is to handle a problem with iOS 4.0 and 4.01 where reactivating the session can fail if
- //task switching is performed too rapidly. A test case that reliably reproduces the issue is to call the
- //iPhone and then hang up after two rings (timing may vary ;))
- //Basically we keep waiting and trying to let the OS catch up with itself but the number of tries is
- //limited.
- if (!activationResult) {
- CDLOG(@"Denshion::CDAudioManager - Failure reactivating audio session, will try wait-try cycle");
- int activateCount = 0;
- while (!activationResult && activateCount < 10) {
- [NSThread sleepForTimeInterval:0.5];
- activationResult = [self audioSessionSetActive:YES];
- activateCount++;
- CDLOGINFO(@"Denshion::CDAudioManager - Reactivation attempt %i status = %i",activateCount,activationResult);
- }
- }
-
- if (alcGetCurrentContext() == NULL) {
- CDLOGINFO(@"Denshion::CDAudioManager - Restoring OpenAL context");
- ALenum error = AL_NO_ERROR;
- // Restore open al context
- alcMakeContextCurrent([soundEngine openALContext]);
- if((error = alGetError()) != AL_NO_ERROR) {
- CDLOG(@"Denshion::CDAudioManager - Error making context current%x\n", error);
- }
- #pragma unused(error)
- }
- }
- }
- +(void) end {
- [sharedManager release];
- sharedManager = nil;
- }
- @end
- ///////////////////////////////////////////////////////////////////////////////////////
- @implementation CDLongAudioSourceFader
- -(void) _setTargetProperty:(float) newVal {
- ((CDLongAudioSource*)target).volume = newVal;
- }
- -(float) _getTargetProperty {
- return ((CDLongAudioSource*)target).volume;
- }
- -(void) _stopTarget {
- //Pause instead of stop as stop releases resources and causes problems in the simulator
- [((CDLongAudioSource*)target) pause];
- }
- -(Class) _allowableType {
- return [CDLongAudioSource class];
- }
- @end
- ///////////////////////////////////////////////////////////////////////////////////////
- @implementation CDBufferManager
- -(id) initWithEngine:(CDSoundEngine *) theSoundEngine {
- if ((self = [super init])) {
- soundEngine = theSoundEngine;
- loadedBuffers = [[NSMutableDictionary alloc] initWithCapacity:CD_BUFFERS_START];
- freedBuffers = [[NSMutableArray alloc] init];
- nextBufferId = 0;
- }
- return self;
- }
- -(void) dealloc {
- [loadedBuffers release];
- [freedBuffers release];
- [super dealloc];
- }
- -(int) bufferForFile:(NSString*) filePath create:(BOOL) create {
-
- NSNumber* soundId = (NSNumber*)[loadedBuffers objectForKey:filePath];
- if(soundId == nil)
- {
- if (create) {
- NSNumber* bufferId = nil;
- //First try to get a buffer from the free buffers
- if ([freedBuffers count] > 0) {
- bufferId = [[[freedBuffers lastObject] retain] autorelease];
- [freedBuffers removeLastObject];
- CDLOGINFO(@"Denshion::CDBufferManager reusing buffer id %i",[bufferId intValue]);
- } else {
- bufferId = [[NSNumber alloc] initWithInt:nextBufferId];
- [bufferId autorelease];
- CDLOGINFO(@"Denshion::CDBufferManager generating new buffer id %i",[bufferId intValue]);
- nextBufferId++;
- }
-
- if ([soundEngine loadBuffer:[bufferId intValue] filePath:filePath]) {
- //File successfully loaded
- CDLOGINFO(@"Denshion::CDBufferManager buffer loaded %@ %@",bufferId,filePath);
- [loadedBuffers setObject:bufferId forKey:filePath];
- return [bufferId intValue];
- } else {
- //File didn't load, put buffer id on free list
- [freedBuffers addObject:bufferId];
- return kCDNoBuffer;
- }
- } else {
- //No matching buffer was found
- return kCDNoBuffer;
- }
- } else {
- return [soundId intValue];
- }
- }
- -(void) releaseBufferForFile:(NSString *) filePath {
- int bufferId = [self bufferForFile:filePath create:NO];
- if (bufferId != kCDNoBuffer) {
- [soundEngine unloadBuffer:bufferId];
- [loadedBuffers removeObjectForKey:filePath];
- NSNumber *freedBufferId = [[NSNumber alloc] initWithInt:bufferId];
- [freedBufferId autorelease];
- [freedBuffers addObject:freedBufferId];
- }
- }
- @end
|