diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6b1a70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# OS X Finder +.DS_Store + +# Xcode per-user config +*.mode1 +*.mode1v3 +*.mode2v3 +*.perspective +*.perspectivev3 +*.pbxuser +xcuserdata +*.xccheckout + +# Build products +build/ +*.o +*.LinkFileList +*.hmap + +# Automatic backup files +*~.nib/ +*.swp +*~ +*.dat +*.dep + +# Cocoapods +Pods + +# AppCode specific files +.idea/ +*.iml diff --git a/MuteUnmuteMic.xcodeproj/project.pbxproj b/MuteUnmuteMic.xcodeproj/project.pbxproj index 58d2ef5..92534f4 100644 --- a/MuteUnmuteMic.xcodeproj/project.pbxproj +++ b/MuteUnmuteMic.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + D9A9F66F1BC35EDC004395E8 /* AudioMixer.c in Sources */ = {isa = PBXBuildFile; fileRef = D9A9F66D1BC35EDC004395E8 /* AudioMixer.c */; settings = {ASSET_TAGS = (); }; }; ECF208BE1BAF2DE6000D3C2C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = ECF208BD1BAF2DE6000D3C2C /* AppDelegate.m */; }; ECF208C11BAF2DE6000D3C2C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = ECF208C01BAF2DE6000D3C2C /* main.m */; }; ECF208C31BAF2DE6000D3C2C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ECF208C21BAF2DE6000D3C2C /* Assets.xcassets */; }; @@ -14,6 +15,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + D9A9F66D1BC35EDC004395E8 /* AudioMixer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AudioMixer.c; path = MuteUnmuteMic/AudioMixer.c; sourceTree = ""; }; + D9A9F66E1BC35EDC004395E8 /* AudioMixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioMixer.h; path = MuteUnmuteMic/AudioMixer.h; sourceTree = ""; }; ECF208B91BAF2DE6000D3C2C /* MuteUnmuteMic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MuteUnmuteMic.app; sourceTree = BUILT_PRODUCTS_DIR; }; ECF208BC1BAF2DE6000D3C2C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; ECF208BD1BAF2DE6000D3C2C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -39,6 +42,8 @@ children = ( ECF208BB1BAF2DE6000D3C2C /* MuteUnmuteMic */, ECF208BA1BAF2DE6000D3C2C /* Products */, + D9A9F66D1BC35EDC004395E8 /* AudioMixer.c */, + D9A9F66E1BC35EDC004395E8 /* AudioMixer.h */, ); sourceTree = ""; }; @@ -142,6 +147,7 @@ files = ( ECF208C11BAF2DE6000D3C2C /* main.m in Sources */, ECF208BE1BAF2DE6000D3C2C /* AppDelegate.m in Sources */, + D9A9F66F1BC35EDC004395E8 /* AudioMixer.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MuteUnmuteMic.xcodeproj/project.xcworkspace/xcuserdata/gustavo.xcuserdatad/UserInterfaceState.xcuserstate b/MuteUnmuteMic.xcodeproj/project.xcworkspace/xcuserdata/gustavo.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 01dc4a9..0000000 Binary files a/MuteUnmuteMic.xcodeproj/project.xcworkspace/xcuserdata/gustavo.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/MuteUnmuteMic.xcodeproj/xcuserdata/btlr.xcuserdatad/xcschemes/MuteUnmuteMic.xcscheme b/MuteUnmuteMic.xcodeproj/xcuserdata/btlr.xcuserdatad/xcschemes/MuteUnmuteMic.xcscheme deleted file mode 100644 index 126cd1a..0000000 --- a/MuteUnmuteMic.xcodeproj/xcuserdata/btlr.xcuserdatad/xcschemes/MuteUnmuteMic.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MuteUnmuteMic.xcodeproj/xcuserdata/btlr.xcuserdatad/xcschemes/xcschememanagement.plist b/MuteUnmuteMic.xcodeproj/xcuserdata/btlr.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 3dfa6b0..0000000 --- a/MuteUnmuteMic.xcodeproj/xcuserdata/btlr.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - SchemeUserState - - MuteUnmuteMic.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - ECF208B81BAF2DE6000D3C2C - - primary - - - - - diff --git a/MuteUnmuteMic.xcodeproj/xcuserdata/gustavo.xcuserdatad/xcschemes/MuteUnmuteMic.xcscheme b/MuteUnmuteMic.xcodeproj/xcuserdata/gustavo.xcuserdatad/xcschemes/MuteUnmuteMic.xcscheme deleted file mode 100644 index 9dea4a5..0000000 --- a/MuteUnmuteMic.xcodeproj/xcuserdata/gustavo.xcuserdatad/xcschemes/MuteUnmuteMic.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MuteUnmuteMic.xcodeproj/xcuserdata/gustavo.xcuserdatad/xcschemes/xcschememanagement.plist b/MuteUnmuteMic.xcodeproj/xcuserdata/gustavo.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 3dfa6b0..0000000 --- a/MuteUnmuteMic.xcodeproj/xcuserdata/gustavo.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - SchemeUserState - - MuteUnmuteMic.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - ECF208B81BAF2DE6000D3C2C - - primary - - - - - diff --git a/MuteUnmuteMic/AppDelegate.m b/MuteUnmuteMic/AppDelegate.m index ddfcd94..a822aac 100644 --- a/MuteUnmuteMic/AppDelegate.m +++ b/MuteUnmuteMic/AppDelegate.m @@ -1,32 +1,37 @@ #import "AppDelegate.h" +#import "AudioMixer.h" -#define MAX_VOLUME 70 +static NSInteger const kDefaultVolume = 70; @interface AppDelegate () -{ - NSStatusItem *menuItem; - BOOL muted; - int inputVolumeToUnmute; -} + +@property (nonatomic) NSStatusItem *menuItem; +@property (nonatomic) BOOL muted; +@property (nonatomic) NSInteger inputVolumeToUnmute; @end @implementation AppDelegate -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ [self initDefaults]; [self configureStatusBar]; + [self updateInputVolume]; } -- (void)initDefaults { - muted = NO; - inputVolumeToUnmute = MAX_VOLUME; +- (void)initDefaults +{ + _muted = IsHardwareMuted(); + _inputVolumeToUnmute = kDefaultVolume; } -- (void)configureStatusBar { +- (void)configureStatusBar +{ NSStatusBar *statusBar = [NSStatusBar systemStatusBar]; - menuItem = [statusBar statusItemWithLength:NSVariableStatusItemLength]; + NSStatusItem *menuItem = + [statusBar statusItemWithLength:NSVariableStatusItemLength]; [menuItem setToolTip:@"MuteUnmuteMic by CocoaHeads Brazil"]; [menuItem setImage:[NSImage imageNamed:@"mic_on"]]; [menuItem setHighlightMode:YES]; @@ -34,9 +39,12 @@ [menuItem setTarget:self]; [menuItem setAction:@selector(menuItemClicked:)]; [menuItem.button sendActionOn:NSLeftMouseUpMask|NSRightMouseUpMask]; + + self.menuItem = menuItem; } -- (void)menuItemClicked:(id)sender { +- (void)menuItemClicked:(id)sender +{ NSEvent *event = [[NSApplication sharedApplication] currentEvent]; if ((event.modifierFlags & NSControlKeyMask) || (event.type == NSRightMouseUp)) { @@ -44,39 +52,59 @@ } else { [self toggleMute]; } - } -- (void)toggleMute { - muted = !muted; +- (void)toggleMute +{ + self.muted = !self.muted; [self updateInputVolume]; } -- (void)updateInputVolume { - int volume = muted ? 0 : inputVolumeToUnmute; - NSString *source = [NSString stringWithFormat:@"set volume input volume %d", volume]; +- (void)updateInputVolume +{ + BOOL muted = self.muted; + + NSInteger volume; + NSString *imageName; + if (muted) { + volume = 0; + imageName = @"mic_off"; + } else { + volume = self.inputVolumeToUnmute; + imageName = @"mic_on"; + } + + // set volume + NSString *source = + [NSString stringWithFormat:@"set volume input volume %ld", (long)volume]; NSAppleScript *script = [[NSAppleScript alloc] initWithSource:source]; + NSDictionary *errorInfo = nil; + [script executeAndReturnError:&errorInfo]; - [script executeAndReturnError:nil]; + if (errorInfo) { + NSLog(@"Error on script %@", errorInfo); + } - NSString *imageName = muted ? @"mic_off" : @"mic_on"; - menuItem.image = [NSImage imageNamed:imageName]; - + // set hardware mute + SetHardwareMute(muted); + + // set image + self.menuItem.image = [NSImage imageNamed:imageName]; } -- (void)showMenu { - [menuItem popUpStatusItemMenu:self.menu]; +- (void)showMenu +{ + [self.menuItem popUpStatusItemMenu:self.menu]; } - -- (IBAction)didSetVolumeInput:(NSMenuItem *)sender { - +- (IBAction)didSetVolumeInput:(NSMenuItem *)sender +{ for (NSMenuItem *item in sender.menu.itemArray) { item.state = 0; } sender.state = 1; - inputVolumeToUnmute = [sender.title intValue]; + self.inputVolumeToUnmute = [sender.title integerValue]; [self updateInputVolume]; } diff --git a/MuteUnmuteMic/AudioMixer.c b/MuteUnmuteMic/AudioMixer.c new file mode 100644 index 0000000..81fa56e --- /dev/null +++ b/MuteUnmuteMic/AudioMixer.c @@ -0,0 +1,154 @@ +// +// AudioMixer.c +// MuteUnmuteMic +// +// Created by Diogo Tridapalli on 10/5/15. +// Copyright © 2015 Gustavo Barbosa. All rights reserved. +// + +#include "AudioMixer.h" + +Boolean GetDefaultInputAudioDevice(AudioDeviceID *defaultInputDeviceID) +{ + UInt32 thePropSize = sizeof(AudioDeviceID); + + AudioObjectPropertyAddress thePropertyAddress = + { kAudioHardwarePropertyDefaultInputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster }; + + OSStatus errorCode = + AudioObjectGetPropertyData(kAudioObjectSystemObject, + &thePropertyAddress, + 0, + NULL, + &thePropSize, + defaultInputDeviceID); + if (errorCode) { + printf("Error in GetDefaultInputAudioDevice: %d\n", errorCode); + } + + return errorCode == 0; +} + +Boolean GetAudioDeviceName(const AudioDeviceID deviceID, + CFStringRef *deviceName) +{ + UInt32 thePropSize = sizeof(CFStringRef); + AudioObjectPropertyAddress thePropertyAddress = + { kAudioObjectPropertyName, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster }; + + OSStatus errorCode = + AudioObjectGetPropertyData(deviceID, + &thePropertyAddress, + 0, + NULL, + &thePropSize, + deviceName); + + if (errorCode) { + printf("Error in GetAudioDeviceName: %d\n", errorCode); + } + + return errorCode == 0; +} + +Boolean GetMuteOnInputAudioDevice(const AudioDeviceID inputDeviceID, + Boolean *isMuted) +{ + AudioObjectPropertyAddress thePropertyAddress = + { kAudioDevicePropertyMute, + kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster }; + + UInt32 mute = 0; + UInt32 thePropSize = sizeof(mute); + + OSStatus errorCode; + if (AudioObjectHasProperty(inputDeviceID, &thePropertyAddress)) { + errorCode = AudioObjectGetPropertyData(inputDeviceID, + &thePropertyAddress, + 0, + NULL, + &thePropSize, + &mute); + if (errorCode) { + printf("Error in GetMuteOnInputAudioDevice: %d\n", errorCode); + } + + *isMuted = mute > 0; + + return errorCode == 0; + } else { + printf("Error in GetMuteOnInputAudioDevice: mute not supported\n"); + + return false; + } +} + +Boolean SetMuteOnInputAudioDevice(const AudioDeviceID inputDeviceID, + const Boolean mute) +{ + AudioObjectPropertyAddress thePropertyAddress = + { kAudioDevicePropertyMute, + kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster }; + + UInt32 theMute = mute; + UInt32 thePropSize = sizeof(theMute); + + + const char *inputDeviceString; + CFStringRef inputDeviceName; + if (GetAudioDeviceName(inputDeviceID, &inputDeviceName)) { + inputDeviceString = + CFStringGetCStringPtr(inputDeviceName, CFStringGetSystemEncoding()); + CFRelease(inputDeviceName); + } else { + inputDeviceString = ""; + } + + Boolean setMute = true; + + if (AudioObjectHasProperty(inputDeviceID, &thePropertyAddress)) { + printf("\tSetting %s mute %s\n", inputDeviceString, (theMute) ? "on" : "off"); + OSStatus errorCode = AudioObjectSetPropertyData(inputDeviceID, + &thePropertyAddress, + 0, + NULL, + thePropSize, + &theMute); + if (errorCode) { + printf("Error in SetMuteOnInputAudioDevice: %d\n", errorCode); + setMute = false; + } + } else { + printf("Error in SetMuteOnInputAudioDevice: mute not supported\n"); + setMute = false; + } + + return setMute; +} + +Boolean IsHardwareMuted() +{ + AudioDeviceID theDefaultInputDeviceID; + + Boolean isMuted = false; + + if (GetDefaultInputAudioDevice(&theDefaultInputDeviceID)) { + GetMuteOnInputAudioDevice(theDefaultInputDeviceID, &isMuted); + } + + return isMuted; +} + +void SetHardwareMute(Boolean theMute) +{ + AudioDeviceID theDefaultInputDeviceID; + if (GetDefaultInputAudioDevice(&theDefaultInputDeviceID)) { + SetMuteOnInputAudioDevice(theDefaultInputDeviceID, theMute); + } +} \ No newline at end of file diff --git a/MuteUnmuteMic/AudioMixer.h b/MuteUnmuteMic/AudioMixer.h new file mode 100644 index 0000000..aaa1e48 --- /dev/null +++ b/MuteUnmuteMic/AudioMixer.h @@ -0,0 +1,69 @@ +// +// AudioMixer.h +// MuteUnmuteMic +// +// Created by Diogo Tridapalli on 10/5/15. +// Copyright © 2015 Gustavo Barbosa. All rights reserved. +// + +#ifndef AudioMixer_h +#define AudioMixer_h + +#import +/** + * Get device id for the default input device + * + * @param defaultInputDeviceID AudioDeviceID pointer + * + * @return @a true if success @a false otherwise + */ +extern Boolean GetDefaultInputAudioDevice(AudioDeviceID *defaultInputDeviceID); + +/** + * Get the name of an audio device + * + * @param deviceID AudioDeviceID + * @param deviceName CFStringRef reference, must be release after use + * + * @return @a true if success @a false otherwise + */ +extern Boolean GetAudioDeviceName(const AudioDeviceID deviceID, + CFStringRef *deviceName); + +/** + * Get mute state on input device + * + * @param inputDeviceID AudioDeviceID + * @param isMuted @a true if device is muted @a false otherwise + * + * @return @a true if success @a false otherwise + */ +extern Boolean GetMuteOnInputAudioDevice(const AudioDeviceID inputDeviceID, + Boolean *isMuted); + +/** + * Mute or unmute an inpute device + * + * @param inputDeviceID AudioDeviceID + * @param mute @a true if device should be muted @a false otherwise + * + * @return @a true if success @a false otherwise + */ +extern Boolean SetMuteOnInputAudioDevice(const AudioDeviceID inputDeviceID, + const Boolean mute); + +/** + * Get the mute state of default input via HAL + * + * @return @a true if is muted @a false otherwise + */ +extern Boolean IsHardwareMuted(); + +/** + * Set the mute state of default input via HAL + * + * @param theMute @a true if should be muted @a false otherwise + */ +extern void SetHardwareMute(Boolean theMute); + +#endif /* AudioMixer_h */ diff --git a/MuteUnmuteMic/Info.plist b/MuteUnmuteMic/Info.plist index 2104d0d..02e5378 100644 --- a/MuteUnmuteMic/Info.plist +++ b/MuteUnmuteMic/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3 + 1.4 CFBundleSignature ???? CFBundleVersion - 2 + 3 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion