Browse Source

CTRL-click on an audio device to display debug FFT window

Thomas Buck 4 years ago
parent
commit
d75e095df9
5 changed files with 114 additions and 92 deletions
  1. 16
    3
      CaseLights/AppDelegate.m
  2. 1
    0
      CaseLights/AudioVisualizer.h
  3. 95
    88
      CaseLights/AudioVisualizer.m
  4. 1
    1
      CaseLights/Info.plist
  5. 1
    0
      README.md

+ 16
- 3
CaseLights/AppDelegate.m View File

@@ -648,6 +648,9 @@
648 648
         
649 649
         // Send command to turn off LEDs
650 650
         [self setLightsR:0 G:0 B:0];
651
+        
652
+        // Close debug window, if created
653
+        [AudioVisualizer setShowWindow:NO];
651 654
     } else {
652 655
         // Try to restore last LED setting
653 656
         if (lastLEDMode != nil) {
@@ -780,6 +783,7 @@
780 783
     }
781 784
     
782 785
     // Check if it is an audio input device
786
+    BOOL foundAudioDev = NO;
783 787
     if ((found == NO) && ([sender tag] == MENU_ITEM_TAG_AUDIO)) {
784 788
         // Stop previous timer setting
785 789
         if (animation != nil) {
@@ -788,7 +792,6 @@
788 792
         }
789 793
         
790 794
         found = YES;
791
-        BOOL foundDev = NO;
792 795
         NSArray *audioDevices = [EZAudioDevice inputDevices];
793 796
         for (int  i = 0; i < [audioDevices count]; i++) {
794 797
             EZAudioDevice *dev = [audioDevices objectAtIndex:i];
@@ -797,7 +800,7 @@
797 800
                 [self setLightsR:0 G:0 B:0];
798 801
                 
799 802
                 // Found device
800
-                foundDev = YES;
803
+                foundAudioDev = YES;
801 804
                 if (microphone != nil) {
802 805
                     [microphone setMicrophoneOn:NO];
803 806
                 } else {
@@ -809,7 +812,7 @@
809 812
             }
810 813
         }
811 814
         
812
-        if (foundDev == NO) {
815
+        if (foundAudioDev == NO) {
813 816
             NSLog(@"Couldn't find device \"%@\"\n", [sender title]);
814 817
             [sender setState:NSOffState];
815 818
             
@@ -828,6 +831,14 @@
828 831
             }
829 832
             
830 833
             return; // Don't store new mode
834
+        } else {
835
+            // Show debug window if CTRL is held while clicking on audio device
836
+            NSUInteger mods = [NSEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
837
+            if (mods & NSControlKeyMask) {
838
+                [AudioVisualizer setShowWindow:YES];
839
+            } else {
840
+                [AudioVisualizer setShowWindow:NO];
841
+            }
831 842
         }
832 843
     }
833 844
     
@@ -871,6 +882,8 @@
871 882
             NSLog(@"Unknown LED Visualization selected!\n");
872 883
             return;
873 884
         }
885
+    } else if (foundAudioDev == NO) {
886
+        [AudioVisualizer setShowWindow:NO];
874 887
     }
875 888
     
876 889
     // Store changed value in preferences

+ 1
- 0
CaseLights/AudioVisualizer.h View File

@@ -14,6 +14,7 @@
14 14
 
15 15
 + (void)setDelegate:(AppDelegate *)delegate;
16 16
 + (void)setSensitivity:(float)sens;
