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

AppDelegate.m 48KB

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