|
@@ -15,10 +15,14 @@
|
15
|
15
|
|
16
|
16
|
// Enabling this will cause crashes when changing audio input
|
17
|
17
|
// devices while the app is running. Select it before enabling.
|
|
18
|
+#ifdef DEBUG
|
18
|
19
|
#define DEBUG_PLOT_FFT
|
19
|
20
|
//#define DEBUG_PLOT_FFT_RAW
|
|
21
|
+#endif
|
20
|
22
|
|
|
23
|
+#ifdef DEBUG_PLOT_FFT
|
21
|
24
|
#define DEBUG_LOG_BEATS
|
|
25
|
+#endif
|
22
|
26
|
|
23
|
27
|
#import "AudioVisualizer.h"
|
24
|
28
|
#import "AppDelegate.h"
|
|
@@ -31,11 +35,19 @@
|
31
|
35
|
|
32
|
36
|
// Parameters for fine-tuning beat detection
|
33
|
37
|
#define FFT_BUCKET_COUNT 64
|
34
|
|
-#define FFT_BUCKET_HISTORY 43
|
35
|
|
-#define FFT_C_FACTOR 4.2
|
36
|
|
-#define FFT_V0_FACTOR 0.000015
|
37
|
|
-#define FFT_MAX_V0_COLOR 0.0002
|
38
|
|
-#define FFT_COLOR_DECAY 0.99
|
|
38
|
+#define FFT_BUCKET_HISTORY 45
|
|
39
|
+#define FFT_C_FACTOR 3.3
|
|
40
|
+#define FFT_V0_FACTOR 0.00001
|
|
41
|
+#define FFT_MAX_V0_COLOR 0.00025
|
|
42
|
+#define FFT_COLOR_DECAY 0.98
|
|
43
|
+
|
|
44
|
+// Use this to skip specific frequencies
|
|
45
|
+// Only check bass frequencies
|
|
46
|
+//#define FFT_BUCKET_SKIP_CONDITION (i > (FFT_BUCKET_COUNT / 4))
|
|
47
|
+// Only check mid frequencies
|
|
48
|
+//#define FFT_BUCKET_SKIP_CONDITION ((i < (FFT_BUCKET_COUNT / 4)) || (i > (FFT_BUCKET_COUNT * 3 / 4)))
|
|
49
|
+// Only check high frequencies
|
|
50
|
+//#define FFT_BUCKET_SKIP_CONDITION (i < (FFT_BUCKET_COUNT * 3 / 4))
|
39
|
51
|
|
40
|
52
|
// Factors for nicer debug display
|
41
|
53
|
#define FFT_DEBUG_RAW_FACTOR 42.0
|
|
@@ -98,6 +110,7 @@ static int maxBufferSize = 0;
|
98
|
110
|
int beatCount = 0;
|
99
|
111
|
#endif
|
100
|
112
|
|
|
113
|
+ // Slowly fade old colors to black
|
101
|
114
|
static unsigned char lastRed = 0, lastGreen = 0, lastBlue = 0;
|
102
|
115
|
lastRed = lastRed * FFT_COLOR_DECAY;
|
103
|
116
|
lastGreen = lastGreen * FFT_COLOR_DECAY;
|
|
@@ -105,11 +118,19 @@ static int maxBufferSize = 0;
|
105
|
118
|
|
106
|
119
|
// Check for any beats
|
107
|
120
|
for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
|
|
121
|
+ // Skip frequency bands, if required
|
|
122
|
+#ifdef FFT_BUCKET_SKIP_CONDITION
|
|
123
|
+ if (FFT_BUCKET_SKIP_CONDITION) continue;
|
|
124
|
+#endif
|
|
125
|
+
|
|
126
|
+ // Calculate average of history of this frequency
|
108
|
127
|
float average = 0.0f;
|
109
|
128
|
for (int j = 0; j < FFT_BUCKET_HISTORY; j++) {
|
110
|
129
|
average += history[i][j];
|
111
|
130
|
}
|
112
|
131
|
average /= FFT_BUCKET_HISTORY;
|
|
132
|
+
|
|
133
|
+ // Calculate variance of current bucket in history
|
113
|
134
|
float v = 0.0f;
|
114
|
135
|
for (int j = 0; j < FFT_BUCKET_HISTORY; j++) {
|
115
|
136
|
float tmp = history[i][j] - average;
|
|
@@ -117,6 +138,8 @@ static int maxBufferSize = 0;
|
117
|
138
|
v += tmp;
|
118
|
139
|
}
|
119
|
140
|
v /= FFT_BUCKET_HISTORY;
|
|
141
|
+
|
|
142
|
+ // Check for beat conditions
|
120
|
143
|
if ((history[i][nextHistory] > (FFT_C_FACTOR * average)) && (v > FFT_V0_FACTOR)) {
|
121
|
144
|
// Found a beat on this frequency band, map to a single color
|
122
|
145
|
if (v < FFT_V0_FACTOR) v = FFT_V0_FACTOR;
|
|
@@ -137,19 +160,29 @@ static int maxBufferSize = 0;
|
137
|
160
|
#ifdef DEBUG_LOG_BEATS
|
138
|
161
|
NSLog(@"Beat in %d with c: %f v: %f", i, (history[i][nextHistory] / average), v);
|
139
|
162
|
#endif
|
|
163
|
+
|
140
|
164
|
#ifdef DEBUG_PLOT_FFT
|
141
|
165
|
beatCount++;
|
142
|
166
|
#endif
|
143
|
167
|
}
|
144
|
168
|
}
|
145
|
169
|
|
146
|
|
- [appDelegate setLightsR:lastRed G:lastGreen B:lastBlue];
|
|
170
|
+ // Send new RGB value to lights, if it has changed
|
|
171
|
+ static unsigned char lastSentRed = 42, lastSentGreen = 23, lastSentBlue = 99;
|
|
172
|
+ if ((lastSentRed != lastRed) || (lastSentGreen != lastGreen) || (lastSentBlue != lastBlue)) {
|
|
173
|
+ [appDelegate setLightsR:lastRed G:lastGreen B:lastBlue];
|
|
174
|
+ lastSentRed = lastRed;
|
|
175
|
+ lastSentGreen = lastGreen;
|
|
176
|
+ lastSentBlue = lastBlue;
|
|
177
|
+ }
|
147
|
178
|
|
|
179
|
+ // Display debug FFT plot, if required
|
148
|
180
|
#ifdef DEBUG_PLOT_FFT
|
149
|
181
|
static NSWindow *window = nil;
|
150
|
182
|
static EZAudioPlot *plot = nil;
|
151
|
183
|
static NSTextField *label = nil;
|
152
|
184
|
if ((window == nil) || (plot == nil) || (label == nil)) {
|
|
185
|
+ // Create window
|
153
|
186
|
NSRect frame = NSMakeRect(450, 300, 600, 400);
|
154
|
187
|
window = [[NSWindow alloc] initWithContentRect:frame
|
155
|
188
|
styleMask:NSClosableWindowMask | NSTitledWindowMask | NSBorderlessWindowMask
|
|
@@ -157,6 +190,7 @@ static int maxBufferSize = 0;
|
157
|
190
|
defer:NO];
|
158
|
191
|
[window setTitle:@"Debug FFT"];
|
159
|
192
|
|
|
193
|
+ // Create FFT Plot and add to window
|
160
|
194
|
plot = [[EZAudioPlot alloc] initWithFrame:window.contentView.frame];
|
161
|
195
|
plot.color = [NSColor whiteColor];
|
162
|
196
|
plot.shouldOptimizeForRealtimePlot = NO; // Not working with 'YES' here?!
|
|
@@ -166,6 +200,7 @@ static int maxBufferSize = 0;
|
166
|
200
|
plot.plotType = EZPlotTypeBuffer;
|
167
|
201
|
[window.contentView addSubview:plot];
|
168
|
202
|
|
|
203
|
+ // Create beat count label
|
169
|
204
|
label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 380, 600, 20)];
|
170
|
205
|
[label setTextColor:[NSColor whiteColor]];
|
171
|
206
|
[label setEditable:NO];
|
|
@@ -175,11 +210,13 @@ static int maxBufferSize = 0;
|
175
|
210
|
[label setStringValue:@"-"];
|
176
|
211
|
[window.contentView addSubview:label];
|
177
|
212
|
|
|
213
|
+ // Make window visible
|
178
|
214
|
[window makeKeyAndOrderFront:appDelegate.application];
|
179
|
215
|
NSLog(@"Created debugging FFT Plot window...\n");
|
180
|
216
|
}
|
181
|
217
|
|
182
|
|
- // Scale so we can see something
|
|
218
|
+ // Copy output to input buffer (a bit ugly, but is always big enough)
|
|
219
|
+ // Scale so user can see something
|
183
|
220
|
# ifdef DEBUG_PLOT_FFT_RAW
|
184
|
221
|
memcpy(buffer, fft.fftData, bufferSize * sizeof(float));
|
185
|
222
|
for (UInt32 i = 0; i < bufferSize; i++) {
|
|
@@ -196,6 +233,7 @@ static int maxBufferSize = 0;
|
196
|
233
|
}
|
197
|
234
|
[plot updateBuffer:buffer withBufferSize:bufferSize];
|
198
|
235
|
|
|
236
|
+ // Change background color to match color output and show beat counter
|
199
|
237
|
[window setBackgroundColor:[NSColor colorWithCalibratedRed:lastRed / 255.0 green:lastGreen / 255.0 blue:lastBlue / 255.0 alpha:1.0]];
|
200
|
238
|
[label setStringValue:[NSString stringWithFormat:@"Beats: %d", beatCount]];
|
201
|
239
|
#endif
|