ios: added MXAudioPlayerFadeOperation to allow easy fade in and out of background music
--- 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;
--- 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 <AudioToolbox/AudioToolbox.h>
-
+#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;
--- 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];
--- 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 = "<group>"; };
615AD9E8120764CA00F2FF04 /* backButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = backButton.png; path = Resources/Frontend/backButton.png; sourceTree = "<group>"; };
615AD9EA1207654E00F2FF04 /* helpButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = helpButton.png; path = Resources/Frontend/helpButton.png; sourceTree = "<group>"; };
+ 615E755814E41E8C00FBA131 /* MXAudioPlayerFadeOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXAudioPlayerFadeOperation.h; sourceTree = "<group>"; };
+ 615E755914E41E8C00FBA131 /* MXAudioPlayerFadeOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXAudioPlayerFadeOperation.m; sourceTree = "<group>"; };
615FEAD912A2A4C10098EE92 /* checkbox@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "checkbox@2x.png"; path = "Resources/Icons/checkbox@2x.png"; sourceTree = "<group>"; };
615FEADE12A2A6640098EE92 /* localplayButton@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "localplayButton@2x~iphone.png"; path = "Resources/Frontend/localplayButton@2x~iphone.png"; sourceTree = "<group>"; };
615FEADF12A2A6640098EE92 /* localplayButton~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "localplayButton~ipad.png"; path = "Resources/Frontend/localplayButton~ipad.png"; sourceTree = "<group>"; };
@@ -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;
};
--- /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 <Foundation/Foundation.h>
+
+@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
--- /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 <AVFoundation/AVFoundation.h>
+
+#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
+