6

I've got an AVAudioEngine setup with a AVAudioPlayerNode that is playing some background music.

I'm trying to find a best approach to create a volume fadeout on the node over a 2 second timeframe. I'm considering using CADisplayLink in order to do this. I was wondering if somebody had experience with this scenario and could advise me on their approach?

Cœur
  • 32,421
  • 21
  • 173
  • 232
Danny Bravo
  • 4,205
  • 1
  • 21
  • 40

3 Answers3

5

My approach is below. Note that I assign the timer to a member var so I can invalidate it at other points (viewWillDisappear, delloc, etc.). I was worried that it wouldn't sound smooth, but I tried it and it works fine, didn't need to use CADisplayLink.

- (void)fadeOutAudioWithDuration:(double)duration {
    double timerInterval = 0.1;
    NSNumber *volumeInterval = [NSNumber numberWithDouble:(timerInterval / duration)];
    self.fadeOutTimer = [NSTimer scheduledTimerWithTimeInterval:timerInterval target:self selector:@selector(fadeOutTimerDidFire:) userInfo:volumeInterval repeats:YES];
}

- (void)fadeOutTimerDidFire:(NSTimer *)timer {
    float volumeInterval = ((NSNumber *)timer.userInfo).floatValue;
    float currentVolume = self.audioEngine.mainMixerNode.outputVolume;
    float newValue = MAX(currentVolume - volumeInterval, 0.0f);
    self.audioEngine.mainMixerNode.outputVolume = newValue;
    if (newValue == 0.0f) {
        [timer invalidate];
    }
}
Nigam Shah
  • 116
  • 1
  • 4
2

You can use global gain in EQ.

for example

AVAudioUnitEQ *Volume;
Volume = [[AVAudioUnitEQ alloc] init];
[engine attachNode:Volume];
[engine connect:Volume to:engine.outputNode format:nil];

And then

Volume.globalGain = /*here your floatValue*/
Ray
  • 51
  • 5
0

In case anyone like me still looking for an answer:

  1. As from docs, AVAudioPlayerNode doesn't support volume property, only AVAudioMixerNode node does. So ensure you envelope your AVAudioPlayerNode into AVAudioMixerNode.

  2. Here's a code used to fade in, fade out and generally fade (Swift 5)

     typealias Completion = (() -> Void)
    
     let mixer = AVAudioMixerNode()
    
     func fade(from: Float, to: Float, duration: TimeInterval, completion: Completion?) {
         let stepTime = 0.01
         let times = duration / stepTime
         let step = (to - from) / Float(times)
         for i in 0...Int(times) {
             DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * stepTime) {
                 mixer.volume = from + Float(i) * step
    
                 if i == Int(times) {
                     completion?()
                 }
             }
         }
     }
    
     func fadeIn(duration: TimeInterval = 1.3, completion: Completion? = nil) {
         fade(from: 0, to: 1, duration: duration, completion: completion)
     }
    
     func fadeOut(duration: TimeInterval = 1.3, completion: Completion? = nil) {
         fade(from: 1, to: 0, duration: duration, completion: completion)
     }
    
iago849
  • 1,650
  • 1
  • 12
  • 9