Browse Source

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

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

+ 1
- 0
CaseLights/AudioVisualizer.h View File

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

+ 95
- 88
CaseLights/AudioVisualizer.m View File

14
 //
14
 //
15
 
15
 
16
 #ifdef DEBUG
16
 #ifdef DEBUG
17
-#define DEBUG_PLOT_FFT
18
-//#define DEBUG_PLOT_FFT_RAW
19
-#endif
20
-
21
-#ifdef DEBUG_PLOT_FFT
22
 #define DEBUG_LOG_BEATS
17
 #define DEBUG_LOG_BEATS
23
 #endif
18
 #endif
24
 
19
 
26
 #import "AppDelegate.h"
21
 #import "AppDelegate.h"
27
 
22
 
28
 #import "EZAudioFFT.h"
23
 #import "EZAudioFFT.h"
29
-
30
-#ifdef DEBUG_PLOT_FFT
31
 #import "EZAudioPlot.h"
24
 #import "EZAudioPlot.h"
32
-#endif
33
 
25
 
34
 // Parameters for fine-tuning beat detection
26
 // Parameters for fine-tuning beat detection
35
 #define FFT_BUCKET_COUNT 64
27
 #define FFT_BUCKET_COUNT 64
55
 static EZAudioFFT *fft = nil;
47
 static EZAudioFFT *fft = nil;
56
 static int maxBufferSize = 0;
48
 static int maxBufferSize = 0;
57
 static float sensitivity = 1.0f;
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
 @implementation AudioVisualizer
60
 @implementation AudioVisualizer
60
 
61
 
61
 + (void)setDelegate:(AppDelegate *)delegate {
62
 + (void)setDelegate:(AppDelegate *)delegate {
62
     appDelegate = delegate;
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
 + (void)setSensitivity:(float)sens {
73
 + (void)setSensitivity:(float)sens {
70
     // Create Fast Fourier Transformation object
78
     // Create Fast Fourier Transformation object
71
     if (fft == nil) {
79
     if (fft == nil) {
72
         maxBufferSize = bufferSize;
80
         maxBufferSize = bufferSize;
81
+        samplesPerBucket = bufferSize / FFT_BUCKET_COUNT;
73
         fft = [EZAudioFFT fftWithMaximumBufferSize:maxBufferSize sampleRate:appDelegate.microphone.audioStreamBasicDescription.mSampleRate];
82
         fft = [EZAudioFFT fftWithMaximumBufferSize:maxBufferSize sampleRate:appDelegate.microphone.audioStreamBasicDescription.mSampleRate];
74
         
83
         
75
 #ifdef DEBUG
84
 #ifdef DEBUG
81
     if (bufferSize > maxBufferSize) {
90
     if (bufferSize > maxBufferSize) {
82
         NSLog(@"Buffer Size changed?! %d != %d\n", maxBufferSize, bufferSize);
91
         NSLog(@"Buffer Size changed?! %d != %d\n", maxBufferSize, bufferSize);
83
         maxBufferSize = bufferSize;
92
         maxBufferSize = bufferSize;
93
+        samplesPerBucket = bufferSize / FFT_BUCKET_COUNT;
84
         fft = [EZAudioFFT fftWithMaximumBufferSize:maxBufferSize sampleRate:appDelegate.microphone.audioStreamBasicDescription.mSampleRate];
94
         fft = [EZAudioFFT fftWithMaximumBufferSize:maxBufferSize sampleRate:appDelegate.microphone.audioStreamBasicDescription.mSampleRate];
85
     }
95
     }
86
     
96
     
94
     // Perform fast fourier transformation
104
     // Perform fast fourier transformation
95
     [fft computeFFTWithBuffer:buffer withBufferSize:bufferSize];
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
     // Split FFT output into a small number of 'buckets' or 'bins' and add to circular history buffer
107
     // Split FFT output into a small number of 'buckets' or 'bins' and add to circular history buffer
112
     for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
108
     for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
113
         float sum = 0.0f;
109
         float sum = 0.0f;
117
         history[i][nextHistory] = sum / samplesPerBucket;
113
         history[i][nextHistory] = sum / samplesPerBucket;
118
     }
114
     }
119
     
115
     
120
-#ifdef DEBUG_PLOT_FFT
121
-    int beatCount = 0;
122
-#endif
123
-    
124
     // Slowly fade old colors to black
116
     // Slowly fade old colors to black
125
-    static unsigned char lastRed = 0, lastGreen = 0, lastBlue = 0;
126
     lastRed = lastRed * FFT_COLOR_DECAY;
117
     lastRed = lastRed * FFT_COLOR_DECAY;
127
     lastGreen = lastGreen * FFT_COLOR_DECAY;
118
     lastGreen = lastGreen * FFT_COLOR_DECAY;
128
     lastBlue = lastBlue * FFT_COLOR_DECAY;
119
     lastBlue = lastBlue * FFT_COLOR_DECAY;
129
     
120
     
130
     // Check for any beats
121
     // Check for any beats
122
+    int beatCount = 0;
131
     for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
123
     for (int i = 0; i < FFT_BUCKET_COUNT; i++) {
132
         // Skip frequency bands, if required
124
         // Skip frequency bands, if required
133
 #ifdef FFT_BUCKET_SKIP_CONDITION
125
 #ifdef FFT_BUCKET_SKIP_CONDITION
172
             NSLog(@"Beat in %d with c: %f v: %f", i, (history[i][nextHistory] / average), v);
164
             NSLog(@"Beat in %d with c: %f v: %f", i, (history[i][nextHistory] / average), v);
173
 #endif
165
 #endif
174
             
166
             
175
-#ifdef DEBUG_PLOT_FFT
176
             beatCount++;
167
             beatCount++;
177
-#endif
178
         }
168
         }
179
     }
169
     }
180
     
170
     
187
         lastSentBlue = lastBlue;
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
     // Point to next history buffer
198
     // Point to next history buffer
253
     nextHistory++;
199
     nextHistory++;
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
 @end
266
 @end

+ 1
- 1
CaseLights/Info.plist View File

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

+ 1
- 0
README.md View File

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.
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
 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.
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
 ## Working with Git Submodules
30
 ## Working with Git Submodules
30
 
31
 

Loading…
Cancel
Save