Simple RGB LED controller for Mac OS X
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

AppDelegate.m 44KB

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