17
++ (void)setShowWindow:(BOOL)showWindow;
17 18
 + (void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize;
18 19
 
19 20
 @end

+ 95
- 88
CaseLights/AudioVisualizer.m View File

@@ -14,11 +14,6 @@
14 14
 //
15 15
 
16 16
 #ifdef DEBUG
17
-#define DEBUG_PLOT_FFT
18
-//#define DEBUG_PLOT_FFT_RAW
19
-#endif
20
-
21
-#ifdef DEBUG_PLOT_FFT
22 17
 #define DEBUG_LOG_BEATS
23 18
 #endif
24 19
 
@@ -26,10 +21,7 @@
26 21
 #import "AppDelegate.h"
27 22
 
28 23
 #import "EZAudioFFT.h"
29
-
30
-#ifdef DEBUG_PLOT_FFT
31 24
 #import "EZAudioPlot.h"
32
-#endif
33 25
 
34 26
 // Parameters for fine-tuning beat detection
35 27
 #define FFT_BUCKET_COUNT 64
@@ -55,11 +47,27 @@ static AppDelegate *appDelegate = nil;
55 47
 static EZAudioFFT *fft = nil;
56 48
 static int maxBufferSize = 0;
57 49
 static float sensitivity = 1.0f;
50
+static float history[FFT_BUCKET_COUNT][FFT_BUCKET_HISTORY];
51
+static int nextHistory =  0;
52
+static int samplesPerBucket = 0;
53
+static unsigned char lastRed = 0, lastGreen = 0, lastBlue = 0;
54
+
55
+static BOOL shouldShowWindow = NO;
56
+static NSWindow *window = nil;
57
+static EZAudioPlot *plot = nil;
58
+static NSTextField *label = nil;
58 59
 
59 60
 @implementation AudioVisualizer
60 61
 
61 62
 + (void)setDelegate:(AppDelegate *)delegate {
62 63
     appDelegate = delegate;
64
+    
65
+    // Initialize static history variables
66
+    for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
67
+        for (int j = 0; j < FFT_BUCKET_HISTORY; j++) {
68
+            history[i][j] = 0.5f;
69
+        }
70
+    }
63 71
 }
64 72
 
65 73
 + (void)setSensitivity:(float)sens {
@@ -70,6 +78,7 @@ static float sensitivity = 1.0f;
70 78
     // Create Fast Fourier Transformation object
71 79
     if (fft == nil) {
72 80
         maxBufferSize = bufferSize;
81
+        samplesPerBucket = bufferSize / FFT_BUCKET_COUNT;
73 82
         fft = [EZAudioFFT fftWithMaximumBufferSize:maxBufferSize sampleRate:appDelegate.microphone.audioStreamBasicDescription.mSampleRate];
74 83
         
75 84
 #ifdef DEBUG
@@ -81,6 +90,7 @@ static float sensitivity = 1.0f;
81 90
     if (bufferSize > maxBufferSize) {
82 91
         NSLog(@"Buffer Size changed?! %d != %d\n", maxBufferSize, bufferSize);
83 92
         maxBufferSize = bufferSize;
93
+        samplesPerBucket = bufferSize / FFT_BUCKET_COUNT;
84 94
         fft = [EZAudioFFT fftWithMaximumBufferSize:maxBufferSize sampleRate:appDelegate.microphone.audioStreamBasicDescription.mSampleRate];
85 95
     }
86 96
     
@@ -94,20 +104,6 @@ static float sensitivity = 1.0f;
94 104
     // Perform fast fourier transformation
95 105
     [fft computeFFTWithBuffer:buffer withBufferSize:bufferSize];
96 106
     
97
-    static float history[FFT_BUCKET_COUNT][FFT_BUCKET_HISTORY];
98
-    static int nextHistory =  0;
99
-    static int samplesPerBucket = 0;
100
-    
101
-    // Initialize static variables
102
-    if (samplesPerBucket == 0) {
103
-        samplesPerBucket = bufferSize / FFT_BUCKET_COUNT;
104
-        for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
105
-            for (int j = 0; j < FFT_BUCKET_HISTORY; j++) {
106
-                history[i][j] = 0.5f;
107
-            }
108
-        }
109
-    }
110
-    
111 107
     // Split FFT output into a small number of 'buckets' or 'bins' and add to circular history buffer
112 108
     for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
113 109
         float sum = 0.0f;
@@ -117,17 +113,13 @@ static float sensitivity = 1.0f;
117 113
         history[i][nextHistory] = sum / samplesPerBucket;
118 114
     }
119 115
     
120
-#ifdef DEBUG_PLOT_FFT
121
-    int beatCount = 0;
122
-#endif
123
-    
124 116
     // Slowly fade old colors to black
125
-    static unsigned char lastRed = 0, lastGreen = 0, lastBlue = 0;
126 117
     lastRed = lastRed * FFT_COLOR_DECAY;
127 118
     lastGreen = lastGreen * FFT_COLOR_DECAY;
128 119
     lastBlue = lastBlue * FFT_COLOR_DECAY;
129 120
     
130 121
     // Check for any beats
122
+    int beatCount = 0;
131 123
     for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
132 124
         // Skip frequency bands, if required
133 125
 #ifdef FFT_BUCKET_SKIP_CONDITION
@@ -172,9 +164,7 @@ static float sensitivity = 1.0f;
172 164
             NSLog(@"Beat in %d with c: %f v: %f", i, (history[i][nextHistory] / average), v);
173 165
 #endif
