Merge pull request #5 from diogot/master

Added hardware mute via Core Audio
This commit is contained in:
Gustavo Barbosa
2015-10-06 09:31:40 -04:00
11 changed files with 320 additions and 257 deletions

32
.gitignore vendored Normal file
View File

@@ -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

View File

@@ -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 = "<group>"; };
D9A9F66E1BC35EDC004395E8 /* AudioMixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioMixer.h; path = MuteUnmuteMic/AudioMixer.h; sourceTree = "<group>"; };
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 = "<group>"; };
ECF208BD1BAF2DE6000D3C2C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@@ -39,6 +42,8 @@
children = (
ECF208BB1BAF2DE6000D3C2C /* MuteUnmuteMic */,
ECF208BA1BAF2DE6000D3C2C /* Products */,
D9A9F66D1BC35EDC004395E8 /* AudioMixer.c */,
D9A9F66E1BC35EDC004395E8 /* AudioMixer.h */,
);
sourceTree = "<group>";
};
@@ -142,6 +147,7 @@
files = (
ECF208C11BAF2DE6000D3C2C /* main.m in Sources */,
ECF208BE1BAF2DE6000D3C2C /* AppDelegate.m in Sources */,
D9A9F66F1BC35EDC004395E8 /* AudioMixer.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF208B81BAF2DE6000D3C2C"
BuildableName = "MuteUnmuteMic.app"
BlueprintName = "MuteUnmuteMic"
ReferencedContainer = "container:MuteUnmuteMic.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF208B81BAF2DE6000D3C2C"
BuildableName = "MuteUnmuteMic.app"
BlueprintName = "MuteUnmuteMic"
ReferencedContainer = "container:MuteUnmuteMic.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF208B81BAF2DE6000D3C2C"
BuildableName = "MuteUnmuteMic.app"
BlueprintName = "MuteUnmuteMic"
ReferencedContainer = "container:MuteUnmuteMic.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF208B81BAF2DE6000D3C2C"
BuildableName = "MuteUnmuteMic.app"
BlueprintName = "MuteUnmuteMic"
ReferencedContainer = "container:MuteUnmuteMic.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>MuteUnmuteMic.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>ECF208B81BAF2DE6000D3C2C</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF208B81BAF2DE6000D3C2C"
BuildableName = "MuteUnmuteMic.app"
BlueprintName = "MuteUnmuteMic"
ReferencedContainer = "container:MuteUnmuteMic.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF208B81BAF2DE6000D3C2C"
BuildableName = "MuteUnmuteMic.app"
BlueprintName = "MuteUnmuteMic"
ReferencedContainer = "container:MuteUnmuteMic.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF208B81BAF2DE6000D3C2C"
BuildableName = "MuteUnmuteMic.app"
BlueprintName = "MuteUnmuteMic"
ReferencedContainer = "container:MuteUnmuteMic.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ECF208B81BAF2DE6000D3C2C"
BuildableName = "MuteUnmuteMic.app"
BlueprintName = "MuteUnmuteMic"
ReferencedContainer = "container:MuteUnmuteMic.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>MuteUnmuteMic.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>ECF208B81BAF2DE6000D3C2C</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@@ -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];
}

154
MuteUnmuteMic/AudioMixer.c Normal file
View File

@@ -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 = "<Unamed device>";
}
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);
}
}

View File

@@ -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 <CoreAudio/CoreAudio.h>
/**
* 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 */

View File

@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.3</string>
<string>1.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>2</string>
<string>3</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>