# HG changeset patch # User koda # Date 1328804885 -3600 # Node ID 6aeaba3ee584763c4b525a9c03e5eb4605063e41 # Parent 1edd500b24713687250909b2bf5a0ae81313de0e ios: added MXAudioPlayerFadeOperation to allow easy fade in and out of background music diff -r 1edd500b2471 -r 6aeaba3ee584 project_files/HedgewarsMobile/Classes/AudioManagerController.h --- a/project_files/HedgewarsMobile/Classes/AudioManagerController.h Thu Feb 09 16:31:02 2012 +0100 +++ b/project_files/HedgewarsMobile/Classes/AudioManagerController.h Thu Feb 09 17:28:05 2012 +0100 @@ -30,6 +30,9 @@ +(void) pauseBackgroundMusic; +(void) stopBackgroundMusic; ++(void) fadeInBackgroundMusic; ++(void) fadeOutBackgroundMusic; + +(void) playClickSound; +(void) playBackSound; +(void) playSelectSound; diff -r 1edd500b2471 -r 6aeaba3ee584 project_files/HedgewarsMobile/Classes/AudioManagerController.m --- a/project_files/HedgewarsMobile/Classes/AudioManagerController.m Thu Feb 09 16:31:02 2012 +0100 +++ b/project_files/HedgewarsMobile/Classes/AudioManagerController.m Thu Feb 09 17:28:05 2012 +0100 @@ -22,13 +22,18 @@ #import "AudioManagerController.h" #import "AVFoundation/AVAudioPlayer.h" #import - +#import "MXAudioPlayerFadeOperation.h" static AVAudioPlayer *backgroundMusic = nil; static SystemSoundID clickSound = -1; static SystemSoundID backSound = -1; static SystemSoundID selSound = -1; +static NSOperationQueue *audioFaderQueue = nil; +static MXAudioPlayerFadeOperation *fadeIn = nil; +static MXAudioPlayerFadeOperation *fadeOut = nil; + + @implementation AudioManagerController #pragma mark - @@ -38,7 +43,7 @@ backgroundMusic = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:musicString] error:nil]; backgroundMusic.delegate = nil; - backgroundMusic.volume = 0.4f; + backgroundMusic.volume = 0; backgroundMusic.numberOfLoops = -1; [backgroundMusic prepareToPlay]; } @@ -50,6 +55,7 @@ if (backgroundMusic == nil) [AudioManagerController loadBackgroundMusic]; + backgroundMusic.volume = 0.45f; [backgroundMusic play]; } @@ -61,6 +67,28 @@ [backgroundMusic stop]; } ++(void) fadeOutBackgroundMusic { + if (audioFaderQueue == nil) + audioFaderQueue = [[NSOperationQueue alloc] init]; + if (backgroundMusic == nil) + [AudioManagerController loadBackgroundMusic]; + if (fadeOut == nil) + fadeOut = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:backgroundMusic toVolume:0.0 overDuration:3.0]; + + [audioFaderQueue addOperation:fadeOut]; +} + ++(void) fadeInBackgroundMusic { + if (audioFaderQueue == nil) + audioFaderQueue = [[NSOperationQueue alloc] init]; + if (backgroundMusic == nil) + [AudioManagerController loadBackgroundMusic]; + if (fadeIn == nil) + fadeIn = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:backgroundMusic toVolume:0.45 overDuration:2.0]; + + [audioFaderQueue addOperation:fadeIn]; +} + #pragma mark - #pragma mark sound effects control +(SystemSoundID) loadSound:(NSString *)snd { @@ -111,6 +139,9 @@ +(void) releaseCache { [backgroundMusic stop]; [backgroundMusic release], backgroundMusic = nil; + [fadeOut release], fadeOut = nil; + [fadeIn release], fadeIn = nil; + [audioFaderQueue release], audioFaderQueue = nil; AudioServicesDisposeSystemSoundID(clickSound), clickSound = -1; AudioServicesDisposeSystemSoundID(backSound), backSound = -1; AudioServicesDisposeSystemSoundID(selSound), selSound = -1; diff -r 1edd500b2471 -r 6aeaba3ee584 project_files/HedgewarsMobile/Classes/GameInterfaceBridge.m --- a/project_files/HedgewarsMobile/Classes/GameInterfaceBridge.m Thu Feb 09 16:31:02 2012 +0100 +++ b/project_files/HedgewarsMobile/Classes/GameInterfaceBridge.m Thu Feb 09 17:28:05 2012 +0100 @@ -36,7 +36,7 @@ // prepares the controllers for hosting a game -(void) earlyEngineLaunch:(NSDictionary *)optionsOrNil { [self retain]; - [AudioManagerController stopBackgroundMusic]; + [AudioManagerController fadeOutBackgroundMusic]; EngineProtocolNetwork *engineProtocol = [[EngineProtocolNetwork alloc] init]; self.proto = engineProtocol; @@ -107,7 +107,7 @@ [[NSFileManager defaultManager] removeItemAtPath:self.savePath error:nil]; // restart music and we're done - [AudioManagerController playBackgroundMusic]; + [AudioManagerController fadeInBackgroundMusic]; [HWUtils setGameStatus:gsNone]; [HWUtils setGameType:gtNone]; [self release]; diff -r 1edd500b2471 -r 6aeaba3ee584 project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj --- a/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj Thu Feb 09 16:31:02 2012 +0100 +++ b/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj Thu Feb 09 17:28:05 2012 +0100 @@ -89,6 +89,7 @@ 615AD96212073B4D00F2FF04 /* startGameButton.png in Resources */ = {isa = PBXBuildFile; fileRef = 615AD96112073B4D00F2FF04 /* startGameButton.png */; }; 615AD9E9120764CA00F2FF04 /* backButton.png in Resources */ = {isa = PBXBuildFile; fileRef = 615AD9E8120764CA00F2FF04 /* backButton.png */; }; 615AD9EB1207654E00F2FF04 /* helpButton.png in Resources */ = {isa = PBXBuildFile; fileRef = 615AD9EA1207654E00F2FF04 /* helpButton.png */; }; + 615E755A14E41E8C00FBA131 /* MXAudioPlayerFadeOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 615E755914E41E8C00FBA131 /* MXAudioPlayerFadeOperation.m */; }; 615FEAE212A2A6640098EE92 /* localplayButton~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 615FEADF12A2A6640098EE92 /* localplayButton~ipad.png */; }; 615FEAE312A2A6640098EE92 /* localplayButton~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = 615FEAE012A2A6640098EE92 /* localplayButton~iphone.png */; }; 6163EE7E11CC2600001C0453 /* SingleWeaponViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6163EE7D11CC2600001C0453 /* SingleWeaponViewController.m */; }; @@ -437,6 +438,8 @@ 615AD96112073B4D00F2FF04 /* startGameButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = startGameButton.png; path = Resources/Frontend/startGameButton.png; sourceTree = ""; }; 615AD9E8120764CA00F2FF04 /* backButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = backButton.png; path = Resources/Frontend/backButton.png; sourceTree = ""; }; 615AD9EA1207654E00F2FF04 /* helpButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = helpButton.png; path = Resources/Frontend/helpButton.png; sourceTree = ""; }; + 615E755814E41E8C00FBA131 /* MXAudioPlayerFadeOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXAudioPlayerFadeOperation.h; sourceTree = ""; }; + 615E755914E41E8C00FBA131 /* MXAudioPlayerFadeOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXAudioPlayerFadeOperation.m; sourceTree = ""; }; 615FEAD912A2A4C10098EE92 /* checkbox@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "checkbox@2x.png"; path = "Resources/Icons/checkbox@2x.png"; sourceTree = ""; }; 615FEADE12A2A6640098EE92 /* localplayButton@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "localplayButton@2x~iphone.png"; path = "Resources/Frontend/localplayButton@2x~iphone.png"; sourceTree = ""; }; 615FEADF12A2A6640098EE92 /* localplayButton~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "localplayButton~ipad.png"; path = "Resources/Frontend/localplayButton~ipad.png"; sourceTree = ""; }; @@ -1162,6 +1165,8 @@ 61F8535314578999002CA294 /* Helpers */ = { isa = PBXGroup; children = ( + 615E755814E41E8C00FBA131 /* MXAudioPlayerFadeOperation.h */, + 615E755914E41E8C00FBA131 /* MXAudioPlayerFadeOperation.m */, 61C28D3D142D380400DA16C2 /* AudioManagerController.h */, 61C28D3E142D380400DA16C2 /* AudioManagerController.m */, 6165922411CA9BD500D6E256 /* CGPointUtils.h */, @@ -1734,6 +1739,7 @@ 61D08D7514AEA7FE0007C078 /* uGearsList.pas in Sources */, 61D08D7614AEA7FE0007C078 /* uGearsUtils.pas in Sources */, 610C8E3714E018D200CF5C4C /* ValueTrackingSliderView.m in Sources */, + 615E755A14E41E8C00FBA131 /* MXAudioPlayerFadeOperation.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff -r 1edd500b2471 -r 6aeaba3ee584 project_files/HedgewarsMobile/MXAudioPlayerFadeOperation.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/HedgewarsMobile/MXAudioPlayerFadeOperation.h Thu Feb 09 17:28:05 2012 +0100 @@ -0,0 +1,55 @@ +// MXAudioPlayerFadeOperation.h +// +// Created by Andrew Mackenzie-Ross on 30/11/10. +// mackross.net +// + +#import + +@class AVAudioPlayer; +@interface MXAudioPlayerFadeOperation : NSOperation { + AVAudioPlayer *_audioPlayer; + NSTimeInterval _fadeDuration; + NSTimeInterval _delay; + float _finishVolume; + BOOL _pauseAfterFade; + BOOL _stopAfterFade; + BOOL _playBeforeFade; +} + +// The AVAudioPlayer that the volume fade will be applied to. +// Retained until the fade is completed. +// Must be set with init method. +@property (nonatomic, retain, readonly) AVAudioPlayer *audioPlayer; + +// The duration of the volume fade. +// Default value is 1.0 +@property (nonatomic, assign) NSTimeInterval fadeDuration; + +// The delay before the volume fade begins. +// Default value is 0.0 +@property (nonatomic, assign) NSTimeInterval delay; + +// The volume that will be faded to. +// Default value is 0.0 +@property (nonatomic, assign) float finishVolume; + +// If YES, audio player will be sent a pause message when the fade has completed. +// Default value is NO, however, if finishVolume is 0.0, default is YES +@property (nonatomic, assign) BOOL pauseAfterFade; + +// If YES, when the fade has completed the audio player will be sent a stop message. +// Default value is NO. +@property (nonatomic, assign) BOOL stopAfterFade; + +// If YES, audio player will be sent a play message after the delay. +// Default value is YES. +@property (nonatomic, assign) BOOL playBeforeFade; + +// Init Methods +- (id)initFadeWithAudioPlayer:(AVAudioPlayer*)player toVolume:(float)volume overDuration:(NSTimeInterval)duration withDelay:(NSTimeInterval)timeDelay; +- (id)initFadeWithAudioPlayer:(AVAudioPlayer*)player toVolume:(float)volume overDuration:(NSTimeInterval)duration; +- (id)initFadeWithAudioPlayer:(AVAudioPlayer*)player toVolume:(float)volume; +- (id)initFadeWithAudioPlayer:(AVAudioPlayer*)player; + +@end diff -r 1edd500b2471 -r 6aeaba3ee584 project_files/HedgewarsMobile/MXAudioPlayerFadeOperation.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/HedgewarsMobile/MXAudioPlayerFadeOperation.m Thu Feb 09 17:28:05 2012 +0100 @@ -0,0 +1,133 @@ +// MXAudioPlayerFadeOperation.m +// +// Created by Andrew Mackenzie-Ross on 30/11/10. +// mackross.net. +// + +#import "MXAudioPlayerFadeOperation.h" +#import + +#define SKVolumeChangesPerSecond 15 + +@interface MXAudioPlayerFadeOperation () +@property (nonatomic, retain, readwrite) AVAudioPlayer *audioPlayer; +- (void)beginFadeOperation; +- (void)finishFadeOperation; +@end + +@implementation MXAudioPlayerFadeOperation +#pragma mark - +#pragma mark Properties +@synthesize audioPlayer = _audioPlayer; +@synthesize fadeDuration = _fadeDuration; +@synthesize finishVolume = _finishVolume; +@synthesize playBeforeFade = _playBeforeFade; +@synthesize pauseAfterFade = _pauseAfterFade; +@synthesize stopAfterFade = _stopAfterFade; +@synthesize delay = _delay; + +#pragma mark - +#pragma mark Accessors +- (AVAudioPlayer *)audioPlayer { + AVAudioPlayer *result; + @synchronized(self) { + result = [_audioPlayer retain]; + } + return [result autorelease]; +} + +- (void)setAudioPlayer:(AVAudioPlayer *)anAudioPlayer { + @synchronized(self) { + if (_audioPlayer != anAudioPlayer) { + [_audioPlayer release]; + _audioPlayer = [anAudioPlayer retain]; + } + } +} + +#pragma mark - +#pragma mark NSOperation +-(id) initFadeWithAudioPlayer:(AVAudioPlayer*)player toVolume:(float)volume overDuration:(NSTimeInterval)duration withDelay:(NSTimeInterval)timeDelay { + if (self = [super init]) { + self.audioPlayer = player; + [player prepareToPlay]; + _fadeDuration = duration; + _finishVolume = volume; + _playBeforeFade = YES; + _stopAfterFade = NO; + _pauseAfterFade = (volume == 0.0) ? YES : NO; + _delay = timeDelay; + } + return self; +} + +- (id)initFadeWithAudioPlayer:(AVAudioPlayer*)player toVolume:(float)volume overDuration:(NSTimeInterval)duration { + return [self initFadeWithAudioPlayer:player toVolume:volume overDuration:duration withDelay:0.0]; +} + +- (id)initFadeWithAudioPlayer:(AVAudioPlayer*)player toVolume:(float)volume { + return [self initFadeWithAudioPlayer:player toVolume:volume overDuration:1.0]; +} + +- (id)initFadeWithAudioPlayer:(AVAudioPlayer*)player { + return [self initFadeWithAudioPlayer:player toVolume:0.0]; +} + +- (id) init { + ALog(@"Failed to init class (%@) with AVAudioPlayer instance, use initFadeWithAudioPlayer:",[self class]); + return nil; +} + +- (void)main { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [NSThread sleepForTimeInterval:_delay]; + if ([self.audioPlayer isKindOfClass:[AVAudioPlayer class]]) { + [self beginFadeOperation]; + } + else { + ALog(@"AudioPlayerFadeOperation began with invalid AVAudioPlayer"); + } + + [pool release]; +} + +- (void)beginFadeOperation { + if (![self.audioPlayer isPlaying] && _playBeforeFade) [self.audioPlayer play]; + + if (_fadeDuration != 0.0) { + + NSTimeInterval sleepInterval = (1.0 / SKVolumeChangesPerSecond); + NSTimeInterval startTime = [[NSDate date] timeIntervalSinceReferenceDate]; + NSTimeInterval now = startTime; + + float startVolume = [self.audioPlayer volume]; + + while (now < (startTime + _fadeDuration)) { + float ratioOfFadeCompleted = (now - startTime)/_fadeDuration; + float volume = (_finishVolume * ratioOfFadeCompleted) + (startVolume * (1-ratioOfFadeCompleted)); + [self.audioPlayer setVolume:volume]; + [NSThread sleepForTimeInterval:sleepInterval]; + now = [[NSDate date] timeIntervalSinceReferenceDate]; + } + + [self.audioPlayer setVolume:_finishVolume]; + [self finishFadeOperation]; + } + else { + [self.audioPlayer setVolume:_finishVolume]; + [self finishFadeOperation]; + } +} + +- (void)finishFadeOperation { + if ([self.audioPlayer isPlaying] && _pauseAfterFade) [self.audioPlayer pause]; + if ([self.audioPlayer isPlaying] && _stopAfterFade) [self.audioPlayer stop]; +} + +- (void)dealloc { + releaseAndNil(_audioPlayer); + [super dealloc]; +} + +@end +