Simple RGB LED controller for Mac OS X
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

AppDelegate.m 45KB

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