-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFFTModel.swift
133 lines (116 loc) · 4.72 KB
/
FFTModel.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
// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKitUI/
import Accelerate
import AudioKit
import SwiftUI
open class FFTModel: ObservableObject {
@Published public var amplitudes: [Float?] = Array(repeating: nil, count: 50)
public var nodeTap: FFTTap!
public var defaultMaxAmplitude: Float = 0.0
public var defaultMinAmplitude: Float = -70.0
public var referenceValueForFFT: Float = 12.0
public var node: Node
private var linearGradient: LinearGradient
private var paddingFraction: CGFloat
private var includeCaps: Bool
private var barCount: Int
private var fftValidBinCount: FFTValidBinCount?
private var minAmplitude: Float
private var maxAmplitude: Float
private let defaultBarCount: Int = 8
private let maxBarCount: Int = 128
private var backgroundColor: Color
public init(_ node: Node,
linearGradient: LinearGradient = LinearGradient(gradient: Gradient(colors: [.red, .yellow, .green]),
startPoint: .top,
endPoint: .center),
paddingFraction: CGFloat = 0.2,
includeCaps: Bool = true,
validBinCount: FFTValidBinCount? = nil,
barCount: Int? = nil,
maxAmplitude: Float = -10.0,
minAmplitude: Float = -150.0,
backgroundColor: Color = Color.black)
{
self.node = node
self.linearGradient = linearGradient
self.paddingFraction = paddingFraction
self.includeCaps = includeCaps
self.maxAmplitude = maxAmplitude
self.minAmplitude = minAmplitude
fftValidBinCount = validBinCount
self.backgroundColor = backgroundColor
if maxAmplitude < minAmplitude {
fatalError("Maximum amplitude cannot be less than minimum amplitude")
}
if minAmplitude > 0.0 || maxAmplitude > 0.0 {
fatalError("Amplitude values must be less than zero")
}
if let requestedBarCount = barCount {
self.barCount = requestedBarCount
} else {
if let fftBinCount = fftValidBinCount {
if Int(fftBinCount.rawValue) > maxBarCount - 1 {
self.barCount = maxBarCount
} else {
self.barCount = Int(fftBinCount.rawValue)
}
} else {
self.barCount = defaultBarCount
}
}
}
func updateNode(_ node: Node, fftValidBinCount: FFTValidBinCount? = nil) {
if node !== self.node {
self.node = node
nodeTap = FFTTap(node, fftValidBinCount: fftValidBinCount, callbackQueue: .main) { fftData in
self.updateAmplitudes(fftData)
}
nodeTap.isNormalized = false
nodeTap.start()
} else {
nodeTap = FFTTap(node, fftValidBinCount: fftValidBinCount, callbackQueue: .main) { fftData in
self.updateAmplitudes(fftData)
}
nodeTap.isNormalized = false
nodeTap.start()
}
}
func updateAmplitudes(_ fftFloats: [Float]) {
var fftData = fftFloats
for index in 0 ..< fftData.count {
if fftData[index].isNaN { fftData[index] = 0.0 }
}
var one = Float(1.0)
var zero = Float(0.0)
var decibelNormalizationFactor = Float(1.0 / (maxAmplitude - minAmplitude))
var decibelNormalizationOffset = Float(-minAmplitude / (maxAmplitude - minAmplitude))
var decibels = [Float](repeating: 0, count: fftData.count)
vDSP_vdbcon(fftData, 1, &referenceValueForFFT, &decibels, 1, vDSP_Length(fftData.count), 0)
vDSP_vsmsa(decibels,
1,
&decibelNormalizationFactor,
&decibelNormalizationOffset,
&decibels,
1,
vDSP_Length(decibels.count))
vDSP_vclip(decibels, 1, &zero, &one, &decibels, 1, vDSP_Length(decibels.count))
// swap the amplitude array
self.amplitudes = decibels
}
func mockAudioInput() {
var mockFloats = [Float]()
for _ in 0...65 {
mockFloats.append(Float.random(in: 0...0.1))
}
updateAmplitudes(mockFloats)
let waitTime: TimeInterval = 0.1
DispatchQueue.main.asyncAfter(deadline: .now() + waitTime) {
self.mockAudioInput()
}
}
func onAppear(node:Node) {
updateNode(node, fftValidBinCount: self.fftValidBinCount)
maxAmplitude = self.defaultMaxAmplitude
minAmplitude = self.defaultMinAmplitude
}
}