174 166
             
175
-#ifdef DEBUG_PLOT_FFT
176 167
             beatCount++;
177
-#endif
178 168
         }
179 169
     }
180 170
     
@@ -187,67 +177,23 @@ static float sensitivity = 1.0f;
187 177
         lastSentBlue = lastBlue;
188 178
     }
189 179
 
190
-    // Display debug FFT plot, if required
191
-#ifdef DEBUG_PLOT_FFT
192
-    static NSWindow *window = nil;
193
-    static EZAudioPlot *plot = nil;
194
-    static NSTextField *label = nil;
195
-    if ((window == nil) || (plot == nil) || (label == nil)) {
196
-        // Create window
197
-        NSRect frame = NSMakeRect(450, 300, 600, 400);
198
-        window = [[NSWindow alloc] initWithContentRect:frame
199
-                                             styleMask:NSClosableWindowMask | NSTitledWindowMask | NSBorderlessWindowMask
200
-                                               backing:NSBackingStoreBuffered
201
-                                                 defer:NO];
202
-        [window setTitle:@"Debug FFT"];
203
-        
204
-        // Create FFT Plot and add to window
205
-        plot = [[EZAudioPlot alloc] initWithFrame:window.contentView.frame];
206
-        plot.color = [NSColor whiteColor];
207
-        plot.shouldOptimizeForRealtimePlot = NO; // Not working with 'YES' here?!
208
-        plot.shouldFill = YES;
209
-        plot.shouldCenterYAxis = NO;
210
-        plot.shouldMirror = NO;
211
-        plot.plotType = EZPlotTypeBuffer;
212
-        [window.contentView addSubview:plot];
213
-        
214
-        // Create beat count label
215
-        label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 380, 600, 20)];
216
-        [label setTextColor:[NSColor whiteColor]];
217
-        [label setEditable:NO];
218
-        [label setBezeled:NO];
219
-        [label setDrawsBackground:NO];
220
-        [label setSelectable:NO];
221
-        [label setStringValue:@"-"];
222
-        [window.contentView addSubview:label];
180
+    // Update debug FFT plot, if required
181
+    if (shouldShowWindow && (window != nil) && (plot != nil) && (label != nil)) {
182
+        for (UInt32 i = 0; i < FFT_BUCKET_COUNT; i++) {
183
+            // Copy output to input buffer (a bit ugly, but is always big enough)
184
+            buffer[i] = history[i][nextHistory];
185
+            
186
+            // Scale so user can see something
187
+            buffer[i] *= FFT_DEBUG_FACTOR;
188
+            if (buffer[i] > 1.0f) buffer[i] = 1.0f;
189
+            if (buffer[i] < -1.0f) buffer[i] = -1.0f;
190
+        }
191
+        [plot updateBuffer:buffer withBufferSize:bufferSize];
223 192
         
224
-        // Make window visible
225
-        [window makeKeyAndOrderFront:appDelegate.application];
226
-        NSLog(@"Created debugging FFT Plot window...\n");
227
-    }
228
-    
229
-    // Copy output to input buffer (a bit ugly, but is always big enough)
230
-    // Scale so user can see something
231
-# ifdef DEBUG_PLOT_FFT_RAW
232
-    memcpy(buffer, fft.fftData, bufferSize * sizeof(float));
233
-    for (UInt32 i = 0; i < bufferSize; i++) {
234
-        buffer[i] *= FFT_DEBUG_RAW_FACTOR;
235
-# else
236
-    for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
237
-        buffer[i] = history[i][nextHistory];
193
+        // Change background color to match color output and show beat counter
194
+        [window setBackgroundColor:[NSColor colorWithCalibratedRed:lastRed / 255.0 green:lastGreen / 255.0 blue:lastBlue / 255.0 alpha:1.0]];
195
+        [label setStringValue:[NSString stringWithFormat:@"Beats: %d", beatCount]];
238 196
     }
239
-    for (UInt32 i = 0; i < FFT_BUCKET_COUNT; i++) {
240
-        buffer[i] *= FFT_DEBUG_FACTOR;
241
-# endif
242
-        if (buffer[i] > 1.0f) buffer[i] = 1.0f;
243
-        if (buffer[i] < -1.0f) buffer[i] = -1.0f;
244
-    }
245
-    [plot updateBuffer:buffer withBufferSize:bufferSize];
246
-    
247
-    // Change background color to match color output and show beat counter
248
-    [window setBackgroundColor:[NSColor colorWithCalibratedRed:lastRed / 255.0 green:lastGreen / 255.0 blue:lastBlue / 255.0 alpha:1.0]];
249
-    [label setStringValue:[NSString stringWithFormat:@"Beats: %d", beatCount]];
250
-#endif
251 197
         
