Reputation: 4658
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?
Upvotes: 7
Views: 2435
Reputation: 1854
In case anyone like me still looking for an answer:
As from docs, AVAudioPlayerNode doesn't support volume property, only AVAudioMixerNode node does. So ensure you envelope your AVAudioPlayerNode into AVAudioMixerNode.
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)
}
Upvotes: 4
Reputation: 126
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];
}
}
Upvotes: 5
Reputation: 61
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*/
Upvotes: 3