Browse Source

Improved comments, storing ambilight state.

Thomas Buck 7 years ago
parent
commit
4a36ea18f4
No account linked to committer's email address
3 changed files with 130 additions and 64 deletions
  1. 0
    2
      DisplayBacklight/AppDelegate.h
  2. 129
    61
      DisplayBacklight/AppDelegate.m
  3. 1
    1
      DisplayBacklight/Info.plist

+ 0
- 2
DisplayBacklight/AppDelegate.h View File

17
 - (void)stopAmbilight;
17
 - (void)stopAmbilight;
18
 - (void)newDisplayList:(NSArray *)displayIDs;
18
 - (void)newDisplayList:(NSArray *)displayIDs;
19
 
19
 
20
-+ (double)map:(double)val FromMin:(double)fmin FromMax:(double)fmax ToMin:(double)tmin ToMax:(double)tmax;
21
-
22
 @end
20
 @end
23
 
21
 

+ 129
- 61
DisplayBacklight/AppDelegate.m View File

10
 #import "Serial.h"
10
 #import "Serial.h"
11
 #import "Screenshot.h"
11
 #import "Screenshot.h"
12
 
12
 
13
+// This defined the update-speed of the Ambilight, in seconds.
14
+// With a baudrate of 115200 and 156 LEDs and 14-bytes Magic-Word,
15
+// theoretically you could transmit:
16
+//     115200 / (14 + (156 * 3) * 8) =~ 30 Frames per Second
17
+// Inserting (1.0 / 30.0) here would try to reach these 30FPS,
18
+// but will probably cause high CPU-Usage.
19
+// (Run-Time of the algorithm is ignored here, so real speed will be
20
+// slightly lower.)
21
+#define DISPLAY_DELAY (1.0 / 30.0)
22
+
23
+// Magic identifying string used to differntiate start of packets.
24
+// Has to be the same here and in the Arduino Sketch.
25
+#define MAGIC_WORD @"xythobuzRGBled"
26
+
13
 // These are the values stored persistently in the preferences
27
 // These are the values stored persistently in the preferences
14
 #define PREF_SERIAL_PORT @"SerialPort"
28
 #define PREF_SERIAL_PORT @"SerialPort"
15
 #define PREF_BRIGHTNESS @"Brightness"
29
 #define PREF_BRIGHTNESS @"Brightness"
16
-
17
-#define DISPLAY_DELAY (1.0 / 10.0)
30
+#define PREF_TURNED_ON @"IsEnabled"
18
 
31
 
19
 @interface AppDelegate ()
32
 @interface AppDelegate ()
20
 
33
 
51
     NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
64
     NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
52
     NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithObject:@"" forKey:PREF_SERIAL_PORT];
65
     NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithObject:@"" forKey:PREF_SERIAL_PORT];
53
     [appDefaults setObject:[NSNumber numberWithFloat:50.0] forKey:PREF_BRIGHTNESS];
66
     [appDefaults setObject:[NSNumber numberWithFloat:50.0] forKey:PREF_BRIGHTNESS];
67
+    [appDefaults setObject:[NSNumber numberWithBool:NO] forKey:PREF_TURNED_ON];
54
     [store registerDefaults:appDefaults];
68
     [store registerDefaults:appDefaults];
55
     [store synchronize];
69
     [store synchronize];
56
     
70
     
57
     // Load existing configuration values
71
     // Load existing configuration values
58
     NSString *savedPort = [store stringForKey:PREF_SERIAL_PORT];
72
     NSString *savedPort = [store stringForKey:PREF_SERIAL_PORT];
59
     float brightness = [store floatForKey:PREF_BRIGHTNESS];
73
     float brightness = [store floatForKey:PREF_BRIGHTNESS];
74
+    BOOL ambilightIsOn = [store boolForKey:PREF_TURNED_ON];
60
     
75
     
61
     // Prepare status bar menu
76
     // Prepare status bar menu
62
     statusImage = [NSImage imageNamed:@"MenuIcon"];
77
     statusImage = [NSImage imageNamed:@"MenuIcon"];
