Simple RGB LED controller for Mac OS X
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AppDelegate.m 41KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158
  1. //
  2. // AppDelegate.m
  3. // CaseLights
  4. //
  5. // Created by Thomas Buck on 21.12.15.
  6. // Copyright © 2015 xythobuz. All rights reserved.
  7. //
  8. #import "AppDelegate.h"
  9. #import "Serial.h"
  10. #import "GPUStats.h"
  11. #import "Screenshot.h"
  12. // These are the values stored persistently in the preferences
  13. #define PREF_SERIAL_PORT @"SerialPort"
  14. #define PREF_LIGHTS_STATE @"LightState"
  15. // LED Mode contains the last selected mode as menu item text
  16. #define PREF_LED_MODE @"LEDMode"
  17. #define PREF_BRIGHTNESS @"Brightness"
  18. #define PREF_COLOR @"ManualColor"
  19. #define TEXT_MANUAL @"Select..."
  20. #define TEXT_CPU_USAGE @"CPU Usage"
  21. #define TEXT_RAM_USAGE @"RAM Usage"
  22. #define TEXT_GPU_USAGE @"GPU Usage"
  23. #define TEXT_VRAM_USAGE @"VRAM Usage"
  24. #define TEXT_CPU_TEMPERATURE @"CPU Temperature"
  25. #define TEXT_GPU_TEMPERATURE @"GPU Temperature"
  26. #define TEXT_RGB_FADE @"RGB Fade"
  27. #define TEXT_HSV_FADE @"HSV Fade"
  28. #define TEXT_RANDOM @"Random"
  29. #define TEXT_TEMPLATE_AUDIO @"AudioDevice_%@"
  30. // SMC keys are checked for existence and used for reading
  31. #define KEY_CPU_TEMPERATURE @"TC0D"
  32. #define KEY_GPU_TEMPERATURE @"TG0D"
  33. // Temperature in Celsius
  34. #define CPU_TEMP_MIN 20
  35. #define CPU_TEMP_MAX 90
  36. // HSV Color (S = V = 1)
  37. #define CPU_COLOR_MIN 120
  38. #define CPU_COLOR_MAX 0
  39. #define GPU_TEMP_MIN 20
  40. #define GPU_TEMP_MAX 90
  41. #define GPU_COLOR_MIN 120
  42. #define GPU_COLOR_MAX 0
  43. #define RAM_COLOR_MIN 0
  44. #define RAM_COLOR_MAX 120
  45. // You can play around with these values (skipped pixels, display timer delay) to change CPU usage in display mode
  46. #define AVERAGE_COLOR_PERFORMANCE_INC 10
  47. #define DISPLAY_DELAY 0.1
  48. // Used to identify selected menu items
  49. // displays are all tags >= 0
  50. #define MENU_ITEM_TAG_NOTHING -1
  51. #define MENU_ITEM_TAG_AUDIO -2
  52. @interface AppDelegate ()
  53. @property (strong) NSStatusItem *statusItem;
  54. @property (strong) NSImage *statusImage;
  55. @property (strong) NSDictionary *staticColors;
  56. @property (strong) NSTimer *animation;
  57. @property (strong) Serial *serial;
  58. @property (strong) NSMenuItem *lastLEDMode;
  59. @property (strong) EZMicrophone *microphone;
  60. @end
  61. @implementation AppDelegate
  62. @synthesize statusMenu, application;
  63. @synthesize menuColors, menuAnimations, menuVisualizations, menuPorts;
  64. @synthesize menuItemDisplays, menuDisplays;
  65. @synthesize menuItemAudio, menuAudio;
  66. @synthesize buttonOff, buttonLights;
  67. @synthesize brightnessItem, brightnessSlider, brightnessLabel;
  68. @synthesize statusItem, statusImage;
  69. @synthesize staticColors, animation;
  70. @synthesize serial, lastLEDMode, microphone;
  71. @synthesize menuItemColor;
  72. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  73. srand((unsigned)time(NULL));
  74. serial = [[Serial alloc] init];
  75. lastLEDMode = nil;
  76. animation = nil;
  77. microphone = nil;
  78. // Prepare status bar menu
  79. statusImage = [NSImage imageNamed:@"MenuIcon"];
  80. [statusImage setTemplate:YES];
  81. statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
  82. [statusItem setImage:statusImage];
  83. [statusItem setMenu:statusMenu];
  84. // Set default configuration values, load existing ones
  85. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  86. NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithObject:@"" forKey:PREF_SERIAL_PORT];
  87. [appDefaults setObject:[NSNumber numberWithBool:NO] forKey:PREF_LIGHTS_STATE];
  88. [appDefaults setObject:@"" forKey:PREF_LED_MODE];
  89. [appDefaults setObject:[NSNumber numberWithFloat:50.0] forKey:PREF_BRIGHTNESS];
  90. [store registerDefaults:appDefaults];
  91. [store synchronize];
  92. NSString *savedPort = [store stringForKey:PREF_SERIAL_PORT];
  93. BOOL turnOnLights = [store boolForKey:PREF_LIGHTS_STATE];
  94. NSString *lastMode = [store stringForKey:PREF_LED_MODE];
  95. float brightness = [store floatForKey:PREF_BRIGHTNESS];
  96. NSData *lastColorData = [store dataForKey:PREF_COLOR];
  97. NSColor *lastColor = nil;
  98. if (lastColorData != nil) {
  99. lastColor = (NSColor *)[NSUnarchiver unarchiveObjectWithData:lastColorData];
  100. }
  101. // Prepare brightness menu
  102. brightnessItem.view = brightnessSlider;
  103. [brightnessSlider setFloatValue:brightness];
  104. [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", brightness]];
  105. // Prepare serial port menu
  106. NSArray *ports = [Serial listSerialPorts];
  107. if ([ports count] > 0) {
  108. [menuPorts removeAllItems];
  109. for (int i = 0; i < [ports count]; i++) {
  110. // Add Menu Item for this port
  111. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  112. [item setTag:MENU_ITEM_TAG_NOTHING];
  113. [menuPorts addItem:item];
  114. // Set Enabled if it was used the last time
  115. if ((savedPort != nil) && [[ports objectAtIndex:i] isEqualToString:savedPort]) {
  116. [[menuPorts itemAtIndex:i] setState:NSOnState];
  117. // Try to open serial port
  118. [serial setPortName:savedPort];
  119. if ([serial openPort]) {
  120. // Unselect it when an error occured opening the port
  121. [[menuPorts itemAtIndex:i] setState:NSOffState];
  122. }
  123. }
  124. }
  125. }
  126. // Select "Off" button if it was last selected
  127. if ([lastMode isEqualToString:@""]) {
  128. [buttonOff setState:NSOffState];
  129. [self turnLEDsOff:buttonOff];
  130. }
  131. // Prepare static colors menu
  132. staticColors = [NSDictionary dictionaryWithObjectsAndKeys:
  133. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:0.0f alpha:0.0f], @"Red",
  134. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:0.0f alpha:0.0f], @"Green",
  135. [NSColor colorWithCalibratedRed:0.0f green:0.0f blue:1.0f alpha:0.0f], @"Blue",
  136. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:1.0f alpha:0.0f], @"Cyan",
  137. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:1.0f alpha:0.0f], @"Magenta",
  138. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:0.0f alpha:0.0f], @"Yellow",
  139. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:1.0f alpha:0.0f], @"White",
  140. nil];
  141. for (NSString *key in [staticColors allKeys]) {
  142. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  143. [item setTag:MENU_ITEM_TAG_NOTHING];
  144. if ([key isEqualToString:lastMode]) {
  145. [self selectedVisualization:item];
  146. }
  147. [menuColors addItem:item];
  148. }
  149. menuItemColor = [[NSMenuItem alloc] initWithTitle:TEXT_MANUAL action:@selector(setColorSelected:) keyEquivalent:@""];
  150. if ([lastMode isEqualToString:TEXT_MANUAL]) {
  151. if (lastColor != nil) {
  152. // Restore previously set RGB color
  153. [self setLightsColor:lastColor];
  154. }
  155. [menuItemColor setState:NSOnState];
  156. }
  157. [menuItemColor setTag:MENU_ITEM_TAG_NOTHING];
  158. [menuColors addItem:menuItemColor];
  159. // Prepare animations menu
  160. NSArray *animationStrings = [NSArray arrayWithObjects:
  161. TEXT_RGB_FADE,
  162. TEXT_HSV_FADE,
  163. TEXT_RANDOM,
  164. nil];
  165. for (NSString *key in animationStrings) {
  166. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  167. [item setTag:MENU_ITEM_TAG_NOTHING];
  168. if ([key isEqualToString:lastMode]) {
  169. [self selectedVisualization:item];
  170. }
  171. [menuAnimations addItem:item];
  172. }
  173. // Add CPU Usage menu item
  174. NSMenuItem *cpuUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  175. [cpuUsageItem setTag:MENU_ITEM_TAG_NOTHING];
  176. if ([lastMode isEqualToString:TEXT_CPU_USAGE]) {
  177. [self selectedVisualization:cpuUsageItem];
  178. }
  179. [menuVisualizations addItem:cpuUsageItem];
  180. // Add Memory Usage item
  181. NSMenuItem *memoryUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_RAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  182. [memoryUsageItem setTag:MENU_ITEM_TAG_NOTHING];
  183. if ([lastMode isEqualToString:TEXT_RAM_USAGE]) {
  184. [self selectedVisualization:memoryUsageItem];
  185. }
  186. [menuVisualizations addItem:memoryUsageItem];
  187. // Check if GPU Stats are available, add menu items if so
  188. NSNumber *usage;
  189. NSNumber *freeVRAM;
  190. NSNumber *usedVRAM;
  191. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  192. NSLog(@"Error reading GPU information\n");
  193. } else {
  194. NSMenuItem *itemUsage = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  195. [itemUsage setTag:MENU_ITEM_TAG_NOTHING];
  196. if ([lastMode isEqualToString:TEXT_GPU_USAGE]) {
  197. [self selectedVisualization:itemUsage];
  198. }
  199. [menuVisualizations addItem:itemUsage];
  200. NSMenuItem *itemVRAM = [[NSMenuItem alloc] initWithTitle:TEXT_VRAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  201. [itemVRAM setTag:MENU_ITEM_TAG_NOTHING];
  202. if ([lastMode isEqualToString:TEXT_VRAM_USAGE]) {
  203. [self selectedVisualization:itemVRAM];
  204. }
  205. [menuVisualizations addItem:itemVRAM];
  206. }
  207. // Check available temperatures and add menu items
  208. JSKSMC *smc = [JSKSMC smc];
  209. for (int i = 0; i < [[smc workingTempKeys] count]; i++) {
  210. NSString *key = [smc.workingTempKeys objectAtIndex:i];
  211. #ifdef DEBUG
  212. NSString *name = [smc humanReadableNameForKey:key];
  213. NSLog(@"Sensor \"%@\": \"%@\"\n", key, name);
  214. #endif
  215. if ([key isEqualToString:KEY_CPU_TEMPERATURE]) {
  216. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  217. [item setTag:MENU_ITEM_TAG_NOTHING];
  218. if ([lastMode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  219. [self selectedVisualization:item];
  220. }
  221. [menuVisualizations addItem:item];
  222. }
  223. if ([key isEqualToString:KEY_GPU_TEMPERATURE]) {
  224. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  225. [item setTag:MENU_ITEM_TAG_NOTHING];
  226. if ([lastMode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  227. [self selectedVisualization:item];
  228. }
  229. [menuVisualizations addItem:item];
  230. }
  231. }
  232. // Restore previously used lights configuration
  233. if (turnOnLights) {
  234. // Turn on lights
  235. if ([serial isOpen]) {
  236. [serial sendString:@"UV 1\n"];
  237. }
  238. [buttonLights setState:NSOnState];
  239. } else {
  240. // Turn off lights
  241. if ([serial isOpen]) {
  242. [serial sendString:@"UV 0\n"];
  243. }
  244. }
  245. // List available displays and add menu items
  246. [Screenshot init:self];
  247. NSArray *displayIDs = [Screenshot listDisplays];
  248. [self updateDisplayUI:displayIDs];
  249. // List available audio input devices and add menu items
  250. NSArray *inputDevices = [EZAudioDevice inputDevices];
  251. [menuAudio removeAllItems];
  252. for (int i = 0; i < [inputDevices count]; i++) {
  253. EZAudioDevice *dev = [inputDevices objectAtIndex:i];
  254. #ifdef DEBUG
  255. NSLog(@"Audio input device: \"%@\"\n", [dev name]);
  256. #endif
  257. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[dev name] action:@selector(selectedVisualization:) keyEquivalent:@""];
  258. [item setTag:MENU_ITEM_TAG_AUDIO];
  259. NSString *lastModeString = [NSString stringWithFormat:TEXT_TEMPLATE_AUDIO, [dev name]];
  260. if ([lastModeString isEqualToString:lastMode]) {
  261. [self selectedVisualization:item];
  262. }
  263. [menuAudio addItem:item];
  264. }
  265. if ([inputDevices count] > 0) {
  266. [menuItemAudio setHidden:NO];
  267. }
  268. }
  269. - (void)applicationWillTerminate:(NSNotification *)aNotification {
  270. // Stop previous timer setting
  271. if (animation != nil) {
  272. [animation invalidate];
  273. animation = nil;
  274. }
  275. // Stop previous audio data retrieval
  276. if (microphone != nil) {
  277. [microphone stopFetchingAudio];
  278. microphone = nil;
  279. }
  280. // Remove display callback
  281. [Screenshot close:self];
  282. // Turn off all lights if possible
  283. if ([serial isOpen]) {
  284. [serial sendString:@"RGB 0 0 0\n"];
  285. [serial sendString:@"UV 0\n"];
  286. [serial closePort];
  287. }
  288. }
  289. - (void)clearDisplayUI {
  290. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  291. if ([[menuDisplays itemAtIndex:i] isEnabled] == YES) {
  292. // A display configuration is currently selected. Disable the timer
  293. if (animation != nil) {
  294. [animation invalidate];
  295. animation = nil;
  296. }
  297. }
  298. }
  299. [menuDisplays removeAllItems];
  300. [menuItemDisplays setHidden:YES];
  301. }
  302. - (void)updateDisplayUI:(NSArray *)displayIDs {
  303. if ([displayIDs count] > 0) {
  304. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  305. NSString *lastMode = [store stringForKey:PREF_LED_MODE];
  306. [menuItemDisplays setHidden:NO];
  307. for (int i = 0; i < [displayIDs count]; i++) {
  308. NSString *title = [Screenshot displayNameFromDisplayID:[displayIDs objectAtIndex:i]];
  309. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title
  310. action:@selector(selectedVisualization:)
  311. keyEquivalent:@""];
  312. [item setTag:[[displayIDs objectAtIndex:i] integerValue]];
  313. if ([title isEqualToString:lastMode]) {
  314. [self selectedVisualization:item];
  315. }
  316. [menuDisplays addItem:item];
  317. }
  318. }
  319. }
  320. - (void)setLightsColor:(NSColor *)color {
  321. CGFloat red, green, blue, alpha;
  322. [color getRed:&red green:&green blue:&blue alpha:&alpha];
  323. [self setLightsR:red * 255 G:green * 255 B:blue * 255];
  324. // Stop previous timer setting
  325. if (animation != nil) {
  326. [animation invalidate];
  327. animation = nil;
  328. }
  329. // Stop previous audio data retrieval
  330. if (microphone != nil) {
  331. [microphone stopFetchingAudio];
  332. microphone = nil;
  333. }
  334. // Turn off all other LED menu items
  335. if (menuColors != nil) {
  336. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  337. [[menuColors itemAtIndex:i] setState:NSOffState];
  338. }
  339. }
  340. if (menuAnimations != nil) {
  341. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  342. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  343. }
  344. }
  345. if (menuVisualizations != nil) {
  346. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  347. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  348. }
  349. }
  350. if (menuAudio != nil) {
  351. for (int i = 0; i < [menuAudio numberOfItems]; i++) {
  352. [[menuAudio itemAtIndex:i] setState:NSOffState];
  353. }
  354. }
  355. if (menuDisplays != nil) {
  356. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  357. [[menuDisplays itemAtIndex:i] setState:NSOffState];
  358. }
  359. }
  360. [buttonOff setState:NSOffState];
  361. [menuItemColor setState:NSOnState];
  362. // Store new manually selected color
  363. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  364. NSData *data = [NSArchiver archivedDataWithRootObject:color];
  365. [store setObject:data forKey:PREF_COLOR];
  366. [store setObject:TEXT_MANUAL forKey:PREF_LED_MODE];
  367. [store synchronize];
  368. }
  369. - (void)setLightsR:(unsigned char)r G:(unsigned char)g B:(unsigned char)b {
  370. if ([serial isOpen]) {
  371. unsigned char red = r * ([brightnessSlider floatValue] / 100.0);
  372. unsigned char green = g * ([brightnessSlider floatValue] / 100.0);
  373. unsigned char blue = b * ([brightnessSlider floatValue] / 100.0);
  374. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", red, green, blue]];
  375. } else {
  376. #ifdef DEBUG
  377. NSLog(@"Trying to send RGB without opened port!\n");
  378. #endif
  379. }
  380. }
  381. - (IBAction)relistSerialPorts:(id)sender {
  382. // Refill audio device list
  383. NSArray *inputDevices = [EZAudioDevice inputDevices];
  384. [menuAudio removeAllItems];
  385. for (int i = 0; i < [inputDevices count]; i++) {
  386. EZAudioDevice *dev = [inputDevices objectAtIndex:i];
  387. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[dev name] action:@selector(selectedVisualization:) keyEquivalent:@""];
  388. [item setTag:MENU_ITEM_TAG_AUDIO];
  389. NSString *lastModeString = [NSString stringWithFormat:TEXT_TEMPLATE_AUDIO, [dev name]];
  390. if ([lastModeString isEqualToString:[[NSUserDefaults standardUserDefaults] stringForKey:PREF_LED_MODE]]) {
  391. [self selectedVisualization:item];
  392. }
  393. [menuAudio addItem:item];
  394. }
  395. if ([inputDevices count] > 0) {
  396. [menuItemAudio setHidden:NO];
  397. } else {
  398. [menuItemAudio setHidden:YES];
  399. }
  400. // Refill port list
  401. NSArray *ports = [Serial listSerialPorts];
  402. [menuPorts removeAllItems];
  403. for (int i = 0; i < [ports count]; i++) {
  404. // Add Menu Item for this port
  405. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  406. [item setTag:MENU_ITEM_TAG_NOTHING];
  407. [menuPorts addItem:item];
  408. // Mark it if it is currently open
  409. if ([serial isOpen]) {
  410. if ([[ports objectAtIndex:i] isEqualToString:[serial portName]]) {
  411. [[menuPorts itemAtIndex:i] setState:NSOnState];
  412. }
  413. }
  414. }
  415. }
  416. - (void)setColorSelected:(NSMenuItem *)sender {
  417. NSColorPanel *cp = [NSColorPanel sharedColorPanel];
  418. [cp setTarget:self];
  419. [cp setAction:@selector(colorSelected:)];
  420. [cp setShowsAlpha:NO];
  421. [cp setContinuous:NO];
  422. [cp setMode:NSRGBModeColorPanel];
  423. // Try to restore last manually selected color
  424. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  425. NSData *lastColorData = [store dataForKey:PREF_COLOR];
  426. NSColor *lastColor = nil;
  427. if (lastColorData != nil) {
  428. lastColor = (NSColor *)[NSUnarchiver unarchiveObjectWithData:lastColorData];
  429. [cp setColor:lastColor];
  430. }
  431. [NSApp activateIgnoringOtherApps:YES];
  432. [application orderFrontColorPanel:cp];
  433. }
  434. - (void)colorSelected:(NSColorPanel *)sender {
  435. [self setLightsColor:[sender color]];
  436. }
  437. - (IBAction)brightnessMoved:(NSSlider *)sender {
  438. [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", [sender floatValue]]];
  439. // Restore the current configuration for items where it won't happen automatically
  440. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  441. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  442. [self selectedVisualization:[menuColors itemAtIndex:i]];
  443. }
  444. }
  445. // Store changed value in preferences
  446. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  447. [store setObject:[NSNumber numberWithFloat:[sender floatValue]] forKey:PREF_BRIGHTNESS];
  448. [store synchronize];
  449. }
  450. - (IBAction)turnLEDsOff:(NSMenuItem *)sender {
  451. if ([sender state] == NSOffState) {
  452. lastLEDMode = nil;
  453. // Stop previous timer setting
  454. if (animation != nil) {
  455. [animation invalidate];
  456. animation = nil;
  457. }
  458. // Stop previous audio data retrieval
  459. if (microphone != nil) {
  460. [microphone stopFetchingAudio];
  461. microphone = nil;
  462. }
  463. // Turn off all other LED menu items
  464. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  465. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  466. lastLEDMode = [menuColors itemAtIndex:i];
  467. }
  468. [[menuColors itemAtIndex:i] setState:NSOffState];
  469. }
  470. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  471. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  472. lastLEDMode = [menuAnimations itemAtIndex:i];
  473. }
  474. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  475. }
  476. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  477. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  478. lastLEDMode = [menuVisualizations itemAtIndex:i];
  479. }
  480. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  481. }
  482. for (int i = 0; i < [menuAudio numberOfItems]; i++) {
  483. if ([[menuAudio itemAtIndex:i] state] == NSOnState) {
  484. lastLEDMode = [menuAudio itemAtIndex:i];
  485. }
  486. [[menuAudio itemAtIndex:i] setState:NSOffState];
  487. }
  488. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  489. if ([[menuDisplays itemAtIndex:i] state] == NSOnState) {
  490. lastLEDMode = [menuDisplays itemAtIndex:i];
  491. }
  492. [[menuDisplays itemAtIndex:i] setState:NSOffState];
  493. }
  494. // Turn on "off" menu item
  495. [sender setState:NSOnState];
  496. // Store changed value in preferences
  497. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  498. [store setObject:@"" forKey:PREF_LED_MODE];
  499. [store synchronize];
  500. #ifdef DEBUG
  501. NSLog(@"Stored new mode: \"off\"!\n");
  502. #endif
  503. // Send command to turn off LEDs
  504. [self setLightsR:0 G:0 B:0];
  505. } else {
  506. // Try to restore last LED setting
  507. if (lastLEDMode != nil) {
  508. [self selectedVisualization:lastLEDMode];
  509. }
  510. }
  511. }
  512. - (IBAction)toggleLights:(NSMenuItem *)sender {
  513. if ([sender state] == NSOffState) {
  514. // Turn on lights
  515. if ([serial isOpen]) {
  516. [serial sendString:@"UV 1\n"];
  517. }
  518. [sender setState:NSOnState];
  519. } else {
  520. // Turn off lights
  521. if ([serial isOpen]) {
  522. [serial sendString:@"UV 0\n"];
  523. }
  524. [sender setState:NSOffState];
  525. }
  526. // Store changed value in preferences
  527. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  528. [store setBool:([sender state] == NSOnState) forKey:PREF_LIGHTS_STATE];
  529. [store synchronize];
  530. }
  531. - (BOOL)timedVisualization:(NSString *)mode {
  532. // Stop previous timer setting
  533. if (animation != nil) {
  534. [animation invalidate];
  535. animation = nil;
  536. }
  537. // Stop previous audio data retrieval
  538. if (microphone != nil) {
  539. [microphone stopFetchingAudio];
  540. microphone = nil;
  541. }
  542. // Schedule next invocation for this animation...
  543. if ([mode isEqualToString:TEXT_GPU_USAGE]) {
  544. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeGPUUsage:) userInfo:mode repeats:YES];
  545. } else if ([mode isEqualToString:TEXT_VRAM_USAGE]) {
  546. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeVRAMUsage:) userInfo:mode repeats:YES];
  547. } else if ([mode isEqualToString:TEXT_CPU_USAGE]) {
  548. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeCPUUsage:) userInfo:mode repeats:YES];
  549. } else if ([mode isEqualToString:TEXT_RAM_USAGE]) {
  550. animation = [NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:@selector(visualizeRAMUsage:) userInfo:mode repeats:YES];
  551. } else if ([mode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  552. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeCPUTemperature:) userInfo:mode repeats:YES];
  553. } else if ([mode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  554. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeGPUTemperature:) userInfo:mode repeats:YES];
  555. } else if ([mode isEqualToString:TEXT_RGB_FADE]) {
  556. animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeRGBFade:) userInfo:mode repeats:YES];
  557. } else if ([mode isEqualToString:TEXT_HSV_FADE]) {
  558. animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeHSVFade:) userInfo:mode repeats:YES];
  559. } else if ([mode isEqualToString:TEXT_RANDOM]) {
  560. animation = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(visualizeRandom:) userInfo:mode repeats:YES];
  561. } else {
  562. return NO;
  563. }
  564. #ifdef DEBUG
  565. NSLog(@"Scheduled animation for \"%@\"!\n", mode);
  566. #endif
  567. // ...and also execute it right now
  568. [animation fire];
  569. return YES;
  570. }
  571. - (void)displayVisualization:(NSMenuItem *)sender {
  572. // Stop previous timer setting
  573. if (animation != nil) {
  574. [animation invalidate];
  575. animation = nil;
  576. }
  577. // Stop previous audio data retrieval
  578. if (microphone != nil) {
  579. [microphone stopFetchingAudio];
  580. microphone = nil;
  581. }
  582. // Schedule next invocation for this animation...
  583. animation = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:[NSNumber numberWithInteger:[sender tag]] repeats:YES];
  584. // ...and also execute it right now
  585. [animation fire];
  586. }
  587. - (void)selectedVisualization:(NSMenuItem *)sender {
  588. // Turn off all other LED menu items
  589. if (menuColors != nil) {
  590. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  591. [[menuColors itemAtIndex:i] setState:NSOffState];
  592. }
  593. }
  594. if (menuAnimations != nil) {
  595. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  596. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  597. }
  598. }
  599. if (menuVisualizations != nil) {
  600. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  601. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  602. }
  603. }
  604. if (menuAudio != nil) {
  605. for (int i = 0; i < [menuAudio numberOfItems]; i++) {
  606. [[menuAudio itemAtIndex:i] setState:NSOffState];
  607. }
  608. }
  609. if (menuDisplays != nil) {
  610. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  611. [[menuDisplays itemAtIndex:i] setState:NSOffState];
  612. }
  613. }
  614. [buttonOff setState:NSOffState];
  615. [sender setState:NSOnState];
  616. // Check if it is a display
  617. BOOL found = NO;
  618. if ([sender tag] > MENU_ITEM_TAG_NOTHING) {
  619. found = YES;
  620. [self displayVisualization:sender];
  621. }
  622. // Check if it is an audio input device
  623. if ((found == NO) && ([sender tag] == MENU_ITEM_TAG_AUDIO)) {
  624. found = YES;
  625. BOOL foundDev = NO;
  626. NSArray *audioDevices = [EZAudioDevice inputDevices];
  627. for (int i = 0; i < [audioDevices count]; i++) {
  628. EZAudioDevice *dev = [audioDevices objectAtIndex:i];
  629. if ([[dev name] isEqualToString:[sender title]]) {
  630. // Found device
  631. foundDev = YES;
  632. if (microphone != nil) {
  633. [microphone stopFetchingAudio];
  634. microphone = nil;
  635. }
  636. microphone = [EZMicrophone microphoneWithDelegate:self];
  637. [microphone setDevice:dev];
  638. [microphone startFetchingAudio];
  639. break;
  640. }
  641. }
  642. if (foundDev == NO) {
  643. NSLog(@"Couldn't find device \"%@\"\n", [sender title]);
  644. [sender setState:NSOffState];
  645. return; // Don't store new mode
  646. }
  647. }
  648. // Check if it is the manual color select item
  649. if ((found == NO) && ([sender.title isEqualToString:TEXT_MANUAL])) {
  650. found = YES;
  651. [self colorSelected:[NSColorPanel sharedColorPanel]];
  652. }
  653. // Check if a static color was selected
  654. if ((found == NO) && (staticColors != nil)) {
  655. for (NSString *key in [staticColors allKeys]) {
  656. if ([sender.title isEqualToString:key]) {
  657. found = YES;
  658. // Stop previous timer setting
  659. if (animation != nil) {
  660. [animation invalidate];
  661. animation = nil;
  662. }
  663. // Stop previous audio data retrieval
  664. if (microphone != nil) {
  665. [microphone stopFetchingAudio];
  666. microphone = nil;
  667. }
  668. NSColor *color = [staticColors valueForKey:key];
  669. unsigned char red = [color redComponent] * 255;
  670. unsigned char green = [color greenComponent] * 255;
  671. unsigned char blue = [color blueComponent] * 255;
  672. [self setLightsR:red G:green B:blue];
  673. break;
  674. }
  675. }
  676. }
  677. if (found == NO) {
  678. // Check if an animated visualization was selected
  679. if ([self timedVisualization:[sender title]] == NO) {
  680. NSLog(@"Unknown LED Visualization selected!\n");
  681. return;
  682. }
  683. }
  684. // Store changed value in preferences
  685. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  686. if ([sender tag] == MENU_ITEM_TAG_AUDIO) {
  687. // Prepend text for audio device names
  688. NSString *tmp = [NSString stringWithFormat:TEXT_TEMPLATE_AUDIO, [sender title]];
  689. [store setObject:tmp forKey:PREF_LED_MODE];
  690. } else {
  691. [store setObject:[sender title] forKey:PREF_LED_MODE];
  692. }
  693. [store synchronize];
  694. #ifdef DEBUG
  695. NSLog(@"Stored new mode: \"%@\"!\n", [sender title]);
  696. #endif
  697. }
  698. - (void)selectedSerialPort:(NSMenuItem *)source {
  699. // Store selection for next start-up
  700. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  701. [store setObject:[source title] forKey:PREF_SERIAL_PORT];
  702. [store synchronize];
  703. // De-select all other ports
  704. for (int i = 0; i < [menuPorts numberOfItems]; i++) {
  705. [[menuPorts itemAtIndex:i] setState:NSOffState];
  706. }
  707. // Select only the current port
  708. [source setState:NSOnState];
  709. // Close previously opened port, if any
  710. if ([serial isOpen]) {
  711. [serial closePort];
  712. }
  713. // Try to open selected port
  714. [serial setPortName:[source title]];
  715. if ([serial openPort] != 0) {
  716. [source setState:NSOffState];
  717. } else {
  718. // Restore the current configuration
  719. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  720. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  721. [self selectedVisualization:[menuColors itemAtIndex:i]];
  722. }
  723. }
  724. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  725. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  726. [self selectedVisualization:[menuAnimations itemAtIndex:i]];
  727. }
  728. }
  729. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  730. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  731. [self selectedVisualization:[menuVisualizations itemAtIndex:i]];
  732. }
  733. }
  734. for (int i = 0; i < [menuAudio numberOfItems]; i++) {
  735. if ([[menuAudio itemAtIndex:i] state] == NSOnState) {
  736. [self selectedVisualization:[menuAudio itemAtIndex:i]];
  737. }
  738. }
  739. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  740. if ([[menuDisplays itemAtIndex:i] state] == NSOnState) {
  741. [self selectedVisualization:[menuDisplays itemAtIndex:i]];
  742. }
  743. }
  744. if ([buttonOff state] == NSOnState) {
  745. [buttonOff setState:NSOffState];
  746. [self turnLEDsOff:buttonOff];
  747. }
  748. if ([buttonLights state] == NSOnState) {
  749. [serial sendString:@"UV 1\n"];
  750. } else {
  751. [serial sendString:@"UV 0\n"];
  752. }
  753. }
  754. }
  755. - (IBAction)showAbout:(id)sender {
  756. [NSApp activateIgnoringOtherApps:YES];
  757. [application orderFrontStandardAboutPanel:self];
  758. }
  759. - (void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize {
  760. if (microphone == nil) {
  761. return; // Old buffer from before we changed mode
  762. }
  763. // TODO visualize sound data somehow
  764. //NSLog(@".");
  765. }
  766. // ------------------------------------------------------
  767. // ----------------- Microphone Delegate ----------------
  768. // ------------------------------------------------------
  769. - (void)microphone:(EZMicrophone *)microphone hasAudioReceived:(float **)buffer withBufferSize:(UInt32)bufferSize withNumberOfChannels:(UInt32)numberOfChannels {
  770. __weak typeof (self) weakSelf = self;
  771. if (weakSelf.microphone == nil) {
  772. return;
  773. }
  774. // Getting audio data as an array of float buffer arrays that can be fed into the
  775. // EZAudioPlot, EZAudioPlotGL, or whatever visualization you would like to do with
  776. // the microphone data.
  777. dispatch_async(dispatch_get_main_queue(),^{
  778. // buffer[0] = left channel, buffer[1] = right channel
  779. [weakSelf updateBuffer:buffer[0] withBufferSize:bufferSize];
  780. });
  781. }
  782. - (void)microphone:(EZMicrophone *)microphone changedDevice:(EZAudioDevice *)device {
  783. // This is not always guaranteed to occur on the main thread so make sure you
  784. // wrap it in a GCD block
  785. dispatch_async(dispatch_get_main_queue(), ^{
  786. NSLog(@"Changed audio input device: %@", [device name]);
  787. });
  788. }
  789. // ------------------------------------------------------
  790. // ------------------- Visualizations -------------------
  791. // ------------------------------------------------------
  792. - (void)visualizeDisplay:(NSTimer *)timer {
  793. NSBitmapImageRep *screen = [Screenshot screenshot:[timer userInfo]];
  794. NSInteger spp = [screen samplesPerPixel];
  795. if (((spp != 3) && (spp != 4)) || ([screen isPlanar] == YES) || ([screen numberOfPlanes] != 1)) {
  796. NSLog(@"Unknown image format (%ld, %c, %ld)!\n", (long)spp, ([screen isPlanar] == YES) ? 'p' : 'n', (long)[screen numberOfPlanes]);
  797. return;
  798. }
  799. int redC = 0, greenC = 1, blueC = 2;
  800. if ([screen bitmapFormat] & NSAlphaFirstBitmapFormat) {
  801. redC = 1; greenC = 2; blueC = 3;
  802. }
  803. unsigned char *data = [screen bitmapData];
  804. unsigned long width = [screen pixelsWide];
  805. unsigned long height = [screen pixelsHigh];
  806. unsigned long max = width * height;
  807. unsigned long red = 0, green = 0, blue = 0;
  808. for (unsigned long i = 0; i < max; i += AVERAGE_COLOR_PERFORMANCE_INC) {
  809. unsigned long off = spp * i;
  810. red += data[off + redC];
  811. green += data[off + greenC];
  812. blue += data[off + blueC];
  813. }
  814. max /= AVERAGE_COLOR_PERFORMANCE_INC;
  815. [self setLightsR:(red / max) G:(green / max) B:(blue / max)];
  816. }
  817. - (void)visualizeGPUUsage:(NSTimer *)timer {
  818. NSNumber *usage;
  819. NSNumber *freeVRAM;
  820. NSNumber *usedVRAM;
  821. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  822. NSLog(@"Error reading GPU information\n");
  823. } else {
  824. double h = [self map:[usage doubleValue] FromMin:0.0 FromMax:100.0 ToMin:GPU_COLOR_MIN ToMax:GPU_COLOR_MAX];
  825. #ifdef DEBUG
  826. NSLog(@"GPU Usage: %.3f%%\n", [usage doubleValue]);
  827. #endif
  828. unsigned char r, g, b;
  829. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  830. [self setLightsR:r G:g B:b];
  831. }
  832. }
  833. - (void)visualizeVRAMUsage:(NSTimer *)timer {
  834. NSNumber *usage;
  835. NSNumber *freeVRAM;
  836. NSNumber *usedVRAM;
  837. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  838. NSLog(@"Error reading GPU information\n");
  839. } else {
  840. double h = [self map:[freeVRAM doubleValue] FromMin:0.0 FromMax:([freeVRAM doubleValue] + [usedVRAM doubleValue]) ToMin:RAM_COLOR_MIN ToMax:RAM_COLOR_MAX];
  841. #ifdef DEBUG
  842. NSLog(@"VRAM %.2fGB Free + %.2fGB Used = %.2fGB mapped to color %.2f!\n", [freeVRAM doubleValue] / (1024.0 * 1024.0 * 1024.0), [usedVRAM doubleValue] / (1024.0 * 1024.0 * 1024.0), ([freeVRAM doubleValue] + [usedVRAM doubleValue]) / (1024.0 * 1024.0 * 1024.0), h);
  843. #endif
  844. unsigned char r, g, b;
  845. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  846. [self setLightsR:r G:g B:b];
  847. }
  848. }
  849. - (void)visualizeCPUUsage:(NSTimer *)timer {
  850. JSKMCPUUsageInfo cpuUsageInfo = [JSKSystemMonitor systemMonitor].cpuUsageInfo;
  851. double h = [self map:cpuUsageInfo.usage FromMin:0.0 FromMax:100.0 ToMin:CPU_COLOR_MIN ToMax:CPU_COLOR_MAX];
  852. #ifdef DEBUG
  853. NSLog(@"CPU Usage: %.3f%%\n", cpuUsageInfo.usage);
  854. #endif
  855. unsigned char r, g, b;
  856. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  857. [self setLightsR:r G:g B:b];
  858. }
  859. - (void)visualizeRAMUsage:(NSTimer *)timer {
  860. JSKMMemoryUsageInfo memoryUsageInfo = [JSKSystemMonitor systemMonitor].memoryUsageInfo;
  861. double h = [self map:memoryUsageInfo.freeMemory FromMin:0.0 FromMax:(memoryUsageInfo.usedMemory + memoryUsageInfo.freeMemory) ToMin:RAM_COLOR_MIN ToMax:RAM_COLOR_MAX];
  862. #ifdef DEBUG
  863. NSLog(@"RAM %.2fGB Free + %.2fGB Used = %.2fGB mapped to color %.2f!\n", memoryUsageInfo.freeMemory / (1024.0 * 1024.0 * 1024.0), memoryUsageInfo.usedMemory / (1024.0 * 1024.0 * 1024.0), (memoryUsageInfo.freeMemory + memoryUsageInfo.usedMemory) / (1024.0 * 1024.0 * 1024.0), h);
  864. #endif
  865. unsigned char r, g, b;
  866. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  867. [self setLightsR:r G:g B:b];
  868. }
  869. - (void)visualizeGPUTemperature:(NSTimer *)timer {
  870. JSKSMC *smc = [JSKSMC smc];
  871. double temp = [smc temperatureInCelsiusForKey:KEY_GPU_TEMPERATURE];
  872. if (temp > 1000.0) {
  873. temp /= 1000.0;
  874. }
  875. if (temp > GPU_TEMP_MAX) {
  876. temp = GPU_TEMP_MAX;
  877. }
  878. if (temp < GPU_TEMP_MIN) {
  879. temp = GPU_TEMP_MIN;
  880. }
  881. double h = [self map:temp FromMin:GPU_TEMP_MIN FromMax:GPU_TEMP_MAX ToMin:GPU_COLOR_MIN ToMax:GPU_COLOR_MAX];
  882. #ifdef DEBUG
  883. NSLog(@"GPU Temp %.2f mapped to color %.2f!\n", temp, h);
  884. #endif
  885. unsigned char r, g, b;
  886. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  887. [self setLightsR:r G:g B:b];
  888. }
  889. - (void)visualizeCPUTemperature:(NSTimer *)timer {
  890. JSKSMC *smc = [JSKSMC smc];
  891. double temp = [smc temperatureInCelsiusForKey:KEY_CPU_TEMPERATURE];
  892. if (temp > 1000.0) {
  893. temp /= 1000.0;
  894. }
  895. if (temp > CPU_TEMP_MAX) {
  896. temp = CPU_TEMP_MAX;
  897. }
  898. if (temp < CPU_TEMP_MIN) {
  899. temp = CPU_TEMP_MIN;
  900. }
  901. double h = [self map:temp FromMin:CPU_TEMP_MIN FromMax:CPU_TEMP_MAX ToMin:CPU_COLOR_MIN ToMax:CPU_COLOR_MAX];
  902. #ifdef DEBUG
  903. NSLog(@"CPU Temp %.2f mapped to color %.2f!\n", temp, h);
  904. #endif
  905. unsigned char r, g, b;
  906. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  907. [self setLightsR:r G:g B:b];
  908. }
  909. - (void)visualizeRGBFade:(NSTimer *)timer {
  910. static unsigned char color[3] = { 255, 0, 0 };
  911. static int dec = 0;
  912. static int val = 0;
  913. // Adapted from:
  914. // https://gist.github.com/jamesotron/766994
  915. if (dec < 3) {
  916. int inc = (dec == 2) ? 0 : (dec + 1);
  917. if (val < 255) {
  918. color[dec] -= 1;
  919. color[inc] += 1;
  920. val++;
  921. } else {
  922. val = 0;
  923. dec++;
  924. }
  925. } else {
  926. dec = 0;
  927. }
  928. [self setLightsR:color[0] G:color[1] B:color[2]];
  929. }
  930. - (void)visualizeHSVFade:(NSTimer *)timer {
  931. static float h = 0.0;
  932. if (h < 359.0) {
  933. h += 0.5;
  934. } else {
  935. h = 0.0;
  936. }
  937. unsigned char r, g, b;
  938. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  939. [self setLightsR:r G:g B:b];
  940. }
  941. - (void)visualizeRandom:(NSTimer *)timer {
  942. [self setLightsR:rand() % 256 G:rand() % 256 B:rand() % 256];
  943. }
  944. // -----------------------------------------------------
  945. // --------------------- Utilities ---------------------
  946. // -----------------------------------------------------
  947. - (double)map:(double)val FromMin:(double)fmin FromMax:(double)fmax ToMin:(double)tmin ToMax:(double)tmax {
  948. double norm = (val - fmin) / (fmax - fmin);
  949. return (norm * (tmax - tmin)) + tmin;
  950. }
  951. - (void)convertH:(double)h S:(double)s V:(double)v toR:(unsigned char *)r G:(unsigned char *)g B:(unsigned char *)b {
  952. // Adapted from:
  953. // https://gist.github.com/hdznrrd/656996
  954. if (s == 0.0) {
  955. // Achromatic
  956. *r = *g = *b = (unsigned char)(v * 255);
  957. return;
  958. }
  959. h /= 60; // sector 0 to 5
  960. int i = floor(h);
  961. double f = h - i; // factorial part of h
  962. double p = v * (1 - s);
  963. double q = v * (1 - s * f);
  964. double t = v * (1 - s * (1 - f));
  965. switch (i) {
  966. case 0:
  967. *r = (unsigned char)round(255 * v);
  968. *g = (unsigned char)round(255 * t);
  969. *b = (unsigned char)round(255 * p);
  970. break;
  971. case 1:
  972. *r = (unsigned char)round(255 * q);
  973. *g = (unsigned char)round(255 * v);
  974. *b = (unsigned char)round(255 * p);
  975. break;
  976. case 2:
  977. *r = (unsigned char)round(255 * p);
  978. *g = (unsigned char)round(255 * v);
  979. *b = (unsigned char)round(255 * t);
  980. break;
  981. case 3:
  982. *r = (unsigned char)round(255 * p);
  983. *g = (unsigned char)round(255 * q);
  984. *b = (unsigned char)round(255 * v);
  985. break;
  986. case 4:
  987. *r = (unsigned char)round(255 * t);
  988. *g = (unsigned char)round(255 * p);
  989. *b = (unsigned char)round(255 * v);
  990. break;
  991. default: case 5:
  992. *r = (unsigned char)round(255 * v);
  993. *g = (unsigned char)round(255 * p);
  994. *b = (unsigned char)round(255 * q);
  995. break;
  996. }
  997. }
  998. @end