diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dfa7f62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.xcworkspace +Pods +Podfile.lock +.DS_Store \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..71468ec --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,110 @@ +whitelist_rules: + - attributes + - block_based_kvo + - class_delegate_protocol + - closing_brace + - closure_end_indentation + - closure_parameter_position + - closure_spacing + - collection_alignment + - colon + - comma + - compiler_protocol_init + - conditional_returns_on_newline + - contains_over_first_not_nil + - control_statement + - convenience_type + - discouraged_direct_init + - discouraged_object_literal + - duplicate_imports + - dynamic_inline + - empty_count + - empty_enum_arguments + - empty_parameters + - empty_parentheses_with_trailing_closure + - empty_string + - empty_xctest_method + - explicit_init + - fallthrough + - fatal_error_message + - file_length + - first_where + - for_where + - force_cast + - force_try + - force_unwrapping + - function_body_length + - function_default_parameter_at_end + - function_parameter_count + - identical_operands + - implicit_getter + - inert_defer + - large_tuple + - last_where + - leading_whitespace + - legacy_cggeometry_functions + - legacy_constant + - legacy_constructor + - legacy_hashing + - legacy_random + - let_var_whitespace + - line_length + - mark + - multiline_literal_brackets + - nesting + - number_separator + - opening_brace + - operator_usage_whitespace + - operator_whitespace + - private_over_fileprivate + - protocol_property_accessors_order + - redundant_discardable_let + - return_arrow_whitespace + - shorthand_operator + - sorted_first_last + - sorted_imports + - statement_position + - switch_case_alignment + - syntactic_sugar + - todo + - trailing_closure + - trailing_comma + - trailing_newline + - trailing_semicolon + - type_name + - unneeded_break_in_switch + - unneeded_parentheses_in_closure_argument + - unused_closure_parameter + - unused_control_flow_label + - unused_enumerated + - unused_import + - unused_setter_value + - vertical_parameter_alignment + - vertical_whitespace + - vertical_whitespace_closing_braces + - vertical_whitespace_opening_braces + - void_return + - weak_delegate + - type_body_length +included: # paths to include during linting. `--path` is ignored if present. + - MultiSoundChanger +excluded: # paths to ignore during linting. Takes precedence over `included`. + - Pods +analyzer_rules: # Rules run by `swiftlint analyze` (experimental) + - explicit_self + +line_length: 150 + +type_body_length: + warning: 300 + error: 400 + +file_length: + warning: 500 + error: 1000 + +function_parameter_count: + warning: 10 + error: 20 + +reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown) diff --git a/DynamicsIllusion.xcodeproj/project.pbxproj b/DynamicsIllusion.xcodeproj/project.pbxproj deleted file mode 100644 index e6d5c74..0000000 --- a/DynamicsIllusion.xcodeproj/project.pbxproj +++ /dev/null @@ -1,325 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 4743EFAB1E91493B0032F5AA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4743EFAA1E91493B0032F5AA /* AppDelegate.swift */; }; - 4743EFAD1E91493B0032F5AA /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4743EFAC1E91493B0032F5AA /* VolumeViewController.swift */; }; - 4743EFAF1E91493B0032F5AA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4743EFAE1E91493B0032F5AA /* Assets.xcassets */; }; - 4743EFB21E91493B0032F5AA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4743EFB01E91493B0032F5AA /* Main.storyboard */; }; - 47E683071E91BCA300C525F7 /* Audio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47E683061E91BCA300C525F7 /* Audio.swift */; }; - 47E6830E1E9273EC00C525F7 /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47E6830D1E9273EC00C525F7 /* StringExtensions.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 4743EFA71E91493B0032F5AA /* DynamicsIllusion.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DynamicsIllusion.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 4743EFAA1E91493B0032F5AA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 4743EFAC1E91493B0032F5AA /* VolumeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeViewController.swift; sourceTree = ""; }; - 4743EFAE1E91493B0032F5AA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 4743EFB11E91493B0032F5AA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 4743EFB31E91493B0032F5AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 47E683061E91BCA300C525F7 /* Audio.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Audio.swift; path = Framework/Audio.swift; sourceTree = ""; }; - 47E6830D1E9273EC00C525F7 /* StringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StringExtensions.swift; path = Extensions/StringExtensions.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 4743EFA41E91493B0032F5AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 4743EF9E1E91493B0032F5AA = { - isa = PBXGroup; - children = ( - 4743EFA91E91493B0032F5AA /* DynamicsIllusion */, - 4743EFA81E91493B0032F5AA /* Products */, - ); - sourceTree = ""; - }; - 4743EFA81E91493B0032F5AA /* Products */ = { - isa = PBXGroup; - children = ( - 4743EFA71E91493B0032F5AA /* DynamicsIllusion.app */, - ); - name = Products; - sourceTree = ""; - }; - 4743EFA91E91493B0032F5AA /* DynamicsIllusion */ = { - isa = PBXGroup; - children = ( - 47E6830C1E9273C600C525F7 /* Extensions */, - 47E683041E91BC6200C525F7 /* Framework */, - 4743EFAA1E91493B0032F5AA /* AppDelegate.swift */, - 4743EFAC1E91493B0032F5AA /* VolumeViewController.swift */, - 4743EFAE1E91493B0032F5AA /* Assets.xcassets */, - 4743EFB01E91493B0032F5AA /* Main.storyboard */, - 4743EFB31E91493B0032F5AA /* Info.plist */, - ); - path = DynamicsIllusion; - sourceTree = ""; - }; - 47E683041E91BC6200C525F7 /* Framework */ = { - isa = PBXGroup; - children = ( - 47E683061E91BCA300C525F7 /* Audio.swift */, - ); - name = Framework; - sourceTree = ""; - }; - 47E6830C1E9273C600C525F7 /* Extensions */ = { - isa = PBXGroup; - children = ( - 47E6830D1E9273EC00C525F7 /* StringExtensions.swift */, - ); - name = Extensions; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 4743EFA61E91493B0032F5AA /* DynamicsIllusion */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4743EFB61E91493B0032F5AA /* Build configuration list for PBXNativeTarget "DynamicsIllusion" */; - buildPhases = ( - 4743EFA31E91493B0032F5AA /* Sources */, - 4743EFA41E91493B0032F5AA /* Frameworks */, - 4743EFA51E91493B0032F5AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = DynamicsIllusion; - productName = DynamicsIllusion; - productReference = 4743EFA71E91493B0032F5AA /* DynamicsIllusion.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 4743EF9F1E91493B0032F5AA /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0830; - ORGANIZATIONNAME = mityny; - TargetAttributes = { - 4743EFA61E91493B0032F5AA = { - CreatedOnToolsVersion = 8.3; - DevelopmentTeam = C6Z5KNNJ86; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 4743EFA21E91493B0032F5AA /* Build configuration list for PBXProject "DynamicsIllusion" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 4743EF9E1E91493B0032F5AA; - productRefGroup = 4743EFA81E91493B0032F5AA /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 4743EFA61E91493B0032F5AA /* DynamicsIllusion */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 4743EFA51E91493B0032F5AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4743EFAF1E91493B0032F5AA /* Assets.xcassets in Resources */, - 4743EFB21E91493B0032F5AA /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 4743EFA31E91493B0032F5AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4743EFAD1E91493B0032F5AA /* VolumeViewController.swift in Sources */, - 4743EFAB1E91493B0032F5AA /* AppDelegate.swift in Sources */, - 47E6830E1E9273EC00C525F7 /* StringExtensions.swift in Sources */, - 47E683071E91BCA300C525F7 /* Audio.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 4743EFB01E91493B0032F5AA /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4743EFB11E91493B0032F5AA /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 4743EFB41E91493B0032F5AA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 4743EFB51E91493B0032F5AA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - }; - name = Release; - }; - 4743EFB71E91493B0032F5AA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = C6Z5KNNJ86; - INFOPLIST_FILE = DynamicsIllusion/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.rlxone.DynamicsIllusion; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_VERSION = 3.0; - }; - name = Debug; - }; - 4743EFB81E91493B0032F5AA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = C6Z5KNNJ86; - INFOPLIST_FILE = DynamicsIllusion/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.rlxone.DynamicsIllusion; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_VERSION = 3.0; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 4743EFA21E91493B0032F5AA /* Build configuration list for PBXProject "DynamicsIllusion" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4743EFB41E91493B0032F5AA /* Debug */, - 4743EFB51E91493B0032F5AA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4743EFB61E91493B0032F5AA /* Build configuration list for PBXNativeTarget "DynamicsIllusion" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4743EFB71E91493B0032F5AA /* Debug */, - 4743EFB81E91493B0032F5AA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 4743EF9F1E91493B0032F5AA /* Project object */; -} diff --git a/DynamicsIllusion.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/DynamicsIllusion.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index b6bc5c6..0000000 --- a/DynamicsIllusion.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/DynamicsIllusion/AppDelegate.swift b/DynamicsIllusion/AppDelegate.swift deleted file mode 100644 index e32c028..0000000 --- a/DynamicsIllusion/AppDelegate.swift +++ /dev/null @@ -1,126 +0,0 @@ -// -// AppDelegate.swift -// DynamicsIllusion -// -// Created by rlxone on 02.04.17. -// Copyright © 2017 rlxone. All rights reserved. -// - -import Cocoa -import AudioToolbox -import ScriptingBridge - -@NSApplicationMain -class AppDelegate: NSObject, NSApplicationDelegate { - - let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength) - let devices = Audio.getOutputDevices() - let selectedDevices = [AudioDeviceID]() - var volumeViewController: VolumeViewController? - - func applicationDidFinishLaunching(_ aNotification: Notification) { - setupApp() - } - - func setupApp() { - volumeViewController = loadViewFromStoryboard(named: "Main", identifier: "VolumeViewControllerId") as? VolumeViewController - createMenu() - } - - func loadViewFromStoryboard(named: String, identifier: String) -> Any { - let storyboard = NSStoryboard(name: named, bundle: nil) - return storyboard.instantiateController(withIdentifier: identifier) - } - - func createMenu() { - if let button = statusItem.button { - button.image = NSImage(named: "StatusBar1Image") - button.action = #selector(self.statusBarAction) - } - - let menu = NSMenu() - - var item = NSMenuItem(title: "Volume:", action: #selector(self.menuItemAction), keyEquivalent: "") - item.isEnabled = false - menu.addItem(item) - - item = NSMenuItem(title: "", action: #selector(self.menuItemAction), keyEquivalent: "") - item.view = volumeViewController?.view - menu.addItem(item) - - item = NSMenuItem(title: "Output Devices:", action: #selector(self.menuItemAction), keyEquivalent: "") - item.isEnabled = false - menu.addItem(item) - - let defaultDevice = Audio.getDefaultOutputDevice() - - for device in devices! { - let item = NSMenuItem(title: device.value.truncate(length: 25, trailing: "..."), action: #selector(self.menuItemAction), keyEquivalent: "") - item.tag = Int(device.key) - if device.key == defaultDevice { - item.state = NSOnState - if Audio.isAggregateDevice(deviceID: defaultDevice) { - volumeViewController?.selectedDevices = Audio.getAggregateDeviceSubDeviceList(deviceID: defaultDevice) - for device in (volumeViewController?.selectedDevices!)! { - if Audio.isOutputDevice(deviceID: device) { - let volume = Audio.getDeviceVolume(deviceID: device).first! * 100 - volumeViewController?.volumeSlider.floatValue = volume - volumeViewController?.changeStatusItemImage(value: volume) - break - } - } - } else { - volumeViewController?.selectedDevices = [defaultDevice] - let volume = Audio.getDeviceVolume(deviceID: defaultDevice).first! * 100 - volumeViewController?.volumeSlider.floatValue = volume - volumeViewController?.changeStatusItemImage(value: volume) - } - } - menu.addItem(item) - } - - menu.addItem(NSMenuItem.separator()) - - item = NSMenuItem(title: "Quit", action: #selector(self.menuQuitAction), keyEquivalent: "q") - menu.addItem(item) - - statusItem.menu = menu - } - - func menuQuitAction() { - NSApplication.shared().terminate(self) - - } - - func menuItemAction(sender: NSMenuItem) { - for item in (statusItem.menu?.items)! { - if item == sender { - if item.state == NSOffState { - item.state = NSOnState - } - let deviceID = AudioDeviceID(item.tag) - if Audio.isAggregateDevice(deviceID: deviceID) { - volumeViewController?.selectedDevices = Audio.getAggregateDeviceSubDeviceList(deviceID: deviceID) - } else { - volumeViewController?.selectedDevices = [deviceID] - } - Audio.setOutputDevice(newDeviceID: deviceID) - } else { - item.state = NSOffState - } - } - } - - func statusBarAction(sender: AnyObject) { - print("you can update") - } - - override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { - return menuItem.isEnabled - } - - func applicationWillTerminate(_ aNotification: Notification) { - - } -} - diff --git a/DynamicsIllusion/Assets.xcassets/AppIcon.appiconset/Contents.json b/DynamicsIllusion/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 2db2b1c..0000000 --- a/DynamicsIllusion/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "images" : [ - { - "idiom" : "mac", - "size" : "16x16", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "16x16", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "32x32", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "32x32", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "128x128", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "128x128", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "256x256", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "256x256", - "scale" : "2x" - }, - { - "idiom" : "mac", - "size" : "512x512", - "scale" : "1x" - }, - { - "idiom" : "mac", - "size" : "512x512", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/DynamicsIllusion/Assets.xcassets/Contents.json b/DynamicsIllusion/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/DynamicsIllusion/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar1Image.imageset/none.png b/DynamicsIllusion/Assets.xcassets/StatusBar1Image.imageset/none.png deleted file mode 100644 index e86e2a1..0000000 Binary files a/DynamicsIllusion/Assets.xcassets/StatusBar1Image.imageset/none.png and /dev/null differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar1Image.imageset/none1.png b/DynamicsIllusion/Assets.xcassets/StatusBar1Image.imageset/none1.png deleted file mode 100644 index b0e5a5b..0000000 Binary files a/DynamicsIllusion/Assets.xcassets/StatusBar1Image.imageset/none1.png and /dev/null differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar2Image.imageset/first.png b/DynamicsIllusion/Assets.xcassets/StatusBar2Image.imageset/first.png deleted file mode 100644 index 7d9cfc4..0000000 Binary files a/DynamicsIllusion/Assets.xcassets/StatusBar2Image.imageset/first.png and /dev/null differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar2Image.imageset/first1.png b/DynamicsIllusion/Assets.xcassets/StatusBar2Image.imageset/first1.png deleted file mode 100644 index 095cf2d..0000000 Binary files a/DynamicsIllusion/Assets.xcassets/StatusBar2Image.imageset/first1.png and /dev/null differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar3Image.imageset/second.png b/DynamicsIllusion/Assets.xcassets/StatusBar3Image.imageset/second.png deleted file mode 100644 index 85e3ccb..0000000 Binary files a/DynamicsIllusion/Assets.xcassets/StatusBar3Image.imageset/second.png and /dev/null differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar3Image.imageset/second1.png b/DynamicsIllusion/Assets.xcassets/StatusBar3Image.imageset/second1.png deleted file mode 100644 index c061ab8..0000000 Binary files a/DynamicsIllusion/Assets.xcassets/StatusBar3Image.imageset/second1.png and /dev/null differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar4Image.imageset/third.png b/DynamicsIllusion/Assets.xcassets/StatusBar4Image.imageset/third.png deleted file mode 100644 index b627bd9..0000000 Binary files a/DynamicsIllusion/Assets.xcassets/StatusBar4Image.imageset/third.png and /dev/null differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar4Image.imageset/third1.png b/DynamicsIllusion/Assets.xcassets/StatusBar4Image.imageset/third1.png deleted file mode 100644 index 9809c57..0000000 Binary files a/DynamicsIllusion/Assets.xcassets/StatusBar4Image.imageset/third1.png and /dev/null differ diff --git a/DynamicsIllusion/Base.lproj/Main.storyboard b/DynamicsIllusion/Base.lproj/Main.storyboard deleted file mode 100644 index 72a7e9e..0000000 --- a/DynamicsIllusion/Base.lproj/Main.storyboard +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DynamicsIllusion/Extensions/StringExtensions.swift b/DynamicsIllusion/Extensions/StringExtensions.swift deleted file mode 100644 index a93d0ee..0000000 --- a/DynamicsIllusion/Extensions/StringExtensions.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// StringExtensions.swift -// DynamicsIllusion -// -// Created by sdd on 03.04.17. -// Copyright © 2017 rlxone. All rights reserved. -// - -import Foundation - -extension String { - - func truncate(length: Int, trailing: String = "…") -> String { - if self.characters.count > length { - return String(self.characters.prefix(length)) + trailing - } else { - return self - } - } - -} diff --git a/DynamicsIllusion/Framework/Audio.swift b/DynamicsIllusion/Framework/Audio.swift deleted file mode 100644 index fdff1b9..0000000 --- a/DynamicsIllusion/Framework/Audio.swift +++ /dev/null @@ -1,215 +0,0 @@ -// -// Audio.swift -// DynamicsIllusion -// -// Created by sdd on 03.04.17. -// Copyright © 2017 rlxone. All rights reserved. -// - -import Foundation -import Cocoa -import AudioToolbox - -public class Audio { - - static func getOutputDevices() -> [AudioDeviceID: String]? { - var result: [AudioDeviceID: String] = [:] - let devices = getAllDevices() - - for device in devices { - if isOutputDevice(deviceID: device) { - result[device] = getDeviceName(deviceID: device) - } - } - - return result - } - - static func isOutputDevice(deviceID: AudioDeviceID) -> Bool { - var propertySize: UInt32 = 256 - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioDevicePropertyStreams), - mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - _ = AudioObjectGetPropertyDataSize(deviceID, &propertyAddress, 0, nil, &propertySize) - - return propertySize > 0 - } - - static func getAggregateDeviceSubDeviceList(deviceID: AudioDeviceID) -> [AudioDeviceID] { - let subDevicesCount = getNumberOfSubDevices(deviceID: deviceID) - var subDevices = [AudioDeviceID](repeating: 0, count: Int(subDevicesCount)) - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioAggregateDevicePropertyActiveSubDeviceList), - mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - var subDevicesSize = subDevicesCount * UInt32(MemoryLayout.size) - - AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &subDevicesSize, &subDevices) - - return subDevices - } - - static func isAggregateDevice(deviceID: AudioDeviceID) -> Bool { - let deviceType = getDeviceTransportType(deviceID: deviceID) - return deviceType == kAudioDeviceTransportTypeAggregate - } - - static func setDeviceVolume(deviceID: AudioDeviceID, leftChannelLevel: Float, rightChannelLevel: Float) { - let channelsCount = 2 - var channels = [UInt32](repeating: 0, count: channelsCount) - var propertySize = UInt32(MemoryLayout.size * channelsCount) - var leftLevel = leftChannelLevel - var rigthLevel = rightChannelLevel - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioDevicePropertyPreferredChannelsForStereo), - mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - let status = AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &channels) - - if status != noErr { return } - - propertyAddress.mSelector = kAudioDevicePropertyVolumeScalar - propertySize = UInt32(MemoryLayout.size) - propertyAddress.mElement = channels[0] - - AudioObjectSetPropertyData(deviceID, &propertyAddress, 0, nil, propertySize, &leftLevel) - - propertyAddress.mElement = channels[1] - - AudioObjectSetPropertyData(deviceID, &propertyAddress, 0, nil, propertySize, &rigthLevel) - } - - static func setOutputDevice(newDeviceID: AudioDeviceID) { - let propertySize = UInt32(MemoryLayout.size) - var deviceID = newDeviceID - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice), - mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - AudioObjectSetPropertyData(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, propertySize, &deviceID) - } - - static func getDeviceVolume(deviceID: AudioDeviceID) -> [Float] { - let channelsCount = 2 - var channels = [UInt32](repeating: 0, count: channelsCount) - var propertySize = UInt32(MemoryLayout.size * channelsCount) - var leftLevel = Float32(-1) - var rigthLevel = Float32(-1) - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioDevicePropertyPreferredChannelsForStereo), - mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - let status = AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &channels) - - if status != noErr { return [-1] } - - propertyAddress.mSelector = kAudioDevicePropertyVolumeScalar - propertySize = UInt32(MemoryLayout.size) - propertyAddress.mElement = channels[0] - - AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &leftLevel) - - propertyAddress.mElement = channels[1] - - AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &rigthLevel) - - return [leftLevel, rigthLevel] - } - - static func getDefaultOutputDevice() -> AudioDeviceID { - var propertySize = UInt32(MemoryLayout.size) - var deviceID = kAudioDeviceUnknown - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice), - mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, &propertySize, &deviceID) - - return deviceID - } - - private static func getDeviceTransportType(deviceID: AudioDeviceID) -> AudioDevicePropertyID { - var deviceTransportType = AudioDevicePropertyID() - var propertySize = UInt32(MemoryLayout.size) - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioDevicePropertyTransportType), - mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &deviceTransportType) - - return deviceTransportType - } - - private static func getNumberOfDevices() -> UInt32 { - var propertySize: UInt32 = 0 - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDevices), - mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - _ = AudioObjectGetPropertyDataSize(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, &propertySize) - - return propertySize / UInt32(MemoryLayout.size) - } - - private static func getNumberOfSubDevices(deviceID: AudioDeviceID) -> UInt32 { - var propertySize: UInt32 = 0 - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioAggregateDevicePropertyActiveSubDeviceList), - mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - _ = AudioObjectGetPropertyDataSize(deviceID, &propertyAddress, 0, nil, &propertySize) - - return propertySize / UInt32(MemoryLayout.size) - } - - private static func getDeviceName(deviceID: AudioDeviceID) -> String { - var propertySize = UInt32(MemoryLayout.size) - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioDevicePropertyDeviceNameCFString), - mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - var result: CFString = "" as CFString - - AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &result) - - return result as String - } - - private static func getAllDevices() -> [AudioDeviceID] { - let devicesCount = getNumberOfDevices() - var devices = [AudioDeviceID](repeating: 0, count: Int(devicesCount)) - - var propertyAddress = AudioObjectPropertyAddress( - mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDevices), - mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), - mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) - - var devicesSize = devicesCount * UInt32(MemoryLayout.size) - - AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, &devicesSize, &devices) - - return devices - } - -} diff --git a/DynamicsIllusion/VolumeViewController.swift b/DynamicsIllusion/VolumeViewController.swift deleted file mode 100644 index f257e68..0000000 --- a/DynamicsIllusion/VolumeViewController.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// ViewController.swift -// DynamicsIllusion -// -// Created by rlxone on 02.04.17. -// Copyright © 2017 rlxone. All rights reserved. -// - -import Cocoa -import AudioToolbox - -class VolumeViewController: NSViewController, NSTableViewDataSource { - - @IBOutlet var volumeSlider: NSSlider! - - var selectedDevices: [AudioDeviceID]? - - func changeVolume(value: Float) { - if selectedDevices != nil { - for device in selectedDevices! { - Audio.setDeviceVolume(deviceID: device, leftChannelLevel: value, rightChannelLevel: value) - } - } - } - - func changeStatusItemImage(value: Float) { - let appDelegate = NSApplication.shared().delegate as! AppDelegate - if value < 1 { - appDelegate.statusItem.button?.image = NSImage(named: "StatusBar1Image") - } else if value > 1 && value < 100 / 3 { - appDelegate.statusItem.button?.image = NSImage(named: "StatusBar2Image") - } else if value > 100 / 3 && value < 100 / 3 * 2 { - appDelegate.statusItem.button?.image = NSImage(named: "StatusBar3Image") - } else if value > 100 / 3 * 2 && value <= 100 { - appDelegate.statusItem.button?.image = NSImage(named: "StatusBar4Image") - } - } - - @IBAction func volumeSliderAction(_ sender: Any) { - changeVolume(value: volumeSlider.floatValue / 100) - changeStatusItemImage(value: volumeSlider.floatValue) - } - - override var representedObject: Any? { - didSet { - // Update the view, if already loaded. - } - } - -} - diff --git a/MultiSoundChanger.xcodeproj/project.pbxproj b/MultiSoundChanger.xcodeproj/project.pbxproj new file mode 100644 index 0000000..75c9711 --- /dev/null +++ b/MultiSoundChanger.xcodeproj/project.pbxproj @@ -0,0 +1,584 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 4743EFAB1E91493B0032F5AA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4743EFAA1E91493B0032F5AA /* AppDelegate.swift */; }; + 6985C6FE251951F8003C2FDB /* OSD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6985C6FD251951F8003C2FDB /* OSD.framework */; }; + E4FFDC0757FD125F92CC0F62 /* Pods_MultiSoundChanger.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C34C05E9BD81D579A0C4957 /* Pods_MultiSoundChanger.framework */; }; + F312C54E25B3741C00205846 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F373D8C02561D24600642274 /* Main.storyboard */; }; + F312C55025B3742200205846 /* Volume.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F373D8BC2561D22000642274 /* Volume.storyboard */; }; + F3433FCB25B36E16009AAE86 /* Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3433FCA25B36E16009AAE86 /* Images.swift */; }; + F373D8B32561D1A600642274 /* StatusBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F373D8B02561D1A600642274 /* StatusBarController.swift */; }; + F373D8B42561D1A600642274 /* AudioManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F373D8B12561D1A600642274 /* AudioManager.swift */; }; + F373D8B52561D1A600642274 /* MediaManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F373D8B22561D1A600642274 /* MediaManager.swift */; }; + F373D8BB2561D21900642274 /* Stories.swift in Sources */ = {isa = PBXBuildFile; fileRef = F373D8BA2561D21900642274 /* Stories.swift */; }; + F373D8BF2561D22000642274 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F373D8BD2561D22000642274 /* VolumeViewController.swift */; }; + F373D8C62561D2A600642274 /* Audio.swift in Sources */ = {isa = PBXBuildFile; fileRef = F373D8C52561D2A600642274 /* Audio.swift */; }; + F373D8C82561D2B000642274 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F373D8C72561D2B000642274 /* Extensions.swift */; }; + F373D8CD2561D36B00642274 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F373D8CB2561D36B00642274 /* Assets.xcassets */; }; + F37C2ECF256AA987001C3D36 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F37C2ECE256AA987001C3D36 /* Localizable.strings */; }; + F37C2ED1256AAA4C001C3D36 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37C2ED0256AAA4C001C3D36 /* Strings.swift */; }; + F383684D2561E39E00C7B454 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = F383684C2561E39E00C7B454 /* Constants.swift */; }; + F3925975262F2B8000B7AD62 /* ApplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3925974262F2B8000B7AD62 /* ApplicationController.swift */; }; + F3925979262F2F9F00B7AD62 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3925978262F2F9F00B7AD62 /* Logger.swift */; }; + F392597C2631ACE700B7AD62 /* Runner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F392597B2631ACE700B7AD62 /* Runner.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 4743EFA71E91493B0032F5AA /* MultiSoundChanger.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MultiSoundChanger.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4743EFAA1E91493B0032F5AA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 4C34C05E9BD81D579A0C4957 /* Pods_MultiSoundChanger.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MultiSoundChanger.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6985C6FD251951F8003C2FDB /* OSD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OSD.framework; sourceTree = ""; }; + 6FD0ED04AFD1CC1242C9B3B3 /* Pods-MultiSoundChanger.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MultiSoundChanger.debug.xcconfig"; path = "Target Support Files/Pods-MultiSoundChanger/Pods-MultiSoundChanger.debug.xcconfig"; sourceTree = ""; }; + D184B2CD842B856AFFE7DF7E /* Pods-MultiSoundChanger.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MultiSoundChanger.release.xcconfig"; path = "Target Support Files/Pods-MultiSoundChanger/Pods-MultiSoundChanger.release.xcconfig"; sourceTree = ""; }; + F3433FCA25B36E16009AAE86 /* Images.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Images.swift; sourceTree = ""; }; + F373D8B02561D1A600642274 /* StatusBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarController.swift; sourceTree = ""; }; + F373D8B12561D1A600642274 /* AudioManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioManager.swift; sourceTree = ""; }; + F373D8B22561D1A600642274 /* MediaManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaManager.swift; sourceTree = ""; }; + F373D8BA2561D21900642274 /* Stories.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stories.swift; sourceTree = ""; }; + F373D8BC2561D22000642274 /* Volume.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Volume.storyboard; sourceTree = ""; }; + F373D8BD2561D22000642274 /* VolumeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VolumeViewController.swift; sourceTree = ""; }; + F373D8C12561D24600642274 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + F373D8C52561D2A600642274 /* Audio.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Audio.swift; path = Sources/Frameworks/Audio.swift; sourceTree = ""; }; + F373D8C72561D2B000642274 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Sources/Extensions/Extensions.swift; sourceTree = ""; }; + F373D8CA2561D36B00642274 /* MultiSoundChanger-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MultiSoundChanger-Bridging-Header.h"; sourceTree = ""; }; + F373D8CB2561D36B00642274 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + F373D8CC2561D36B00642274 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F37C2ECE256AA987001C3D36 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + F37C2ED0256AAA4C001C3D36 /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; + F383684C2561E39E00C7B454 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + F3925974262F2B8000B7AD62 /* ApplicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationController.swift; sourceTree = ""; }; + F3925978262F2F9F00B7AD62 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; + F392597B2631ACE700B7AD62 /* Runner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Runner.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4743EFA41E91493B0032F5AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6985C6FE251951F8003C2FDB /* OSD.framework in Frameworks */, + E4FFDC0757FD125F92CC0F62 /* Pods_MultiSoundChanger.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4743EF9E1E91493B0032F5AA = { + isa = PBXGroup; + children = ( + 4743EFA91E91493B0032F5AA /* MultiSoundChanger */, + 4743EFA81E91493B0032F5AA /* Products */, + 5E653FF732220067826E9384 /* Pods */, + 83889335DD9089B748A33010 /* Frameworks */, + ); + sourceTree = ""; + }; + 4743EFA81E91493B0032F5AA /* Products */ = { + isa = PBXGroup; + children = ( + 4743EFA71E91493B0032F5AA /* MultiSoundChanger.app */, + ); + name = Products; + sourceTree = ""; + }; + 4743EFA91E91493B0032F5AA /* MultiSoundChanger */ = { + isa = PBXGroup; + children = ( + F373D8A42561D11900642274 /* Sources */, + F373D8C92561D31700642274 /* Other */, + ); + path = MultiSoundChanger; + sourceTree = ""; + }; + 47E683041E91BC6200C525F7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F373D8C52561D2A600642274 /* Audio.swift */, + ); + name = Frameworks; + path = ..; + sourceTree = ""; + }; + 47E6830C1E9273C600C525F7 /* Extensions */ = { + isa = PBXGroup; + children = ( + F373D8C72561D2B000642274 /* Extensions.swift */, + ); + name = Extensions; + path = ..; + sourceTree = ""; + }; + 5E653FF732220067826E9384 /* Pods */ = { + isa = PBXGroup; + children = ( + 6FD0ED04AFD1CC1242C9B3B3 /* Pods-MultiSoundChanger.debug.xcconfig */, + D184B2CD842B856AFFE7DF7E /* Pods-MultiSoundChanger.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 83889335DD9089B748A33010 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6985C6FD251951F8003C2FDB /* OSD.framework */, + 4C34C05E9BD81D579A0C4957 /* Pods_MultiSoundChanger.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + F373D8A22561D10800642274 /* AppDelegate */ = { + isa = PBXGroup; + children = ( + 4743EFAA1E91493B0032F5AA /* AppDelegate.swift */, + ); + path = AppDelegate; + sourceTree = ""; + }; + F373D8A42561D11900642274 /* Sources */ = { + isa = PBXGroup; + children = ( + F373D8A22561D10800642274 /* AppDelegate */, + F373D8A92561D17C00642274 /* Classes */, + 47E683041E91BC6200C525F7 /* Frameworks */, + F373D8B72561D1C600642274 /* Stories */, + 47E6830C1E9273C600C525F7 /* Extensions */, + F392596C2610DECC00B7AD62 /* Utils */, + ); + path = Sources; + sourceTree = ""; + }; + F373D8A92561D17C00642274 /* Classes */ = { + isa = PBXGroup; + children = ( + F3925974262F2B8000B7AD62 /* ApplicationController.swift */, + F373D8B02561D1A600642274 /* StatusBarController.swift */, + F373D8B12561D1A600642274 /* AudioManager.swift */, + F373D8B22561D1A600642274 /* MediaManager.swift */, + ); + path = Classes; + sourceTree = ""; + }; + F373D8B72561D1C600642274 /* Stories */ = { + isa = PBXGroup; + children = ( + F373D8BA2561D21900642274 /* Stories.swift */, + F373D8B92561D1F200642274 /* Main */, + F373D8B82561D1ED00642274 /* Volume */, + ); + path = Stories; + sourceTree = ""; + }; + F373D8B82561D1ED00642274 /* Volume */ = { + isa = PBXGroup; + children = ( + F373D8BC2561D22000642274 /* Volume.storyboard */, + F373D8BD2561D22000642274 /* VolumeViewController.swift */, + ); + path = Volume; + sourceTree = ""; + }; + F373D8B92561D1F200642274 /* Main */ = { + isa = PBXGroup; + children = ( + F373D8C02561D24600642274 /* Main.storyboard */, + ); + path = Main; + sourceTree = ""; + }; + F373D8C92561D31700642274 /* Other */ = { + isa = PBXGroup; + children = ( + F37C2ECD256AA8F0001C3D36 /* Localization */, + F3433FCA25B36E16009AAE86 /* Images.swift */, + F383684C2561E39E00C7B454 /* Constants.swift */, + F373D8CB2561D36B00642274 /* Assets.xcassets */, + F373D8CA2561D36B00642274 /* MultiSoundChanger-Bridging-Header.h */, + F373D8CC2561D36B00642274 /* Info.plist */, + ); + path = Other; + sourceTree = ""; + }; + F37C2ECD256AA8F0001C3D36 /* Localization */ = { + isa = PBXGroup; + children = ( + F37C2ED0256AAA4C001C3D36 /* Strings.swift */, + F37C2ECE256AA987001C3D36 /* Localizable.strings */, + ); + path = Localization; + sourceTree = ""; + }; + F392596C2610DECC00B7AD62 /* Utils */ = { + isa = PBXGroup; + children = ( + F3925978262F2F9F00B7AD62 /* Logger.swift */, + F392597B2631ACE700B7AD62 /* Runner.swift */, + ); + path = Utils; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4743EFA61E91493B0032F5AA /* MultiSoundChanger */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4743EFB61E91493B0032F5AA /* Build configuration list for PBXNativeTarget "MultiSoundChanger" */; + buildPhases = ( + 11DA7F4B3D2C9321E3505857 /* [CP] Check Pods Manifest.lock */, + F373D8802561638C00642274 /* SwiftLint */, + 4743EFA31E91493B0032F5AA /* Sources */, + 4743EFA41E91493B0032F5AA /* Frameworks */, + 4743EFA51E91493B0032F5AA /* Resources */, + 8ABB500BA33A8DA7ECBE36B7 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MultiSoundChanger; + productName = DynamicsIllusion; + productReference = 4743EFA71E91493B0032F5AA /* MultiSoundChanger.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4743EF9F1E91493B0032F5AA /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 1200; + ORGANIZATIONNAME = "Dmitry Medyuho"; + TargetAttributes = { + 4743EFA61E91493B0032F5AA = { + CreatedOnToolsVersion = 8.3; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 4743EFA21E91493B0032F5AA /* Build configuration list for PBXProject "MultiSoundChanger" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4743EF9E1E91493B0032F5AA; + productRefGroup = 4743EFA81E91493B0032F5AA /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4743EFA61E91493B0032F5AA /* MultiSoundChanger */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4743EFA51E91493B0032F5AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F312C55025B3742200205846 /* Volume.storyboard in Resources */, + F312C54E25B3741C00205846 /* Main.storyboard in Resources */, + F37C2ECF256AA987001C3D36 /* Localizable.strings in Resources */, + F373D8CD2561D36B00642274 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 11DA7F4B3D2C9321E3505857 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-MultiSoundChanger-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 8ABB500BA33A8DA7ECBE36B7 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-MultiSoundChanger/Pods-MultiSoundChanger-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/MediaKeyTap/MediaKeyTap.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MediaKeyTap.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MultiSoundChanger/Pods-MultiSoundChanger-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F373D8802561638C00642274 /* SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftLint; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4743EFA31E91493B0032F5AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F373D8C82561D2B000642274 /* Extensions.swift in Sources */, + F392597C2631ACE700B7AD62 /* Runner.swift in Sources */, + F3433FCB25B36E16009AAE86 /* Images.swift in Sources */, + F3925975262F2B8000B7AD62 /* ApplicationController.swift in Sources */, + F373D8C62561D2A600642274 /* Audio.swift in Sources */, + F37C2ED1256AAA4C001C3D36 /* Strings.swift in Sources */, + F373D8B52561D1A600642274 /* MediaManager.swift in Sources */, + F373D8B42561D1A600642274 /* AudioManager.swift in Sources */, + F373D8B32561D1A600642274 /* StatusBarController.swift in Sources */, + 4743EFAB1E91493B0032F5AA /* AppDelegate.swift in Sources */, + F373D8BF2561D22000642274 /* VolumeViewController.swift in Sources */, + F3925979262F2F9F00B7AD62 /* Logger.swift in Sources */, + F373D8BB2561D21900642274 /* Stories.swift in Sources */, + F383684D2561E39E00C7B454 /* Constants.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + F373D8C02561D24600642274 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F373D8C12561D24600642274 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 4743EFB41E91493B0032F5AA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 4743EFB51E91493B0032F5AA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 4743EFB71E91493B0032F5AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6FD0ED04AFD1CC1242C9B3B3 /* Pods-MultiSoundChanger.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; + EXCLUDED_ARCHS = arm64; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = MultiSoundChanger/Other/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MARKETING_VERSION = 1.0.1; + PRODUCT_BUNDLE_IDENTIFIER = com.rlxone.multisoundchanger; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "MultiSoundChanger/Other/MultiSoundChanger-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 4743EFB81E91493B0032F5AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D184B2CD842B856AFFE7DF7E /* Pods-MultiSoundChanger.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; + EXCLUDED_ARCHS = arm64; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = MultiSoundChanger/Other/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MARKETING_VERSION = 1.0.1; + PRODUCT_BUNDLE_IDENTIFIER = com.rlxone.multisoundchanger; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "MultiSoundChanger/Other/MultiSoundChanger-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4743EFA21E91493B0032F5AA /* Build configuration list for PBXProject "MultiSoundChanger" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4743EFB41E91493B0032F5AA /* Debug */, + 4743EFB51E91493B0032F5AA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4743EFB61E91493B0032F5AA /* Build configuration list for PBXNativeTarget "MultiSoundChanger" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4743EFB71E91493B0032F5AA /* Debug */, + 4743EFB81E91493B0032F5AA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4743EF9F1E91493B0032F5AA /* Project object */; +} diff --git a/MultiSoundChanger.xcodeproj/xcshareddata/xcschemes/MultiSoundChanger.xcscheme b/MultiSoundChanger.xcodeproj/xcshareddata/xcschemes/MultiSoundChanger.xcscheme new file mode 100644 index 0000000..050b720 --- /dev/null +++ b/MultiSoundChanger.xcodeproj/xcshareddata/xcschemes/MultiSoundChanger.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MultiSoundChanger.xcodeproj/xcuserdata/ichi.xcuserdatad/xcschemes/xcschememanagement.plist b/MultiSoundChanger.xcodeproj/xcuserdata/ichi.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..72d9f2c --- /dev/null +++ b/MultiSoundChanger.xcodeproj/xcuserdata/ichi.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + MultiSoundChanger.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/MultiSoundChanger.xcodeproj/xcuserdata/rlxone.xcuserdatad/xcschemes/xcschememanagement.plist b/MultiSoundChanger.xcodeproj/xcuserdata/rlxone.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..ec26fd0 --- /dev/null +++ b/MultiSoundChanger.xcodeproj/xcuserdata/rlxone.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + MultiSoundChanger.xcscheme_^#shared#^_ + + orderHint + 3 + + + SuppressBuildableAutocreation + + 4743EFA61E91493B0032F5AA + + primary + + + + + diff --git a/DynamicsIllusion.xcodeproj/xcuserdata/sddsd.xcuserdatad/xcschemes/DynamicsIllusion.xcscheme b/MultiSoundChanger.xcodeproj/xcuserdata/sddsd.xcuserdatad/xcschemes/DynamicsIllusion.xcscheme similarity index 100% rename from DynamicsIllusion.xcodeproj/xcuserdata/sddsd.xcuserdatad/xcschemes/DynamicsIllusion.xcscheme rename to MultiSoundChanger.xcodeproj/xcuserdata/sddsd.xcuserdatad/xcschemes/DynamicsIllusion.xcscheme diff --git a/DynamicsIllusion.xcodeproj/xcuserdata/sddsd.xcuserdatad/xcschemes/xcschememanagement.plist b/MultiSoundChanger.xcodeproj/xcuserdata/sddsd.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 100% rename from DynamicsIllusion.xcodeproj/xcuserdata/sddsd.xcuserdatad/xcschemes/xcschememanagement.plist rename to MultiSoundChanger.xcodeproj/xcuserdata/sddsd.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/Contents.json b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..f591fe9 --- /dev/null +++ b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "filename" : "icon_512x512_Normal@2x-16.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "icon_512x512_Normal@2x-32.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "icon_512x512_Normal@2x-32.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "icon_512x512_Normal@2x-64.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "icon_512x512_Normal@2x-128.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "icon_512x512_Normal@2x-256.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "icon_512x512_Normal@2x-256.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "icon_512x512_Normal@2x-512.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "icon_512x512_Normal@2x-512.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "icon_512x512_Normal@2x-1024.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-1024.png b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-1024.png new file mode 100644 index 0000000..4c99b55 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-1024.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-128.png b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-128.png new file mode 100644 index 0000000..92f4adc Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-128.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-16.png b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-16.png new file mode 100644 index 0000000..edec36b Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-16.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-256.png b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-256.png new file mode 100644 index 0000000..d01d927 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-256.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-32.png b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-32.png new file mode 100644 index 0000000..7c983e6 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-32.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-512.png b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-512.png new file mode 100644 index 0000000..4fa29f1 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-512.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-64.png b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-64.png new file mode 100644 index 0000000..3b3b314 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/AppIcon.appiconset/icon_512x512_Normal@2x-64.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/Contents.json b/MultiSoundChanger/Other/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/MultiSoundChanger/Other/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MultiSoundChanger/Other/Assets.xcassets/StatusBar1Image.imageset/@1xMedium-S.png b/MultiSoundChanger/Other/Assets.xcassets/StatusBar1Image.imageset/@1xMedium-S.png new file mode 100644 index 0000000..10da220 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/StatusBar1Image.imageset/@1xMedium-S.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/StatusBar1Image.imageset/@2xMedium-S.png b/MultiSoundChanger/Other/Assets.xcassets/StatusBar1Image.imageset/@2xMedium-S.png new file mode 100644 index 0000000..ab577e2 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/StatusBar1Image.imageset/@2xMedium-S.png differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar3Image.imageset/Contents.json b/MultiSoundChanger/Other/Assets.xcassets/StatusBar1Image.imageset/Contents.json similarity index 64% rename from DynamicsIllusion/Assets.xcassets/StatusBar3Image.imageset/Contents.json rename to MultiSoundChanger/Other/Assets.xcassets/StatusBar1Image.imageset/Contents.json index 2534c94..2b780b5 100644 --- a/DynamicsIllusion/Assets.xcassets/StatusBar3Image.imageset/Contents.json +++ b/MultiSoundChanger/Other/Assets.xcassets/StatusBar1Image.imageset/Contents.json @@ -1,21 +1,21 @@ { "images" : [ { + "filename" : "@1xMedium-S.png", "idiom" : "mac", - "filename" : "second.png", "scale" : "1x" }, { + "filename" : "@2xMedium-S.png", "idiom" : "mac", - "filename" : "second1.png", "scale" : "2x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/MultiSoundChanger/Other/Assets.xcassets/StatusBar2Image.imageset/@1xMedium-S.png b/MultiSoundChanger/Other/Assets.xcassets/StatusBar2Image.imageset/@1xMedium-S.png new file mode 100644 index 0000000..10063e0 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/StatusBar2Image.imageset/@1xMedium-S.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/StatusBar2Image.imageset/@2xMedium-S.png b/MultiSoundChanger/Other/Assets.xcassets/StatusBar2Image.imageset/@2xMedium-S.png new file mode 100644 index 0000000..bf9b278 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/StatusBar2Image.imageset/@2xMedium-S.png differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar1Image.imageset/Contents.json b/MultiSoundChanger/Other/Assets.xcassets/StatusBar2Image.imageset/Contents.json similarity index 64% rename from DynamicsIllusion/Assets.xcassets/StatusBar1Image.imageset/Contents.json rename to MultiSoundChanger/Other/Assets.xcassets/StatusBar2Image.imageset/Contents.json index 0ad204c..2b780b5 100644 --- a/DynamicsIllusion/Assets.xcassets/StatusBar1Image.imageset/Contents.json +++ b/MultiSoundChanger/Other/Assets.xcassets/StatusBar2Image.imageset/Contents.json @@ -1,21 +1,21 @@ { "images" : [ { + "filename" : "@1xMedium-S.png", "idiom" : "mac", - "filename" : "none.png", "scale" : "1x" }, { + "filename" : "@2xMedium-S.png", "idiom" : "mac", - "filename" : "none1.png", "scale" : "2x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/MultiSoundChanger/Other/Assets.xcassets/StatusBar3Image.imageset/@1xMedium-S.png b/MultiSoundChanger/Other/Assets.xcassets/StatusBar3Image.imageset/@1xMedium-S.png new file mode 100644 index 0000000..f6b2bc9 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/StatusBar3Image.imageset/@1xMedium-S.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/StatusBar3Image.imageset/@2xMedium-S.png b/MultiSoundChanger/Other/Assets.xcassets/StatusBar3Image.imageset/@2xMedium-S.png new file mode 100644 index 0000000..ce3ae6f Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/StatusBar3Image.imageset/@2xMedium-S.png differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar2Image.imageset/Contents.json b/MultiSoundChanger/Other/Assets.xcassets/StatusBar3Image.imageset/Contents.json similarity index 64% rename from DynamicsIllusion/Assets.xcassets/StatusBar2Image.imageset/Contents.json rename to MultiSoundChanger/Other/Assets.xcassets/StatusBar3Image.imageset/Contents.json index a5a3915..2b780b5 100644 --- a/DynamicsIllusion/Assets.xcassets/StatusBar2Image.imageset/Contents.json +++ b/MultiSoundChanger/Other/Assets.xcassets/StatusBar3Image.imageset/Contents.json @@ -1,21 +1,21 @@ { "images" : [ { + "filename" : "@1xMedium-S.png", "idiom" : "mac", - "filename" : "first.png", "scale" : "1x" }, { + "filename" : "@2xMedium-S.png", "idiom" : "mac", - "filename" : "first1.png", "scale" : "2x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/MultiSoundChanger/Other/Assets.xcassets/StatusBar4Image.imageset/@1xMedium-S.png b/MultiSoundChanger/Other/Assets.xcassets/StatusBar4Image.imageset/@1xMedium-S.png new file mode 100644 index 0000000..3bd2cf6 Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/StatusBar4Image.imageset/@1xMedium-S.png differ diff --git a/MultiSoundChanger/Other/Assets.xcassets/StatusBar4Image.imageset/@2xMedium-S.png b/MultiSoundChanger/Other/Assets.xcassets/StatusBar4Image.imageset/@2xMedium-S.png new file mode 100644 index 0000000..d7f12ad Binary files /dev/null and b/MultiSoundChanger/Other/Assets.xcassets/StatusBar4Image.imageset/@2xMedium-S.png differ diff --git a/DynamicsIllusion/Assets.xcassets/StatusBar4Image.imageset/Contents.json b/MultiSoundChanger/Other/Assets.xcassets/StatusBar4Image.imageset/Contents.json similarity index 64% rename from DynamicsIllusion/Assets.xcassets/StatusBar4Image.imageset/Contents.json rename to MultiSoundChanger/Other/Assets.xcassets/StatusBar4Image.imageset/Contents.json index f028cef..2b780b5 100644 --- a/DynamicsIllusion/Assets.xcassets/StatusBar4Image.imageset/Contents.json +++ b/MultiSoundChanger/Other/Assets.xcassets/StatusBar4Image.imageset/Contents.json @@ -1,21 +1,21 @@ { "images" : [ { + "filename" : "@1xMedium-S.png", "idiom" : "mac", - "filename" : "third.png", "scale" : "1x" }, { + "filename" : "@2xMedium-S.png", "idiom" : "mac", - "filename" : "third1.png", "scale" : "2x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/MultiSoundChanger/Other/Constants.swift b/MultiSoundChanger/Other/Constants.swift new file mode 100644 index 0000000..a6ea1b1 --- /dev/null +++ b/MultiSoundChanger/Other/Constants.swift @@ -0,0 +1,55 @@ +// +// Constants.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 16.11.2020. +// Copyright © 2020 Dmitry Medyuho. All rights reserved. +// + +import Foundation + +enum Constants { + static let chicletsCount = 16 + static let optionMaxLength = 25 + static let muteVolumeLowerbound: Float = 0.001 + static let logFilename = "app.log" + + enum AppBundleIdentifier { + static let systemPreferences = "com.apple.systempreferences" + static let audioDevices = "com.apple.audio.AudioMIDISetup" + } + + enum SystemPreferencesPane { + static let sound = "/System/Library/PreferencePanes/Sound.prefPane" + } + + enum Notifications { + static let accessibility = "com.apple.accessibility.api" + } + + enum Keys: String { + case empty = "" + case q + } + + enum InnerMessages { + static let accessEnabled = "Access enabled" + static let accessDenied = "Access denied" + static let getDisplayError = "Error getting display under cursor" + static let outputDevices = "Output devices" + static let bundleIdentifierError = "Can't get bundle identifier" + static let controllerIdentifierError = "Wrong controller identifier" + + static func debugDevice(deviceID: String, deviceName: String) -> String { + return "id: \(deviceID) | name: \(deviceName)" + } + + static func selectDevice(deviceID: String) -> String { + return "Select device id: \(deviceID)" + } + + static func selectedDeviceVolume(volume: String) -> String { + return "Selected device volume: \(volume)" + } + } +} diff --git a/MultiSoundChanger/Other/Images.swift b/MultiSoundChanger/Other/Images.swift new file mode 100644 index 0000000..b379568 --- /dev/null +++ b/MultiSoundChanger/Other/Images.swift @@ -0,0 +1,16 @@ +// +// Images.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 16.01.2021. +// Copyright © 2021 Dmitry Medyuho. All rights reserved. +// + +import Cocoa + +enum Images { + static let volumeImage1 = NSImage(named: "StatusBar1Image") + static let volumeImage2 = NSImage(named: "StatusBar2Image") + static let volumeImage3 = NSImage(named: "StatusBar3Image") + static let volumeImage4 = NSImage(named: "StatusBar4Image") +} diff --git a/DynamicsIllusion/Info.plist b/MultiSoundChanger/Other/Info.plist similarity index 89% rename from DynamicsIllusion/Info.plist rename to MultiSoundChanger/Other/Info.plist index 9e6a8ca..8902329 100644 --- a/DynamicsIllusion/Info.plist +++ b/MultiSoundChanger/Other/Info.plist @@ -17,18 +17,18 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion 1 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) + LSUIElement + NSHumanReadableCopyright - Copyright © 2017 rlxone. All rights reserved. + Copyright © 2017 Dmitry Medyuho. All rights reserved. NSMainStoryboardFile Main NSPrincipalClass NSApplication - LSUIElement - diff --git a/MultiSoundChanger/Other/Localization/Localizable.strings b/MultiSoundChanger/Other/Localization/Localizable.strings new file mode 100644 index 0000000..abc3bf5 --- /dev/null +++ b/MultiSoundChanger/Other/Localization/Localizable.strings @@ -0,0 +1,13 @@ +/* + Localization.strings + MultiSoundChanger + + Created by Dmitry Medyuho on 22.11.2020. + Copyright © 2020 Dmitry Medyuho. All rights reserved. +*/ + +"volume" = "Volume:"; +"output" = "Output Device:"; +"quit" = "Quit"; +"sound.preferences" = "Sound Preferences..."; +"audio.devices" = "Audio Devices..."; diff --git a/MultiSoundChanger/Other/Localization/Strings.swift b/MultiSoundChanger/Other/Localization/Strings.swift new file mode 100644 index 0000000..2c77f12 --- /dev/null +++ b/MultiSoundChanger/Other/Localization/Strings.swift @@ -0,0 +1,17 @@ +// +// Strings.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 22.11.2020. +// Copyright © 2020 Dmitry Medyuho. All rights reserved. +// + +import Foundation + +enum Strings { + static var volume = NSLocalizedString("volume", comment: "") + static var output = NSLocalizedString("output", comment: "") + static var quit = NSLocalizedString("quit", comment: "") + static var soundPreferences = NSLocalizedString("sound.preferences", comment: "") + static var audioDevices = NSLocalizedString("audio.devices", comment: "") +} diff --git a/MultiSoundChanger/Other/MultiSoundChanger-Bridging-Header.h b/MultiSoundChanger/Other/MultiSoundChanger-Bridging-Header.h new file mode 100644 index 0000000..6b74749 --- /dev/null +++ b/MultiSoundChanger/Other/MultiSoundChanger-Bridging-Header.h @@ -0,0 +1,13 @@ +// +// MultiSoundChanger-Bridging-Header.h +// MultiSoundChanger +// +// + +#ifndef MultiSoundChanger_Bridging_Header_h +#define MultiSoundChanger_Bridging_Header_h + +#import +#import + +#endif /* MultiSoundChanger_Bridging_Header_h */ diff --git a/MultiSoundChanger/Sources/AppDelegate/AppDelegate.swift b/MultiSoundChanger/Sources/AppDelegate/AppDelegate.swift new file mode 100644 index 0000000..7ff7d4d --- /dev/null +++ b/MultiSoundChanger/Sources/AppDelegate/AppDelegate.swift @@ -0,0 +1,18 @@ +// +// AppDelegate.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 02.04.17. +// Copyright © 2017 Dmitry Medyuho. All rights reserved. +// + +import Cocoa + +@NSApplicationMain +class AppDelegate: NSObject, NSApplicationDelegate { + private let applicationController: ApplicationController = ApplicationControllerImp() + + func applicationDidFinishLaunching(_ aNotification: Notification) { + applicationController.start() + } +} diff --git a/MultiSoundChanger/Sources/Classes/ApplicationController.swift b/MultiSoundChanger/Sources/Classes/ApplicationController.swift new file mode 100644 index 0000000..6498952 --- /dev/null +++ b/MultiSoundChanger/Sources/Classes/ApplicationController.swift @@ -0,0 +1,70 @@ +// +// ApplicationController.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 20.04.21. +// Copyright © 2021 Dmitry Medyuho. All rights reserved. +// + +import Foundation +import MediaKeyTap + +// MARK: - Protocols + +protocol ApplicationController: class { + func start() +} + +// MARK: - Implementation + +final class ApplicationControllerImp: ApplicationController { + private lazy var audioManager: AudioManager = AudioManagerImpl() + private lazy var mediaManager: MediaManager = MediaManagerImpl(delegate: self) + private lazy var statusBarController: StatusBarController = StatusBarControllerImpl(audioManager: audioManager) + + func start() { + statusBarController.createMenu() + mediaManager.listenMediaKeyTaps() + } +} + +// MARK: - MediaManagerDelegate + +extension ApplicationControllerImp: MediaManagerDelegate { + func onMediaKeyTap(mediaKey: MediaKey) { + guard let selectedDeviceVolume = audioManager.getSelectedDeviceVolume() else { + return + } + + let volumeStep: Float = 1 / Float(Constants.chicletsCount) + var volume: Float = (selectedDeviceVolume / volumeStep).rounded() * volumeStep + + switch mediaKey { + case .volumeUp: + volume = (volume + volumeStep).clamped(to: 0...1) + audioManager.setSelectedDeviceVolume(masterChannelLevel: volume, leftChannelLevel: volume, rightChannelLevel: volume) + + case .volumeDown: + volume = (volume - volumeStep).clamped(to: 0...1) + audioManager.setSelectedDeviceVolume(masterChannelLevel: volume, leftChannelLevel: volume, rightChannelLevel: volume) + + case .mute: + audioManager.toggleMute() + if audioManager.isSelectedDeviceMuted() { + volume = 0 + } else { + volume = audioManager.getSelectedDeviceVolume() ?? 0 + } + + default: + break + } + + let correctedVolume = volume * 100 + + statusBarController.updateVolume(value: correctedVolume) + mediaManager.showOSD(volume: correctedVolume, chicletsCount: Constants.chicletsCount) + + Logger.debug(Constants.InnerMessages.selectedDeviceVolume(volume: String(correctedVolume))) + } +} diff --git a/MultiSoundChanger/Sources/Classes/AudioManager.swift b/MultiSoundChanger/Sources/Classes/AudioManager.swift new file mode 100644 index 0000000..9f54308 --- /dev/null +++ b/MultiSoundChanger/Sources/Classes/AudioManager.swift @@ -0,0 +1,165 @@ +// +// AudioManager.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 15.11.2020. +// Copyright © 2020 Dmitry Medyuho. All rights reserved. +// + +import AudioToolbox +import Foundation + +// MARK: - Protocols + +protocol AudioManager: class { + func getDefaultOutputDevice() -> AudioDeviceID + func getOutputDevices() -> [AudioDeviceID: String]? + func selectDevice(deviceID: AudioDeviceID) + func getSelectedDeviceVolume() -> Float? + func setSelectedDeviceVolume(masterChannelLevel: Float, leftChannelLevel: Float, rightChannelLevel: Float) + func isSelectedDeviceMuted() -> Bool + func toggleMute() + + var isMuted: Bool { get } +} + +// MARK: - Implementation + +final class AudioManagerImpl: AudioManager { + private let audio: Audio = AudioImpl() + private let devices: [AudioDeviceID: String]? + private var selectedDevice: AudioDeviceID? + + init() { + devices = audio.getOutputDevices() + printDevices() + } + + func getDefaultOutputDevice() -> AudioDeviceID { + return audio.getDefaultOutputDevice() + } + + func getOutputDevices() -> [AudioDeviceID: String]? { + return devices + } + + func isAggregateDevice(deviceID: AudioDeviceID) -> Bool { + return audio.isAggregateDevice(deviceID: deviceID) + } + + func selectDevice(deviceID: AudioDeviceID) { + selectedDevice = deviceID + audio.setOutputDevice(newDeviceID: deviceID) + Logger.debug(Constants.InnerMessages.selectDevice(deviceID: String(deviceID))) + } + + func getSelectedDeviceVolume() -> Float? { + guard let selectedDevice = selectedDevice else { + return nil + } + + if audio.isAggregateDevice(deviceID: selectedDevice) { + let aggregatedDevices = audio.getAggregateDeviceSubDeviceList(deviceID: selectedDevice) + + for device in aggregatedDevices { + if audio.isOutputDevice(deviceID: device) { + return audio.getDeviceVolume(deviceID: device).max() + } + } + } else { + return audio.getDeviceVolume(deviceID: selectedDevice).max() + } + + return nil + } + + func setSelectedDeviceVolume(masterChannelLevel: Float, leftChannelLevel: Float, rightChannelLevel: Float) { + guard let selectedDevice = selectedDevice else { + return + } + + let isMute = masterChannelLevel < Constants.muteVolumeLowerbound + && leftChannelLevel < Constants.muteVolumeLowerbound + && rightChannelLevel < Constants.muteVolumeLowerbound + + if audio.isAggregateDevice(deviceID: selectedDevice) { + let aggregatedDevices = audio.getAggregateDeviceSubDeviceList(deviceID: selectedDevice) + + for device in aggregatedDevices { + audio.setDeviceVolume( + deviceID: device, + masterChannelLevel: masterChannelLevel, + leftChannelLevel: leftChannelLevel, + rightChannelLevel: rightChannelLevel + ) + audio.setDeviceMute(deviceID: device, isMute: isMute) + } + } else { + audio.setDeviceVolume( + deviceID: selectedDevice, + masterChannelLevel: masterChannelLevel, + leftChannelLevel: leftChannelLevel, + rightChannelLevel: rightChannelLevel + ) + audio.setDeviceMute(deviceID: selectedDevice, isMute: isMute) + } + } + + func setSelectedDeviceMute(isMute: Bool) { + guard let selectedDevice = selectedDevice else { + return + } + + if audio.isAggregateDevice(deviceID: selectedDevice) { + let aggregatedDevices = audio.getAggregateDeviceSubDeviceList(deviceID: selectedDevice) + + for device in aggregatedDevices { + audio.setDeviceMute(deviceID: device, isMute: isMute) + } + } else { + audio.setDeviceMute(deviceID: selectedDevice, isMute: isMute) + } + } + + func isSelectedDeviceMuted() -> Bool { + guard let selectedDevice = selectedDevice else { + return false + } + + if audio.isAggregateDevice(deviceID: selectedDevice) { + let aggregatedDevices = audio.getAggregateDeviceSubDeviceList(deviceID: selectedDevice) + + guard let device = aggregatedDevices.first else { + return false + } + + return audio.isDeviceMuted(deviceID: device) + } else { + return audio.isDeviceMuted(deviceID: selectedDevice) + } + } + + func toggleMute() { + if isSelectedDeviceMuted() { + setSelectedDeviceMute(isMute: false) + let volume = getSelectedDeviceVolume() ?? 0 + setSelectedDeviceVolume(masterChannelLevel: volume, leftChannelLevel: volume, rightChannelLevel: volume) + } else { + setSelectedDeviceMute(isMute: true) + } + } + + var isMuted: Bool { + return isSelectedDeviceMuted() + } + + private func printDevices() { + guard let devices = devices else { + return + } + Logger.debug(Constants.InnerMessages.outputDevices) + for device in devices { + Logger.debug(Constants.InnerMessages.debugDevice(deviceID: String(device.key), deviceName: device.value)) + } + } +} diff --git a/MultiSoundChanger/Sources/Classes/MediaManager.swift b/MultiSoundChanger/Sources/Classes/MediaManager.swift new file mode 100644 index 0000000..b3a4799 --- /dev/null +++ b/MultiSoundChanger/Sources/Classes/MediaManager.swift @@ -0,0 +1,126 @@ +// +// MediaManager.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 15.11.2020. +// Copyright © 2020 Dmitry Medyuho. All rights reserved. +// + +import Cocoa +import Foundation +import MediaKeyTap + +// MARK: - Protocols + +protocol MediaManagerDelegate: class { + func onMediaKeyTap(mediaKey: MediaKey) +} + +protocol MediaManager: class { + func listenMediaKeyTaps() + func showOSD(volume: Float, chicletsCount: Int) +} + +// MARK: - Implementation + +final class MediaManagerImpl: MediaManager { + private weak var delegate: MediaManagerDelegate? + private var mediaKeyTap: MediaKeyTap? + + init(delegate: MediaManagerDelegate) { + self.delegate = delegate + } + + deinit { + DistributedNotificationCenter.default().removeObserver(self) + } + + // MARK: Public + + func listenMediaKeyTaps() { + observeMediaKeyOnAccessibiltiyApiChange() + startMediaKeyTap() + } + + func showOSD(volume: Float, chicletsCount: Int = 16) { + guard let manager = OSDManager.sharedManager() as? OSDManager else { + return + } + + let mouseloc: NSPoint = NSEvent.mouseLocation + var displayForPoint: CGDirectDisplayID = 0 + var count: UInt32 = 0 + + if CGGetDisplaysWithPoint(mouseloc, 1, &displayForPoint, &count) != .success { + Logger.warning(Constants.InnerMessages.getDisplayError) + displayForPoint = CGMainDisplayID() + } + + let image = (volume == 0) ? OSDGraphicSpeakerMuted.rawValue : OSDGraphicSpeaker.rawValue + let volumeStep: Float = 100 / Float(chicletsCount) + + manager.showImage( + Int64(image), + onDisplayID: displayForPoint, + priority: 0x1F4, + msecUntilFade: 1_000, + filledChiclets: UInt32(volume / volumeStep), + totalChiclets: UInt32(100.0 / volumeStep), + locked: false + ) + } + + // MARK: Private + + private func acquirePrivileges() { + let trusted = kAXTrustedCheckOptionPrompt.takeUnretainedValue() + let privOptions = [trusted: true] as CFDictionary + let accessEnabled = AXIsProcessTrustedWithOptions(privOptions) + + if accessEnabled { + Logger.warning(Constants.InnerMessages.accessEnabled) + } else { + Logger.warning(Constants.InnerMessages.accessDenied) + } + } + + private func startMediaKeyTap() { + acquirePrivileges() + + let keys: [MediaKey] = [ + .volumeUp, + .volumeDown, + .mute + ] + + mediaKeyTap?.stop() + mediaKeyTap = MediaKeyTap(delegate: self, for: keys, observeBuiltIn: true) + mediaKeyTap?.start() + } + + private func observeMediaKeyOnAccessibiltiyApiChange() { + let notificaion = NSNotification.Name(rawValue: Constants.Notifications.accessibility) + + DistributedNotificationCenter.default().addObserver( + self, + selector: #selector(onAccessibilityNotification), + name: notificaion, + object: nil + ) + } + + @objc + private func onAccessibilityNotification(_ aNotification: Notification) { + DispatchQueue.main.async { [weak self] in + self?.startMediaKeyTap() + } + } +} + +// MARK: - MediaKeyTapDelegate + +extension MediaManagerImpl: MediaKeyTapDelegate { + func handle(mediaKey: MediaKey, event: KeyEvent?, modifiers: NSEvent.ModifierFlags?) { + delegate?.onMediaKeyTap(mediaKey: mediaKey) + } +} diff --git a/MultiSoundChanger/Sources/Classes/StatusBarController.swift b/MultiSoundChanger/Sources/Classes/StatusBarController.swift new file mode 100644 index 0000000..a23eb41 --- /dev/null +++ b/MultiSoundChanger/Sources/Classes/StatusBarController.swift @@ -0,0 +1,210 @@ +// +// StatusBarController.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 15.11.2020. +// Copyright © 2020 Dmitry Medyuho. All rights reserved. +// + +import AudioToolbox +import Cocoa + +// MARK: - Protocols + +protocol StatusBarController: class { + func createMenu() + func changeStatusItemImage(value: Float) + func updateVolume(value: Float) +} + +// MARK: - Extensions + +extension StatusBarControllerImpl { + enum MenuItem { + case volume + case slider + case output + case separator + case soundPreferences + case audioSetup + case quit + } +} + +// MARK: - Implementation + +final class StatusBarControllerImpl: StatusBarController { + private let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) + private let volumeController: VolumeViewController + private let audioManager: AudioManager + + init(audioManager: AudioManager) { + self.audioManager = audioManager + + self.volumeController = Stories.volume.controller(VolumeViewController.self) + self.volumeController.audioManager = audioManager + self.volumeController.statusBarController = self + } + + func createMenu() { + if let button = statusItem.button { + button.image = Images.volumeImage1 + } + + let menu = NSMenu() + menu.autoenablesItems = false + + let volumeItem = getMenuItem(by: .volume) + let sliderItem = getMenuItem(by: .slider) + let outputItem = getMenuItem(by: .output) + let firstSeparatorItem = getMenuItem(by: .separator) + let soundPreferencesItem = getMenuItem(by: .soundPreferences) + let audioSetupItem = getMenuItem(by: .audioSetup) + let secondSeparatorItem = getMenuItem(by: .separator) + let quitItem = getMenuItem(by: .quit) + + menu.addItem(volumeItem) + menu.addItem(sliderItem) + menu.addItem(outputItem) + setOutputDeviceList(for: menu) + menu.addItem(firstSeparatorItem) + menu.addItem(soundPreferencesItem) + menu.addItem(audioSetupItem) + menu.addItem(secondSeparatorItem) + menu.addItem(quitItem) + + statusItem.menu = menu + } + + func changeStatusItemImage(value: Float) { + if value < 1 { + statusItem.button?.image = Images.volumeImage1 + } else if value > 1 && value <= 100 / 3 { + statusItem.button?.image = Images.volumeImage2 + } else if value > 100 / 3 && value <= 100 / 3 * 2 { + statusItem.button?.image = Images.volumeImage3 + } else if value > 100 / 3 * 2 && value <= 100 { + statusItem.button?.image = Images.volumeImage4 + } + } + + func updateVolume(value: Float) { + volumeController.updateSliderVolume(volume: value) + changeStatusItemImage(value: value) + } + + private func getMenuItem(by type: MenuItem) -> NSMenuItem { + switch type { + case .volume: + let item = NSMenuItem(title: Strings.volume, action: nil, keyEquivalent: Constants.Keys.empty.rawValue) + item.isEnabled = false + return item + + case .slider: + let item = NSMenuItem(title: String(), action: nil, keyEquivalent: Constants.Keys.empty.rawValue) + item.view = volumeController.view + return item + + case .output: + let item = NSMenuItem(title: Strings.output, action: nil, keyEquivalent: Constants.Keys.empty.rawValue) + item.isEnabled = false + return item + + case .separator: + return NSMenuItem.separator() + + case .soundPreferences: + let item = NSMenuItem( + title: Strings.soundPreferences, + action: #selector(menuSoundPreferencesAction), + keyEquivalent: Constants.Keys.empty.rawValue + ) + item.target = self + return item + + case .audioSetup: + let item = NSMenuItem(title: Strings.audioDevices, action: #selector(menuAudioSetupAction), keyEquivalent: Constants.Keys.empty.rawValue) + item.target = self + return item + + case .quit: + let item = NSMenuItem(title: Strings.quit, action: #selector(menuQuitAction), keyEquivalent: Constants.Keys.q.rawValue) + item.target = self + return item + } + } + + private func setOutputDeviceList(for menu: NSMenu) { + guard let devices = audioManager.getOutputDevices() else { + return + } + + let defaultDevice = audioManager.getDefaultOutputDevice() + + for device in devices { + let item = NSMenuItem( + title: truncate(device.value, length: Constants.optionMaxLength), + action: #selector(menuItemAction), + keyEquivalent: String() + ) + item.target = self + item.tag = Int(device.key) + + if device.key == defaultDevice { + item.state = .on + selectDevice(device: defaultDevice) + } + + menu.addItem(item) + } + } + + private func selectDevice(device: AudioDeviceID) { + audioManager.selectDevice(deviceID: device) + guard let volume = audioManager.getSelectedDeviceVolume() else { + return + } + let correctedVolume = audioManager.isMuted ? 0 : volume * 100 + volumeController.updateSliderVolume(volume: correctedVolume) + changeStatusItemImage(value: correctedVolume) + } + + private func truncate(_ string: String, length: Int, trailing: String = "…") -> String { + if string.count > length { + return String(string.prefix(length)) + trailing + } else { + return string + } + } + + @objc + private func menuItemAction(sender: NSMenuItem) { + guard let items = statusItem.menu?.items else { + return + } + for item in items { + if item == sender { + item.state = .on + let deviceID = AudioDeviceID(item.tag) + selectDevice(device: deviceID) + } else { + item.state = NSControl.StateValue.off + } + } + } + + @objc + private func menuSoundPreferencesAction() { + Runner.shell("open -b \(Constants.AppBundleIdentifier.systemPreferences) \(Constants.SystemPreferencesPane.sound)") + } + + @objc + private func menuAudioSetupAction() { + Runner.launchApplication(bundleIndentifier: Constants.AppBundleIdentifier.audioDevices, options: .default) + } + + @objc + private func menuQuitAction() { + NSApplication.shared.terminate(self) + } +} diff --git a/MultiSoundChanger/Sources/Extensions/Extensions.swift b/MultiSoundChanger/Sources/Extensions/Extensions.swift new file mode 100644 index 0000000..e86f6e4 --- /dev/null +++ b/MultiSoundChanger/Sources/Extensions/Extensions.swift @@ -0,0 +1,15 @@ +// +// StringExtensions.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 03.04.17. +// Copyright © 2017 Dmitry Medyuho. All rights reserved. +// + +import Foundation + +extension Comparable { + func clamped(to limits: ClosedRange) -> Self { + return min(max(self, limits.lowerBound), limits.upperBound) + } +} diff --git a/MultiSoundChanger/Sources/Frameworks/Audio.swift b/MultiSoundChanger/Sources/Frameworks/Audio.swift new file mode 100644 index 0000000..1c245f6 --- /dev/null +++ b/MultiSoundChanger/Sources/Frameworks/Audio.swift @@ -0,0 +1,298 @@ +// +// Audio.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 03.04.17. +// Copyright © 2017 Dmitry Medyuho. All rights reserved. +// + +import AudioToolbox +import Cocoa +import Foundation + +// MARK: - Protocols + +protocol Audio { + func getOutputDevices() -> [AudioDeviceID: String]? + func isOutputDevice(deviceID: AudioDeviceID) -> Bool + func getAggregateDeviceSubDeviceList(deviceID: AudioDeviceID) -> [AudioDeviceID] + func isAggregateDevice(deviceID: AudioDeviceID) -> Bool + func setDeviceVolume(deviceID: AudioDeviceID, masterChannelLevel: Float, leftChannelLevel: Float, rightChannelLevel: Float) + func setDeviceMute(deviceID: AudioDeviceID, isMute: Bool) + func setOutputDevice(newDeviceID: AudioDeviceID) + func isDeviceMuted(deviceID: AudioDeviceID) -> Bool + func getDeviceVolume(deviceID: AudioDeviceID) -> [Float] + func getDefaultOutputDevice() -> AudioDeviceID + func getDeviceTransportType(deviceID: AudioDeviceID) -> AudioDevicePropertyID +} + +// MARK: - Implementation + +final class AudioImpl: Audio { + func getOutputDevices() -> [AudioDeviceID: String]? { + var result: [AudioDeviceID: String] = [:] + let devices = getAllDevices() + + for device in devices where isOutputDevice(deviceID: device) { + result[device] = getDeviceName(deviceID: device) + } + + return result + } + + func isOutputDevice(deviceID: AudioDeviceID) -> Bool { + var propertySize: UInt32 = 256 + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyStreams), + mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + AudioObjectGetPropertyDataSize(deviceID, &propertyAddress, 0, nil, &propertySize) + + return propertySize > 0 + } + + func getAggregateDeviceSubDeviceList(deviceID: AudioDeviceID) -> [AudioDeviceID] { + let subDevicesCount = getNumberOfSubDevices(deviceID: deviceID) + var subDevices = [AudioDeviceID](repeating: 0, count: Int(subDevicesCount)) + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioAggregateDevicePropertyActiveSubDeviceList), + mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + var subDevicesSize = subDevicesCount * UInt32(MemoryLayout.size) + + AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &subDevicesSize, &subDevices) + + return subDevices + } + + func isAggregateDevice(deviceID: AudioDeviceID) -> Bool { + let deviceType = getDeviceTransportType(deviceID: deviceID) + return deviceType == kAudioDeviceTransportTypeAggregate + } + + func isDeviceMuted(deviceID: AudioDeviceID) -> Bool { + var mutedValue: UInt32 = 0 + var propertySize = UInt32(MemoryLayout.size) + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyMute), + mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + let status = AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &mutedValue) + + if status != noErr { + return false + } + + return mutedValue == 1 + } + + func setDeviceVolume(deviceID: AudioDeviceID, masterChannelLevel: Float, leftChannelLevel: Float, rightChannelLevel: Float) { + var leftLevel = leftChannelLevel + var rigthLevel = rightChannelLevel + var masterLevel = masterChannelLevel + + var masterLevelPropertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyVolumeScalar), + mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), + mElement: AudioObjectPropertyElement(0) + ) + + var leftLevelPropertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyVolumeScalar), + mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), + mElement: AudioObjectPropertyElement(1) + ) + + var rightLevelPropertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyVolumeScalar), + mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), + mElement: AudioObjectPropertyElement(2) + ) + + var size = UInt32(0) + + AudioObjectGetPropertyDataSize(deviceID, &masterLevelPropertyAddress, 0, nil, &size) + AudioObjectSetPropertyData(deviceID, &masterLevelPropertyAddress, 0, nil, size, &masterLevel) + + AudioObjectGetPropertyDataSize(deviceID, &leftLevelPropertyAddress, 0, nil, &size) + AudioObjectSetPropertyData(deviceID, &leftLevelPropertyAddress, 0, nil, size, &leftLevel) + + AudioObjectGetPropertyDataSize(deviceID, &rightLevelPropertyAddress, 0, nil, &size) + AudioObjectSetPropertyData(deviceID, &rightLevelPropertyAddress, 0, nil, size, &rigthLevel) + } + + func setDeviceMute(deviceID: AudioDeviceID, isMute: Bool) { + var mutedValue: UInt32 = isMute ? 1 : 0 + let propertySize = UInt32(MemoryLayout.size) + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyMute), + mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + AudioObjectSetPropertyData(deviceID, &propertyAddress, 0, nil, propertySize, &mutedValue) + } + + func setOutputDevice(newDeviceID: AudioDeviceID) { + let propertySize = UInt32(MemoryLayout.size) + var deviceID = newDeviceID + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice), + mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + AudioObjectSetPropertyData(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, propertySize, &deviceID) + } + + func getDeviceVolume(deviceID: AudioDeviceID) -> [Float] { + var leftLevel = Float32(0) + var rigthLevel = Float32(0) + var masterLevel = Float32(0) + + var masterLevelPropertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyVolumeScalar), + mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), + mElement: AudioObjectPropertyElement(0) + ) + + var leftLevelPropertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyVolumeScalar), + mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), + mElement: AudioObjectPropertyElement(1) + ) + + var rightLevelPropertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyVolumeScalar), + mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput), + mElement: AudioObjectPropertyElement(2) + ) + + var size = UInt32(0) + + AudioObjectGetPropertyDataSize(deviceID, &masterLevelPropertyAddress, 0, nil, &size) + AudioObjectGetPropertyData(deviceID, &masterLevelPropertyAddress, 0, nil, &size, &masterLevel) + + AudioObjectGetPropertyDataSize(deviceID, &leftLevelPropertyAddress, 0, nil, &size) + AudioObjectGetPropertyData(deviceID, &leftLevelPropertyAddress, 0, nil, &size, &leftLevel) + + AudioObjectGetPropertyDataSize(deviceID, &rightLevelPropertyAddress, 0, nil, &size) + AudioObjectGetPropertyData(deviceID, &rightLevelPropertyAddress, 0, nil, &size, &rigthLevel) + + return [masterLevel, leftLevel, rigthLevel] + } + + func getDefaultOutputDevice() -> AudioDeviceID { + var propertySize = UInt32(MemoryLayout.size) + var deviceID = kAudioDeviceUnknown + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice), + mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, &propertySize, &deviceID) + + return deviceID + } + + func getDeviceTransportType(deviceID: AudioDeviceID) -> AudioDevicePropertyID { + var deviceTransportType = AudioDevicePropertyID() + var propertySize = UInt32(MemoryLayout.size) + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyTransportType), + mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &deviceTransportType) + + return deviceTransportType + } + + private func getNumberOfDevices() -> UInt32 { + var propertySize: UInt32 = 0 + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDevices), + mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + AudioObjectGetPropertyDataSize(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, &propertySize) + + return propertySize / UInt32(MemoryLayout.size) + } + + private func getNumberOfSubDevices(deviceID: AudioDeviceID) -> UInt32 { + var propertySize: UInt32 = 0 + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioAggregateDevicePropertyActiveSubDeviceList), + mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + AudioObjectGetPropertyDataSize(deviceID, &propertyAddress, 0, nil, &propertySize) + + return propertySize / UInt32(MemoryLayout.size) + } + + private func getDeviceName(deviceID: AudioDeviceID) -> String { + var propertySize = UInt32(MemoryLayout.size) + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyDeviceNameCFString), + mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + var result: CFString = "" as CFString + + AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &result) + + return result as String + } + + private func getDeviceType(deviceID: AudioDeviceID) -> String { + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioDevicePropertyDataSourceNameForIDCFString), + mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeOutput), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + var sourceID: UInt32 = 0 + var result: CFString = "" as CFString + + var translation = AudioValueTranslation( + mInputData: withUnsafeMutablePointer(to: &sourceID) { pointer in pointer }, + mInputDataSize: UInt32(MemoryLayout.size), + mOutputData: withUnsafeMutablePointer(to: &result) { pointer in pointer }, + mOutputDataSize: UInt32(MemoryLayout.size) + ) + + var propertySize = UInt32(MemoryLayout.size) + + AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, nil, &propertySize, &translation) + + return result as String + } + + private func getAllDevices() -> [AudioDeviceID] { + let devicesCount = getNumberOfDevices() + var devices = [AudioDeviceID](repeating: 0, count: Int(devicesCount)) + + var propertyAddress = AudioObjectPropertyAddress( + mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDevices), + mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal), + mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)) + + var devicesSize = devicesCount * UInt32(MemoryLayout.size) + + AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &propertyAddress, 0, nil, &devicesSize, &devices) + + return devices + } +} diff --git a/MultiSoundChanger/Sources/Stories/Main/Base.lproj/Main.storyboard b/MultiSoundChanger/Sources/Stories/Main/Base.lproj/Main.storyboard new file mode 100644 index 0000000..926ecba --- /dev/null +++ b/MultiSoundChanger/Sources/Stories/Main/Base.lproj/Main.storyboard @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MultiSoundChanger/Sources/Stories/Stories.swift b/MultiSoundChanger/Sources/Stories/Stories.swift new file mode 100644 index 0000000..83a0bc3 --- /dev/null +++ b/MultiSoundChanger/Sources/Stories/Stories.swift @@ -0,0 +1,28 @@ +// +// Stories.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 15.11.2020. +// Copyright © 2020 Dmitry Medyuho. All rights reserved. +// + +import Cocoa + +enum Stories: String { + case main = "Main" + case volume = "Volume" +} + +extension Stories { + func controller(_ classType: T.Type) -> T { + let storyboard = NSStoryboard(name: rawValue, bundle: nil) + let identifier = String(describing: classType) + + guard let controller = storyboard.instantiateController(withIdentifier: identifier) as? T else { + Logger.error(Constants.InnerMessages.controllerIdentifierError) + fatalError(Constants.InnerMessages.controllerIdentifierError) + } + + return controller + } +} diff --git a/MultiSoundChanger/Sources/Stories/Volume/Volume.storyboard b/MultiSoundChanger/Sources/Stories/Volume/Volume.storyboard new file mode 100644 index 0000000..be6283b --- /dev/null +++ b/MultiSoundChanger/Sources/Stories/Volume/Volume.storyboard @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MultiSoundChanger/Sources/Stories/Volume/VolumeViewController.swift b/MultiSoundChanger/Sources/Stories/Volume/VolumeViewController.swift new file mode 100644 index 0000000..e1861c7 --- /dev/null +++ b/MultiSoundChanger/Sources/Stories/Volume/VolumeViewController.swift @@ -0,0 +1,32 @@ +// +// ViewController.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 02.04.17. +// Copyright © 2017 Dmitry Medyuho. All rights reserved. +// + +import AudioToolbox +import Cocoa +import MediaKeyTap + +final class VolumeViewController: NSViewController { + @IBOutlet weak var volumeSlider: NSSlider! + private var muted: Bool = false + + weak var statusBarController: StatusBarController? + var audioManager: AudioManager? + + private func changeDeviceVolume(value: Float) { + audioManager?.setSelectedDeviceVolume(masterChannelLevel: value, leftChannelLevel: value, rightChannelLevel: value) + } + + func updateSliderVolume(volume: Float) { + volumeSlider.floatValue = volume.clamped(to: 0...100) + } + + @IBAction func volumeSliderAction(_ sender: Any) { + changeDeviceVolume(value: volumeSlider.floatValue / 100) + statusBarController?.changeStatusItemImage(value: volumeSlider.floatValue) + } +} diff --git a/MultiSoundChanger/Sources/Utils/Logger.swift b/MultiSoundChanger/Sources/Utils/Logger.swift new file mode 100644 index 0000000..b234053 --- /dev/null +++ b/MultiSoundChanger/Sources/Utils/Logger.swift @@ -0,0 +1,136 @@ +// +// Logger.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 20.04.21. +// Copyright © 2021 Dmitry Medyuho. All rights reserved. +// + +import Foundation + +enum Logger { + private enum DebugSymbol: String { + case info = "🔵" + case debug = "🟢" + case warning = "🟠" + case error = "🔴" + } + + private enum Symbol: String { + case newLine = "\n" + } + + private enum LoggerError: Error { + case fileError(String) + case dataError + } + + private static var isLogFileRemoved = false + + private static var bundleIdentifier: String { + guard let bundleIdentifier = Bundle.main.bundleIdentifier else { + outPrint(symbol: .error, string: Constants.InnerMessages.bundleIdentifierError) + fatalError(Constants.InnerMessages.bundleIdentifierError) + } + return bundleIdentifier + } + + static func info(_ string: String) { + outAndFilePrint(symbol: .info, string: string) + } + + static func debug(_ string: String) { + outAndFilePrint(symbol: .debug, string: string) + } + + static func warning(_ string: String) { + outAndFilePrint(symbol: .warning, string: string) + } + + static func error(_ string: String) { + outAndFilePrint(symbol: .error, string: string) + } + + private static func getDebugLine(symbol: DebugSymbol, string: String) -> String { + let symbol = DebugSymbol.info.rawValue + let logDate = getLogDate() + return "\(symbol) [\(logDate)] \(string)" + } + + private static func outAndFilePrint(symbol: DebugSymbol, string: String) { + outPrint(symbol: .error, string: string) + do { + try filePrint(symbol: .info, string: string) + } catch let error { + outPrint(symbol: .error, string: error.localizedDescription) + } + } + + private static func outPrint(symbol: DebugSymbol, string: String) { + let line = getDebugLine(symbol: symbol, string: string) + print(line) + } + + private static func filePrint(symbol: DebugSymbol, string: String, filename: String = Constants.logFilename) throws { + do { + var directoryUrl = try FileManager.default.url( + for: .cachesDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true + ) + directoryUrl.appendPathComponent(bundleIdentifier) + try createDirectoryIfNeeded(url: directoryUrl) + let fileUrl = directoryUrl.appendingPathComponent(Constants.logFilename, isDirectory: false) + let line = wrapNewLine(getDebugLine(symbol: symbol, string: string)) + try removeLogFileIfNeeded(url: fileUrl) + try appendToFile(url: fileUrl, content: line) + } catch let error { + throw LoggerError.fileError(error.localizedDescription) + } + } + + private static func appendToFile(url: URL, content: String) throws { + if FileManager.default.fileExists(atPath: url.path) { + let fileHandle = try FileHandle(forWritingTo: url) + guard let data = content.data(using: .utf8) else { + throw LoggerError.dataError + } + fileHandle.seekToEndOfFile() + fileHandle.write(data) + fileHandle.closeFile() + } else { + try content.write(to: url, atomically: true, encoding: .utf8) + } + } + + private static func createDirectoryIfNeeded(url: URL) throws { + guard !FileManager.default.fileExists(atPath: url.path) else { + return + } + try FileManager.default.createDirectory(at: url, withIntermediateDirectories: false, attributes: nil) + } + + private static func removeLogFileIfNeeded(url: URL) throws { + guard !isLogFileRemoved else { + return + } + isLogFileRemoved = true + guard FileManager.default.fileExists(atPath: url.path) else { + return + } + try FileManager.default.removeItem(at: url) + } + + private static func wrapNewLine(_ string: String) -> String { + return string + Symbol.newLine.rawValue + } + + private static func getLogDate() -> String { + let date = Date() + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .medium + return formatter.string(from: date) + } +} diff --git a/MultiSoundChanger/Sources/Utils/Runner.swift b/MultiSoundChanger/Sources/Utils/Runner.swift new file mode 100644 index 0000000..2bf1fda --- /dev/null +++ b/MultiSoundChanger/Sources/Utils/Runner.swift @@ -0,0 +1,40 @@ +// +// Runner.swift +// MultiSoundChanger +// +// Created by Dmitry Medyuho on 22.04.21. +// Copyright © 2021 Dmitry Medyuho. All rights reserved. +// + +import Cocoa + +enum Runner { + @discardableResult + static func shell(_ command: String) -> String? { + let task = Process() + let pipe = Pipe() + + task.standardOutput = pipe + task.standardError = pipe + task.arguments = ["-c", command] + task.launchPath = "/bin/sh" + task.launch() + + let data = pipe.fileHandleForReading.readDataToEndOfFile() + + guard let output = String(data: data, encoding: .utf8) else { + return nil + } + + return output + } + + static func launchApplication(bundleIndentifier: String, options: NSWorkspace.LaunchOptions) { + NSWorkspace.shared.launchApplication( + withBundleIdentifier: bundleIndentifier, + options: options, + additionalEventParamDescriptor: nil, + launchIdentifier: nil + ) + } +} diff --git a/OSD.framework/Headers/OSDManager.h b/OSD.framework/Headers/OSDManager.h new file mode 100644 index 0000000..255db3f --- /dev/null +++ b/OSD.framework/Headers/OSDManager.h @@ -0,0 +1,37 @@ +#import "OSDUIHelperProtocol.h" + +@class NSXPCConnection; + +@interface OSDManager : NSObject +{ + id _proxyObject; + NSXPCConnection *connection; +} + ++ (id)sharedManager; +@property(retain) NSXPCConnection *connection; // @synthesize connection; +- (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4; +- (void)fadeClassicImageOnDisplay:(unsigned int)arg1; +- (void)showImageAtPath:(id)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4; +@property(readonly) id remoteObjectProxy; // @dynamic remoteObjectProxy; + +typedef enum { + OSDGraphicBacklight = 1, // 1, 2, 7, 8 + OSDGraphicSpeaker = 3, // 3, 5, 17, 23 + OSDGraphicSpeakerMuted = 4, // 4, 16, 21, 22 + OSDGraphicEject = 6, + OSDGraphicNoWiFi = 9, + OSDGraphicKeyboardBacklightMeter = 11, // 11, 25 + OSDGraphicKeyboardBacklightDisabledMeter = 12, // 12, 26 + OSDGraphicKeyboardBacklightNotConnected = 13, // 13, 27 + OSDGraphicKeyboardBacklightDisabledNotConnected = 14, // 14, 28 + OSDGraphicMacProOpen = 15, + OSDGraphicHotspot = 19, + OSDGraphicSleep = 20, + // There may be more +} OSDGraphic; + +@end diff --git a/OSD.framework/Headers/OSDUIHelperProtocol.h b/OSD.framework/Headers/OSDUIHelperProtocol.h new file mode 100644 index 0000000..a1246c0 --- /dev/null +++ b/OSD.framework/Headers/OSDUIHelperProtocol.h @@ -0,0 +1,11 @@ +@class NSString; + +@protocol OSDUIHelperProtocol +- (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4; +- (void)fadeClassicImageOnDisplay:(unsigned int)arg1; +- (void)showImageAtPath:(NSString *)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4; +@end + diff --git a/OSD.framework/OSD b/OSD.framework/OSD new file mode 120000 index 0000000..ee6bfa0 --- /dev/null +++ b/OSD.framework/OSD @@ -0,0 +1 @@ +Versions/Current/OSD \ No newline at end of file diff --git a/OSD.framework/Resources b/OSD.framework/Resources new file mode 120000 index 0000000..953ee36 --- /dev/null +++ b/OSD.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/OSD.framework/Versions/A/.DS_Store b/OSD.framework/Versions/A/.DS_Store new file mode 100644 index 0000000..82fca9e Binary files /dev/null and b/OSD.framework/Versions/A/.DS_Store differ diff --git a/OSD.framework/Versions/A/OSD b/OSD.framework/Versions/A/OSD new file mode 100755 index 0000000..58813ae Binary files /dev/null and b/OSD.framework/Versions/A/OSD differ diff --git a/OSD.framework/Versions/A/Resources/Info.plist b/OSD.framework/Versions/A/Resources/Info.plist new file mode 100644 index 0000000..824b8c0 --- /dev/null +++ b/OSD.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,46 @@ + + + + + BuildMachineOSBuild + 16B2657 + CFBundleDevelopmentRegion + en + CFBundleExecutable + OSD + CFBundleIdentifier + com.apple.OSD + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + OSD + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 9L173x + DTPlatformVersion + GM + DTSDKBuild + 17A317 + DTSDKName + macosx10.13internal + DTXcode + 0900 + DTXcodeBuild + 9L173x + NSHumanReadableCopyright + Copyright © 2015 Apple Inc. All rights reserved. + + diff --git a/OSD.framework/Versions/A/Resources/version.plist b/OSD.framework/Versions/A/Resources/version.plist new file mode 100644 index 0000000..a387863 --- /dev/null +++ b/OSD.framework/Versions/A/Resources/version.plist @@ -0,0 +1,18 @@ + + + + + BuildAliasOf + OSDFramework + BuildVersion + 487 + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + ProjectName + OSDFramework + SourceVersion + 27000000000000 + + diff --git a/OSD.framework/Versions/A/_CodeSignature/CodeResources b/OSD.framework/Versions/A/_CodeSignature/CodeResources new file mode 100644 index 0000000..cd4347d --- /dev/null +++ b/OSD.framework/Versions/A/_CodeSignature/CodeResources @@ -0,0 +1,139 @@ + + + + + files + + Resources/Info.plist + + bTy7OXKIr2tY7ToPw28ekz1xUXU= + + Resources/version.plist + + d0I/dBV8v16urCBanZt9RaZvG1E= + + + files2 + + Resources/Info.plist + + hash2 + + uEmRq0D23jBsIWK+0+UH3bCcn16eQdAwKWglrQbTfQc= + + + Resources/version.plist + + hash2 + + f4xR2tymy1G7xEyxX1+yXJmSgOrrndsypu67avrQ8Ss= + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/OSD.framework/Versions/Current b/OSD.framework/Versions/Current new file mode 120000 index 0000000..8c7e5a6 --- /dev/null +++ b/OSD.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/OSD.framework/XPCServices b/OSD.framework/XPCServices new file mode 120000 index 0000000..99c46ea --- /dev/null +++ b/OSD.framework/XPCServices @@ -0,0 +1 @@ +Versions/Current/XPCServices \ No newline at end of file diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..001b53d --- /dev/null +++ b/Podfile @@ -0,0 +1,7 @@ +target 'MultiSoundChanger' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + pod 'SwiftLint' + pod 'MediaKeyTap', :git => 'https://github.com/the0neyouseek/MediaKeyTap.git', :branch => 'master' +end