71
     [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", brightness]];
86
     [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", brightness]];
72
     
87
     
73
     // Prepare serial port menu
88
     // Prepare serial port menu
74
-    BOOL startTimer = NO;
89
+    BOOL foundPort = NO;
75
     NSArray *ports = [Serial listSerialPorts];
90
     NSArray *ports = [Serial listSerialPorts];
76
     if ([ports count] > 0) {
91
     if ([ports count] > 0) {
77
         [menuPorts removeAllItems];
92
         [menuPorts removeAllItems];
82
             
97
             
83
             // Set Enabled if it was used the last time
98
             // Set Enabled if it was used the last time
84
             if ((savedPort != nil) && [[ports objectAtIndex:i] isEqualToString:savedPort]) {
99
             if ((savedPort != nil) && [[ports objectAtIndex:i] isEqualToString:savedPort]) {
85
-                [[menuPorts itemAtIndex:i] setState:NSOnState];
86
-                
87
                 // Try to open serial port
100
                 // Try to open serial port
88
                 [serial setPortName:savedPort];
101
                 [serial setPortName:savedPort];
89
-                if ([serial openPort]) {
90
-                    // Unselect it when an error occured opening the port
91
-                    [[menuPorts itemAtIndex:i] setState:NSOffState];
92
-                } else {
93
-                    startTimer = YES;
102
+                if (![serial openPort]) {
103
+                    foundPort = YES;
104
+                    [[menuPorts itemAtIndex:i] setState:NSOnState];
94
                 }
105
                 }
95
             }
106
             }
96
         }
107
         }
97
         
108
         
98
-        if (!startTimer) {
99
-            // TODO try to find out new UART port name for controller
109
+        if (!foundPort) {
110
+            // I'm using a cheap chinese Arduino Nano clone with a CH340 chipset.
111
+            // This driver creates device-files in /dev/cu.* that don't correspond
112
+            // to the chip-id and change every time the adapter is re-enumerated.
113
+            // That means we may have to try and find the device again after the
114
+            // stored name does no longer exist. In this case, we simply try the first
115
+            // device that starts with /dev/cu.wchusbserial*...
116
+            for (int i = 0; i < [ports count]; i++) {
117
+                if ([[ports objectAtIndex:i] hasPrefix:@"/dev/cu.wchusbserial"]) {
118
+                    // Try to open serial port
119
+                    [serial setPortName:savedPort];
120
+                    if (![serial openPort]) {
121
+                        [[menuPorts itemAtIndex:i] setState:NSOnState];
122
+                        
123
+                        // Reattempt next matching device when opening this one fails.
124
+                        break;
125
+                    }
126
+                }
127
+            }
100
         }
128
         }
101
     }
129
     }
102
     
130
     
103
     [Screenshot init:self];
131
     [Screenshot init:self];
104
     lastDisplayIDs = [Screenshot listDisplays];
132
     lastDisplayIDs = [Screenshot listDisplays];
105
     
133
     
106
-    if (startTimer) {
107
-        timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:YES];
134
+    if (ambilightIsOn) {
135
+        timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:NO];
108
         
136
         
109
         [buttonAmbilight setState:NSOnState];
137
         [buttonAmbilight setState:NSOnState];
110
     }
138
     }
189
             [timer invalidate];
217
             [timer invalidate];
190
             timer = nil;
218
             timer = nil;
191
         }
219
         }
220
+        
221
+        [self sendNullFrame];
222
+        
223
+        // Store state
224
+        NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
225
+        [store setObject:[NSNumber numberWithBool:NO] forKey:PREF_TURNED_ON];
226
+        [store synchronize];
192
     } else {
227
     } else {
193
         [sender setState:NSOnState];
228
         [sender setState:NSOnState];
194
         
229
         
195
-        timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:YES];
230
+        timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:NO];
231
+        
232
+        // Store state
233
+        NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
234
+        [store setObject:[NSNumber numberWithBool:YES] forKey:PREF_TURNED_ON];
235
+        [store synchronize];
196
     }
236
     }
197
 }
237
 }
198
 
238
 
214
         restartAmbilight = NO;
254
         restartAmbilight = NO;
215
         [buttonAmbilight setState:NSOnState];
255
         [buttonAmbilight setState:NSOnState];
216
         
256
         
217
-        timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:YES];
257
+        timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:NO];
218
     }
258
     }
219
 }
259
 }
220
 
260
 
232
     [application orderFrontStandardAboutPanel:self];
272
     [application orderFrontStandardAboutPanel:self];
233
 }
273
 }
234
 
274
 
275
+// ----------------------------------------------------
276
+// ------------ 'Ambilight' Visualizations ------------
277
+// ----------------------------------------------------
278
+
279
+// ToDo: add support for display names or IDs here, so we can distinguish
280
+// between multiple displays with the same resolution
281
+struct DisplayAssignment {
282
+    int width, height;
283
+};
284
+
285
+struct LEDStrand {
286
+    int idMin, idMax;
287
+    int display;
288
+    int startX, startY;
289
+    int direction;
290
+    int size;
291
+};
292
+
293
+#define DIR_LEFT 0
294
+#define DIR_RIGHT 1
295
+#define DIR_UP 2
296
+#define DIR_DOWN 3
297
+
298
+// ----------------------- Config starts here -----------------------
299
+
300
+// The idea behind this algorithm is very simple. It assumes that each LED strand
301
+// follows one edge of one of your displays. So one of the two coordinates should
302
+// always be zero or the width / height of your display.
303
+
304
+// Define the amount of LEDs in your strip here
305
+#define LED_COUNT 156
306
+
307
+// This defined how large the averaging-boxes should be in the dimension perpendicular
308
+// to the strand. So eg. for a bottom strand, how high the box should be in px.
309
+#define COLOR_AVERAGE_OTHER_DIMENSION_SIZE 150
310
+
311
+// Identify your displays here. Currently they're only distinguished by their resolution.
312
+// The ID will be the index in the list, so the first entry is display 0 and so on.
313
+struct DisplayAssignment displays[] = {
314
+    { 1920, 1080 },
315
+    {  900, 1600 }
316
+};
317
+
318
+// This defined the orientation and placement of your strands and is the most important part.
319
+// It begins with the LED IDs this strand includes, starting with ID 0 up to LED_COUNT - 1.
320
+// The third item is the display ID, defined by the previous struct.
321
+// The fourth and fifth items are the starting X and Y coordinates of the strand.
322
+// As described above, one should always be zero or the display width / height.
323
+// The sixth element is the direction the strand goes (no diagonals supported yet).
324
+// The last element is the size of the averaging-box for each LED, moving with the strand.
325
+// So, if your strand contains 33 LEDs and spans 1920 pixels, this should be (1920 / 33).
326
+struct LEDStrand strands[] = {
327
+    {   0,  32, 0, 1920, 1080,  DIR_LEFT, 1920 / 33 },
328
+    {  33,  51, 0,    0, 1080,    DIR_UP, 1080 / 19 },
329
+    {  52,  84, 0,    0,    0, DIR_RIGHT, 1920 / 33 },
330
+    {  85,  89, 1,    0,  250,    DIR_UP,  250 / 5 },
331
+    {  90, 106, 1,    0,    0, DIR_RIGHT,  900 / 17 },
332
+    { 107, 134, 1,  900,    0,  DIR_DOWN, 1600 / 28 },
333
+    { 135, 151, 1,  900, 1600,  DIR_LEFT,  900 / 17 },
334
+    { 152, 155, 1,    0, 1600,    DIR_UP,  180 / 4 }
335
+};
336
+
337
+// ------------------------ Config ends here ------------------------
338
+
339
+UInt8 ledColorData[LED_COUNT * 3];
340
+
235
 - (UInt32)calculateAverage:(unsigned char *)data Width:(NSInteger)width Height:(NSInteger)height SPP:(NSInteger)spp Alpha:(BOOL)alpha StartX:(NSInteger)startX StartY:(NSInteger)startY EndX:(NSInteger)endX EndY:(NSInteger)endY {
341
 - (UInt32)calculateAverage:(unsigned char *)data Width:(NSInteger)width Height:(NSInteger)height SPP:(NSInteger)spp Alpha:(BOOL)alpha StartX:(NSInteger)startX StartY:(NSInteger)startY EndX:(NSInteger)endX EndY:(NSInteger)endY {
236
     int redC = 0, greenC = 1, blueC = 2;
342
     int redC = 0, greenC = 1, blueC = 2;
237
     if (alpha) {
343
     if (alpha) {
268
     green /= count;
374
     green /= count;
269
     blue /= count;
375
     blue /= count;
270
     
376
     
377
+    red *= [brightnessSlider floatValue] / 100.0f;
378
+    green *= [brightnessSlider floatValue] / 100.0f;
379
+    blue *= [brightnessSlider floatValue] / 100.0f;
380
+    
271
     return ((UInt32)red << 16) | ((UInt32)green << 8) | ((UInt32)blue);
381
     return ((UInt32)red << 16) | ((UInt32)green << 8) | ((UInt32)blue);
272
 }
382
 }
273
 
383
 
274
-struct DisplayAssignment {
275
-    int n;
276
-    int width, height;
277
-};
278
-
279
-struct LEDStrand {
280
-    int idMin, idMax;
281
-    int display;
282
-    int startX, startY;
283
-    int direction;
284
-    int size;
285
-};
286
-
287
-#define DIR_LEFT 0
288
-#define DIR_RIGHT 1
289
-#define DIR_UP 2
290
-#define DIR_DOWN 3
291
-
292
-// TODO remove first
293
-struct DisplayAssignment displays[] = {
294
-    { 0, 1920, 1080 },
295
-    { 1,  900, 1600 }
296
-};
297
-
298
-struct LEDStrand strands[] = {
299
-    {   0,  32, 0, 1920, 1080,  DIR_LEFT, 1920 / 33 },
300
-    {  33,  51, 0,    0, 1080,    DIR_UP, 1080 / 19 },
301
-    {  52,  84, 0,    0,    0, DIR_RIGHT, 1920 / 33 },
302
-    {  85,  89, 1,    0,  250,    DIR_UP,  250 / 5 },
303
-    {  90, 106, 1,    0,    0, DIR_RIGHT,  900 / 17 },
304
-    { 107, 134, 1,  900,    0,  DIR_DOWN, 1600 / 28 },
305
-    { 135, 151, 1,  900, 1600,  DIR_LEFT,  900 / 17 },
306
-    { 152, 155, 1,    0, 1600,    DIR_UP,  180 / 4 }
307
-};
308
-
309
-UInt8 ledColorData[156 * 3];
310
-
311
-#define COLOR_AVERAGE_OTHER_DIMENSION_SIZE 150
312
-
313
 - (void)visualizeSingleDisplay:(NSInteger)disp Data:(unsigned char *)data Width:(unsigned long)width Height:(unsigned long)height SPP:(NSInteger)spp Alpha:(BOOL)alpha {
384
 - (void)visualizeSingleDisplay:(NSInteger)disp Data:(unsigned char *)data Width:(unsigned long)width Height:(unsigned long)height SPP:(NSInteger)spp Alpha:(BOOL)alpha {
314
     for (int i = 0; i < (sizeof(strands) / sizeof(strands[0])); i++) {
385
     for (int i = 0; i < (sizeof(strands) / sizeof(strands[0])); i++) {
315
         if (strands[i].display == disp) {
386
         if (strands[i].display == disp) {
406
     }
477
     }
407
     
478
     
408
     [self sendLEDFrame];
479
     [self sendLEDFrame];
480
+    
481
+    timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:NO];
409
 }
482
 }
410
 
483
 
411
 - (void)sendLEDFrame {
484
 - (void)sendLEDFrame {
412
     if ([serial isOpen]) {
485
     if ([serial isOpen]) {
413
-        [serial sendString:@"xythobuzRGBled"];
486
+        [serial sendString:MAGIC_WORD];
414
         [serial sendData:(char *)ledColorData withLength:(sizeof(ledColorData) / sizeof(ledColorData[0]))];
487
         [serial sendData:(char *)ledColorData withLength:(sizeof(ledColorData) / sizeof(ledColorData[0]))];
415
     }
488
     }
416
 }
489
 }
422
     [self sendLEDFrame];
495
     [self sendLEDFrame];
423
 }
496
 }
424
 
497
 
425
-+ (double)map:(double)val FromMin:(double)fmin FromMax:(double)fmax ToMin:(double)tmin ToMax:(double)tmax {
426
-    double norm = (val - fmin) / (fmax - fmin);
427
-    return (norm * (tmax - tmin)) + tmin;
428
-}
429
-
430
 @end
498
 @end

+ 1
- 1
DisplayBacklight/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>53</string>
24
+	<string>65</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>

Loading…
Cancel
Save