├── .gitignore ├── LICENSE ├── NoiseBuddy.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── NoiseBuddy.xcscheme │ └── NoiseCore.xcscheme ├── NoiseBuddy ├── Bootstrap │ └── AppDelegate.swift ├── Controllers │ ├── KeyboardShortcutController.swift │ ├── MenuBarUIController.swift │ ├── NoiseControlUIController.swift │ ├── PreferencesViewController.swift │ └── TouchBarUIController.swift ├── Extensions │ └── UserDefaults+Codable.swift ├── Models │ └── KeyBoardShortcut.swift ├── Resources │ ├── Assets.xcassets │ │ ├── AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation-menu.imageset │ │ │ ├── AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.pdf │ │ │ └── Contents.json │ │ ├── AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.imageset │ │ │ ├── AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.pdf │ │ │ └── Contents.json │ │ ├── AVOutputDeviceBluetoothListeningModeAudioTransparency-menu.imageset │ │ │ ├── AVOutputDeviceBluetoothListeningModeAudioTransparency.pdf │ │ │ └── Contents.json │ │ ├── AVOutputDeviceBluetoothListeningModeAudioTransparency.imageset │ │ │ ├── AVOutputDeviceBluetoothListeningModeAudioTransparency.pdf │ │ │ └── Contents.json │ │ ├── AVOutputDeviceBluetoothListeningModeNormal-menu.imageset │ │ │ ├── AVOutputDeviceBluetoothListeningModeNormal.pdf │ │ │ └── Contents.json │ │ ├── AVOutputDeviceBluetoothListeningModeNormal.imageset │ │ │ ├── AVOutputDeviceBluetoothListeningModeNormal.pdf │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_128x128.png │ │ │ ├── icon_128x128@2x.png │ │ │ ├── icon_16x16.png │ │ │ ├── icon_16x16@2x.png │ │ │ ├── icon_256x256.png │ │ │ ├── icon_256x256@2x.png │ │ │ ├── icon_32x32.png │ │ │ ├── icon_32x32@2x.png │ │ │ ├── icon_512x512.png │ │ │ └── icon_512x512@2x.png │ │ └── Contents.json │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Credits.rtf │ ├── Info.plist │ ├── NoiseBuddy.entitlements │ └── Preferences.storyboard ├── Support │ ├── BridgingHeader.h │ ├── MockListeningModeController.swift │ ├── NSTouchBar-Private.h │ ├── Preferences.swift │ ├── SharedFileList.h │ └── SharedFileList.m └── Views │ └── ActionLabel.swift ├── NoiseCore ├── Info.plist ├── NoiseCore.h └── Source │ ├── Models │ ├── NCDevice-Private.h │ ├── NCDevice.h │ ├── NCDevice.m │ └── NCListeningMode.swift │ ├── NCAVListeningModeController.h │ ├── NCAVListeningModeController.m │ ├── NCBTListeningModeController.swift │ └── Support │ ├── AVFoundation-Private.h │ ├── IOBluetooth-Private.h │ ├── fishhook.c │ └── fishhook.h ├── README.md └── screenshots ├── NoiseBuddyIcon.png ├── screenshot-menubar.png └── screenshot-touchbar.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | __MACOSX 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | DerivedData 17 | .idea/ 18 | Crashlytics.sh 19 | generatechangelog.sh 20 | Pods/ 21 | Carthage 22 | Provisioning 23 | Crashlytics.sh 24 | Sharing.h 25 | Tests/Private 26 | Design/Icon -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Guilherme Rambo 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | - Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /NoiseBuddy.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 01A27F172385A94E00F7C0CC /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = 01A27F162385A94E00F7C0CC /* HotKey */; }; 11 | 01A27F1C2385BC9600F7C0CC /* KeyboardShortcutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A27F1B2385BC9500F7C0CC /* KeyboardShortcutController.swift */; }; 12 | 01A27F1F2386F14200F7C0CC /* UserDefaults+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A27F1E2386F14200F7C0CC /* UserDefaults+Codable.swift */; }; 13 | 01A27F222386F16C00F7C0CC /* KeyBoardShortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A27F212386F16C00F7C0CC /* KeyBoardShortcut.swift */; }; 14 | 01C57A052384C9ED003B0934 /* NoiseCore.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = DD41F981237C911A00A1B976 /* NoiseCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | DD41F95F237C8D5800A1B976 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41F95E237C8D5800A1B976 /* AppDelegate.swift */; }; 16 | DD41F963237C8D5C00A1B976 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD41F962237C8D5C00A1B976 /* Assets.xcassets */; }; 17 | DD41F966237C8D5C00A1B976 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DD41F964237C8D5C00A1B976 /* Main.storyboard */; }; 18 | DD41F985237C911A00A1B976 /* NoiseCore.h in Headers */ = {isa = PBXBuildFile; fileRef = DD41F983237C911A00A1B976 /* NoiseCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | DD41F990237C913000A1B976 /* AVFoundation-Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DD41F98F237C913000A1B976 /* AVFoundation-Private.h */; }; 20 | DD41F993237C913E00A1B976 /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = DD41F991237C913E00A1B976 /* fishhook.c */; }; 21 | DD41F994237C913E00A1B976 /* fishhook.h in Headers */ = {isa = PBXBuildFile; fileRef = DD41F992237C913E00A1B976 /* fishhook.h */; }; 22 | DD41F997237C915900A1B976 /* NCAVListeningModeController.h in Headers */ = {isa = PBXBuildFile; fileRef = DD41F995237C915900A1B976 /* NCAVListeningModeController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 | DD41F998237C915900A1B976 /* NCAVListeningModeController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD41F996237C915900A1B976 /* NCAVListeningModeController.m */; }; 24 | DD41F99B237C928400A1B976 /* NCDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = DD41F999237C928400A1B976 /* NCDevice.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25 | DD41F99C237C928400A1B976 /* NCDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = DD41F99A237C928400A1B976 /* NCDevice.m */; }; 26 | DD41F99E237C98B900A1B976 /* NCListeningMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41F99D237C98B900A1B976 /* NCListeningMode.swift */; }; 27 | DD41F9A7237C9D8100A1B976 /* TouchBarUIController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41F9A6237C9D8100A1B976 /* TouchBarUIController.swift */; }; 28 | DD50DC44237CF43F00F5AEB3 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = DD50DC43237CF43F00F5AEB3 /* Credits.rtf */; }; 29 | DD50DC46237D91BE00F5AEB3 /* NCBTListeningModeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD50DC45237D91BE00F5AEB3 /* NCBTListeningModeController.swift */; }; 30 | DD50DC4A237D988400F5AEB3 /* IOBluetooth-Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DD50DC49237D988100F5AEB3 /* IOBluetooth-Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; 31 | DDE6D814237CA05D009BE951 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE6D813237CA05D009BE951 /* Preferences.swift */; }; 32 | DDE6D816237CA5E2009BE951 /* NCDevice-Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DDE6D815237CA5E2009BE951 /* NCDevice-Private.h */; }; 33 | DDE6D81A237CBC8A009BE951 /* MockListeningModeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE6D819237CBC8A009BE951 /* MockListeningModeController.swift */; }; 34 | DDE6D81C237CDF56009BE951 /* NoiseControlUIController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE6D81B237CDF56009BE951 /* NoiseControlUIController.swift */; }; 35 | DDE6D81E237CE18D009BE951 /* MenuBarUIController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE6D81D237CE18D009BE951 /* MenuBarUIController.swift */; }; 36 | DDE6D820237CE88F009BE951 /* Preferences.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DDE6D81F237CE88F009BE951 /* Preferences.storyboard */; }; 37 | DDE6D823237CE981009BE951 /* ActionLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE6D822237CE981009BE951 /* ActionLabel.swift */; }; 38 | DDE6D825237CEA6C009BE951 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE6D824237CEA6C009BE951 /* PreferencesViewController.swift */; }; 39 | DDE6D82C237CED20009BE951 /* SharedFileList.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE6D82A237CED20009BE951 /* SharedFileList.m */; }; 40 | /* End PBXBuildFile section */ 41 | 42 | /* Begin PBXCopyFilesBuildPhase section */ 43 | 01C57A042384C9E5003B0934 /* CopyFiles */ = { 44 | isa = PBXCopyFilesBuildPhase; 45 | buildActionMask = 12; 46 | dstPath = ""; 47 | dstSubfolderSpec = 10; 48 | files = ( 49 | 01C57A052384C9ED003B0934 /* NoiseCore.framework in CopyFiles */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | DD41F98D237C911A00A1B976 /* Embed Frameworks */ = { 54 | isa = PBXCopyFilesBuildPhase; 55 | buildActionMask = 2147483647; 56 | dstPath = ""; 57 | dstSubfolderSpec = 10; 58 | files = ( 59 | ); 60 | name = "Embed Frameworks"; 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXCopyFilesBuildPhase section */ 64 | 65 | /* Begin PBXFileReference section */ 66 | 01A27F1B2385BC9500F7C0CC /* KeyboardShortcutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcutController.swift; sourceTree = ""; }; 67 | 01A27F1E2386F14200F7C0CC /* UserDefaults+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Codable.swift"; sourceTree = ""; }; 68 | 01A27F212386F16C00F7C0CC /* KeyBoardShortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBoardShortcut.swift; sourceTree = ""; }; 69 | DD41F95B237C8D5800A1B976 /* NoiseBuddy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NoiseBuddy.app; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | DD41F95E237C8D5800A1B976 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 71 | DD41F962237C8D5C00A1B976 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 72 | DD41F965237C8D5C00A1B976 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 73 | DD41F967237C8D5C00A1B976 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 74 | DD41F968237C8D5C00A1B976 /* NoiseBuddy.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NoiseBuddy.entitlements; sourceTree = ""; }; 75 | DD41F981237C911A00A1B976 /* NoiseCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NoiseCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | DD41F983237C911A00A1B976 /* NoiseCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NoiseCore.h; sourceTree = ""; }; 77 | DD41F984237C911A00A1B976 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 78 | DD41F98F237C913000A1B976 /* AVFoundation-Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AVFoundation-Private.h"; sourceTree = ""; }; 79 | DD41F991237C913E00A1B976 /* fishhook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fishhook.c; sourceTree = ""; }; 80 | DD41F992237C913E00A1B976 /* fishhook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fishhook.h; sourceTree = ""; }; 81 | DD41F995237C915900A1B976 /* NCAVListeningModeController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NCAVListeningModeController.h; sourceTree = ""; }; 82 | DD41F996237C915900A1B976 /* NCAVListeningModeController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NCAVListeningModeController.m; sourceTree = ""; }; 83 | DD41F999237C928400A1B976 /* NCDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NCDevice.h; sourceTree = ""; }; 84 | DD41F99A237C928400A1B976 /* NCDevice.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NCDevice.m; sourceTree = ""; }; 85 | DD41F99D237C98B900A1B976 /* NCListeningMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCListeningMode.swift; sourceTree = ""; }; 86 | DD41F9A4237C9D0600A1B976 /* NSTouchBar-Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSTouchBar-Private.h"; sourceTree = ""; }; 87 | DD41F9A5237C9D2900A1B976 /* BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = ""; }; 88 | DD41F9A6237C9D8100A1B976 /* TouchBarUIController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarUIController.swift; sourceTree = ""; }; 89 | DD50DC43237CF43F00F5AEB3 /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; 90 | DD50DC45237D91BE00F5AEB3 /* NCBTListeningModeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCBTListeningModeController.swift; sourceTree = ""; }; 91 | DD50DC49237D988100F5AEB3 /* IOBluetooth-Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "IOBluetooth-Private.h"; sourceTree = ""; }; 92 | DDE6D813237CA05D009BE951 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; 93 | DDE6D815237CA5E2009BE951 /* NCDevice-Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NCDevice-Private.h"; sourceTree = ""; }; 94 | DDE6D819237CBC8A009BE951 /* MockListeningModeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockListeningModeController.swift; sourceTree = ""; }; 95 | DDE6D81B237CDF56009BE951 /* NoiseControlUIController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseControlUIController.swift; sourceTree = ""; }; 96 | DDE6D81D237CE18D009BE951 /* MenuBarUIController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarUIController.swift; sourceTree = ""; }; 97 | DDE6D81F237CE88F009BE951 /* Preferences.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Preferences.storyboard; sourceTree = ""; }; 98 | DDE6D822237CE981009BE951 /* ActionLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionLabel.swift; sourceTree = ""; }; 99 | DDE6D824237CEA6C009BE951 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = ""; }; 100 | DDE6D828237CED20009BE951 /* SharedFileList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SharedFileList.h; sourceTree = ""; }; 101 | DDE6D82A237CED20009BE951 /* SharedFileList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SharedFileList.m; sourceTree = ""; }; 102 | /* End PBXFileReference section */ 103 | 104 | /* Begin PBXFrameworksBuildPhase section */ 105 | DD41F958237C8D5800A1B976 /* Frameworks */ = { 106 | isa = PBXFrameworksBuildPhase; 107 | buildActionMask = 2147483647; 108 | files = ( 109 | 01A27F172385A94E00F7C0CC /* HotKey in Frameworks */, 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | DD41F97E237C911A00A1B976 /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | ); 118 | runOnlyForDeploymentPostprocessing = 0; 119 | }; 120 | /* End PBXFrameworksBuildPhase section */ 121 | 122 | /* Begin PBXGroup section */ 123 | 01A27F1D2386F11E00F7C0CC /* Extensions */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 01A27F1E2386F14200F7C0CC /* UserDefaults+Codable.swift */, 127 | ); 128 | path = Extensions; 129 | sourceTree = ""; 130 | }; 131 | 01A27F202386F16100F7C0CC /* Models */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 01A27F212386F16C00F7C0CC /* KeyBoardShortcut.swift */, 135 | ); 136 | path = Models; 137 | sourceTree = ""; 138 | }; 139 | DD41F952237C8D5800A1B976 = { 140 | isa = PBXGroup; 141 | children = ( 142 | DD41F95D237C8D5800A1B976 /* NoiseBuddy */, 143 | DD41F982237C911A00A1B976 /* NoiseCore */, 144 | DD41F95C237C8D5800A1B976 /* Products */, 145 | ); 146 | sourceTree = ""; 147 | }; 148 | DD41F95C237C8D5800A1B976 /* Products */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | DD41F95B237C8D5800A1B976 /* NoiseBuddy.app */, 152 | DD41F981237C911A00A1B976 /* NoiseCore.framework */, 153 | ); 154 | name = Products; 155 | sourceTree = ""; 156 | }; 157 | DD41F95D237C8D5800A1B976 /* NoiseBuddy */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | DDE6D821237CE971009BE951 /* Views */, 161 | DD41F99F237C9AC500A1B976 /* Bootstrap */, 162 | DD41F9A0237C9CCE00A1B976 /* Resources */, 163 | DD41F9A2237C9CF400A1B976 /* Support */, 164 | 01A27F202386F16100F7C0CC /* Models */, 165 | 01A27F1D2386F11E00F7C0CC /* Extensions */, 166 | DD41F9A1237C9CEE00A1B976 /* Controllers */, 167 | ); 168 | path = NoiseBuddy; 169 | sourceTree = ""; 170 | }; 171 | DD41F982237C911A00A1B976 /* NoiseCore */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | DD41F98E237C911F00A1B976 /* Source */, 175 | DD41F983237C911A00A1B976 /* NoiseCore.h */, 176 | DD41F984237C911A00A1B976 /* Info.plist */, 177 | ); 178 | path = NoiseCore; 179 | sourceTree = ""; 180 | }; 181 | DD41F98E237C911F00A1B976 /* Source */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | DDE6D818237CBAF3009BE951 /* Models */, 185 | DDE6D817237CBAEB009BE951 /* Support */, 186 | DD41F995237C915900A1B976 /* NCAVListeningModeController.h */, 187 | DD41F996237C915900A1B976 /* NCAVListeningModeController.m */, 188 | DD50DC45237D91BE00F5AEB3 /* NCBTListeningModeController.swift */, 189 | ); 190 | path = Source; 191 | sourceTree = ""; 192 | }; 193 | DD41F99F237C9AC500A1B976 /* Bootstrap */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | DD41F95E237C8D5800A1B976 /* AppDelegate.swift */, 197 | ); 198 | path = Bootstrap; 199 | sourceTree = ""; 200 | }; 201 | DD41F9A0237C9CCE00A1B976 /* Resources */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | DD41F962237C8D5C00A1B976 /* Assets.xcassets */, 205 | DD41F964237C8D5C00A1B976 /* Main.storyboard */, 206 | DDE6D81F237CE88F009BE951 /* Preferences.storyboard */, 207 | DD41F967237C8D5C00A1B976 /* Info.plist */, 208 | DD41F968237C8D5C00A1B976 /* NoiseBuddy.entitlements */, 209 | DD50DC43237CF43F00F5AEB3 /* Credits.rtf */, 210 | ); 211 | path = Resources; 212 | sourceTree = ""; 213 | }; 214 | DD41F9A1237C9CEE00A1B976 /* Controllers */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | DDE6D81B237CDF56009BE951 /* NoiseControlUIController.swift */, 218 | DD41F9A6237C9D8100A1B976 /* TouchBarUIController.swift */, 219 | DDE6D81D237CE18D009BE951 /* MenuBarUIController.swift */, 220 | 01A27F1B2385BC9500F7C0CC /* KeyboardShortcutController.swift */, 221 | DDE6D824237CEA6C009BE951 /* PreferencesViewController.swift */, 222 | ); 223 | path = Controllers; 224 | sourceTree = ""; 225 | }; 226 | DD41F9A2237C9CF400A1B976 /* Support */ = { 227 | isa = PBXGroup; 228 | children = ( 229 | DD41F9A5237C9D2900A1B976 /* BridgingHeader.h */, 230 | DDE6D828237CED20009BE951 /* SharedFileList.h */, 231 | DDE6D82A237CED20009BE951 /* SharedFileList.m */, 232 | DDE6D813237CA05D009BE951 /* Preferences.swift */, 233 | DD41F9A4237C9D0600A1B976 /* NSTouchBar-Private.h */, 234 | DDE6D819237CBC8A009BE951 /* MockListeningModeController.swift */, 235 | ); 236 | path = Support; 237 | sourceTree = ""; 238 | }; 239 | DDE6D817237CBAEB009BE951 /* Support */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | DD41F991237C913E00A1B976 /* fishhook.c */, 243 | DD41F992237C913E00A1B976 /* fishhook.h */, 244 | DD41F98F237C913000A1B976 /* AVFoundation-Private.h */, 245 | DD50DC49237D988100F5AEB3 /* IOBluetooth-Private.h */, 246 | ); 247 | path = Support; 248 | sourceTree = ""; 249 | }; 250 | DDE6D818237CBAF3009BE951 /* Models */ = { 251 | isa = PBXGroup; 252 | children = ( 253 | DD41F999237C928400A1B976 /* NCDevice.h */, 254 | DDE6D815237CA5E2009BE951 /* NCDevice-Private.h */, 255 | DD41F99A237C928400A1B976 /* NCDevice.m */, 256 | DD41F99D237C98B900A1B976 /* NCListeningMode.swift */, 257 | ); 258 | path = Models; 259 | sourceTree = ""; 260 | }; 261 | DDE6D821237CE971009BE951 /* Views */ = { 262 | isa = PBXGroup; 263 | children = ( 264 | DDE6D822237CE981009BE951 /* ActionLabel.swift */, 265 | ); 266 | path = Views; 267 | sourceTree = ""; 268 | }; 269 | /* End PBXGroup section */ 270 | 271 | /* Begin PBXHeadersBuildPhase section */ 272 | DD41F97C237C911A00A1B976 /* Headers */ = { 273 | isa = PBXHeadersBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | DD41F997237C915900A1B976 /* NCAVListeningModeController.h in Headers */, 277 | DD50DC4A237D988400F5AEB3 /* IOBluetooth-Private.h in Headers */, 278 | DD41F990237C913000A1B976 /* AVFoundation-Private.h in Headers */, 279 | DD41F985237C911A00A1B976 /* NoiseCore.h in Headers */, 280 | DD41F99B237C928400A1B976 /* NCDevice.h in Headers */, 281 | DD41F994237C913E00A1B976 /* fishhook.h in Headers */, 282 | DDE6D816237CA5E2009BE951 /* NCDevice-Private.h in Headers */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | /* End PBXHeadersBuildPhase section */ 287 | 288 | /* Begin PBXNativeTarget section */ 289 | DD41F95A237C8D5800A1B976 /* NoiseBuddy */ = { 290 | isa = PBXNativeTarget; 291 | buildConfigurationList = DD41F96B237C8D5C00A1B976 /* Build configuration list for PBXNativeTarget "NoiseBuddy" */; 292 | buildPhases = ( 293 | DD41F957237C8D5800A1B976 /* Sources */, 294 | DD41F958237C8D5800A1B976 /* Frameworks */, 295 | DD41F959237C8D5800A1B976 /* Resources */, 296 | DD41F98D237C911A00A1B976 /* Embed Frameworks */, 297 | 01C57A042384C9E5003B0934 /* CopyFiles */, 298 | ); 299 | buildRules = ( 300 | ); 301 | dependencies = ( 302 | ); 303 | name = NoiseBuddy; 304 | packageProductDependencies = ( 305 | 01A27F162385A94E00F7C0CC /* HotKey */, 306 | ); 307 | productName = NoiseBuddy; 308 | productReference = DD41F95B237C8D5800A1B976 /* NoiseBuddy.app */; 309 | productType = "com.apple.product-type.application"; 310 | }; 311 | DD41F980237C911A00A1B976 /* NoiseCore */ = { 312 | isa = PBXNativeTarget; 313 | buildConfigurationList = DD41F98A237C911A00A1B976 /* Build configuration list for PBXNativeTarget "NoiseCore" */; 314 | buildPhases = ( 315 | DD41F97C237C911A00A1B976 /* Headers */, 316 | DD41F97D237C911A00A1B976 /* Sources */, 317 | DD41F97E237C911A00A1B976 /* Frameworks */, 318 | DD41F97F237C911A00A1B976 /* Resources */, 319 | ); 320 | buildRules = ( 321 | ); 322 | dependencies = ( 323 | ); 324 | name = NoiseCore; 325 | productName = NoiseCore; 326 | productReference = DD41F981237C911A00A1B976 /* NoiseCore.framework */; 327 | productType = "com.apple.product-type.framework"; 328 | }; 329 | /* End PBXNativeTarget section */ 330 | 331 | /* Begin PBXProject section */ 332 | DD41F953237C8D5800A1B976 /* Project object */ = { 333 | isa = PBXProject; 334 | attributes = { 335 | LastSwiftUpdateCheck = 1120; 336 | LastUpgradeCheck = 1120; 337 | ORGANIZATIONNAME = "Guilherme Rambo"; 338 | TargetAttributes = { 339 | DD41F95A237C8D5800A1B976 = { 340 | CreatedOnToolsVersion = 11.2.1; 341 | }; 342 | DD41F980237C911A00A1B976 = { 343 | CreatedOnToolsVersion = 11.2.1; 344 | LastSwiftMigration = 1120; 345 | }; 346 | }; 347 | }; 348 | buildConfigurationList = DD41F956237C8D5800A1B976 /* Build configuration list for PBXProject "NoiseBuddy" */; 349 | compatibilityVersion = "Xcode 9.3"; 350 | developmentRegion = en; 351 | hasScannedForEncodings = 0; 352 | knownRegions = ( 353 | en, 354 | Base, 355 | ); 356 | mainGroup = DD41F952237C8D5800A1B976; 357 | packageReferences = ( 358 | 01A27F152385A94E00F7C0CC /* XCRemoteSwiftPackageReference "HotKey" */, 359 | ); 360 | productRefGroup = DD41F95C237C8D5800A1B976 /* Products */; 361 | projectDirPath = ""; 362 | projectRoot = ""; 363 | targets = ( 364 | DD41F95A237C8D5800A1B976 /* NoiseBuddy */, 365 | DD41F980237C911A00A1B976 /* NoiseCore */, 366 | ); 367 | }; 368 | /* End PBXProject section */ 369 | 370 | /* Begin PBXResourcesBuildPhase section */ 371 | DD41F959237C8D5800A1B976 /* Resources */ = { 372 | isa = PBXResourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | DDE6D820237CE88F009BE951 /* Preferences.storyboard in Resources */, 376 | DD41F963237C8D5C00A1B976 /* Assets.xcassets in Resources */, 377 | DD41F966237C8D5C00A1B976 /* Main.storyboard in Resources */, 378 | DD50DC44237CF43F00F5AEB3 /* Credits.rtf in Resources */, 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | }; 382 | DD41F97F237C911A00A1B976 /* Resources */ = { 383 | isa = PBXResourcesBuildPhase; 384 | buildActionMask = 2147483647; 385 | files = ( 386 | ); 387 | runOnlyForDeploymentPostprocessing = 0; 388 | }; 389 | /* End PBXResourcesBuildPhase section */ 390 | 391 | /* Begin PBXSourcesBuildPhase section */ 392 | DD41F957237C8D5800A1B976 /* Sources */ = { 393 | isa = PBXSourcesBuildPhase; 394 | buildActionMask = 2147483647; 395 | files = ( 396 | 01A27F1C2385BC9600F7C0CC /* KeyboardShortcutController.swift in Sources */, 397 | DDE6D81C237CDF56009BE951 /* NoiseControlUIController.swift in Sources */, 398 | DDE6D82C237CED20009BE951 /* SharedFileList.m in Sources */, 399 | DDE6D81A237CBC8A009BE951 /* MockListeningModeController.swift in Sources */, 400 | DDE6D81E237CE18D009BE951 /* MenuBarUIController.swift in Sources */, 401 | 01A27F1F2386F14200F7C0CC /* UserDefaults+Codable.swift in Sources */, 402 | DDE6D825237CEA6C009BE951 /* PreferencesViewController.swift in Sources */, 403 | DD41F95F237C8D5800A1B976 /* AppDelegate.swift in Sources */, 404 | DDE6D814237CA05D009BE951 /* Preferences.swift in Sources */, 405 | DDE6D823237CE981009BE951 /* ActionLabel.swift in Sources */, 406 | 01A27F222386F16C00F7C0CC /* KeyBoardShortcut.swift in Sources */, 407 | DD41F9A7237C9D8100A1B976 /* TouchBarUIController.swift in Sources */, 408 | ); 409 | runOnlyForDeploymentPostprocessing = 0; 410 | }; 411 | DD41F97D237C911A00A1B976 /* Sources */ = { 412 | isa = PBXSourcesBuildPhase; 413 | buildActionMask = 2147483647; 414 | files = ( 415 | DD41F993237C913E00A1B976 /* fishhook.c in Sources */, 416 | DD41F99C237C928400A1B976 /* NCDevice.m in Sources */, 417 | DD41F998237C915900A1B976 /* NCAVListeningModeController.m in Sources */, 418 | DD50DC46237D91BE00F5AEB3 /* NCBTListeningModeController.swift in Sources */, 419 | DD41F99E237C98B900A1B976 /* NCListeningMode.swift in Sources */, 420 | ); 421 | runOnlyForDeploymentPostprocessing = 0; 422 | }; 423 | /* End PBXSourcesBuildPhase section */ 424 | 425 | /* Begin PBXVariantGroup section */ 426 | DD41F964237C8D5C00A1B976 /* Main.storyboard */ = { 427 | isa = PBXVariantGroup; 428 | children = ( 429 | DD41F965237C8D5C00A1B976 /* Base */, 430 | ); 431 | name = Main.storyboard; 432 | sourceTree = ""; 433 | }; 434 | /* End PBXVariantGroup section */ 435 | 436 | /* Begin XCBuildConfiguration section */ 437 | DD41F969237C8D5C00A1B976 /* Debug */ = { 438 | isa = XCBuildConfiguration; 439 | buildSettings = { 440 | ALWAYS_SEARCH_USER_PATHS = NO; 441 | CLANG_ANALYZER_NONNULL = YES; 442 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 443 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 444 | CLANG_CXX_LIBRARY = "libc++"; 445 | CLANG_ENABLE_MODULES = YES; 446 | CLANG_ENABLE_OBJC_ARC = YES; 447 | CLANG_ENABLE_OBJC_WEAK = YES; 448 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 449 | CLANG_WARN_BOOL_CONVERSION = YES; 450 | CLANG_WARN_COMMA = YES; 451 | CLANG_WARN_CONSTANT_CONVERSION = YES; 452 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 453 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 454 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 455 | CLANG_WARN_EMPTY_BODY = YES; 456 | CLANG_WARN_ENUM_CONVERSION = YES; 457 | CLANG_WARN_INFINITE_RECURSION = YES; 458 | CLANG_WARN_INT_CONVERSION = YES; 459 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 460 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 461 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 463 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 464 | CLANG_WARN_STRICT_PROTOTYPES = YES; 465 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 466 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 467 | CLANG_WARN_UNREACHABLE_CODE = YES; 468 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 469 | CODE_SIGN_IDENTITY = "Developer ID Application: GUILHERME RAMBO (8C7439RJLG)"; 470 | COPY_PHASE_STRIP = NO; 471 | DEBUG_INFORMATION_FORMAT = dwarf; 472 | ENABLE_STRICT_OBJC_MSGSEND = YES; 473 | ENABLE_TESTABILITY = YES; 474 | GCC_C_LANGUAGE_STANDARD = gnu11; 475 | GCC_DYNAMIC_NO_PIC = NO; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_OPTIMIZATION_LEVEL = 0; 478 | GCC_PREPROCESSOR_DEFINITIONS = ( 479 | "DEBUG=1", 480 | "$(inherited)", 481 | ); 482 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 483 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 484 | GCC_WARN_UNDECLARED_SELECTOR = YES; 485 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 486 | GCC_WARN_UNUSED_FUNCTION = YES; 487 | GCC_WARN_UNUSED_VARIABLE = YES; 488 | MACOSX_DEPLOYMENT_TARGET = 10.15; 489 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 490 | MTL_FAST_MATH = YES; 491 | ONLY_ACTIVE_ARCH = YES; 492 | SDKROOT = macosx; 493 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 494 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 495 | }; 496 | name = Debug; 497 | }; 498 | DD41F96A237C8D5C00A1B976 /* Release */ = { 499 | isa = XCBuildConfiguration; 500 | buildSettings = { 501 | ALWAYS_SEARCH_USER_PATHS = NO; 502 | CLANG_ANALYZER_NONNULL = YES; 503 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 504 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 505 | CLANG_CXX_LIBRARY = "libc++"; 506 | CLANG_ENABLE_MODULES = YES; 507 | CLANG_ENABLE_OBJC_ARC = YES; 508 | CLANG_ENABLE_OBJC_WEAK = YES; 509 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 510 | CLANG_WARN_BOOL_CONVERSION = YES; 511 | CLANG_WARN_COMMA = YES; 512 | CLANG_WARN_CONSTANT_CONVERSION = YES; 513 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 514 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 515 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 516 | CLANG_WARN_EMPTY_BODY = YES; 517 | CLANG_WARN_ENUM_CONVERSION = YES; 518 | CLANG_WARN_INFINITE_RECURSION = YES; 519 | CLANG_WARN_INT_CONVERSION = YES; 520 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 521 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 522 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 523 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 524 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 525 | CLANG_WARN_STRICT_PROTOTYPES = YES; 526 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 527 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 528 | CLANG_WARN_UNREACHABLE_CODE = YES; 529 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 530 | CODE_SIGN_IDENTITY = "Developer ID Application: GUILHERME RAMBO (8C7439RJLG)"; 531 | COPY_PHASE_STRIP = NO; 532 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 533 | ENABLE_NS_ASSERTIONS = NO; 534 | ENABLE_STRICT_OBJC_MSGSEND = YES; 535 | GCC_C_LANGUAGE_STANDARD = gnu11; 536 | GCC_NO_COMMON_BLOCKS = YES; 537 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 538 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 539 | GCC_WARN_UNDECLARED_SELECTOR = YES; 540 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 541 | GCC_WARN_UNUSED_FUNCTION = YES; 542 | GCC_WARN_UNUSED_VARIABLE = YES; 543 | MACOSX_DEPLOYMENT_TARGET = 10.15; 544 | MTL_ENABLE_DEBUG_INFO = NO; 545 | MTL_FAST_MATH = YES; 546 | SDKROOT = macosx; 547 | SWIFT_COMPILATION_MODE = wholemodule; 548 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 549 | }; 550 | name = Release; 551 | }; 552 | DD41F96C237C8D5C00A1B976 /* Debug */ = { 553 | isa = XCBuildConfiguration; 554 | buildSettings = { 555 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 556 | CODE_SIGN_ENTITLEMENTS = NoiseBuddy/Resources/NoiseBuddy.entitlements; 557 | CODE_SIGN_IDENTITY = "-"; 558 | CODE_SIGN_STYLE = Manual; 559 | COMBINE_HIDPI_IMAGES = YES; 560 | DEVELOPMENT_TEAM = ""; 561 | ENABLE_HARDENED_RUNTIME = YES; 562 | INFOPLIST_FILE = NoiseBuddy/Resources/Info.plist; 563 | LD_RUNPATH_SEARCH_PATHS = ( 564 | "$(inherited)", 565 | "@executable_path/../Frameworks", 566 | ); 567 | MARKETING_VERSION = 1.2; 568 | OTHER_LDFLAGS = ( 569 | "-framework", 570 | DFRFoundation, 571 | ); 572 | PRODUCT_BUNDLE_IDENTIFIER = codes.rambo.NoiseBuddy; 573 | PRODUCT_NAME = "$(TARGET_NAME)"; 574 | PROVISIONING_PROFILE_SPECIFIER = ""; 575 | SWIFT_OBJC_BRIDGING_HEADER = NoiseBuddy/Support/BridgingHeader.h; 576 | SWIFT_VERSION = 5.0; 577 | SYSTEM_FRAMEWORK_SEARCH_PATHS = ( 578 | "$(inherited)", 579 | /System/Library/PrivateFrameworks, 580 | ); 581 | }; 582 | name = Debug; 583 | }; 584 | DD41F96D237C8D5C00A1B976 /* Release */ = { 585 | isa = XCBuildConfiguration; 586 | buildSettings = { 587 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 588 | CODE_SIGN_ENTITLEMENTS = NoiseBuddy/Resources/NoiseBuddy.entitlements; 589 | CODE_SIGN_IDENTITY = "-"; 590 | CODE_SIGN_STYLE = Manual; 591 | COMBINE_HIDPI_IMAGES = YES; 592 | DEVELOPMENT_TEAM = ""; 593 | ENABLE_HARDENED_RUNTIME = YES; 594 | INFOPLIST_FILE = NoiseBuddy/Resources/Info.plist; 595 | LD_RUNPATH_SEARCH_PATHS = ( 596 | "$(inherited)", 597 | "@executable_path/../Frameworks", 598 | ); 599 | MARKETING_VERSION = 1.2; 600 | OTHER_LDFLAGS = ( 601 | "-framework", 602 | DFRFoundation, 603 | ); 604 | PRODUCT_BUNDLE_IDENTIFIER = codes.rambo.NoiseBuddy; 605 | PRODUCT_NAME = "$(TARGET_NAME)"; 606 | PROVISIONING_PROFILE_SPECIFIER = ""; 607 | SWIFT_OBJC_BRIDGING_HEADER = NoiseBuddy/Support/BridgingHeader.h; 608 | SWIFT_VERSION = 5.0; 609 | SYSTEM_FRAMEWORK_SEARCH_PATHS = ( 610 | "$(inherited)", 611 | /System/Library/PrivateFrameworks, 612 | ); 613 | }; 614 | name = Release; 615 | }; 616 | DD41F98B237C911A00A1B976 /* Debug */ = { 617 | isa = XCBuildConfiguration; 618 | buildSettings = { 619 | CLANG_ENABLE_MODULES = YES; 620 | CODE_SIGN_STYLE = Automatic; 621 | COMBINE_HIDPI_IMAGES = YES; 622 | CURRENT_PROJECT_VERSION = 2; 623 | DEFINES_MODULE = YES; 624 | DYLIB_COMPATIBILITY_VERSION = 1; 625 | DYLIB_CURRENT_VERSION = 2; 626 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 627 | INFOPLIST_FILE = NoiseCore/Info.plist; 628 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 629 | LD_RUNPATH_SEARCH_PATHS = ( 630 | "$(inherited)", 631 | "@executable_path/../Frameworks", 632 | "@loader_path/Frameworks", 633 | ); 634 | OTHER_LDFLAGS = ( 635 | "-framework", 636 | BluetoothAudio, 637 | ); 638 | PRODUCT_BUNDLE_IDENTIFIER = codes.rambo.NoiseCore; 639 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 640 | SKIP_INSTALL = YES; 641 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 642 | SWIFT_VERSION = 5.0; 643 | SYSTEM_FRAMEWORK_SEARCH_PATHS = ( 644 | "$(inherited)", 645 | /System/Library/PrivateFrameworks, 646 | ); 647 | VERSIONING_SYSTEM = "apple-generic"; 648 | VERSION_INFO_PREFIX = ""; 649 | }; 650 | name = Debug; 651 | }; 652 | DD41F98C237C911A00A1B976 /* Release */ = { 653 | isa = XCBuildConfiguration; 654 | buildSettings = { 655 | CLANG_ENABLE_MODULES = YES; 656 | CODE_SIGN_STYLE = Automatic; 657 | COMBINE_HIDPI_IMAGES = YES; 658 | CURRENT_PROJECT_VERSION = 2; 659 | DEFINES_MODULE = YES; 660 | DYLIB_COMPATIBILITY_VERSION = 1; 661 | DYLIB_CURRENT_VERSION = 2; 662 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 663 | INFOPLIST_FILE = NoiseCore/Info.plist; 664 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 665 | LD_RUNPATH_SEARCH_PATHS = ( 666 | "$(inherited)", 667 | "@executable_path/../Frameworks", 668 | "@loader_path/Frameworks", 669 | ); 670 | OTHER_LDFLAGS = ( 671 | "-framework", 672 | BluetoothAudio, 673 | ); 674 | PRODUCT_BUNDLE_IDENTIFIER = codes.rambo.NoiseCore; 675 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 676 | SKIP_INSTALL = YES; 677 | SWIFT_VERSION = 5.0; 678 | SYSTEM_FRAMEWORK_SEARCH_PATHS = ( 679 | "$(inherited)", 680 | /System/Library/PrivateFrameworks, 681 | ); 682 | VERSIONING_SYSTEM = "apple-generic"; 683 | VERSION_INFO_PREFIX = ""; 684 | }; 685 | name = Release; 686 | }; 687 | /* End XCBuildConfiguration section */ 688 | 689 | /* Begin XCConfigurationList section */ 690 | DD41F956237C8D5800A1B976 /* Build configuration list for PBXProject "NoiseBuddy" */ = { 691 | isa = XCConfigurationList; 692 | buildConfigurations = ( 693 | DD41F969237C8D5C00A1B976 /* Debug */, 694 | DD41F96A237C8D5C00A1B976 /* Release */, 695 | ); 696 | defaultConfigurationIsVisible = 0; 697 | defaultConfigurationName = Release; 698 | }; 699 | DD41F96B237C8D5C00A1B976 /* Build configuration list for PBXNativeTarget "NoiseBuddy" */ = { 700 | isa = XCConfigurationList; 701 | buildConfigurations = ( 702 | DD41F96C237C8D5C00A1B976 /* Debug */, 703 | DD41F96D237C8D5C00A1B976 /* Release */, 704 | ); 705 | defaultConfigurationIsVisible = 0; 706 | defaultConfigurationName = Release; 707 | }; 708 | DD41F98A237C911A00A1B976 /* Build configuration list for PBXNativeTarget "NoiseCore" */ = { 709 | isa = XCConfigurationList; 710 | buildConfigurations = ( 711 | DD41F98B237C911A00A1B976 /* Debug */, 712 | DD41F98C237C911A00A1B976 /* Release */, 713 | ); 714 | defaultConfigurationIsVisible = 0; 715 | defaultConfigurationName = Release; 716 | }; 717 | /* End XCConfigurationList section */ 718 | 719 | /* Begin XCRemoteSwiftPackageReference section */ 720 | 01A27F152385A94E00F7C0CC /* XCRemoteSwiftPackageReference "HotKey" */ = { 721 | isa = XCRemoteSwiftPackageReference; 722 | repositoryURL = "https://github.com/soffes/HotKey.git"; 723 | requirement = { 724 | kind = upToNextMajorVersion; 725 | minimumVersion = 0.1.3; 726 | }; 727 | }; 728 | /* End XCRemoteSwiftPackageReference section */ 729 | 730 | /* Begin XCSwiftPackageProductDependency section */ 731 | 01A27F162385A94E00F7C0CC /* HotKey */ = { 732 | isa = XCSwiftPackageProductDependency; 733 | package = 01A27F152385A94E00F7C0CC /* XCRemoteSwiftPackageReference "HotKey" */; 734 | productName = HotKey; 735 | }; 736 | /* End XCSwiftPackageProductDependency section */ 737 | }; 738 | rootObject = DD41F953237C8D5800A1B976 /* Project object */; 739 | } 740 | -------------------------------------------------------------------------------- /NoiseBuddy.xcodeproj/xcshareddata/xcschemes/NoiseBuddy.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 57 | 58 | 61 | 62 | 65 | 66 | 67 | 68 | 74 | 76 | 82 | 83 | 84 | 85 | 87 | 88 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /NoiseBuddy.xcodeproj/xcshareddata/xcschemes/NoiseCore.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /NoiseBuddy/Bootstrap/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import NoiseCore 11 | 12 | @NSApplicationMain 13 | class AppDelegate: NSObject, NSApplicationDelegate { 14 | 15 | private func makeListeningModeController() -> NCListeningModeStatusProvider { 16 | guard let arg = UserDefaults.standard.string(forKey: "NBListeningModeControllerType") else { 17 | return NCAVListeningModeController() 18 | } 19 | 20 | switch arg { 21 | case "bt": return NCBTListeningModeController() 22 | case "mock": return MockListeningModeController() 23 | default: return NCAVListeningModeController() 24 | } 25 | } 26 | 27 | private lazy var preferences = Preferences() 28 | 29 | private lazy var touchBarController: TouchBarUIController = { 30 | TouchBarUIController( 31 | listeningModeController: self.makeListeningModeController(), 32 | preferences: self.preferences 33 | ) 34 | }() 35 | 36 | private lazy var menuBarController: MenuBarUIController = { 37 | MenuBarUIController( 38 | listeningModeController: self.makeListeningModeController(), 39 | preferences: self.preferences 40 | ) 41 | }() 42 | 43 | private lazy var keyboardShortcutController: KeyboardShortcutController = { 44 | KeyboardShortcutController( 45 | listeningModeController: self.makeListeningModeController(), 46 | preferences: self.preferences 47 | ) 48 | }() 49 | 50 | func applicationDidFinishLaunching(_ aNotification: Notification) { 51 | preferences.register() 52 | 53 | touchBarController.install() 54 | menuBarController.install() 55 | 56 | keyboardShortcutController.setup() 57 | 58 | if !preferences.hasLaunchedBefore || UserDefaults.standard.bool(forKey: "NBShowPreferences") { 59 | preferences.hasLaunchedBefore = true 60 | showPreferences(self) 61 | } 62 | } 63 | 64 | private func makePreferencesController() -> NSWindowController { 65 | PreferencesViewController.instantiate(with: self.preferences).0 66 | } 67 | 68 | private var preferencesController: NSWindowController? 69 | 70 | @IBAction func showPreferences(_ sender: Any) { 71 | preferencesController = makePreferencesController() 72 | 73 | preferencesController?.showWindow(sender) 74 | NSApp.activate(ignoringOtherApps: false) 75 | } 76 | 77 | private var activationCount = 0 78 | 79 | func applicationDidBecomeActive(_ notification: Notification) { 80 | if activationCount > 0 { showPreferences(self) } 81 | 82 | activationCount += 1 83 | 84 | DispatchQueue.main.async { 85 | NSApp.activate(ignoringOtherApps: false) 86 | } 87 | } 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /NoiseBuddy/Controllers/KeyboardShortcutController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardShortcutController.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Nick Hayward on 11/20/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import NoiseCore 11 | import HotKey 12 | 13 | final class KeyboardShortcutController: NSObject { 14 | private var toggleListeningModeHotKey: HotKey? 15 | 16 | let preferences: Preferences 17 | let listeningModeController: NCListeningModeStatusProvider 18 | 19 | init(listeningModeController: NCListeningModeStatusProvider, preferences: Preferences) { 20 | self.listeningModeController = listeningModeController 21 | self.preferences = preferences 22 | super.init() 23 | } 24 | 25 | func setup() { 26 | listeningModeController.startListeningForUpdates() 27 | 28 | NotificationCenter.default.addObserver(forName: Preferences.didChangeNotification, object: preferences, queue: .main) { [weak self] _ in 29 | guard let preferences = self?.preferences else { return } 30 | self?.updateShortcut(preferences: preferences) 31 | } 32 | 33 | // MARK: Initial Setup 34 | self.updateShortcut(preferences: self.preferences) 35 | } 36 | 37 | func toggleNoiseControlMode() { 38 | let nextMode = preferences.nextListeningMode(from: listeningModeController.listeningMode) 39 | 40 | listeningModeController.listeningMode = nextMode 41 | } 42 | 43 | private func updateShortcut(preferences: Preferences) { 44 | let keyboardShortcutToggleEnabled = preferences.keyboardShortcutToggleEnabled 45 | toggleListeningModeHotKey?.isPaused = preferences.keyboardShortcutPause 46 | 47 | if preferences.keyboardShortcutPause { 48 | return 49 | } 50 | 51 | // MARK: Disable Keyboard Shortcut 52 | if !keyboardShortcutToggleEnabled { 53 | toggleListeningModeHotKey = nil 54 | return 55 | } 56 | 57 | guard let keyboardShortcut = preferences.keyboardShortcut else { return } 58 | setupShortcut(with: keyboardShortcut) 59 | } 60 | 61 | private func setupShortcut(with shortcut: KeyBoardShortcut) { 62 | guard let keyboardShortcutKey = preferences.keyboardShortcut else { return } 63 | toggleListeningModeHotKey = HotKey(carbonKeyCode: keyboardShortcutKey.eventCode, carbonModifiers: keyboardShortcutKey.modifier) 64 | toggleListeningModeHotKey?.keyDownHandler = { 65 | self.toggleNoiseControlMode() 66 | } 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /NoiseBuddy/Controllers/MenuBarUIController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuBarUIController.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import NoiseCore 11 | 12 | final class MenuBarUIController: NoiseControlUIController { 13 | 14 | override var isEnabled: Bool { preferences.menuBarEnabled } 15 | 16 | private var item: NSStatusItem? 17 | 18 | private func makeStatusItem() -> NSStatusItem { 19 | let i = NSStatusBar.system.statusItem(withLength: 30) 20 | 21 | i.autosaveName = "codes.rambo.NoiseBuddy.Menu" 22 | i.button?.imageScaling = .scaleProportionallyDown 23 | i.button?.target = self 24 | i.button?.action = #selector(toggleNoiseControlMode) 25 | 26 | return i 27 | } 28 | 29 | override func configureUI() { 30 | guard isEnabled, item == nil else { return } 31 | 32 | item = makeStatusItem() 33 | } 34 | 35 | override func reevaluateVisibility() { 36 | item?.isVisible = shouldShow(for: currentDevice) 37 | } 38 | 39 | override func handleListeningModeDidChange(_ device: NCDevice) { 40 | item?.button?.image = device.listeningMode.menuBarImage 41 | item?.button?.toolTip = currentDevice?.tooltip 42 | } 43 | 44 | override func handleDeviceDidChange(_ device: NCDevice?) { 45 | super.handleDeviceDidChange(device) 46 | 47 | item?.button?.toolTip = currentDevice?.tooltip 48 | } 49 | 50 | } 51 | 52 | extension NCDevice { 53 | var tooltip: String? { 54 | guard supportsListeningModes else { return nil } 55 | 56 | return "NoiseBuddy: \(name) (\(listeningMode.title))" 57 | } 58 | } 59 | 60 | extension NCListeningMode { 61 | var title: String { 62 | switch self { 63 | case .normal: 64 | return "Normal" 65 | case .anc: 66 | return "Noise Cancelling" 67 | case .transparency: 68 | return "Transparency" 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /NoiseBuddy/Controllers/NoiseControlUIController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NoiseControlUIController.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import NoiseCore 11 | 12 | class NoiseControlUIController: NSObject { 13 | 14 | let preferences: Preferences 15 | let listeningModeController: NCListeningModeStatusProvider 16 | 17 | init(listeningModeController: NCListeningModeStatusProvider, preferences: Preferences) 18 | { 19 | self.listeningModeController = listeningModeController 20 | self.preferences = preferences 21 | 22 | super.init() 23 | } 24 | 25 | func configureUI() { 26 | assertionFailure("Subclasses must override configureUI and not call super") 27 | } 28 | 29 | func install() { 30 | configureUI() 31 | 32 | listeningModeController.outputDeviceDidChange = { [weak self] device in 33 | self?.handleDeviceDidChange(device) 34 | } 35 | 36 | listeningModeController.startListeningForUpdates() 37 | 38 | NotificationCenter.default.addObserver(forName: Preferences.didChangeNotification, object: preferences, queue: .main) { [weak self] _ in 39 | self?.reevaluateVisibility() 40 | } 41 | } 42 | 43 | var isEnabled: Bool { fatalError("Subclasses must override isEnabled and not call super") } 44 | 45 | func shouldShow(for device: NCDevice?) -> Bool { 46 | guard let device = device else { return false } 47 | return device.supportsListeningModes && isEnabled 48 | } 49 | 50 | private(set) var currentDevice: NCDevice? 51 | 52 | func handleDeviceDidChange(_ device: NCDevice?) { 53 | defer { reevaluateVisibility() } 54 | 55 | currentDevice = device 56 | 57 | guard let device = device else { return } 58 | 59 | handleListeningModeDidChange(device) 60 | 61 | device.listeningModeDidChange = { [weak self] inDevice in 62 | self?.handleListeningModeDidChange(inDevice) 63 | } 64 | } 65 | 66 | func reevaluateVisibility() { 67 | assertionFailure("Subclasses must override reevaluateVisibility and not call super") 68 | } 69 | 70 | func handleListeningModeDidChange(_ device: NCDevice) { 71 | assertionFailure("Subclasses must override handleListeningModeDidChange and not call super") 72 | } 73 | 74 | @objc func toggleNoiseControlMode(_ sender: Any) { 75 | let nextMode = preferences.nextListeningMode(from: listeningModeController.listeningMode) 76 | 77 | listeningModeController.listeningMode = nextMode 78 | } 79 | 80 | } 81 | 82 | extension Preferences { 83 | 84 | func nextListeningMode(from currentMode: NCListeningMode) -> NCListeningMode { 85 | let fallbackMode = listeningModes.first ?? currentMode 86 | 87 | guard let idx = listeningModes.lastIndex(of: currentMode) else { return fallbackMode } 88 | 89 | let nextIdx = idx + 1 90 | guard nextIdx < listeningModes.count else { return fallbackMode } 91 | 92 | return listeningModes[nextIdx] 93 | } 94 | 95 | } 96 | 97 | extension NCListeningMode { 98 | var touchBarImage: NSImage? { NSImage(named: NSImage.Name(rawValue)) } 99 | var menuBarImage: NSImage? { NSImage(named: NSImage.Name("\(rawValue)-menu")) } 100 | } 101 | 102 | extension NCDevice { 103 | var supportsListeningModes: Bool { availableListeningModes.count > 1 } 104 | } 105 | -------------------------------------------------------------------------------- /NoiseBuddy/Controllers/PreferencesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreferencesViewController.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import Carbon.HIToolbox 11 | 12 | class PreferencesViewController: NSViewController { 13 | 14 | @IBOutlet weak var launchAtLoginButton: NSButton! 15 | @IBOutlet weak var menuBarEnabledButton: NSButton! 16 | @IBOutlet weak var touchBarEnabledButton: NSButton! 17 | @IBOutlet weak var toggleKeyboardShortcutEnabledButton: NSButton! 18 | 19 | @IBOutlet weak var keyboardShortcutToggleModeButton: NSButton! 20 | 21 | var isListening = false { 22 | didSet { 23 | DispatchQueue.main.async { [weak self] in 24 | self?.keyboardShortcutToggleModeButton.isHighlighted.toggle() 25 | } 26 | } 27 | } 28 | 29 | private var preferences: Preferences! 30 | 31 | static func instantiate(with preferences: Preferences) -> (NSWindowController, PreferencesViewController) { 32 | let sb = NSStoryboard(name: NSStoryboard.Name("Preferences"), bundle: nil) 33 | guard let controller = sb.instantiateInitialController() as? PreferencesViewController else { 34 | fatalError("Corrupted storyboard") 35 | } 36 | controller.preferences = preferences 37 | 38 | let window = NSWindow(contentRect: .zero, styleMask: [.titled, .closable], backing: .buffered, defer: false) 39 | window.contentViewController = controller 40 | window.title = "NoiseBuddy Settings" 41 | let windowController = NSWindowController(window: window) 42 | 43 | window.center() 44 | 45 | return (windowController, controller) 46 | } 47 | 48 | private var isDFRAvailable: Bool { 49 | NSFunctionRow.isDynamicFunctionRowAvailable() && !UserDefaults.standard.bool(forKey: "NBSimulateDFRNotAvailable") 50 | } 51 | 52 | override func viewDidLoad() { 53 | super.viewDidLoad() 54 | NSEvent.addLocalMonitorForEvents(matching: .keyDown) { 55 | self.keyDown(with: $0) 56 | return nil 57 | } 58 | } 59 | 60 | override func viewWillAppear() { 61 | super.viewWillAppear() 62 | 63 | touchBarEnabledButton.isHidden = !isDFRAvailable 64 | } 65 | 66 | override func viewDidAppear() { 67 | super.viewDidAppear() 68 | 69 | launchAtLoginButton.state = preferences.launchAtLoginEnabled ? .on : .off 70 | menuBarEnabledButton.state = preferences.menuBarEnabled ? .on : .off 71 | touchBarEnabledButton.state = preferences.touchBarEnabled ? .on : .off 72 | toggleKeyboardShortcutEnabledButton.state = preferences.keyboardShortcutToggleEnabled ? .on : .off 73 | keyboardShortcutToggleModeButton.isEnabled = preferences.keyboardShortcutToggleEnabled 74 | keyboardShortcutToggleModeButton.title = preferences.keyboardShortcut?.title ?? "Record Shortcut" 75 | } 76 | 77 | @IBAction func launchAtLoginAction(_ sender: NSButton) { 78 | preferences.launchAtLoginEnabled = sender.state == .on 79 | } 80 | 81 | @IBAction func menuBarEnabledAction(_ sender: NSButton) { 82 | preferences.menuBarEnabled = sender.state == .on 83 | } 84 | 85 | @IBAction func touchBarEnabledAction(_ sender: NSButton) { 86 | preferences.touchBarEnabled = sender.state == .on 87 | } 88 | 89 | @IBAction func keyboardShortcutToggleEnabledAction(_ sender: NSButton) { 90 | preferences.keyboardShortcutToggleEnabled = sender.state == .on 91 | keyboardShortcutToggleModeButton.isEnabled = sender.state == .on ? true : false 92 | } 93 | 94 | @IBAction func toggleAirPodsProModeAction(_ sender: NSButton) { 95 | isListening = true 96 | preferences.keyboardShortcutPause = true 97 | view.window?.makeFirstResponder(nil) 98 | } 99 | 100 | @IBAction func openRepositoryWebsite(_ sender: ActionLabel) { 101 | let url = URL(string: "https://github.com/insidegui/NoiseBuddy")! 102 | NSWorkspace.shared.open(url) 103 | } 104 | 105 | @IBAction func openAirBuddyWebsite(_ sender: ActionLabel) { 106 | let url = URL(string: "https://airbuddy.app")! 107 | NSWorkspace.shared.open(url) 108 | } 109 | 110 | func updateGlobalShortcut(_ event : NSEvent) { 111 | self.isListening = false 112 | 113 | if let characters = event.charactersIgnoringModifiers { 114 | updateKeybindButton(with: characters) 115 | } 116 | } 117 | 118 | func updateKeybindButton(with shortcut: String) { 119 | keyboardShortcutToggleModeButton.title = shortcut 120 | } 121 | 122 | override func keyDown(with event: NSEvent) { 123 | if isListening == false { return } 124 | defer { 125 | isListening = false 126 | preferences.keyboardShortcutPause = false 127 | } 128 | 129 | if Int(event.keyCode) == kVK_Escape { return } 130 | 131 | keyboardShortcutToggleModeButton.title = event.modifierFlags.description + event.charactersIgnoringModifiers!.description.uppercased() 132 | 133 | guard let key = event.charactersIgnoringModifiers?.description.uppercased() else { return } 134 | 135 | let newShortcut = KeyBoardShortcut(eventCode: UInt32(event.keyCode), modifier: event.modifierFlags.carbonFlags, title: event.modifierFlags.description + key) 136 | preferences.keyboardShortcut = newShortcut 137 | 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /NoiseBuddy/Controllers/TouchBarUIController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TouchBarUIController.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import NoiseCore 11 | 12 | fileprivate extension NSTouchBarItem.Identifier { 13 | static let noiseControl = NSTouchBarItem.Identifier("codes.rambo.NoiseBuddy") 14 | } 15 | 16 | final class TouchBarUIController: NoiseControlUIController { 17 | 18 | private lazy var item: NSCustomTouchBarItem = { 19 | let i = NSCustomTouchBarItem(identifier: .noiseControl) 20 | i.customizationLabel = "Listening Mode" 21 | return i 22 | }() 23 | 24 | private lazy var button: NSButton = { 25 | NSButton(image: NSImage(), target: self, action: #selector(toggleNoiseControlMode)) 26 | }() 27 | 28 | override var isEnabled: Bool { preferences.touchBarEnabled } 29 | 30 | override func configureUI() { 31 | item.view = button 32 | 33 | NSTouchBarItem.addSystemTrayItem(item) 34 | 35 | DFRSystemModalShowsCloseBoxWhenFrontMost(true) 36 | } 37 | 38 | override func reevaluateVisibility() { 39 | DFRElementSetControlStripPresenceForIdentifier(.noiseControl, shouldShow(for: currentDevice)) 40 | } 41 | 42 | override func toggleNoiseControlMode(_ sender: Any) { 43 | let nextMode = preferences.nextListeningMode(from: listeningModeController.listeningMode) 44 | 45 | listeningModeController.listeningMode = nextMode 46 | 47 | button.image = nextMode.touchBarImage 48 | } 49 | 50 | override func handleListeningModeDidChange(_ device: NCDevice) { 51 | button.image = device.listeningMode.touchBarImage 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /NoiseBuddy/Extensions/UserDefaults+Codable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaults+Codable.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Nick Hayward on 11/21/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension UserDefaults { 12 | func codable(forKey key: String) -> T? { 13 | guard let data = data(forKey: key) else { return nil } 14 | return try? JSONDecoder().decode(T.self, from: data) 15 | } 16 | 17 | func setEncode(_ value: T, forKey defaultName: String) { 18 | guard let data = try? JSONEncoder().encode(value) else { return } 19 | set(data, forKey: defaultName) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /NoiseBuddy/Models/KeyBoardShortcut.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyBoardShortcut.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Nick Hayward on 11/21/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct KeyBoardShortcut: Codable { 12 | let eventCode: UInt32 13 | let modifier: UInt32 14 | let title: String 15 | } 16 | -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation-menu.imageset/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation-menu.imageset/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.pdf -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation-menu.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.imageset/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.imageset/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.pdf -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeAudioTransparency-menu.imageset/AVOutputDeviceBluetoothListeningModeAudioTransparency.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeAudioTransparency-menu.imageset/AVOutputDeviceBluetoothListeningModeAudioTransparency.pdf -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeAudioTransparency-menu.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "AVOutputDeviceBluetoothListeningModeAudioTransparency.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeAudioTransparency.imageset/AVOutputDeviceBluetoothListeningModeAudioTransparency.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeAudioTransparency.imageset/AVOutputDeviceBluetoothListeningModeAudioTransparency.pdf -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeAudioTransparency.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "AVOutputDeviceBluetoothListeningModeAudioTransparency.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeNormal-menu.imageset/AVOutputDeviceBluetoothListeningModeNormal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeNormal-menu.imageset/AVOutputDeviceBluetoothListeningModeNormal.pdf -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeNormal-menu.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "AVOutputDeviceBluetoothListeningModeNormal.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeNormal.imageset/AVOutputDeviceBluetoothListeningModeNormal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeNormal.imageset/AVOutputDeviceBluetoothListeningModeNormal.pdf -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AVOutputDeviceBluetoothListeningModeNormal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "AVOutputDeviceBluetoothListeningModeNormal.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/NoiseBuddy/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | Default 529 | 530 | 531 | 532 | 533 | 534 | 535 | Left to Right 536 | 537 | 538 | 539 | 540 | 541 | 542 | Right to Left 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | Default 554 | 555 | 556 | 557 | 558 | 559 | 560 | Left to Right 561 | 562 | 563 | 564 | 565 | 566 | 567 | Right to Left 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf2511 2 | \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 SFProDisplay-Regular;\f1\fnil\fcharset0 SFProDisplay-Bold;} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\expandedcolortbl;;} 5 | \paperw11900\paperh16840\margl1440\margr1440\vieww11880\viewh14160\viewkind0 6 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 7 | 8 | \f0\fs26 \cf0 Created by 9 | \f1\b Guilherme Rambo 10 | \f0\b0 .\ 11 | \ 12 | Special thanks to 13 | \f1\b Dave DeLong 14 | \f0\b0 .\ 15 | \ 16 | 17 | \f1\b Acknowledgements\ 18 | \ 19 | Fishhook by Facebook: 20 | \f0\b0 \ 21 | \ 22 | Copyright (c) 2013, Facebook, Inc.\ 23 | All rights reserved.\ 24 | Redistribution and use in source and binary forms, with or without\ 25 | modification, are permitted provided that the following conditions are met:\ 26 | * Redistributions of source code must retain the above copyright notice,\ 27 | this list of conditions and the following disclaimer.\ 28 | * Redistributions in binary form must reproduce the above copyright notice,\ 29 | this list of conditions and the following disclaimer in the documentation\ 30 | and/or other materials provided with the distribution.\ 31 | * Neither the name Facebook nor the names of its contributors may be used to\ 32 | endorse or promote products derived from this software without specific\ 33 | prior written permission.\ 34 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\ 35 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\ 36 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\ 37 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\ 38 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ 39 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\ 40 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ 41 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ 42 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\ 43 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.} -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.1 21 | CFBundleVersion 22 | 1 23 | LSApplicationCategoryType 24 | public.app-category.utilities 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2019 Guilherme Rambo. All rights reserved. 31 | NSMainStoryboardFile 32 | Main 33 | NSPrincipalClass 34 | NSApplication 35 | NSSupportsAutomaticTermination 36 | 37 | NSSupportsSuddenTermination 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /NoiseBuddy/Resources/NoiseBuddy.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NoiseBuddy/Resources/Preferences.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 29 | 39 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 184 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /NoiseBuddy/Support/BridgingHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // BridgingHeader.h 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | #import "NSTouchBar-Private.h" 10 | #import "SharedFileList.h" 11 | -------------------------------------------------------------------------------- /NoiseBuddy/Support/MockListeningModeController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockListeningModeController.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import NoiseCore 11 | 12 | final class MockListeningModeController: NSObject, NCListeningModeStatusProvider { 13 | 14 | private var fakeUpdateTimer: Timer? 15 | 16 | private var fakeAirPodsPro: NCDevice { 17 | let dev = NCDevice() 18 | dev.identifier = "1" 19 | dev.name = "John's AirPods Pro" 20 | dev._availableListeningModes = NCListeningMode.allCases.map { $0.rawValue } 21 | dev._listeningMode = NCListeningMode.anc.rawValue 22 | return dev 23 | } 24 | 25 | private var fakeBeatsStudio: NCDevice { 26 | let dev = NCDevice() 27 | dev.identifier = "2" 28 | dev.name = "John's Beats Studio" 29 | dev._availableListeningModes = [NCListeningMode.normal.rawValue] 30 | dev._listeningMode = NCListeningMode.normal.rawValue 31 | return dev 32 | } 33 | 34 | private lazy var currentFakeDevice: NCDevice = { 35 | fakeAirPodsPro 36 | }() 37 | 38 | func startListeningForUpdates() { 39 | fakeUpdateTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true, block: { _ in 40 | if self.currentFakeDevice.identifier == self.fakeAirPodsPro.identifier { 41 | self.currentFakeDevice = self.fakeBeatsStudio 42 | } else { 43 | self.currentFakeDevice = self.fakeAirPodsPro 44 | } 45 | 46 | self.outputDeviceDidChange(self.currentFakeDevice) 47 | }) 48 | 49 | self.outputDeviceDidChange(self.currentFakeDevice) 50 | } 51 | 52 | var outputDeviceDidChange: (NCDevice?) -> Void = { _ in } 53 | 54 | var _availableListeningModes: [String] { 55 | currentFakeDevice._availableListeningModes 56 | } 57 | 58 | var _listeningMode: String { 59 | get { 60 | currentFakeDevice._listeningMode 61 | } 62 | set { 63 | _setListeningMode(newValue) 64 | } 65 | } 66 | 67 | func _setListeningMode(_ listeningMode: String) { 68 | currentFakeDevice._listeningMode = listeningMode 69 | self.outputDeviceDidChange(self.currentFakeDevice) 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /NoiseBuddy/Support/NSTouchBar-Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTouchBar-Private.h 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | @import Cocoa; 10 | 11 | @interface NSFunctionRow: NSObject 12 | + (BOOL)isDynamicFunctionRowAvailable; 13 | @end 14 | 15 | extern void DFRElementSetControlStripPresenceForIdentifier(NSTouchBarItemIdentifier, BOOL); 16 | extern void DFRSystemModalShowsCloseBoxWhenFrontMost(BOOL); 17 | 18 | @interface NSTouchBarItem () 19 | 20 | + (void)addSystemTrayItem:(NSTouchBarItem *)item; 21 | 22 | @end 23 | 24 | @interface NSTouchBar () 25 | 26 | // macOS 10.14 and above 27 | + (void)presentSystemModalTouchBar:(NSTouchBar *)touchBar systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier NS_AVAILABLE_MAC(10.14); 28 | 29 | // macOS 10.13 and below 30 | + (void)presentSystemModalFunctionBar:(NSTouchBar *)touchBar systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier NS_DEPRECATED_MAC(10.12.2, 10.14); 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /NoiseBuddy/Support/Preferences.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Preferences.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import NoiseCore 11 | 12 | final class Preferences { 13 | 14 | static let didChangeNotification = Notification.Name("codes.rambo.NoiseBuddy.PrefsChanged") 15 | 16 | private let defaults: UserDefaults 17 | 18 | init(defaults: UserDefaults = .standard) { 19 | self.defaults = defaults 20 | } 21 | 22 | func register() { 23 | defaults.register(defaults: [ 24 | "listeningModes": [ 25 | NCListeningMode.anc.rawValue, 26 | NCListeningMode.transparency.rawValue 27 | ], 28 | "menuBarEnabled": true, 29 | "touchBarEnabled": true, 30 | "keyboardShortcutToggleEnabled": false, 31 | "keyboardShortcutPause": false 32 | ]) 33 | } 34 | 35 | private func didChange() { 36 | NotificationCenter.default.post(name: Self.didChangeNotification, object: self) 37 | } 38 | 39 | var hasLaunchedBefore: Bool { 40 | get { defaults.bool(forKey: #function) } 41 | set { defaults.set(newValue, forKey: #function) } 42 | } 43 | 44 | var menuBarEnabled: Bool { 45 | get { defaults.bool(forKey: #function) } 46 | set { 47 | defaults.set(newValue, forKey: #function) 48 | didChange() 49 | } 50 | } 51 | 52 | var touchBarEnabled: Bool { 53 | get { defaults.bool(forKey: #function) } 54 | set { 55 | defaults.set(newValue, forKey: #function) 56 | didChange() 57 | } 58 | } 59 | 60 | var keyboardShortcutToggleEnabled: Bool { 61 | get { defaults.bool(forKey: #function) } 62 | set { 63 | defaults.set(newValue, forKey: #function) 64 | didChange() 65 | } 66 | } 67 | 68 | var keyboardShortcutPause: Bool { 69 | get { defaults.bool(forKey: #function) } 70 | set { 71 | defaults.set(newValue, forKey: #function) 72 | didChange() 73 | } 74 | } 75 | 76 | var keyboardShortcut: KeyBoardShortcut? { 77 | get { defaults.codable(forKey: #function) } 78 | set { 79 | defaults.setEncode(newValue, forKey: #function) 80 | didChange() 81 | } 82 | } 83 | 84 | private var appURL: URL { Bundle.main.bundleURL } 85 | 86 | var launchAtLoginEnabled: Bool { 87 | get { SharedFileList.sessionLoginItems().containsItem(appURL) } 88 | set { 89 | if newValue { 90 | SharedFileList.sessionLoginItems().addItem(appURL) 91 | } else { 92 | SharedFileList.sessionLoginItems().removeItem(appURL) 93 | } 94 | 95 | didChange() 96 | } 97 | } 98 | 99 | var listeningModes: [NCListeningMode] { 100 | get { 101 | let rawValues = defaults.array(forKey: #function) as? [String] 102 | return rawValues?.compactMap({ NCListeningMode(rawValue: $0) }) ?? [] 103 | } 104 | set { 105 | defaults.set(newValue, forKey: #function) 106 | 107 | didChange() 108 | } 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /NoiseBuddy/Support/SharedFileList.h: -------------------------------------------------------------------------------- 1 | // 2 | // SharedFileList.h 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SharedFileList : NSObject 14 | 15 | + (instancetype)sessionLoginItems; 16 | 17 | @property (nonatomic, readonly) NSSet *items; 18 | 19 | @property (nonatomic, copy, nullable) void(^changeHandler)(SharedFileList *); 20 | 21 | - (BOOL)containsItem:(NSURL *)url; 22 | - (void)addItem:(NSURL *)url; 23 | - (void)removeItem:(NSURL *)url; 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /NoiseBuddy/Support/SharedFileList.m: -------------------------------------------------------------------------------- 1 | // 2 | // SharedFileList.m 3 | // SyzygyKit 4 | // 5 | // Created by Dave DeLong on 9/22/18. 6 | // Copyright © 2018 Syzygy. All rights reserved. 7 | // 8 | 9 | #import "SharedFileList.h" 10 | 11 | #import 12 | 13 | #pragma clang diagnostic push 14 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 15 | 16 | void sharedFileListDidChange(LSSharedFileListRef inList, void *context); 17 | 18 | @implementation SharedFileList { 19 | LSSharedFileListRef _listRef; 20 | 21 | NSSet *_listSnapshot; 22 | } 23 | 24 | + (BOOL)automaticallyNotifiesObserversOfItems { return NO; } 25 | 26 | + (instancetype)sessionLoginItems { 27 | return [[self alloc] initWithType:kLSSharedFileListSessionLoginItems]; 28 | } 29 | 30 | - (instancetype)initWithType:(CFStringRef)type { 31 | self = [super init]; 32 | if (self) { 33 | _listRef = LSSharedFileListCreate(NULL, type, NULL); 34 | _listSnapshot = [self _snapshot]; 35 | 36 | LSSharedFileListAddObserver(_listRef, 37 | CFRunLoopGetMain(), 38 | (CFStringRef)NSDefaultRunLoopMode, 39 | sharedFileListDidChange, 40 | (voidPtr)CFBridgingRetain(self)); 41 | } 42 | return self; 43 | } 44 | 45 | - (void)dealloc { 46 | LSSharedFileListRemoveObserver(_listRef, 47 | CFRunLoopGetMain(), 48 | (CFStringRef)NSDefaultRunLoopMode, 49 | sharedFileListDidChange, 50 | (__bridge void *)(self)); 51 | CFRelease(_listRef); 52 | } 53 | 54 | - (NSSet *)items { return [_listSnapshot copy]; } 55 | 56 | - (NSSet *)_snapshot { 57 | NSMutableSet *snapshot = [NSMutableSet set]; 58 | 59 | NSArray *listSnapshot = (NSArray *)CFBridgingRelease(LSSharedFileListCopySnapshot(_listRef, NULL)); 60 | for (id itemObject in listSnapshot) { 61 | LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject; 62 | UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; 63 | CFURLRef currentItemURL = NULL; 64 | LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); 65 | NSURL *itemURL = CFBridgingRelease(currentItemURL); 66 | if (itemURL != nil) { 67 | [snapshot addObject:itemURL]; 68 | } 69 | } 70 | 71 | return snapshot; 72 | } 73 | 74 | - (void)_listDidChange { 75 | NSSet *newSnapshot = [self _snapshot]; 76 | 77 | [self willChangeValueForKey:@"items"]; 78 | _listSnapshot = newSnapshot; 79 | [self didChangeValueForKey:@"items"]; 80 | 81 | if (self.changeHandler != nil) { 82 | self.changeHandler(self); 83 | } 84 | } 85 | 86 | - (BOOL)containsItem:(NSURL *)url { return [_listSnapshot containsObject:url]; } 87 | 88 | - (void)addItem:(NSURL *)url { 89 | if ([self containsItem:url] == YES) { return; } 90 | LSSharedFileListInsertItemURL(_listRef, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)url, NULL, NULL); 91 | } 92 | 93 | - (void)removeItem:(NSURL *)url { 94 | if ([self containsItem:url] == NO) { return; } 95 | 96 | NSArray *listSnapshot = (NSArray *)CFBridgingRelease(LSSharedFileListCopySnapshot(_listRef, NULL)); 97 | for (id itemObject in listSnapshot) { 98 | LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject; 99 | UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; 100 | CFURLRef currentItemURL = NULL; 101 | LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); 102 | NSURL *itemURL = CFBridgingRelease(currentItemURL); 103 | if ([itemURL isEqual:url]) { 104 | LSSharedFileListItemRemove(_listRef, item); 105 | } 106 | } 107 | } 108 | 109 | @end 110 | 111 | void sharedFileListDidChange(LSSharedFileListRef inList, void *context) { 112 | SharedFileList *list = (__bridge id)context; 113 | [list _listDidChange]; 114 | } 115 | 116 | #pragma clang diagnostic pop 117 | -------------------------------------------------------------------------------- /NoiseBuddy/Views/ActionLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionLabel.swift 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class ActionLabel: NSTextField { 12 | 13 | private var cursorTrackingArea: NSTrackingArea! 14 | 15 | override func updateTrackingAreas() { 16 | super.updateTrackingAreas() 17 | 18 | if cursorTrackingArea != nil { 19 | removeTrackingArea(cursorTrackingArea) 20 | } 21 | 22 | cursorTrackingArea = NSTrackingArea(rect: bounds, 23 | options: [.cursorUpdate, .inVisibleRect, .activeInActiveApp], 24 | owner: self, 25 | userInfo: nil) 26 | addTrackingArea(cursorTrackingArea) 27 | } 28 | 29 | override func cursorUpdate(with event: NSEvent) { 30 | if event.trackingArea == cursorTrackingArea { 31 | NSCursor.pointingHand.push() 32 | } else { 33 | super.cursorUpdate(with: event) 34 | } 35 | } 36 | 37 | override func mouseDown(with event: NSEvent) { 38 | if let action = action { 39 | NSApp.sendAction(action, to: target, from: self) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /NoiseCore/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.1 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2019 Guilherme Rambo. All rights reserved. 23 | 24 | 25 | -------------------------------------------------------------------------------- /NoiseCore/NoiseCore.h: -------------------------------------------------------------------------------- 1 | // 2 | // NoiseCore.h 3 | // NoiseCore 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for NoiseCore. 12 | FOUNDATION_EXPORT double NoiseCoreVersionNumber; 13 | 14 | //! Project version string for NoiseCore. 15 | FOUNDATION_EXPORT const unsigned char NoiseCoreVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | -------------------------------------------------------------------------------- /NoiseCore/Source/Models/NCDevice-Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // NCDevice-Private.h 3 | // NoiseCore 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | #import "NCDevice.h" 10 | 11 | @class AVOutputDevice; 12 | 13 | @interface NCDevice (Private) 14 | 15 | @property (nonatomic, strong) AVOutputDevice *avDevice; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /NoiseCore/Source/Models/NCDevice.h: -------------------------------------------------------------------------------- 1 | // 2 | // NCDevice.h 3 | // NoiseCore 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NCDevice: NSObject 14 | 15 | @property (copy) NSString *identifier; 16 | @property (copy) NSString *name; 17 | 18 | @property (copy) NSString *_listeningMode; 19 | @property (copy) NSArray *_availableListeningModes; 20 | 21 | @property (copy) void(^listeningModeDidChange)(NCDevice *device); 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /NoiseCore/Source/Models/NCDevice.m: -------------------------------------------------------------------------------- 1 | // 2 | // NCDevice.m 3 | // NoiseCore 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | #import "NCDevice.h" 10 | 11 | #import "AVFoundation-Private.h" 12 | 13 | NSString * const kCurrentListeningModeKeyPath = @"currentBluetoothListeningMode"; 14 | 15 | @interface NCDevice () 16 | 17 | @property (nonatomic, strong) AVOutputDevice *avDevice; 18 | 19 | // TODO: Figure out a way to get notified when the current listening mode changes, instead of using this timer hack. 20 | @property (nonatomic, strong) NSTimer *currentModeTimer; 21 | 22 | @end 23 | 24 | @implementation NCDevice 25 | 26 | - (NSString *)debugDescription 27 | { 28 | return [NSString stringWithFormat:@"NCDevice(identifier: %@, name: %@, listeningMode: %@, availableListeningModes: %@)", 29 | self.identifier, self.name, self._listeningMode, self._availableListeningModes]; 30 | } 31 | 32 | - (NSString *)description { 33 | return self.debugDescription; 34 | } 35 | 36 | - (void)setAvDevice:(AVOutputDevice *)avDevice 37 | { 38 | [self.currentModeTimer invalidate]; 39 | self.currentModeTimer = nil; 40 | 41 | _avDevice = avDevice; 42 | 43 | self.currentModeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateCurrentListeningMode:) userInfo:nil repeats:YES]; 44 | self.currentModeTimer.tolerance = 5.0; 45 | } 46 | 47 | - (void)updateCurrentListeningMode:(id)sender 48 | { 49 | NSString *previousMode = self._listeningMode; 50 | NSString *currentMode = self.avDevice.currentBluetoothListeningMode; 51 | if ([previousMode isEqualToString:currentMode]) return; 52 | 53 | self._listeningMode = currentMode; 54 | 55 | if (self.listeningModeDidChange) self.listeningModeDidChange(self); 56 | } 57 | 58 | - (void)dealloc 59 | { 60 | [self.avDevice removeObserver:self forKeyPath:kCurrentListeningModeKeyPath]; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /NoiseCore/Source/Models/NCListeningMode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NCListeningMode.swift 3 | // NoiseCore 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum NCListeningMode: String, CaseIterable { 12 | case normal = "AVOutputDeviceBluetoothListeningModeNormal" 13 | case anc = "AVOutputDeviceBluetoothListeningModeActiveNoiseCancellation" 14 | case transparency = "AVOutputDeviceBluetoothListeningModeAudioTransparency" 15 | } 16 | 17 | public extension NCListeningModeStatusProvider { 18 | 19 | var listeningMode: NCListeningMode { 20 | get { 21 | NCListeningMode(rawValue: _listeningMode) ?? .normal 22 | } 23 | set { 24 | _setListeningMode(newValue.rawValue) 25 | } 26 | } 27 | 28 | var availableListeningModes: [NCListeningMode] { 29 | _availableListeningModes.compactMap({ NCListeningMode(rawValue: $0) }) 30 | } 31 | 32 | } 33 | 34 | public extension NCDevice { 35 | 36 | var listeningMode: NCListeningMode { 37 | NCListeningMode(rawValue: _listeningMode) ?? .normal 38 | } 39 | 40 | var availableListeningModes: [NCListeningMode] { 41 | _availableListeningModes.compactMap({ NCListeningMode(rawValue: $0) }) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /NoiseCore/Source/NCAVListeningModeController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NCAVListeningModeController.h 3 | // NoiseCore 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | @class NCDevice; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @protocol NCListeningModeStatusProvider 16 | 17 | - (void)startListeningForUpdates; 18 | 19 | @property (nonatomic, copy) void(^outputDeviceDidChange)(NCDevice *__nullable device); 20 | 21 | @property (nonatomic, readonly) NSArray *_availableListeningModes; 22 | @property (nonatomic, copy) NSString *_listeningMode; 23 | - (void)_setListeningMode:(NSString *)listeningMode; 24 | 25 | @end 26 | 27 | @interface NCAVListeningModeController : NSObject 28 | 29 | - (void)startListeningForUpdates; 30 | 31 | @property (nonatomic, copy) void(^outputDeviceDidChange)(NCDevice *__nullable device); 32 | 33 | @property (nonatomic, readonly) NSArray *_availableListeningModes; 34 | @property (nonatomic, copy) NSString *_listeningMode; 35 | - (void)_setListeningMode:(NSString *)listeningMode; 36 | 37 | @end 38 | 39 | NS_ASSUME_NONNULL_END 40 | -------------------------------------------------------------------------------- /NoiseCore/Source/NCAVListeningModeController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NCAVListeningModeController.m 3 | // NoiseCore 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | #import "NCAVListeningModeController.h" 10 | 11 | @import AVFoundation; 12 | 13 | #import "AVFoundation-Private.h" 14 | 15 | #import "fishhook.h" 16 | 17 | #import "NCDevice.h" 18 | #import "NCDevice-Private.h" 19 | 20 | @import os.log; 21 | 22 | @interface NCDevice (AV) 23 | 24 | + (NCDevice *)deviceWithOutputDevice:(AVOutputDevice *)avDevice; 25 | 26 | @end 27 | 28 | // HACK! 29 | // AVFoundation does the entitlement checking for the system output context in-process, 30 | // which means we can just hook the entitlement-checking method at runtime and override it 31 | // to always return true. 32 | 33 | static CFTypeRef (*orig_stcvfe)(SecTaskRef _Nonnull task, CFStringRef _Nonnull entitlement, CFErrorRef _Nullable * _Nullable error); 34 | 35 | CFTypeRef my_SecTaskCopyValueForEntitlement(SecTaskRef _Nonnull task, CFStringRef _Nonnull entitlement, CFErrorRef _Nullable * _Nullable error) { 36 | if (kCFCompareEqualTo == CFStringCompare(entitlement, CFSTR("com.apple.avfoundation.allow-system-wide-context"), 0)) { 37 | return kCFBooleanTrue; 38 | } else { 39 | return orig_stcvfe(task, entitlement, error); 40 | } 41 | } 42 | 43 | static void hookAVFEntitlement() { 44 | rebind_symbols((struct rebinding[1]){"SecTaskCopyValueForEntitlement", my_SecTaskCopyValueForEntitlement, (void *)&orig_stcvfe}, 1); 45 | } 46 | 47 | @interface NCAVListeningModeController () 48 | 49 | @property (strong) os_log_t log; 50 | @property (strong) AVOutputContext *context; 51 | @property (nonatomic, strong) AVOutputDevice *currentOutputDevice; 52 | 53 | // TODO: Figure out a way to get notified when the current output device changes, instead of using this timer hack. 54 | @property (nonatomic, strong) NSTimer *deviceTimer; 55 | 56 | @end 57 | 58 | @implementation NCAVListeningModeController 59 | 60 | + (instancetype)shared 61 | { 62 | static NCAVListeningModeController *controller; 63 | static dispatch_once_t onceToken; 64 | dispatch_once(&onceToken, ^{ 65 | controller = [NCAVListeningModeController new]; 66 | }); 67 | return controller; 68 | } 69 | 70 | - (instancetype)init 71 | { 72 | self = [super init]; 73 | 74 | self.log = os_log_create("codes.rambo.NoiseCore", "NCAVListeningModeController"); 75 | 76 | return self; 77 | } 78 | 79 | - (void)startListeningForUpdates 80 | { 81 | os_log_debug(self.log, "%{public}@", NSStringFromSelector(_cmd)); 82 | 83 | hookAVFEntitlement(); 84 | 85 | self.context = [AVOutputContext sharedSystemAudioContext]; 86 | 87 | self.deviceTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkOutputDevice:) userInfo:nil repeats:YES]; 88 | self.deviceTimer.tolerance = 5.0; 89 | 90 | self.currentOutputDevice = self.context.outputDevices.firstObject; 91 | 92 | [self _callOutputDeviceDidChangeWithDevice:self.currentOutputDevice]; 93 | } 94 | 95 | - (void)checkOutputDevice:(id)sender 96 | { 97 | AVOutputDevice *previousDevice = self.currentOutputDevice; 98 | AVOutputDevice *currentDevice = self.context.outputDevices.firstObject; 99 | 100 | if (!previousDevice && !currentDevice) return; 101 | 102 | if ([previousDevice.deviceID isEqualToString:currentDevice.deviceID]) return; 103 | 104 | self.currentOutputDevice = self.context.outputDevices.firstObject; 105 | 106 | [self _callOutputDeviceDidChangeWithDevice:self.currentOutputDevice]; 107 | } 108 | 109 | - (void)_callOutputDeviceDidChangeWithDevice:(AVOutputDevice *)outputDevice 110 | { 111 | os_log_debug(self.log, "%{public}@ %@", NSStringFromSelector(_cmd), outputDevice); 112 | 113 | if (!self.outputDeviceDidChange) return; 114 | 115 | NCDevice *dev = (outputDevice) ? [NCDevice deviceWithOutputDevice:outputDevice] : nil; 116 | 117 | dispatch_async(dispatch_get_main_queue(), ^{ 118 | self.outputDeviceDidChange(dev); 119 | }); 120 | } 121 | 122 | - (void)_setListeningMode:(NSString *)listeningMode 123 | { 124 | NSError *error; 125 | 126 | if (![self.currentOutputDevice setCurrentBluetoothListeningMode:listeningMode error:&error]) { 127 | os_log_error(self.log, "Error setting listening mode on %@: %{public}@", self.currentOutputDevice.name, error); 128 | } else { 129 | os_log_debug(self.log, "Changed listening mode on %@ to %@", self.currentOutputDevice.name, listeningMode); 130 | } 131 | } 132 | 133 | - (NSString *)_listeningMode 134 | { 135 | return [self.currentOutputDevice.currentBluetoothListeningMode copy]; 136 | } 137 | 138 | - (NSArray *)_availableListeningModes 139 | { 140 | return [self.currentOutputDevice.availableBluetoothListeningModes copy]; 141 | } 142 | 143 | @end 144 | 145 | @implementation NCDevice (AV) 146 | 147 | + (NCDevice *)deviceWithOutputDevice:(AVOutputDevice *)avDevice 148 | { 149 | NCDevice *device = [NCDevice new]; 150 | 151 | device.identifier = avDevice.deviceID; 152 | device.name = avDevice.name; 153 | device._listeningMode = avDevice.currentBluetoothListeningMode; 154 | device._availableListeningModes = avDevice.availableBluetoothListeningModes; 155 | device.avDevice = avDevice; 156 | 157 | return device; 158 | } 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /NoiseCore/Source/NCBTListeningModeController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NCBTListeningModeController.swift 3 | // NoiseCore 4 | // 5 | // Created by Guilherme Rambo on 14/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import IOBluetooth 11 | import os.log 12 | 13 | public final class NCBTListeningModeController: NSObject, NCListeningModeStatusProvider { 14 | 15 | private let log = OSLog(subsystem: "codes.rambo.NoiseCore", category: "NCBTListeningModeController") 16 | 17 | private var airPodsProDevices: [IOBluetoothDevice] { 18 | IOBluetoothDevice.pairedDevices()? 19 | .compactMap { $0 as? IOBluetoothDevice } 20 | .filter { $0.isANCSupported() } ?? [] 21 | } 22 | 23 | private var currentDevice: IOBluetoothDevice? { 24 | airPodsProDevices.first 25 | } 26 | 27 | private var connectionNotification: IOBluetoothUserNotification? 28 | 29 | public func startListeningForUpdates() { 30 | guard connectionNotification == nil else { return } 31 | 32 | connectionNotification = IOBluetoothDevice.register(forConnectNotifications: self, selector: #selector(connectionNotificationReceived)) 33 | 34 | sendDeviceDidChange() 35 | } 36 | 37 | private func sendDeviceDidChange() { 38 | DispatchQueue.main.async { 39 | guard let currentDevice = self.currentDevice else { return } 40 | 41 | let device = NCDevice(btDevice: currentDevice) 42 | self.outputDeviceDidChange(device) 43 | } 44 | } 45 | 46 | public var outputDeviceDidChange: (NCDevice?) -> Void = { _ in } 47 | 48 | public var _availableListeningModes: [String] = [] 49 | 50 | public var _listeningMode: String = NCListeningMode.normal.rawValue 51 | 52 | public func _setListeningMode(_ listeningMode: String) { 53 | guard let device = currentDevice else { 54 | os_log("AirPods Pro device not found", log: self.log, type: .error) 55 | return 56 | } 57 | 58 | let mode = UInt8(listeningModeName: listeningMode) 59 | 60 | os_log("Setting listening mode on %@ to %d", log: self.log, type: .debug, device.name ?? "", mode) 61 | 62 | device.listeningMode = mode 63 | 64 | _listeningMode = listeningMode 65 | } 66 | 67 | @objc private func connectionNotificationReceived(_ sender: Any) { 68 | os_log("%{public}@", log: log, type: .debug, #function) 69 | 70 | guard let device = currentDevice else { 71 | _availableListeningModes = [] 72 | return 73 | } 74 | 75 | os_log("Supported device found: %@", log: self.log, type: .debug, String(describing: device)) 76 | 77 | _availableListeningModes = NCListeningMode.allCases.map { $0.rawValue } 78 | 79 | sendDeviceDidChange() 80 | } 81 | 82 | deinit { 83 | connectionNotification?.unregister() 84 | } 85 | 86 | } 87 | 88 | extension UInt8 { 89 | init(listeningModeName: String) { 90 | switch listeningModeName { 91 | case NCListeningMode.anc.rawValue: self = 2 92 | case NCListeningMode.transparency.rawValue: self = 3 93 | default: self = 1 94 | } 95 | } 96 | } 97 | 98 | extension String { 99 | init(listeningModeCode: UInt8) { 100 | switch listeningModeCode { 101 | case 2: self = NCListeningMode.anc.rawValue 102 | case 3: self = NCListeningMode.transparency.rawValue 103 | default: self = NCListeningMode.normal.rawValue 104 | } 105 | } 106 | } 107 | 108 | extension NCDevice { 109 | convenience init(btDevice: IOBluetoothDevice) { 110 | self.init() 111 | 112 | self.identifier = btDevice.addressString ?? "" 113 | self.name = btDevice.name ?? "" 114 | self._listeningMode = String(listeningModeCode: btDevice.listeningMode) 115 | self._availableListeningModes = NCListeningMode.allCases.map { $0.rawValue } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /NoiseCore/Source/Support/AVFoundation-Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVFoundation-Private.h 3 | // OutputDevices 4 | // 5 | // Created by Guilherme Rambo on 13/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | @import AVFoundation; 10 | 11 | @interface AVOutputDevice: NSObject 12 | 13 | @property(readonly, nonatomic) NSString *currentBluetoothListeningMode; 14 | @property(readonly, nonatomic) NSArray *availableBluetoothListeningModes; 15 | - (BOOL)setCurrentBluetoothListeningMode:(NSString *)mode error:(NSError **)outError; 16 | 17 | @property (readonly, nonatomic) NSString *deviceID; 18 | @property (readonly, nonatomic) NSString *name; 19 | 20 | @end 21 | 22 | @interface AVOutputContext: NSObject 23 | + (instancetype)sharedSystemAudioContext; 24 | @property (nonatomic, readonly) NSArray *outputDevices; 25 | @end 26 | -------------------------------------------------------------------------------- /NoiseCore/Source/Support/IOBluetooth-Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // IOBluetooth-Private.h 3 | // NoiseBuddy 4 | // 5 | // Created by Guilherme Rambo on 14/11/19. 6 | // Copyright © 2019 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | @import IOBluetooth; 10 | 11 | @interface IOBluetoothDevice (Private) 12 | 13 | - (BOOL)isANCSupported; 14 | 15 | @property(readonly) BOOL isTransparencySupported; 16 | @property(nonatomic) unsigned char listeningMode; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /NoiseCore/Source/Support/fishhook.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #include "fishhook.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef __LP64__ 40 | typedef struct mach_header_64 mach_header_t; 41 | typedef struct segment_command_64 segment_command_t; 42 | typedef struct section_64 section_t; 43 | typedef struct nlist_64 nlist_t; 44 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 45 | #else 46 | typedef struct mach_header mach_header_t; 47 | typedef struct segment_command segment_command_t; 48 | typedef struct section section_t; 49 | typedef struct nlist nlist_t; 50 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT 51 | #endif 52 | 53 | #ifndef SEG_DATA_CONST 54 | #define SEG_DATA_CONST "__DATA_CONST" 55 | #endif 56 | 57 | struct rebindings_entry { 58 | struct rebinding *rebindings; 59 | size_t rebindings_nel; 60 | struct rebindings_entry *next; 61 | }; 62 | 63 | static struct rebindings_entry *_rebindings_head; 64 | 65 | static int prepend_rebindings(struct rebindings_entry **rebindings_head, 66 | struct rebinding rebindings[], 67 | size_t nel) { 68 | struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); 69 | if (!new_entry) { 70 | return -1; 71 | } 72 | new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel); 73 | if (!new_entry->rebindings) { 74 | free(new_entry); 75 | return -1; 76 | } 77 | memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); 78 | new_entry->rebindings_nel = nel; 79 | new_entry->next = *rebindings_head; 80 | *rebindings_head = new_entry; 81 | return 0; 82 | } 83 | 84 | #if 0 85 | static int get_protection(void *addr, vm_prot_t *prot, vm_prot_t *max_prot) { 86 | mach_port_t task = mach_task_self(); 87 | vm_size_t size = 0; 88 | vm_address_t address = (vm_address_t)addr; 89 | memory_object_name_t object; 90 | #ifdef __LP64__ 91 | mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; 92 | vm_region_basic_info_data_64_t info; 93 | kern_return_t info_ret = vm_region_64( 94 | task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_64_t)&info, &count, &object); 95 | #else 96 | mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT; 97 | vm_region_basic_info_data_t info; 98 | kern_return_t info_ret = vm_region(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object); 99 | #endif 100 | if (info_ret == KERN_SUCCESS) { 101 | if (prot != NULL) 102 | *prot = info.protection; 103 | 104 | if (max_prot != NULL) 105 | *max_prot = info.max_protection; 106 | 107 | return 0; 108 | } 109 | 110 | return -1; 111 | } 112 | #endif 113 | 114 | static void perform_rebinding_with_section(struct rebindings_entry *rebindings, 115 | section_t *section, 116 | intptr_t slide, 117 | nlist_t *symtab, 118 | char *strtab, 119 | uint32_t *indirect_symtab) { 120 | uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; 121 | void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); 122 | 123 | for (uint i = 0; i < section->size / sizeof(void *); i++) { 124 | uint32_t symtab_index = indirect_symbol_indices[i]; 125 | if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || 126 | symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { 127 | continue; 128 | } 129 | uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; 130 | char *symbol_name = strtab + strtab_offset; 131 | bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; 132 | struct rebindings_entry *cur = rebindings; 133 | while (cur) { 134 | for (uint j = 0; j < cur->rebindings_nel; j++) { 135 | if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { 136 | kern_return_t err; 137 | 138 | if (cur->rebindings[j].replaced != NULL && indirect_symbol_bindings[i] != cur->rebindings[j].replacement) 139 | *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; 140 | 141 | /** 142 | * 1. Moved the vm protection modifying codes to here to reduce the 143 | * changing scope. 144 | * 2. Adding VM_PROT_WRITE mode unconditionally because vm_region 145 | * API on some iOS/Mac reports mismatch vm protection attributes. 146 | * -- Lianfu Hao Jun 16th, 2021 147 | **/ 148 | err = vm_protect (mach_task_self (), (uintptr_t)indirect_symbol_bindings, section->size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); 149 | if (err == KERN_SUCCESS) { 150 | /** 151 | * Once we failed to change the vm protection, we 152 | * MUST NOT continue the following write actions! 153 | * iOS 15 has corrected the const segments prot. 154 | * -- Lionfore Hao Jun 11th, 2021 155 | **/ 156 | indirect_symbol_bindings[i] = cur->rebindings[j].replacement; 157 | } 158 | goto symbol_loop; 159 | } 160 | } 161 | cur = cur->next; 162 | } 163 | symbol_loop:; 164 | } 165 | } 166 | 167 | static void rebind_symbols_for_image(struct rebindings_entry *rebindings, 168 | const struct mach_header *header, 169 | intptr_t slide) { 170 | Dl_info info; 171 | if (dladdr(header, &info) == 0) { 172 | return; 173 | } 174 | 175 | segment_command_t *cur_seg_cmd; 176 | segment_command_t *linkedit_segment = NULL; 177 | struct symtab_command* symtab_cmd = NULL; 178 | struct dysymtab_command* dysymtab_cmd = NULL; 179 | 180 | uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); 181 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 182 | cur_seg_cmd = (segment_command_t *)cur; 183 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 184 | if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { 185 | linkedit_segment = cur_seg_cmd; 186 | } 187 | } else if (cur_seg_cmd->cmd == LC_SYMTAB) { 188 | symtab_cmd = (struct symtab_command*)cur_seg_cmd; 189 | } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { 190 | dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; 191 | } 192 | } 193 | 194 | if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || 195 | !dysymtab_cmd->nindirectsyms) { 196 | return; 197 | } 198 | 199 | // Find base symbol/string table addresses 200 | uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; 201 | nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); 202 | char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); 203 | 204 | // Get indirect symbol table (array of uint32_t indices into symbol table) 205 | uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); 206 | 207 | cur = (uintptr_t)header + sizeof(mach_header_t); 208 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 209 | cur_seg_cmd = (segment_command_t *)cur; 210 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 211 | if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && 212 | strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { 213 | continue; 214 | } 215 | for (uint j = 0; j < cur_seg_cmd->nsects; j++) { 216 | section_t *sect = 217 | (section_t *)(cur + sizeof(segment_command_t)) + j; 218 | if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { 219 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 220 | } 221 | if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { 222 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 223 | } 224 | } 225 | } 226 | } 227 | } 228 | 229 | static void _rebind_symbols_for_image(const struct mach_header *header, 230 | intptr_t slide) { 231 | rebind_symbols_for_image(_rebindings_head, header, slide); 232 | } 233 | 234 | int rebind_symbols_image(void *header, 235 | intptr_t slide, 236 | struct rebinding rebindings[], 237 | size_t rebindings_nel) { 238 | struct rebindings_entry *rebindings_head = NULL; 239 | int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); 240 | rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); 241 | if (rebindings_head) { 242 | free(rebindings_head->rebindings); 243 | } 244 | free(rebindings_head); 245 | return retval; 246 | } 247 | 248 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { 249 | int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); 250 | if (retval < 0) { 251 | return retval; 252 | } 253 | // If this was the first call, register callback for image additions (which is also invoked for 254 | // existing images, otherwise, just run on existing images 255 | if (!_rebindings_head->next) { 256 | _dyld_register_func_for_add_image(_rebind_symbols_for_image); 257 | } else { 258 | uint32_t c = _dyld_image_count(); 259 | for (uint32_t i = 0; i < c; i++) { 260 | _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); 261 | } 262 | } 263 | return retval; 264 | } 265 | -------------------------------------------------------------------------------- /NoiseCore/Source/Support/fishhook.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #ifndef fishhook_h 25 | #define fishhook_h 26 | 27 | #include 28 | #include 29 | 30 | #if !defined(FISHHOOK_EXPORT) 31 | #define FISHHOOK_VISIBILITY __attribute__((visibility("hidden"))) 32 | #else 33 | #define FISHHOOK_VISIBILITY __attribute__((visibility("default"))) 34 | #endif 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif //__cplusplus 39 | 40 | /* 41 | * A structure representing a particular intended rebinding from a symbol 42 | * name to its replacement 43 | */ 44 | struct rebinding { 45 | const char *name; 46 | void *replacement; 47 | void **replaced; 48 | }; 49 | 50 | /* 51 | * For each rebinding in rebindings, rebinds references to external, indirect 52 | * symbols with the specified name to instead point at replacement for each 53 | * image in the calling process as well as for all future images that are loaded 54 | * by the process. If rebind_functions is called more than once, the symbols to 55 | * rebind are added to the existing list of rebindings, and if a given symbol 56 | * is rebound more than once, the later rebinding will take precedence. 57 | */ 58 | FISHHOOK_VISIBILITY 59 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); 60 | 61 | /* 62 | * Rebinds as above, but only in the specified image. The header should point 63 | * to the mach-o header, the slide should be the slide offset. Others as above. 64 | */ 65 | FISHHOOK_VISIBILITY 66 | int rebind_symbols_image(void *header, 67 | intptr_t slide, 68 | struct rebinding rebindings[], 69 | size_t rebindings_nel); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif //__cplusplus 74 | 75 | #endif //fishhook_h 76 | 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NoiseBuddy 2 | 3 | **NOTE: This project doesn't work in recent versions of macOS. Similar functionality is available as part of [AirBuddy](https://airbuddy.app)** 4 | 5 | Change noise cancelling settings on your AirPods Pro or Beats Solo Pro directly from your Mac's Menu Bar or Touch Bar. 6 | 7 | I wanted an easier way to change the listening mode settings on AirPods Pro and Beats Solo Pro on the Mac, without having to open the volume menu, so I made this simple app that shows a button in both the Menu Bar and Touch Bar that lets you toggle between the ANC and Transparency modes with a single click. When your Mac is not using a device that supports listening modes, the button will not show up. 8 | 9 | You can customize where the button will appear by launching the app and changing the options in its settings screen. 10 | 11 | ![screenshot-menubar](./screenshots/screenshot-menubar.png) 12 | ![screenshot-touchbar](./screenshots/screenshot-touchbar.png) 13 | 14 | ## Installing 15 | 16 | **Requires macOS Catalina**. 17 | 18 | Download the [latest release](https://github.com/insidegui/NoiseBuddy/releases) or build from source. 19 | 20 | _AirPods, Touch Bar and Beats Solo Pro are trademarks of Apple Inc., registered in the U.S. and other countries. This app is not made by, affiliated with or endorsed by Apple._ 21 | 22 | -------------------------------------------------------------------------------- /screenshots/NoiseBuddyIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/screenshots/NoiseBuddyIcon.png -------------------------------------------------------------------------------- /screenshots/screenshot-menubar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/screenshots/screenshot-menubar.png -------------------------------------------------------------------------------- /screenshots/screenshot-touchbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/NoiseBuddy/9d1a32d244d7063a21132a90413203351768cefc/screenshots/screenshot-touchbar.png --------------------------------------------------------------------------------