├── AggregateVolumeMenu.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ └── xcuserdata │ │ └── guru.xcuserdatad │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── guru.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── AggregateVolumeMenu ├── AggregateVolumeControl.swift ├── AggregateVolumeMenu.entitlements ├── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── Contents.json │ └── StatusIcon.imageset │ │ ├── Contents.json │ │ ├── StatusIcon-1.png │ │ ├── StatusIcon-2.png │ │ ├── StatusIcon-3.png │ │ ├── StatusIcon-4.png │ │ ├── StatusIcon-5.png │ │ └── StatusIcon.png ├── Base.lproj │ └── Main.storyboard ├── Info.plist ├── MyApplication.swift └── ViewController.swift └── README.md /AggregateVolumeMenu.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CABDF1192590FD56006F420A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABDF1182590FD56006F420A /* AppDelegate.swift */; }; 11 | CABDF11B2590FD56006F420A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABDF11A2590FD56006F420A /* ViewController.swift */; }; 12 | CABDF11D2590FD57006F420A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CABDF11C2590FD57006F420A /* Assets.xcassets */; }; 13 | CABDF1202590FD57006F420A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CABDF11E2590FD57006F420A /* Main.storyboard */; }; 14 | CABDF13225911675006F420A /* MyApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABDF12E25911571006F420A /* MyApplication.swift */; }; 15 | CABDF136259138E8006F420A /* AggregateVolumeControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABDF135259138E8006F420A /* AggregateVolumeControl.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | CABDF1152590FD56006F420A /* AggregateVolumeMenu.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AggregateVolumeMenu.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | CABDF1182590FD56006F420A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | CABDF11A2590FD56006F420A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 22 | CABDF11C2590FD57006F420A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 23 | CABDF11F2590FD57006F420A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | CABDF1212590FD57006F420A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 25 | CABDF1222590FD57006F420A /* AggregateVolumeMenu.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AggregateVolumeMenu.entitlements; sourceTree = ""; }; 26 | CABDF12E25911571006F420A /* MyApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyApplication.swift; sourceTree = ""; }; 27 | CABDF135259138E8006F420A /* AggregateVolumeControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AggregateVolumeControl.swift; sourceTree = ""; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | CABDF1122590FD56006F420A /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | CABDF10C2590FD56006F420A = { 42 | isa = PBXGroup; 43 | children = ( 44 | CABDF1172590FD56006F420A /* AggregateVolumeMenu */, 45 | CABDF1162590FD56006F420A /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | CABDF1162590FD56006F420A /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | CABDF1152590FD56006F420A /* AggregateVolumeMenu.app */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | CABDF1172590FD56006F420A /* AggregateVolumeMenu */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | CABDF12E25911571006F420A /* MyApplication.swift */, 61 | CABDF1182590FD56006F420A /* AppDelegate.swift */, 62 | CABDF11A2590FD56006F420A /* ViewController.swift */, 63 | CABDF11C2590FD57006F420A /* Assets.xcassets */, 64 | CABDF11E2590FD57006F420A /* Main.storyboard */, 65 | CABDF1212590FD57006F420A /* Info.plist */, 66 | CABDF1222590FD57006F420A /* AggregateVolumeMenu.entitlements */, 67 | CABDF135259138E8006F420A /* AggregateVolumeControl.swift */, 68 | ); 69 | path = AggregateVolumeMenu; 70 | sourceTree = ""; 71 | }; 72 | /* End PBXGroup section */ 73 | 74 | /* Begin PBXNativeTarget section */ 75 | CABDF1142590FD56006F420A /* AggregateVolumeMenu */ = { 76 | isa = PBXNativeTarget; 77 | buildConfigurationList = CABDF1252590FD57006F420A /* Build configuration list for PBXNativeTarget "AggregateVolumeMenu" */; 78 | buildPhases = ( 79 | CABDF1112590FD56006F420A /* Sources */, 80 | CABDF1122590FD56006F420A /* Frameworks */, 81 | CABDF1132590FD56006F420A /* Resources */, 82 | ); 83 | buildRules = ( 84 | ); 85 | dependencies = ( 86 | ); 87 | name = AggregateVolumeMenu; 88 | productName = AggregateVolumeMenu; 89 | productReference = CABDF1152590FD56006F420A /* AggregateVolumeMenu.app */; 90 | productType = "com.apple.product-type.application"; 91 | }; 92 | /* End PBXNativeTarget section */ 93 | 94 | /* Begin PBXProject section */ 95 | CABDF10D2590FD56006F420A /* Project object */ = { 96 | isa = PBXProject; 97 | attributes = { 98 | LastSwiftUpdateCheck = 1230; 99 | LastUpgradeCheck = 1230; 100 | TargetAttributes = { 101 | CABDF1142590FD56006F420A = { 102 | CreatedOnToolsVersion = 12.3; 103 | }; 104 | }; 105 | }; 106 | buildConfigurationList = CABDF1102590FD56006F420A /* Build configuration list for PBXProject "AggregateVolumeMenu" */; 107 | compatibilityVersion = "Xcode 9.3"; 108 | developmentRegion = en; 109 | hasScannedForEncodings = 0; 110 | knownRegions = ( 111 | en, 112 | Base, 113 | ); 114 | mainGroup = CABDF10C2590FD56006F420A; 115 | productRefGroup = CABDF1162590FD56006F420A /* Products */; 116 | projectDirPath = ""; 117 | projectRoot = ""; 118 | targets = ( 119 | CABDF1142590FD56006F420A /* AggregateVolumeMenu */, 120 | ); 121 | }; 122 | /* End PBXProject section */ 123 | 124 | /* Begin PBXResourcesBuildPhase section */ 125 | CABDF1132590FD56006F420A /* Resources */ = { 126 | isa = PBXResourcesBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | CABDF11D2590FD57006F420A /* Assets.xcassets in Resources */, 130 | CABDF1202590FD57006F420A /* Main.storyboard in Resources */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | /* End PBXResourcesBuildPhase section */ 135 | 136 | /* Begin PBXSourcesBuildPhase section */ 137 | CABDF1112590FD56006F420A /* Sources */ = { 138 | isa = PBXSourcesBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | CABDF13225911675006F420A /* MyApplication.swift in Sources */, 142 | CABDF11B2590FD56006F420A /* ViewController.swift in Sources */, 143 | CABDF1192590FD56006F420A /* AppDelegate.swift in Sources */, 144 | CABDF136259138E8006F420A /* AggregateVolumeControl.swift in Sources */, 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | /* End PBXSourcesBuildPhase section */ 149 | 150 | /* Begin PBXVariantGroup section */ 151 | CABDF11E2590FD57006F420A /* Main.storyboard */ = { 152 | isa = PBXVariantGroup; 153 | children = ( 154 | CABDF11F2590FD57006F420A /* Base */, 155 | ); 156 | name = Main.storyboard; 157 | sourceTree = ""; 158 | }; 159 | /* End PBXVariantGroup section */ 160 | 161 | /* Begin XCBuildConfiguration section */ 162 | CABDF1232590FD57006F420A /* Debug */ = { 163 | isa = XCBuildConfiguration; 164 | buildSettings = { 165 | ALWAYS_SEARCH_USER_PATHS = NO; 166 | CLANG_ANALYZER_NONNULL = YES; 167 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 168 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 169 | CLANG_CXX_LIBRARY = "libc++"; 170 | CLANG_ENABLE_MODULES = YES; 171 | CLANG_ENABLE_OBJC_ARC = YES; 172 | CLANG_ENABLE_OBJC_WEAK = YES; 173 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 174 | CLANG_WARN_BOOL_CONVERSION = YES; 175 | CLANG_WARN_COMMA = YES; 176 | CLANG_WARN_CONSTANT_CONVERSION = YES; 177 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 178 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 179 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 180 | CLANG_WARN_EMPTY_BODY = YES; 181 | CLANG_WARN_ENUM_CONVERSION = YES; 182 | CLANG_WARN_INFINITE_RECURSION = YES; 183 | CLANG_WARN_INT_CONVERSION = YES; 184 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 185 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 186 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 187 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 188 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 189 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 190 | CLANG_WARN_STRICT_PROTOTYPES = YES; 191 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 192 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 193 | CLANG_WARN_UNREACHABLE_CODE = YES; 194 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 195 | COPY_PHASE_STRIP = NO; 196 | DEBUG_INFORMATION_FORMAT = dwarf; 197 | ENABLE_STRICT_OBJC_MSGSEND = YES; 198 | ENABLE_TESTABILITY = YES; 199 | GCC_C_LANGUAGE_STANDARD = gnu11; 200 | GCC_DYNAMIC_NO_PIC = NO; 201 | GCC_NO_COMMON_BLOCKS = YES; 202 | GCC_OPTIMIZATION_LEVEL = 0; 203 | GCC_PREPROCESSOR_DEFINITIONS = ( 204 | "DEBUG=1", 205 | "$(inherited)", 206 | ); 207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 209 | GCC_WARN_UNDECLARED_SELECTOR = YES; 210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 211 | GCC_WARN_UNUSED_FUNCTION = YES; 212 | GCC_WARN_UNUSED_VARIABLE = YES; 213 | MACOSX_DEPLOYMENT_TARGET = 10.10; 214 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 215 | MTL_FAST_MATH = YES; 216 | ONLY_ACTIVE_ARCH = YES; 217 | SDKROOT = macosx; 218 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 219 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 220 | }; 221 | name = Debug; 222 | }; 223 | CABDF1242590FD57006F420A /* Release */ = { 224 | isa = XCBuildConfiguration; 225 | buildSettings = { 226 | ALWAYS_SEARCH_USER_PATHS = NO; 227 | CLANG_ANALYZER_NONNULL = YES; 228 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 229 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 230 | CLANG_CXX_LIBRARY = "libc++"; 231 | CLANG_ENABLE_MODULES = YES; 232 | CLANG_ENABLE_OBJC_ARC = YES; 233 | CLANG_ENABLE_OBJC_WEAK = YES; 234 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 235 | CLANG_WARN_BOOL_CONVERSION = YES; 236 | CLANG_WARN_COMMA = YES; 237 | CLANG_WARN_CONSTANT_CONVERSION = YES; 238 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 239 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 240 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 241 | CLANG_WARN_EMPTY_BODY = YES; 242 | CLANG_WARN_ENUM_CONVERSION = YES; 243 | CLANG_WARN_INFINITE_RECURSION = YES; 244 | CLANG_WARN_INT_CONVERSION = YES; 245 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 246 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 247 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 248 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 249 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 250 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 251 | CLANG_WARN_STRICT_PROTOTYPES = YES; 252 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 253 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 254 | CLANG_WARN_UNREACHABLE_CODE = YES; 255 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 256 | COPY_PHASE_STRIP = NO; 257 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 258 | ENABLE_NS_ASSERTIONS = NO; 259 | ENABLE_STRICT_OBJC_MSGSEND = YES; 260 | GCC_C_LANGUAGE_STANDARD = gnu11; 261 | GCC_NO_COMMON_BLOCKS = YES; 262 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 263 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 264 | GCC_WARN_UNDECLARED_SELECTOR = YES; 265 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 266 | GCC_WARN_UNUSED_FUNCTION = YES; 267 | GCC_WARN_UNUSED_VARIABLE = YES; 268 | MACOSX_DEPLOYMENT_TARGET = 10.10; 269 | MTL_ENABLE_DEBUG_INFO = NO; 270 | MTL_FAST_MATH = YES; 271 | SDKROOT = macosx; 272 | SWIFT_COMPILATION_MODE = wholemodule; 273 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 274 | }; 275 | name = Release; 276 | }; 277 | CABDF1262590FD57006F420A /* Debug */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 281 | CODE_SIGN_ENTITLEMENTS = AggregateVolumeMenu/AggregateVolumeMenu.entitlements; 282 | CODE_SIGN_STYLE = Automatic; 283 | COMBINE_HIDPI_IMAGES = YES; 284 | INFOPLIST_FILE = AggregateVolumeMenu/Info.plist; 285 | LD_RUNPATH_SEARCH_PATHS = ( 286 | "$(inherited)", 287 | "@executable_path/../Frameworks", 288 | ); 289 | MARKETING_VERSION = 1.5; 290 | PRODUCT_BUNDLE_IDENTIFIER = com.guru.AggregateVolumeMenu; 291 | PRODUCT_NAME = "$(TARGET_NAME)"; 292 | SWIFT_VERSION = 5.0; 293 | }; 294 | name = Debug; 295 | }; 296 | CABDF1272590FD57006F420A /* Release */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 300 | CODE_SIGN_ENTITLEMENTS = AggregateVolumeMenu/AggregateVolumeMenu.entitlements; 301 | CODE_SIGN_STYLE = Automatic; 302 | COMBINE_HIDPI_IMAGES = YES; 303 | INFOPLIST_FILE = AggregateVolumeMenu/Info.plist; 304 | LD_RUNPATH_SEARCH_PATHS = ( 305 | "$(inherited)", 306 | "@executable_path/../Frameworks", 307 | ); 308 | MARKETING_VERSION = 1.5; 309 | PRODUCT_BUNDLE_IDENTIFIER = com.guru.AggregateVolumeMenu; 310 | PRODUCT_NAME = "$(TARGET_NAME)"; 311 | SWIFT_VERSION = 5.0; 312 | }; 313 | name = Release; 314 | }; 315 | /* End XCBuildConfiguration section */ 316 | 317 | /* Begin XCConfigurationList section */ 318 | CABDF1102590FD56006F420A /* Build configuration list for PBXProject "AggregateVolumeMenu" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | CABDF1232590FD57006F420A /* Debug */, 322 | CABDF1242590FD57006F420A /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | CABDF1252590FD57006F420A /* Build configuration list for PBXNativeTarget "AggregateVolumeMenu" */ = { 328 | isa = XCConfigurationList; 329 | buildConfigurations = ( 330 | CABDF1262590FD57006F420A /* Debug */, 331 | CABDF1272590FD57006F420A /* Release */, 332 | ); 333 | defaultConfigurationIsVisible = 0; 334 | defaultConfigurationName = Release; 335 | }; 336 | /* End XCConfigurationList section */ 337 | }; 338 | rootObject = CABDF10D2590FD56006F420A /* Project object */; 339 | } 340 | -------------------------------------------------------------------------------- /AggregateVolumeMenu.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AggregateVolumeMenu.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AggregateVolumeMenu.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AggregateVolumeMenu.xcodeproj/project.xcworkspace/xcuserdata/guru.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | IssueFilterStyle 12 | ShowActiveSchemeOnly 13 | LiveSourceIssuesEnabled 14 | 15 | ShowSharedSchemesAutomaticallyEnabled 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /AggregateVolumeMenu.xcodeproj/xcuserdata/guru.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /AggregateVolumeMenu.xcodeproj/xcuserdata/guru.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AggregateVolumeMenu.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | CABDF1142590FD56006F420A 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/AggregateVolumeControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AggregateVolumeControl.swift 3 | // AggregateVolumeMenu 4 | // 5 | // Created by Gurhan Polat on 21.12.2020. 6 | // 7 | import Foundation 8 | import Cocoa 9 | import AVFoundation 10 | 11 | class AggregateVolumeControl { 12 | var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(); 13 | var propsize:UInt32 = 0; 14 | var result:OSStatus = 0; 15 | var audioDeviceID: AudioDeviceID = 0; 16 | var audioDeviceUID:CFString? = nil; 17 | var subDeviceCount:Int = 0; 18 | var subDevicesID = [AudioDeviceID]() 19 | 20 | init() { 21 | 22 | // get default audio output device 23 | address = AudioObjectPropertyAddress( 24 | mSelector:AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice), 25 | mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), 26 | mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) 27 | 28 | audioDeviceID = 0 29 | propsize = UInt32(MemoryLayout.size) 30 | result = AudioObjectGetPropertyData(AudioDeviceID(kAudioObjectSystemObject), &address, 0, nil, &propsize, &audioDeviceID) 31 | 32 | if (result != 0) { 33 | print("kAudioHardwarePropertyDefaultOutputDevice result:\(result)") 34 | exit(-1) 35 | } 36 | 37 | // get default audio output device uid 38 | address = AudioObjectPropertyAddress( 39 | mSelector:AudioObjectPropertySelector(kAudioDevicePropertyDeviceUID), 40 | mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), 41 | mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) 42 | 43 | 44 | propsize = UInt32(MemoryLayout.size) 45 | result = AudioObjectGetPropertyData(audioDeviceID, &address, 0, nil, &propsize, &audioDeviceUID) 46 | 47 | if (result != 0) { 48 | print("kAudioDevicePropertyDeviceUID result:\(result)") 49 | exit(-1) 50 | } 51 | 52 | // get default audio output device transport type 53 | address = AudioObjectPropertyAddress( 54 | mSelector:AudioObjectPropertySelector(kAudioDevicePropertyTransportType), 55 | mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), 56 | mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) 57 | var transportType:UInt32 = 0 58 | propsize = UInt32(MemoryLayout.size) 59 | result = AudioObjectGetPropertyData(audioDeviceID, &address, 0, nil, &propsize, &transportType) 60 | 61 | if (result != 0) { 62 | print("kAudioDevicePropertyTransportType result:\(result)") 63 | exit(-1) 64 | } 65 | 66 | // if transportType is not Aggregate then exit the tool 67 | if (transportType != kAudioDeviceTransportTypeAggregate) { 68 | print("audioDeviceID: \(audioDeviceID) uid: \(audioDeviceUID as String? ?? "") transportType: \(transportType == kAudioDeviceTransportTypeAggregate)") 69 | print("this tool only works with a kAudioDeviceTransportTypeAggregate result:\(result)") 70 | exit(1) 71 | } 72 | 73 | // get the sublist of the Aggregate Audio Device 74 | address = AudioObjectPropertyAddress( 75 | mSelector:AudioObjectPropertySelector(kAudioAggregateDevicePropertyActiveSubDeviceList), 76 | mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), 77 | mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) 78 | for _ in 0..<32 { 79 | subDevicesID.append(AudioDeviceID()) 80 | } 81 | propsize = UInt32(MemoryLayout.size * 32) 82 | result = AudioObjectGetPropertyData(audioDeviceID, &address, 0, nil, &propsize, &subDevicesID) 83 | 84 | if (result != 0) { 85 | print("kAudioAggregateDevicePropertyActiveSubDeviceList result:\(result)") 86 | exit(-1) 87 | } 88 | 89 | subDeviceCount = Int((propsize / UInt32(MemoryLayout.size))) 90 | } 91 | 92 | func getChannels(subDevice: AudioDeviceID) -> [UInt32] { 93 | var channelNumbers = [UInt32]() 94 | 95 | for _ in 0..<32 { 96 | channelNumbers.append(UInt32()) 97 | } 98 | 99 | address = AudioObjectPropertyAddress( 100 | mSelector:AudioObjectPropertySelector(kAudioDevicePropertyPreferredChannelsForStereo), 101 | mScope:AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), 102 | mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) 103 | 104 | propsize = UInt32(MemoryLayout.size * 32) 105 | result = AudioObjectGetPropertyData(subDevice, &address, 0, nil, &propsize, &channelNumbers) 106 | if (result != 0) { 107 | print("kAudioDevicePropertyPreferredChannelsForStereo result:\(result)") 108 | exit(-1) 109 | } 110 | 111 | if ((Int(propsize) / MemoryLayout.size) < 32) { 112 | let range = (Int(propsize) / MemoryLayout.size)...31 113 | channelNumbers.removeSubrange(range) 114 | } 115 | return channelNumbers 116 | } 117 | 118 | func getVolume() -> Float { 119 | var volAvg:Float = 0.0 120 | 121 | for i in 0...size) 133 | 134 | result = AudioObjectGetPropertyData(subDevice, &address, 0, nil, &propsize, &vol) 135 | if (result != 0) { 136 | print("kAudioDevicePropertyVolumeScalar channel:\(channel) result:\(result)") 137 | exit(-1) 138 | } 139 | 140 | volSum += vol 141 | } 142 | 143 | volAvg += volSum / Float(channels.count) 144 | } 145 | 146 | volAvg = volAvg / Float(subDeviceCount) 147 | return volAvg 148 | } 149 | 150 | func setVolume(volume:Float) { 151 | var vol = volume 152 | if (vol == 0) { 153 | if (!getMute()) { 154 | switchMute() 155 | } 156 | return 157 | } 158 | if (getMute()) { 159 | switchMute() 160 | } 161 | for i in 0...size) 171 | 172 | result = AudioObjectSetPropertyData(subDevice, &address, 0, nil, propsize, &vol) 173 | if (result != 0) { 174 | print("kAudioDevicePropertyVolumeScalar channel:\(channel) result:\(result)") 175 | exit(-1) 176 | } 177 | } 178 | } 179 | } 180 | 181 | func getMute() -> Bool { 182 | for i in 0...size) 194 | 195 | result = AudioObjectGetPropertyData(subDevice, &address, 0, nil, &propsize, &mute) 196 | if (result != 0) { 197 | print("kAudioDevicePropertyVolumeScalar channel:\(channel) result:\(result)") 198 | exit(-1) 199 | } 200 | if (mute == 1) { 201 | return true 202 | } 203 | } 204 | } 205 | return false 206 | } 207 | 208 | func switchMute() { 209 | let mute = !getMute() 210 | 211 | var mut: UInt32 = (mute == true) ? 1 : 0 212 | for i in 0...size) 222 | 223 | result = AudioObjectSetPropertyData(subDevice, &address, 0, nil, propsize, &mut) 224 | if (result != 0) { 225 | print("kAudioDevicePropertyMute channel:\(channel) result:\(result)") 226 | exit(-1) 227 | } 228 | } 229 | } 230 | } 231 | } 232 | 233 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/AggregateVolumeMenu.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // AggregateVolumeMenu 4 | // 5 | // Created by Gurhan Polat on 21.12.2020. 6 | // 7 | 8 | import Cocoa 9 | 10 | @main 11 | class AppDelegate: NSObject, NSApplicationDelegate { 12 | 13 | let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength) 14 | let popover = NSPopover() 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | if let button = self.statusItem.button { 18 | button.image = NSImage(named: NSImage.Name("StatusIcon")) 19 | button.action = #selector(AppDelegate.togglePopover(_:)) 20 | } 21 | self.popover.contentViewController = ViewController.newInstance() 22 | } 23 | 24 | func applicationWillTerminate(_ aNotification: Notification) { 25 | } 26 | 27 | @objc func togglePopover(_ sender: NSStatusItem) { 28 | if self.popover.isShown { 29 | closePopover(sender: sender) 30 | } 31 | else { 32 | showPopover(sender: sender) 33 | } 34 | } 35 | 36 | func showPopover(sender: Any?) { 37 | if let button = self.statusItem.button { 38 | self.popover.behavior = NSPopover.Behavior.transient 39 | NSApp.activate(ignoringOtherApps: true) 40 | self.popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY) 41 | } 42 | } 43 | 44 | func closePopover(sender: Any?) { 45 | self.popover.performClose(sender) 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "1.000", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "light" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | }, 33 | { 34 | "appearances" : [ 35 | { 36 | "appearance" : "luminosity", 37 | "value" : "dark" 38 | } 39 | ], 40 | "color" : { 41 | "color-space" : "srgb", 42 | "components" : { 43 | "alpha" : "1.000", 44 | "blue" : "1.000", 45 | "green" : "1.000", 46 | "red" : "1.000" 47 | } 48 | }, 49 | "idiom" : "universal" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "StatusIcon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "light" 13 | } 14 | ], 15 | "filename" : "StatusIcon-2.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "appearances" : [ 21 | { 22 | "appearance" : "luminosity", 23 | "value" : "dark" 24 | } 25 | ], 26 | "filename" : "StatusIcon-4.png", 27 | "idiom" : "universal", 28 | "scale" : "1x" 29 | }, 30 | { 31 | "filename" : "StatusIcon-1.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "appearances" : [ 37 | { 38 | "appearance" : "luminosity", 39 | "value" : "light" 40 | } 41 | ], 42 | "filename" : "StatusIcon-5.png", 43 | "idiom" : "universal", 44 | "scale" : "2x" 45 | }, 46 | { 47 | "appearances" : [ 48 | { 49 | "appearance" : "luminosity", 50 | "value" : "dark" 51 | } 52 | ], 53 | "filename" : "StatusIcon-3.png", 54 | "idiom" : "universal", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "idiom" : "universal", 59 | "scale" : "3x" 60 | }, 61 | { 62 | "appearances" : [ 63 | { 64 | "appearance" : "luminosity", 65 | "value" : "light" 66 | } 67 | ], 68 | "idiom" : "universal", 69 | "scale" : "3x" 70 | }, 71 | { 72 | "appearances" : [ 73 | { 74 | "appearance" : "luminosity", 75 | "value" : "dark" 76 | } 77 | ], 78 | "idiom" : "universal", 79 | "scale" : "3x" 80 | } 81 | ], 82 | "info" : { 83 | "author" : "xcode", 84 | "version" : 1 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adaskar/AggregateVolumeMenu/2c2d431cc173c1779ca94aea681d8dab12c8b38b/AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-1.png -------------------------------------------------------------------------------- /AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adaskar/AggregateVolumeMenu/2c2d431cc173c1779ca94aea681d8dab12c8b38b/AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-2.png -------------------------------------------------------------------------------- /AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adaskar/AggregateVolumeMenu/2c2d431cc173c1779ca94aea681d8dab12c8b38b/AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-3.png -------------------------------------------------------------------------------- /AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adaskar/AggregateVolumeMenu/2c2d431cc173c1779ca94aea681d8dab12c8b38b/AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-4.png -------------------------------------------------------------------------------- /AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adaskar/AggregateVolumeMenu/2c2d431cc173c1779ca94aea681d8dab12c8b38b/AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon-5.png -------------------------------------------------------------------------------- /AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adaskar/AggregateVolumeMenu/2c2d431cc173c1779ca94aea681d8dab12c8b38b/AggregateVolumeMenu/Assets.xcassets/StatusIcon.imageset/StatusIcon.png -------------------------------------------------------------------------------- /AggregateVolumeMenu/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleVersion 22 | 1 23 | LSApplicationCategoryType 24 | public.app-category.utilities 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | AggregateVolumeMenu.MyApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/MyApplication.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyApplication.swift 3 | // AggregateVolumeMenu 4 | // 5 | // Created by Gurhan Polat on 21.12.2020. 6 | // 7 | import Cocoa 8 | 9 | class MyApplication: NSApplication { 10 | let avcControl = AggregateVolumeControl() 11 | override func sendEvent(_ event: NSEvent) { 12 | if (event.type == .systemDefined && event.subtype.rawValue == 8) { 13 | let keyCode = ((event.data1 & 0xFFFF0000) >> 16) 14 | let keyState = ((((event.data1 & 0x0000FFFF) & 0xFF00) >> 8)) == 0xA 15 | let keyRepeat = ((event.data1 & 0x0000FFFF) & 0x1) == 1 16 | mediaKeyEvent(key: Int32(keyCode), state: keyState, keyRepeat: Bool(keyRepeat)) 17 | } 18 | 19 | super.sendEvent(event) 20 | } 21 | 22 | func mediaKeyEvent(key: Int32, state: Bool, keyRepeat: Bool) { 23 | if (state) { 24 | switch(key) { 25 | case NX_KEYTYPE_MUTE: 26 | avcControl.switchMute() 27 | break 28 | case NX_KEYTYPE_SOUND_DOWN: 29 | var vol = avcControl.getVolume() 30 | vol = (vol - 0.1) <= 0 ? 0 : vol - 0.1 31 | avcControl.setVolume(volume: vol) 32 | break 33 | case NX_KEYTYPE_SOUND_UP: 34 | var vol = avcControl.getVolume() 35 | vol = (vol + 0.1) >= 1 ? 1 : vol + 0.1 36 | avcControl.setVolume(volume: vol) 37 | break 38 | default: 39 | break 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /AggregateVolumeMenu/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // AggregateVolumeMenu 4 | // 5 | // Created by Gurhan Polat on 21.12.2020. 6 | // 7 | 8 | import Cocoa 9 | 10 | class ViewController: NSViewController { 11 | let avcControl = AggregateVolumeControl() 12 | 13 | @IBOutlet weak var hsVolume: NSSlider! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | hsVolume.minValue = 0 18 | hsVolume.maxValue = 1 19 | hsVolume.isContinuous = false 20 | hsVolume.floatValue = avcControl.getVolume() 21 | } 22 | 23 | override func viewWillAppear() { 24 | hsVolume.floatValue = avcControl.getVolume() 25 | } 26 | 27 | override var representedObject: Any? { 28 | didSet { 29 | } 30 | } 31 | @IBAction func hsVolumeChanged(_ sender: Any) { 32 | avcControl.setVolume(volume: hsVolume.floatValue) 33 | } 34 | 35 | static func newInstance() -> ViewController { 36 | let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil) 37 | let identifier = NSStoryboard.SceneIdentifier("ViewController") 38 | 39 | guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? ViewController else { 40 | fatalError("Unable to instantiate ViewController in Main.storyboard") 41 | } 42 | return viewcontroller 43 | } 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AggregateVolumeMenu 2 | AggregateVolumeMenu gives you the ability to change the volume of the aggregate sound device 3 | --------------------------------------------------------------------------------