252 198
     // Point to next history buffer
253 199
     nextHistory++;
@@ -256,4 +202,65 @@ static float sensitivity = 1.0f;
256 202
     }
257 203
 }
258 204
 
205
++ (void)setShowWindow:(BOOL)showWindow {
206
+    shouldShowWindow = showWindow;
207
+    
208
+    // Close window if it was visible and should no longer be
209
+    if (showWindow == YES) {
210
+        if ((window == nil) || (plot == nil) || (label == nil)) {
211
+            // Create window
212
+            NSRect frame = NSMakeRect(450, 300, 600, 400);
213
+            window = [[NSWindow alloc] initWithContentRect:frame
214
+                                                 styleMask:NSClosableWindowMask | NSTitledWindowMask | NSBorderlessWindowMask
215
+                                                   backing:NSBackingStoreBuffered
216
+                                                     defer:NO];
217
+            [window setTitle:@"CaseLights FFT"];
218
+            [window setReleasedWhenClosed:NO];
219
+            
220
+            // Create FFT Plot and add to window
221
+            plot = [[EZAudioPlot alloc] initWithFrame:window.contentView.frame];
222
+            plot.color = [NSColor whiteColor];
223
+            plot.shouldOptimizeForRealtimePlot = NO; // Not working with 'YES' here?!
224
+            plot.shouldFill = YES;
225
+            plot.shouldCenterYAxis = NO;
226
+            plot.shouldMirror = NO;
227
+            plot.plotType = EZPlotTypeBuffer;
228
+            [window.contentView addSubview:plot];
229
+            
230
+            // Create beat count label
231
+            label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 380, 600, 20)];
232
+            [label setTextColor:[NSColor whiteColor]];
233
+            [label setEditable:NO];
234
+            [label setBezeled:NO];
235
+            [label setDrawsBackground:NO];
236
+            [label setSelectable:NO];
237
+            [label setStringValue:@"-"];
238
+            [window.contentView addSubview:label];
239
+            
240
+#ifdef DEBUG
241
+            NSLog(@"Created debugging FFT Plot window...\n");
242
+#endif
243
+        }
244
+        
245
+        if ([window isVisible] == NO) {
246
+            // Make window visible
247
+            [window makeKeyAndOrderFront:appDelegate.application];
248
+            
249
+#ifdef DEBUG
250
+            NSLog(@"Made debugging FFT Plot window visible...\n");
251
+#endif
252
+        }
253
+    } else {
254
+        if (window != nil) {
255
+            if ([window isVisible] == YES) {
256
+                [window close];
257
+                
258
+#ifdef DEBUG
259
+                NSLog(@"Closed debugging FFT Plot window...\n");
260
+#endif
261
+            }
262
+        }
263
+    }
264
+}
265
+
259 266
 @end

+ 1
- 1
CaseLights/Info.plist View File

@@ -21,7 +21,7 @@
21 21
 	<key>CFBundleSignature</key>
22 22
 	<string>????</string>
23 23
 	<key>CFBundleVersion</key>
24
-	<string>410</string>
24
+	<string>415</string>
25 25
 	<key>LSApplicationCategoryType</key>
26 26
 	<string>public.app-category.utilities</string>
27 27
 	<key>LSMinimumSystemVersion</key>

+ 1
- 0
README.md View File

@@ -25,6 +25,7 @@ CaseLights is only visible in the system menu bar. You can enable or disable the
25 25
 You can also select one of the displays connected to the Host machine. The CaseLights App will then create a Screenshot of this display 10-times per second and calculate the average color to display it on the RGB LEDs.
26 26
 
27 27
 CaseLights is also able to visualize sound coming from a system audio input. To be able to directly visualize the system sound output, install [Soundflower](https://github.com/mattingalls/Soundflower) and create a Multi-Output-Device in the Mac `Audio Midi Setup.app` consisting of `Soundflower (2ch)` and your normally used output device. Set this device as audio output. Then, in CaseLights, select `Soundflower (2ch)` as input.
28
+Click on an audio device with a CTRL-click to display a window with the fast-fourier-transformation plot.
28 29
 
29 30
 ## Working with Git Submodules
30 31
 

Loading…
Cancel
Save