Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cross fade between AVAudioPlayers

I want to use AVAudioPlayer and cross fade more than 2 mp3 files.

I have 5 mp3 files and 5 pages "myScrollView" with UIScrollView class and pagingEnabled = YES.

When user move page, I want to play each songs for each page with volume fade out for previous mp3 file and fade in for next mp3.

Please help this problem.

like image 828
fluiday Avatar asked Sep 15 '12 08:09

fluiday


2 Answers

It's a really old thread, but I just looked for a solution concerning the cross fading issue and found it. I solved it like this:

-(void)crossFadePlayerOne:(AVAudioPlayer *)player1 andPlayerTwo:(AVAudioPlayer *)player2 withCompletion:(void(^)())completion{
    if([player1 volume] > 0){
        [player1 setVolume:[player1 volume] - 0.05];
        [player2 setVolume:[player2 volume] + 0.05];

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC),dispatch_get_main_queue(),^{
            [self crossFadePlayerOne:player1 andPlayerTwo:player2 withCompletion:completion];
        });

    }else{
        if(completion){
            completion();
        }
    }
}
like image 63
Paul Fröhling Avatar answered Oct 30 '22 00:10

Paul Fröhling


This is not cross fading operation. But, this can fade-in/out one at a time with new thread using NSOperation and fade-in/out effects can be added to the thread in any order.

I found Linear fade Object MXAudioPlayerFadeOperation created by Andrew Mackenzie-Ross on 30/11/10. mackross.net.

MXAudioPlayerFadeOperation.h

#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

MXAudioPlayerFadeOperation.m

#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

With sample for using AudioManagerController

AudioManagerController.h

/*
 * 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

AudioManagerController.m

/*
 * 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

ref.:hedgewars

like image 21
fluiday Avatar answered Oct 30 '22 02:10

fluiday