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 42KB

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