AVAudioPlayer fade volume out

Viewed 31944

I have an AVAudioPlayer playing some audio (duh!)

The audio is initiated when the user presses a button. When they release it I want the audio to fade out.

I am using Interface builder...so I am trying to hook up a function on "touch up inside" that fades the audio out over 1 sec then stops.

Any ideas?

Thanks

13 Answers

An extension for swift 3 inspired from the most voted answer. For those of you who like copy-pasting :)

extension AVAudioPlayer {
    func fadeOut() {
        if volume > 0.1 {
            // Fade
            volume -= 0.1
            perform(#selector(fadeOut), with: nil, afterDelay: 0.1)
        } else {
            // Stop and get the sound ready for playing again
            stop()
            prepareToPlay()
            volume = 1
        }
    }
}

Swift 3

I like Ambroise Collon answer's , so i voted up but Swift is statically typed so the performSelector: methods are to fall by the wayside, maybe an alternative could be dispatch async (in this version I've added also the destination volume as parameter)

func dispatchDelay(delay:Double, closure:@escaping ()->()) {
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: closure)
}

extension AVAudioPlayer {
    func fadeOut(vol:Float) {
        if volume > vol {
            //print("vol is : \(vol) and volume is: \(volume)")
            dispatchDelay(delay: 0.1, closure: {
                [weak self] in
                guard let strongSelf = self else { return }
                strongSelf.volume -= 0.01
                strongSelf.fadeOut(vol: vol)
            })
        } else {
            volume = vol
        }
    }
    func fadeIn(vol:Float) {
        if volume < vol {
            dispatchDelay(delay: 0.1, closure: {
                [weak self] in
                guard let strongSelf = self else { return }
                strongSelf.volume += 0.01
                strongSelf.fadeIn(vol: vol)
            })
        } else {
            volume = vol
        }
    }
}

How about this: (if time passed in is negative then fade out the sound, otherwise fade in)

- (void) fadeInOutVolumeOverTime: (NSNumber *)time
{
#define fade_out_steps  0.1
    float           theVolume = player.volume;
    NSTimeInterval  theTime = [time doubleValue];
    int             sign = (theTime >= 0) ? 1 : -1;

// before we call this, if we are fading out, we save the volume
// so that we can restore back to that level in the fade in
    if ((sign == 1) &&
            ((theVolume >= savedVolume) ||
                            (theTime == 0))) {
        player.volume = savedVolume;
    }
    else if ((sign == -1) && (theVolume <= 0)) {
        NSLog(@"fading");
        [player pause];
        [self performSelector:@selector(fadeInOutVolumeOverTime:) withObject:[NSNumber numberWithDouble:0] afterDelay:1.0];

    }
    else {
        theTime *= fade_out_steps;
        player.volume = theVolume + fade_out_steps * sign;
        [self performSelector:@selector(fadeInOutVolumeOverTime:) withObject:time afterDelay:fabs(theTime)];
    }
}

A very good solution to this problem is available from Nick Lockwood at objc.io. It leverages Core Animation to time volume changes which can come in handy if one needs to synchronise UI animations with audio volume changes, including interactive animations.

Related