-
Notifications
You must be signed in to change notification settings - Fork 12
/
iiFaderForAvAudioPlayer.swift
166 lines (123 loc) · 4.7 KB
/
iiFaderForAvAudioPlayer.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import Foundation
import AVFoundation
let iiFaderForAvAudioPlayer_defaultFadeDurationSeconds = 3.0
let iiFaderForAvAudioPlayer_defaultVelocity = 2.0
public class iiFaderForAvAudioPlayer: NSObject {
let player: AVAudioPlayer
private var timer: NSTimer?
// The higher the number - the higher the quality of fade
// and it will consume more CPU.
var volumeAlterationsPerSecond = 30.0
private var fadeDurationSeconds = iiFaderForAvAudioPlayer_defaultFadeDurationSeconds
private var fadeVelocity = iiFaderForAvAudioPlayer_defaultVelocity
private var fromVolume = 0.0
private var toVolume = 0.0
private var currentStep = 0
private var onFinished: ((Bool)->())? = nil
init(player: AVAudioPlayer) {
self.player = player
}
deinit {
callOnFinished(false)
stop()
}
private var fadeIn: Bool {
return fromVolume < toVolume
}
func fadeIn(duration: Double = iiFaderForAvAudioPlayer_defaultFadeDurationSeconds,
velocity: Double = iiFaderForAvAudioPlayer_defaultVelocity, onFinished: ((Bool)->())? = nil) {
fade(
fromVolume: Double(player.volume), toVolume: 1,
duration: duration, velocity: velocity, onFinished: onFinished)
}
func fadeOut(duration: Double = iiFaderForAvAudioPlayer_defaultFadeDurationSeconds,
velocity: Double = iiFaderForAvAudioPlayer_defaultVelocity, onFinished: ((Bool)->())? = nil) {
fade(
fromVolume: Double(player.volume), toVolume: 0,
duration: duration, velocity: velocity, onFinished: onFinished)
}
func fade(fromVolume fromVolume: Double, toVolume: Double,
duration: Double = iiFaderForAvAudioPlayer_defaultFadeDurationSeconds,
velocity: Double = iiFaderForAvAudioPlayer_defaultVelocity, onFinished: ((Bool)->())? = nil) {
self.fromVolume = iiFaderForAvAudioPlayer.makeSureValueIsBetween0and1(fromVolume)
self.toVolume = iiFaderForAvAudioPlayer.makeSureValueIsBetween0and1(toVolume)
self.fadeDurationSeconds = duration
self.fadeVelocity = velocity
callOnFinished(false)
self.onFinished = onFinished
player.volume = Float(self.fromVolume)
if self.fromVolume == self.toVolume {
callOnFinished(true)
return
}
startTimer()
}
// Stop fading. Does not stop the sound.
func stop() {
stopTimer()
}
private func callOnFinished(finished: Bool) {
onFinished?(finished)
onFinished = nil
}
private func startTimer() {
stopTimer()
currentStep = 0
timer = NSTimer.scheduledTimerWithTimeInterval(1 / volumeAlterationsPerSecond, target: self,
selector: #selector(iiFaderForAvAudioPlayer.timerFired(_:)), userInfo: nil, repeats: true)
}
private func stopTimer() {
if let currentTimer = timer {
currentTimer.invalidate()
timer = nil
}
}
func timerFired(timer: NSTimer) {
if shouldStopTimer {
player.volume = Float(toVolume)
stopTimer()
callOnFinished(true)
return
}
let currentTimeFrom0To1 = iiFaderForAvAudioPlayer.timeFrom0To1(
currentStep, fadeDurationSeconds: fadeDurationSeconds, volumeAlterationsPerSecond: volumeAlterationsPerSecond)
var volumeMultiplier: Double
var newVolume: Double = 0
if fadeIn {
volumeMultiplier = iiFaderForAvAudioPlayer.fadeInVolumeMultiplier(currentTimeFrom0To1,
velocity: fadeVelocity)
newVolume = fromVolume + (toVolume - fromVolume) * volumeMultiplier
} else {
volumeMultiplier = iiFaderForAvAudioPlayer.fadeOutVolumeMultiplier(currentTimeFrom0To1,
velocity: fadeVelocity)
newVolume = toVolume - (toVolume - fromVolume) * volumeMultiplier
}
player.volume = Float(newVolume)
currentStep += 1
}
var shouldStopTimer: Bool {
let totalSteps = fadeDurationSeconds * volumeAlterationsPerSecond
return Double(currentStep) > totalSteps
}
public class func timeFrom0To1(currentStep: Int, fadeDurationSeconds: Double,
volumeAlterationsPerSecond: Double) -> Double {
let totalSteps = fadeDurationSeconds * volumeAlterationsPerSecond
var result = Double(currentStep) / totalSteps
result = makeSureValueIsBetween0and1(result)
return result
}
// Graph: https://www.desmos.com/calculator/wnstesdf0h
public class func fadeOutVolumeMultiplier(timeFrom0To1: Double, velocity: Double) -> Double {
let time = makeSureValueIsBetween0and1(timeFrom0To1)
return pow(M_E, -velocity * time) * (1 - time)
}
public class func fadeInVolumeMultiplier(timeFrom0To1: Double, velocity: Double) -> Double {
let time = makeSureValueIsBetween0and1(timeFrom0To1)
return pow(M_E, velocity * (time - 1)) * time
}
private class func makeSureValueIsBetween0and1(value: Double) -> Double {
if value < 0 { return 0 }
if value > 1 { return 1 }
return value
}
}