copy-paste of the entire code in one place source
Last active
October 21, 2023 21:06
-
-
Save chunkyguy/be6c98b30722dac3af2116c469885a5e to your computer and use it in GitHub Desktop.
How to cross fade between AVAudioPlayers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Hedgewars-iOS, a Hedgewars port for iOS devices | |
| * Copyright (c) 2009-2011 Vittorio Giovara <[email protected]> | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; version 2 of the License | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| * | |
| * File created on 23/09/2011. | |
| */ | |
| #import <Foundation/Foundation.h> | |
| @interface AudioManagerController : NSObject { | |
| } | |
| +(void) playBackgroundMusic; | |
| +(void) pauseBackgroundMusic; | |
| +(void) stopBackgroundMusic; | |
| +(void) fadeInBackgroundMusic; | |
| +(void) fadeOutBackgroundMusic; | |
| +(void) playClickSound; | |
| +(void) playBackSound; | |
| +(void) playSelectSound; | |
| +(void) releaseCache; | |
| @end | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Hedgewars-iOS, a Hedgewars port for iOS devices | |
| * Copyright (c) 2009-2011 Vittorio Giovara <[email protected]> | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; version 2 of the License | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| * | |
| * File created on 23/09/2011. | |
| */ | |
| #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 - | |
| #pragma mark background music control | |
| +(void) loadBackgroundMusic { | |
| NSString *musicString = [[NSBundle mainBundle] pathForResource:@"hwclassic" ofType:@"mp3"]; | |
| backgroundMusic = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:musicString] error:nil]; | |
| backgroundMusic.delegate = nil; | |
| backgroundMusic.volume = 0; | |
| backgroundMusic.numberOfLoops = -1; | |
| [backgroundMusic prepareToPlay]; | |
| } | |
| +(void) playBackgroundMusic { | |
| if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"music"] boolValue] == NO) | |
| return; | |
| if (backgroundMusic == nil) | |
| [AudioManagerController loadBackgroundMusic]; | |
| backgroundMusic.volume = 0.45f; | |
| [backgroundMusic play]; | |
| } | |
| +(void) pauseBackgroundMusic { | |
| [backgroundMusic pause]; | |
| } | |
| +(void) stopBackgroundMusic { | |
| [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 { | |
| // get the filename of the sound file: | |
| NSString *path = [NSString stringWithFormat:@"%@/%@",[[NSBundle mainBundle] resourcePath],snd]; | |
| // declare a system sound id and get a URL for the sound file | |
| SystemSoundID soundID; | |
| NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO]; | |
| // use audio sevices to create and play the sound | |
| AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID); | |
| return soundID; | |
| } | |
| +(void) playClickSound { | |
| if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"sound"] boolValue] == NO) | |
| return; | |
| if (clickSound == -1) | |
| clickSound = [AudioManagerController loadSound:@"clickSound.wav"]; | |
| AudioServicesPlaySystemSound(clickSound); | |
| } | |
| +(void) playBackSound { | |
| if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"sound"] boolValue] == NO) | |
| return; | |
| if (backSound == -1) | |
| backSound = [AudioManagerController loadSound:@"backSound.wav"]; | |
| AudioServicesPlaySystemSound(backSound); | |
| } | |
| +(void) playSelectSound { | |
| if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"sound"] boolValue] == NO) | |
| return; | |
| if (selSound == -1) | |
| selSound = [AudioManagerController loadSound:@"selSound.wav"]; | |
| AudioServicesPlaySystemSound(selSound); | |
| } | |
| #pragma mark - | |
| #pragma mark memory management | |
| +(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; | |
| MSG_MEMCLEAN(); | |
| } | |
| @end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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, strong, 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #import "MXAudioPlayerFadeOperation.h" | |
| #import <AVFoundation/AVFoundation.h> | |
| #define SKVolumeChangesPerSecond 15 | |
| @interface MXAudioPlayerFadeOperation () | |
| - (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; | |
| } | |
| return result; | |
| } | |
| - (void)setAudioPlayer:(AVAudioPlayer *)anAudioPlayer { | |
| @synchronized(self) { | |
| if (_audioPlayer != anAudioPlayer) { | |
| _audioPlayer = nil; | |
| _audioPlayer = anAudioPlayer; | |
| } | |
| } | |
| } | |
| #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 { | |
| NSLog(@"Failed to init class (%@) with AVAudioPlayer instance, use initFadeWithAudioPlayer:",[self class]); | |
| return nil; | |
| } | |
| - (void)main { | |
| @autoreleasepool { | |
| [NSThread sleepForTimeInterval:_delay]; | |
| if ([self.audioPlayer isKindOfClass:[AVAudioPlayer class]]) { | |
| [self beginFadeOperation]; | |
| } | |
| else { | |
| NSLog(@"AudioPlayerFadeOperation began with invalid AVAudioPlayer"); | |
| } | |
| } | |
| } | |
| - (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]; | |
| } | |
| @end | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class MXAudioPlayerFadeOperation: NSOperation { | |
| private static let VolumeChangesPerSecond = 15.0 | |
| // The AVAudioPlayer that the volume fade will be applied to. | |
| // Retained until the fade is completed. | |
| // Must be set with init method. | |
| private let audioPlayer: AVAudioPlayer | |
| // The duration of the volume fade. | |
| // Default value is 1.0 | |
| private let fadeDuration: NSTimeInterval | |
| // The delay before the volume fade begins. | |
| // Default value is 0.0 | |
| private let delay: NSTimeInterval | |
| // The volume that will be faded to. | |
| // Default value is 0.0 | |
| private let finishVolume: Double | |
| // 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 | |
| private let pauseAfterFade: Bool | |
| // If YES, when the fade has completed the audio player will be sent a stop message. | |
| // Default value is NO. | |
| private let stopAfterFade: Bool | |
| // If YES, audio player will be sent a play message after the delay. | |
| // Default value is YES. | |
| private let playBeforeFade: Bool | |
| // Init Methods | |
| init(audioPlayer player: AVAudioPlayer, toVolume volume: Double = 0.0, overDuration duration :NSTimeInterval = 1.0, withDelay timeDelay: NSTimeInterval = 0.0) { | |
| self.audioPlayer = player | |
| self.fadeDuration = duration | |
| self.finishVolume = volume | |
| self.playBeforeFade = true | |
| self.stopAfterFade = false | |
| self.pauseAfterFade = (volume == 0.0) | |
| self.delay = timeDelay | |
| player.prepareToPlay() | |
| } | |
| override func main() { | |
| NSThread.sleepForTimeInterval(delay) | |
| beginFadeOperation() | |
| } | |
| } | |
| private extension MXAudioPlayerFadeOperation { | |
| private func beginFadeOperation() { | |
| if !audioPlayer.playing && playBeforeFade { | |
| audioPlayer.play() | |
| } | |
| if fadeDuration != 0.0 { | |
| let sleepInterval = 1.0/MXAudioPlayerFadeOperation.VolumeChangesPerSecond | |
| let startTime = NSDate().timeIntervalSinceReferenceDate | |
| var now = startTime | |
| let startVolume = Double(audioPlayer.volume) | |
| while now < (startTime + fadeDuration) { | |
| let ratioOfFadeCompleted = (now - startTime)/fadeDuration | |
| let volume = (finishVolume * ratioOfFadeCompleted) + (startVolume * (1.0-ratioOfFadeCompleted)) | |
| audioPlayer.volume = Float(volume) | |
| NSThread.sleepForTimeInterval(sleepInterval) | |
| now = NSDate().timeIntervalSinceReferenceDate | |
| } | |
| audioPlayer.volume = Float(finishVolume) | |
| finishFadeOperation() | |
| } else { | |
| audioPlayer.volume = Float(finishVolume) | |
| finishFadeOperation() | |
| } | |
| } | |
| private func finishFadeOperation() { | |
| if audioPlayer.playing && pauseAfterFade { | |
| audioPlayer.pause() | |
| } | |
| if audioPlayer.playing && stopAfterFade { | |
| audioPlayer.stop() | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment