|
@@ -10,16 +10,62 @@
|
10
|
10
|
#import "Serial.h"
|
11
|
11
|
#import "Screenshot.h"
|
12
|
12
|
|
13
|
|
-// This defined the update-speed of the Ambilight, in seconds.
|
|
13
|
+// ----------------------- Config starts here -----------------------
|
|
14
|
+
|
|
15
|
+// The idea behind this algorithm is very simple. It assumes that each LED strand
|
|
16
|
+// follows one edge of one of your displays. So one of the two coordinates should
|
|
17
|
+// always be zero or the width / height of your display.
|
|
18
|
+
|
|
19
|
+// Define the amount of LEDs in your strip here
|
|
20
|
+#define LED_COUNT 156
|
|
21
|
+
|
|
22
|
+// This defines how large the averaging-boxes should be in the dimension perpendicular
|
|
23
|
+// to the strand. So eg. for a bottom strand, how high the box should be in px.
|
|
24
|
+#define COLOR_AVERAGE_OTHER_DIMENSION_SIZE 100
|
|
25
|
+
|
|
26
|
+// Identify your displays here. Currently they're only distinguished by their resolution.
|
|
27
|
+// The ID will be the index in the list, so the first entry is display 0 and so on.
|
|
28
|
+struct DisplayAssignment displays[] = {
|
|
29
|
+ { 1920, 1080 },
|
|
30
|
+ { 900, 1600 }
|
|
31
|
+};
|
|
32
|
+
|
|
33
|
+// This defines the orientation and placement of your strands and is the most important part.
|
|
34
|
+// It begins with the LED IDs this strand includes, starting with ID 0 up to LED_COUNT - 1.
|
|
35
|
+// The second item is the length of this strip, as in the count of LEDs in it.
|
|
36
|
+// The third item is the display ID, defined by the previous struct.
|
|
37
|
+// The fourth and fifth items are the starting X and Y coordinates of the strand.
|
|
38
|
+// As described above, one should always be zero or the display width / height.
|
|
39
|
+// The sixth element is the direction the strand goes (no diagonals supported yet).
|
|
40
|
+// The last element is the size of the averaging-box for each LED, moving with the strand.
|
|
41
|
+// So, if your strand contains 33 LEDs and spans 1920 pixels, this should be (1920 / 33).
|
|
42
|
+// By default you can always use (length in pixel / LED count) for the last item, except
|
|
43
|
+// if your strand does not span the whole length of this screen edge.
|
|
44
|
+struct LEDStrand strands[] = {
|
|
45
|
+ { 0, 33, 0, 1920, 1080, DIR_LEFT, 1920 / 33 },
|
|
46
|
+ { 33, 19, 0, 0, 1080, DIR_UP, 1080 / 19 },
|
|
47
|
+ { 52, 33, 0, 0, 0, DIR_RIGHT, 1920 / 33 },
|
|
48
|
+ { 85, 5, 1, 0, 250, DIR_UP, 250 / 5 },
|
|
49
|
+ { 90, 17, 1, 0, 0, DIR_RIGHT, 900 / 17 },
|
|
50
|
+ { 107, 28, 1, 900, 0, DIR_DOWN, 1600 / 28 },
|
|
51
|
+ { 135, 17, 1, 900, 1600, DIR_LEFT, 900 / 17 },
|
|
52
|
+ { 152, 4, 1, 0, 1600, DIR_UP, 180 / 4 }
|
|
53
|
+};
|
|
54
|
+
|
|
55
|
+// This defines the update-speed of the Ambilight, in seconds.
|
14
|
56
|
// With a baudrate of 115200 and 156 LEDs and 14-bytes Magic-Word,
|
15
|
57
|
// theoretically you could transmit:
|
16
|
|
-// 115200 / (14 + (156 * 3) * 8) =~ 30 Frames per Second
|
|
58
|
+// 115200 / (14 + (156 * 3)) * 8 =~ 30 Frames per Second
|
17
|
59
|
// Inserting (1.0 / 30.0) here would try to reach these 30FPS,
|
18
|
60
|
// but will probably cause high CPU-Usage.
|
19
|
61
|
// (Run-Time of the algorithm is ignored here, so real speed will be
|
20
|
62
|
// slightly lower.)
|
21
|
63
|
#define DISPLAY_DELAY (1.0 / 30.0)
|
22
|
64
|
|
|
65
|
+// How many pixels to skip when calculating the average color.
|
|
66
|
+// Slightly increases performance and doesn't really alter the result.
|
|
67
|
+#define AVERAGE_PIXEL_SKIP 2
|
|
68
|
+
|
23
|
69
|
// Magic identifying string used to differntiate start of packets.
|
24
|
70
|
// Has to be the same here and in the Arduino Sketch.
|
25
|
71
|
#define MAGIC_WORD @"xythobuzRGBled"
|
|
@@ -29,6 +75,11 @@
|
29
|
75
|
#define PREF_BRIGHTNESS @"Brightness"
|
30
|
76
|
#define PREF_TURNED_ON @"IsEnabled"
|
31
|
77
|
|
|
78
|
+// If this is defined it will print the FPS every DEBUG_PRINT_FPS seconds
|
|
79
|
+//#define DEBUG_PRINT_FPS 10
|
|
80
|
+
|
|
81
|
+// ------------------------ Config ends here ------------------------
|
|
82
|
+
|
32
|
83
|
@interface AppDelegate ()
|
33
|
84
|
|
34
|
85
|
@property (weak) IBOutlet NSMenu *statusMenu;
|
|
@@ -272,70 +323,24 @@
|
272
|
323
|
[application orderFrontStandardAboutPanel:self];
|
273
|
324
|
}
|
274
|
325
|
|
|
326
|
+- (void)sendLEDFrame {
|
|
327
|
+ if ([serial isOpen]) {
|
|
328
|
+ [serial sendString:MAGIC_WORD];
|
|
329
|
+ [serial sendData:(char *)ledColorData withLength:(sizeof(ledColorData) / sizeof(ledColorData[0]))];
|
|
330
|
+ }
|
|
331
|
+}
|
|
332
|
+
|
|
333
|
+- (void)sendNullFrame {
|
|
334
|
+ for (int i = 0; i < (sizeof(ledColorData) / sizeof(ledColorData[0])); i++) {
|
|
335
|
+ ledColorData[i] = 0;
|
|
336
|
+ }
|
|
337
|
+ [self sendLEDFrame];
|
|
338
|
+}
|
|
339
|
+
|
275
|
340
|
// ----------------------------------------------------
|
276
|
341
|
// ------------ 'Ambilight' Visualizations ------------
|
277
|
342
|
// ----------------------------------------------------
|
278
|
343
|
|
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
|
344
|
UInt8 ledColorData[LED_COUNT * 3];
|
340
|
345
|
|
341
|
346
|
- (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 {
|
|
@@ -361,7 +366,7 @@ UInt8 ledColorData[LED_COUNT * 3];
|
361
|
366
|
}
|
362
|
367
|
|
363
|
368
|
unsigned long red = 0, green = 0, blue = 0, count = 0;
|
364
|
|
- for (NSInteger i = xa; i < xb; i++) {
|
|
369
|
+ for (NSInteger i = xa; i < xb; i += AVERAGE_PIXEL_SKIP) {
|
365
|
370
|
for (NSInteger j = ya; j < yb; j++) {
|
366
|
371
|
count++;
|
367
|
372
|
unsigned long index = i + (j * width);
|
|
@@ -396,7 +401,7 @@ UInt8 ledColorData[LED_COUNT * 3];
|
396
|
401
|
blockHeight = strands[i].size;
|
397
|
402
|
}
|
398
|
403
|
|
399
|
|
- for (int led = strands[i].idMin; led <= strands[i].idMax; led++) {
|
|
404
|
+ for (int led = strands[i].idMin; led < (strands[i].idMin + strands[i].count); led++) {
|
400
|
405
|
// First move appropriately in the direction of the strand
|
401
|
406
|
unsigned long endX = x, endY = y;
|
402
|
407
|
if (strands[i].direction == DIR_LEFT) {
|
|
@@ -428,7 +433,6 @@ UInt8 ledColorData[LED_COUNT * 3];
|
428
|
433
|
// Calculate average color for this led
|
429
|
434
|
UInt32 color = [self calculateAverage:data Width:width Height:height SPP:spp Alpha:alpha StartX:x StartY:y EndX:endX EndY:endY];
|
430
|
435
|
|
431
|
|
-
|
432
|
436
|
ledColorData[led * 3] = (color & 0xFF0000) >> 16;
|
433
|
437
|
ledColorData[(led * 3) + 1] = (color & 0x00FF00) >> 8;
|
434
|
438
|
ledColorData[(led * 3) + 2] = color & 0x0000FF;
|
|
@@ -445,6 +449,14 @@ UInt8 ledColorData[LED_COUNT * 3];
|
445
|
449
|
}
|
446
|
450
|
|
447
|
451
|
- (void)visualizeDisplay:(NSTimer *)time {
|
|
452
|
+#ifdef DEBUG_PRINT_FPS
|
|
453
|
+ static NSInteger frameCount = 0;
|
|
454
|
+ static NSDate *lastPrintTime = nil;
|
|
455
|
+ if (lastPrintTime == nil) {
|
|
456
|
+ lastPrintTime = [NSDate date];
|
|
457
|
+ }
|
|
458
|
+#endif
|
|
459
|
+
|
448
|
460
|
//NSLog(@"Running Ambilight-Algorithm (%lu)...", (unsigned long)[lastDisplayIDs count]);
|
449
|
461
|
|
450
|
462
|
// Create a Screenshot for all connected displays
|
|
@@ -478,21 +490,18 @@ UInt8 ledColorData[LED_COUNT * 3];
|
478
|
490
|
|
479
|
491
|
[self sendLEDFrame];
|
480
|
492
|
|
481
|
|
- timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:NO];
|
482
|
|
-}
|
483
|
|
-
|
484
|
|
-- (void)sendLEDFrame {
|
485
|
|
- if ([serial isOpen]) {
|
486
|
|
- [serial sendString:MAGIC_WORD];
|
487
|
|
- [serial sendData:(char *)ledColorData withLength:(sizeof(ledColorData) / sizeof(ledColorData[0]))];
|
488
|
|
- }
|
489
|
|
-}
|
490
|
|
-
|
491
|
|
-- (void)sendNullFrame {
|
492
|
|
- for (int i = 0; i < (sizeof(ledColorData) / sizeof(ledColorData[0])); i++) {
|
493
|
|
- ledColorData[i] = 0;
|
|
493
|
+#ifdef DEBUG_PRINT_FPS
|
|
494
|
+ frameCount++;
|
|
495
|
+ NSDate *now = [NSDate date];
|
|
496
|
+ NSTimeInterval interval = [now timeIntervalSinceDate:lastPrintTime];
|
|
497
|
+ if (interval >= DEBUG_PRINT_FPS) {
|
|
498
|
+ NSLog(@"FPS: %.2f", frameCount / interval);
|
|
499
|
+ frameCount = 0;
|
|
500
|
+ lastPrintTime = now;
|
494
|
501
|
}
|
495
|
|
- [self sendLEDFrame];
|
|
502
|
+#endif
|
|
503
|
+
|
|
504
|
+ timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:NO];
|
496
|
505
|
}
|
497
|
506
|
|
498
|
507
|
@end
|