├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── other.md └── workflows │ └── build-test-release.yml ├── .gitignore ├── BGM.xcworkspace └── contents.xcworkspacedata ├── BGMApp ├── BGMApp.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── BGMXPCHelper.xcscheme │ │ └── Background Music.xcscheme ├── BGMApp │ ├── BGMApp-Debug.entitlements │ ├── BGMApp.entitlements │ ├── BGMAppDelegate.h │ ├── BGMAppDelegate.mm │ ├── BGMAppVolumes.h │ ├── BGMAppVolumes.m │ ├── BGMAppVolumesController.h │ ├── BGMAppVolumesController.mm │ ├── BGMAppWatcher.h │ ├── BGMAppWatcher.m │ ├── BGMAudioDevice.cpp │ ├── BGMAudioDevice.h │ ├── BGMAudioDeviceManager.h │ ├── BGMAudioDeviceManager.mm │ ├── BGMAutoPauseMenuItem.h │ ├── BGMAutoPauseMenuItem.m │ ├── BGMAutoPauseMusic.h │ ├── BGMAutoPauseMusic.mm │ ├── BGMBackgroundMusicDevice.cpp │ ├── BGMBackgroundMusicDevice.h │ ├── BGMDebugLoggingMenuItem.h │ ├── BGMDebugLoggingMenuItem.m │ ├── BGMDeviceControlSync.cpp │ ├── BGMDeviceControlSync.h │ ├── BGMDeviceControlsList.cpp │ ├── BGMDeviceControlsList.h │ ├── BGMOutputDeviceMenuSection.h │ ├── BGMOutputDeviceMenuSection.mm │ ├── BGMOutputVolumeMenuItem.h │ ├── BGMOutputVolumeMenuItem.mm │ ├── BGMPlayThrough.cpp │ ├── BGMPlayThrough.h │ ├── BGMPlayThroughRTLogger.cpp │ ├── BGMPlayThroughRTLogger.h │ ├── BGMPreferredOutputDevices.h │ ├── BGMPreferredOutputDevices.mm │ ├── BGMStatusBarItem.h │ ├── BGMStatusBarItem.mm │ ├── BGMSystemSoundsVolume.h │ ├── BGMSystemSoundsVolume.mm │ ├── BGMTermination.h │ ├── BGMTermination.mm │ ├── BGMUserDefaults.h │ ├── BGMUserDefaults.m │ ├── BGMVolumeChangeListener.cpp │ ├── BGMVolumeChangeListener.h │ ├── BGMXPCListener.h │ ├── BGMXPCListener.mm │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Images.xcassets │ │ ├── AirPlayIcon.imageset │ │ │ ├── AirPlay.pdf │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── appicon_1024.png │ │ │ ├── appicon_128.png │ │ │ ├── appicon_16.png │ │ │ ├── appicon_256.png │ │ │ ├── appicon_32.png │ │ │ ├── appicon_512.png │ │ │ └── appicon_64.png │ │ ├── Contents.json │ │ ├── FermataIcon.imageset │ │ │ ├── Contents.json │ │ │ └── FermataIcon.pdf │ │ ├── Volume0.imageset │ │ │ ├── Contents.json │ │ │ └── Volume0.pdf │ │ ├── Volume1.imageset │ │ │ ├── Contents.json │ │ │ └── Volume1.pdf │ │ ├── Volume2.imageset │ │ │ ├── Contents.json │ │ │ └── Volume2.pdf │ │ └── Volume3.imageset │ │ │ ├── Contents.json │ │ │ └── Volume3.pdf │ ├── Info.plist │ ├── LICENSE │ ├── Music Players │ │ ├── BGMDecibel.h │ │ ├── BGMDecibel.m │ │ ├── BGMGooglePlayMusicDesktopPlayer.h │ │ ├── BGMGooglePlayMusicDesktopPlayer.m │ │ ├── BGMGooglePlayMusicDesktopPlayerConnection.h │ │ ├── BGMGooglePlayMusicDesktopPlayerConnection.m │ │ ├── BGMHermes.h │ │ ├── BGMHermes.m │ │ ├── BGMMusic.h │ │ ├── BGMMusic.m │ │ ├── BGMMusicPlayer.h │ │ ├── BGMMusicPlayer.m │ │ ├── BGMMusicPlayers.h │ │ ├── BGMMusicPlayers.mm │ │ ├── BGMScriptingBridge.h │ │ ├── BGMScriptingBridge.m │ │ ├── BGMSpotify.h │ │ ├── BGMSpotify.m │ │ ├── BGMSwinsian.h │ │ ├── BGMSwinsian.m │ │ ├── BGMVLC.h │ │ ├── BGMVLC.m │ │ ├── BGMVOX.h │ │ ├── BGMVOX.m │ │ ├── BGMiTunes.h │ │ ├── BGMiTunes.m │ │ ├── Decibel.h │ │ ├── GooglePlayMusicDesktopPlayer.js │ │ ├── Hermes.h │ │ ├── Music.h │ │ ├── Spotify.h │ │ ├── Swinsian.h │ │ ├── VLC.h │ │ ├── VOX.h │ │ └── iTunes.h │ ├── Preferences │ │ ├── BGMAboutPanel.h │ │ ├── BGMAboutPanel.m │ │ ├── BGMAutoPauseMusicPrefs.h │ │ ├── BGMAutoPauseMusicPrefs.mm │ │ ├── BGMPreferencesMenu.h │ │ └── BGMPreferencesMenu.mm │ ├── Scripting │ │ ├── BGMASApplication.h │ │ ├── BGMASApplication.m │ │ ├── BGMASOutputDevice.h │ │ ├── BGMASOutputDevice.mm │ │ ├── BGMApp.sdef │ │ ├── BGMAppDelegate+AppleScript.h │ │ └── BGMAppDelegate+AppleScript.mm │ ├── SystemPreferences.h │ ├── _uninstall-non-interactive.sh │ └── main.m ├── BGMAppTests │ ├── UITests │ │ ├── BGMApp.h │ │ ├── BGMAppUITests-Info.plist │ │ ├── BGMAppUITests.mm │ │ └── skip-ui-tests.py │ └── UnitTests │ │ ├── BGMAppUnitTests-Info.plist │ │ ├── BGMMusicPlayersUnitTests.mm │ │ ├── BGMPlayThroughRTLoggerTests.mm │ │ ├── BGMPlayThroughTests.mm │ │ └── Mocks │ │ ├── MockAudioDevice.cpp │ │ ├── MockAudioDevice.h │ │ ├── MockAudioObject.cpp │ │ ├── MockAudioObject.h │ │ ├── MockAudioObjects.cpp │ │ ├── MockAudioObjects.h │ │ ├── Mock_CAHALAudioDevice.cpp │ │ ├── Mock_CAHALAudioObject.cpp │ │ └── Mock_CAHALAudioSystemObject.cpp ├── BGMThreadSafetyAnalysis.h ├── BGMXPCHelper │ ├── BGMXPCHelperService.h │ ├── BGMXPCHelperService.mm │ ├── BGMXPCListenerDelegate.h │ ├── BGMXPCListenerDelegate.m │ ├── Info.plist │ ├── com.bearisdriving.BGM.XPCHelper.plist.template │ ├── main.m │ ├── post_install.sh │ └── safe_install_dir.sh ├── BGMXPCHelperTests │ ├── BGMXPCHelperTests-Info.plist │ └── BGMXPCHelperTests.m ├── OptimizationProfiles │ └── BGMApp.profdata └── PublicUtility │ ├── BGMDebugLogging.c │ ├── BGMDebugLogging.h │ ├── CAAtomic.h │ ├── CAAutoDisposer.h │ ├── CABitOperations.h │ ├── CACFArray.cpp │ ├── CACFArray.h │ ├── CACFDictionary.cpp │ ├── CACFDictionary.h │ ├── CACFNumber.cpp │ ├── CACFNumber.h │ ├── CACFString.cpp │ ├── CACFString.h │ ├── CADebugMacros.cpp │ ├── CADebugMacros.h │ ├── CADebugPrintf.cpp │ ├── CADebugPrintf.h │ ├── CADebugger.cpp │ ├── CADebugger.h │ ├── CAException.h │ ├── CAHALAudioDevice.cpp │ ├── CAHALAudioDevice.h │ ├── CAHALAudioObject.cpp │ ├── CAHALAudioObject.h │ ├── CAHALAudioStream.cpp │ ├── CAHALAudioStream.h │ ├── CAHALAudioSystemObject.cpp │ ├── CAHALAudioSystemObject.h │ ├── CAHostTimeBase.cpp │ ├── CAHostTimeBase.h │ ├── CAMutex.cpp │ ├── CAMutex.h │ ├── CAPThread.cpp │ ├── CAPThread.h │ ├── CAPropertyAddress.h │ ├── CARingBuffer.cpp │ └── CARingBuffer.h ├── BGMDriver ├── BGMDriver.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── Background Music Device.xcscheme │ │ └── PublicUtility.xcscheme ├── BGMDriver │ ├── BGM_AbstractDevice.cpp │ ├── BGM_AbstractDevice.h │ ├── BGM_AudibleState.cpp │ ├── BGM_AudibleState.h │ ├── BGM_Control.cpp │ ├── BGM_Control.h │ ├── BGM_Device.cpp │ ├── BGM_Device.h │ ├── BGM_MuteControl.cpp │ ├── BGM_MuteControl.h │ ├── BGM_NullDevice.cpp │ ├── BGM_NullDevice.h │ ├── BGM_Object.cpp │ ├── BGM_Object.h │ ├── BGM_PlugIn.cpp │ ├── BGM_PlugIn.h │ ├── BGM_PlugInInterface.cpp │ ├── BGM_Stream.cpp │ ├── BGM_Stream.h │ ├── BGM_TaskQueue.cpp │ ├── BGM_TaskQueue.h │ ├── BGM_VolumeControl.cpp │ ├── BGM_VolumeControl.h │ ├── BGM_WrappedAudioEngine.cpp │ ├── BGM_WrappedAudioEngine.h │ ├── BGM_XPCHelper.h │ ├── BGM_XPCHelper.m │ ├── DeviceClients │ │ ├── BGM_Client.cpp │ │ ├── BGM_Client.h │ │ ├── BGM_ClientMap.cpp │ │ ├── BGM_ClientMap.h │ │ ├── BGM_ClientTasks.h │ │ ├── BGM_Clients.cpp │ │ └── BGM_Clients.h │ ├── DeviceIcon.icns │ ├── Info.plist │ └── quick_install.sh ├── BGMDriverTests │ ├── BGM_ClientMapTests.mm │ ├── BGM_ClientsTests.mm │ ├── BGM_DeviceTests.mm │ └── Info.plist └── PublicUtility │ ├── CAAtomic.h │ ├── CAAtomicStack.h │ ├── CAAutoDisposer.h │ ├── CABitOperations.h │ ├── CACFArray.cpp │ ├── CACFArray.h │ ├── CACFDictionary.cpp │ ├── CACFDictionary.h │ ├── CACFNumber.cpp │ ├── CACFNumber.h │ ├── CACFString.cpp │ ├── CACFString.h │ ├── CADebugMacros.cpp │ ├── CADebugMacros.h │ ├── CADebugPrintf.cpp │ ├── CADebugPrintf.h │ ├── CADebugger.cpp │ ├── CADebugger.h │ ├── CADispatchQueue.cpp │ ├── CADispatchQueue.h │ ├── CAException.h │ ├── CAHostTimeBase.cpp │ ├── CAHostTimeBase.h │ ├── CAMutex.cpp │ ├── CAMutex.h │ ├── CAPThread.cpp │ ├── CAPThread.h │ ├── CAPropertyAddress.h │ ├── CARingBuffer.cpp │ ├── CARingBuffer.h │ ├── CAVolumeCurve.cpp │ └── CAVolumeCurve.h ├── CONTRIBUTING.md ├── DEVELOPING.md ├── Images ├── FermataIcon.pdf ├── FermataIcon.tex ├── README │ ├── FermataIcon.png │ ├── Screenshot.png │ └── pkg-icon.png ├── VolumeIcons.tex ├── generate_icon_pngs.sh └── iconizer.sh ├── LICENSE ├── LICENSE-Apple-Sample-Code ├── MANUAL-INSTALL.md ├── MANUAL-UNINSTALL.md ├── README.md ├── SharedSource ├── BGMXPCProtocols.h ├── BGM_TestUtils.h ├── BGM_Types.h ├── BGM_Utils.cpp ├── BGM_Utils.h └── Scripts │ └── set-version.sh ├── TODO.md ├── build_and_install.sh ├── package.sh ├── pkg ├── Distribution.xml.template ├── ListInputDevices.swift ├── README.md ├── pkgbuild.plist ├── postinstall └── preinstall └── uninstall.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file was added to get GitHub to display our source code correctly when 2 | # it has mixed tabs and spaces. (I didn't realise the sample code BGMDriver is 3 | # based on used tabs and it's too late to fix it now.) 4 | # 5 | # See http://editorconfig.org. 6 | 7 | # This is the top-most .editorconfig file. 8 | root = true 9 | 10 | # Set tabs to the width of 4 spaces in C, C++, Objective-C and Objective-C++ 11 | # source files. 12 | [*.{h,c,cpp,m,mm}] 13 | tab_width = 4 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Example bug report template 11 | 12 | > Don't worry if you have trouble getting some of this info. Just leave it out. 13 | 14 | **Description of the bug** 15 | > Please don't just say it's "not working". 16 | 17 | **Steps to reproduce** 18 | > Steps to reproduce the bug. This usually doesn't need to be super detailed. 19 | 1. Go to '...' 20 | 2. Click on '...' 21 | 3. See error message '...' 22 | 23 | **Versions** 24 | > Please complete the following information. 25 | - Background Music: [e.g. "0.4.3" or "0.4.0-SNAPSHOT-c0ab98b". `Preferences > About Background Music`] 26 | - macOS: [e.g. "11.3 Beta (20E5172i)" or "Big Sur". ` > About This Mac`] 27 | 28 | **Hardware** 29 | > Delete this part if you think it's probably not necessary. 30 | - Computer: [e.g. "MacBook Pro (13-inch, 2016, Four Thunderbolt 3 Ports)". ` > About This Mac`] 31 | - Audio Device: [e.g. "Built-in Output. Manufacturer: Apple Inc. Output Channels: 2 [...]". `System Information app > Hardware > Audio`] 32 | 33 | **Debug logs** 34 | > If you think the developers might not be able to reproduce the bug on their computers, e.g. because an important feature is completely broken and they would have noticed, it can help to include [debug logs](https://github.com/kyleneideck/BackgroundMusic/wiki/Getting-Debug-Logs). This takes a little effort, so feel free to leave it out at first. 35 | 36 | [Debug logs attached here](https://github.com/example/background-music-debug-logs.txt) 37 | 38 | **Other info** 39 | > Anything else you want to add? 40 | 41 | --- 42 | 43 | > Tips 44 | > (Delete this section before posting.) 45 | > - https://github.com/kyleneideck/BackgroundMusic#troubleshooting 46 | > - Try the latest SNAPSHOT version from https://github.com/kyleneideck/BackgroundMusic/releases (if it's newer than the latest non-SNAPSHOT release). 47 | > - If your bug is one of these common issues, consider leaving a comment or a +1 (👍) on an existing issue: 48 | > - Background Music currently only supports audio devices with two channels. Bluetooth devices often only have one. 49 | > - Volumes having no effect for certain apps: Microsoft Teams ([workaround](https://github.com/kyleneideck/BackgroundMusic/issues/268#issuecomment-604977210)), Zoom ([workaround](https://github.com/kyleneideck/BackgroundMusic/issues/396#issuecomment-741992157)), Discord ([workaround](https://github.com/kyleneideck/BackgroundMusic/issues/210#issuecomment-507048957), [see also](https://github.com/kyleneideck/BackgroundMusic/issues/267#issuecomment-617327850)), Chrome (sometimes) 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: Feature request, question, support request or anything else 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | > There's no template for this issue type. I just wanted to make it clear that it's OK to submit other types of issues. 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .*.swp 3 | /BGMDriver/BGMDriver/quick_install.conf 4 | /build_and_install.log 5 | .idea/ 6 | tags 7 | cmake-build-debug/ 8 | /Background-Music-*/ 9 | BGM.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist 10 | Images/*.aux 11 | Images/*.log 12 | /archives/ 13 | 14 | # Everything below is from https://github.com/github/gitignore/blob/master/Objective-C.gitignore 15 | 16 | ## Build generated 17 | build/ 18 | DerivedData 19 | 20 | ## Various settings 21 | *.pbxuser 22 | !default.pbxuser 23 | *.mode1v3 24 | !default.mode1v3 25 | *.mode2v3 26 | !default.mode2v3 27 | *.perspectivev3 28 | !default.perspectivev3 29 | xcuserdata 30 | 31 | ## Other 32 | *.xccheckout 33 | *.moved-aside 34 | *.xcuserstate 35 | *.xcscmblueprint 36 | 37 | ## Obj-C/Swift specific 38 | *.hmap 39 | *.ipa 40 | -------------------------------------------------------------------------------- /BGM.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 21 | 22 | 24 | 25 | 27 | 28 | 30 | 31 | 33 | 34 | 36 | 37 | 39 | 40 | 42 | 43 | 45 | 46 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMApp-Debug.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.automation.apple-events 6 | 7 | com.apple.security.device.audio-input 8 | 9 | 12 | com.apple.security.cs.disable-library-validation 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMApp.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.automation.apple-events 6 | 7 | com.apple.security.device.audio-input 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMAppDelegate.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMAppDelegate.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016, 2017, 2020 Kyle Neideck 21 | // Copyright © 2021 Marcus Wu 22 | // 23 | // Sets up and tears down the app. 24 | // 25 | 26 | // System Includes 27 | #import 28 | 29 | @class BGMAudioDeviceManager; 30 | @class BGMAppVolumesController; 31 | 32 | // Tags for UI elements in MainMenu.xib 33 | static NSInteger const kVolumesHeadingMenuItemTag = 3; 34 | static NSInteger const kSeparatorBelowVolumesMenuItemTag = 4; 35 | 36 | @interface BGMAppDelegate : NSObject 37 | 38 | @property (weak) IBOutlet NSMenu* bgmMenu; 39 | 40 | @property (weak) IBOutlet NSView* outputVolumeView; 41 | @property (weak) IBOutlet NSTextField* outputVolumeLabel; 42 | @property (weak) IBOutlet NSSlider* outputVolumeSlider; 43 | 44 | @property (weak) IBOutlet NSView* systemSoundsView; 45 | @property (weak) IBOutlet NSSlider* systemSoundsSlider; 46 | 47 | @property (weak) IBOutlet NSView* appVolumeView; 48 | 49 | @property (weak) IBOutlet NSPanel* aboutPanel; 50 | @property (unsafe_unretained) IBOutlet NSTextView* aboutPanelLicenseView; 51 | 52 | @property (weak) IBOutlet NSMenuItem* autoPauseMenuItemUnwrapped; 53 | @property (weak) IBOutlet NSMenuItem* debugLoggingMenuItemUnwrapped; 54 | 55 | @property (readonly) BGMAudioDeviceManager* audioDevices; 56 | @property BGMAppVolumesController* appVolumes; 57 | 58 | @end 59 | 60 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMAppVolumes.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMAppVolumes.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016, 2017 Kyle Neideck 21 | // Copyright © 2021 Marcus Wu 22 | // 23 | 24 | // Local Includes 25 | #import "BGMAppVolumesController.h" 26 | 27 | // System Includes 28 | #import 29 | 30 | 31 | #pragma clang assume_nonnull begin 32 | 33 | @interface BGMAppVolumes : NSObject 34 | 35 | - (id) initWithController:(BGMAppVolumesController*)inController 36 | bgmMenu:(NSMenu*)inMenu 37 | appVolumeView:(NSView*)inView; 38 | 39 | // Pass -1 for initialVolume or kAppPanNoValue for initialPan to leave the volume/pan at its default level. 40 | - (void) insertMenuItemForApp:(NSRunningApplication*)app 41 | initialVolume:(int)volume 42 | initialPan:(int)pan; 43 | 44 | - (void) removeMenuItemForApp:(NSRunningApplication*)app; 45 | 46 | - (void) removeAllAppVolumeMenuItems; 47 | 48 | - (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication*)app; 49 | - (void) setVolumeAndPan:(BGMAppVolumeAndPan)volumeAndPan forApp:(NSRunningApplication*)app; 50 | 51 | @end 52 | 53 | // Protocol for the UI custom classes 54 | 55 | @protocol BGMAppVolumeMenuItemSubview 56 | 57 | - (void) setUpWithApp:(NSRunningApplication*)app 58 | context:(BGMAppVolumes*)ctx 59 | controller:(BGMAppVolumesController*)ctrl 60 | menuItem:(NSMenuItem*)item; 61 | 62 | @end 63 | 64 | // Custom classes for the UI elements in the app volume menu items 65 | 66 | @interface BGMAVM_AppIcon : NSImageView 67 | @end 68 | 69 | @interface BGMAVM_AppNameLabel : NSTextField 70 | @end 71 | 72 | @interface BGMAVM_ShowMoreControlsButton : NSButton 73 | @end 74 | 75 | @interface BGMAVM_VolumeSlider : NSSlider 76 | 77 | - (void) setRelativeVolume:(int)relativeVolume; 78 | 79 | @end 80 | 81 | @interface BGMAVM_PanSlider : NSSlider 82 | 83 | - (void) setPanPosition:(int)panPosition; 84 | 85 | @end 86 | 87 | #pragma clang assume_nonnull end 88 | 89 | 90 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMAppVolumesController.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMAppVolumesController.h 18 | // BGMApp 19 | // 20 | // Copyright © 2017 Kyle Neideck 21 | // Copyright © 2021 Marcus Wu 22 | // 23 | 24 | // Local Includes 25 | #import "BGMAudioDeviceManager.h" 26 | 27 | // System Includes 28 | #import 29 | 30 | 31 | #pragma clang assume_nonnull begin 32 | 33 | typedef struct BGMAppVolumeAndPan { 34 | int volume; 35 | int pan; 36 | } BGMAppVolumeAndPan; 37 | 38 | @interface BGMAppVolumesController : NSObject 39 | 40 | - (id) initWithMenu:(NSMenu*)menu 41 | appVolumeView:(NSView*)view 42 | audioDevices:(BGMAudioDeviceManager*)audioDevices; 43 | 44 | // See BGMBackgroundMusicDevice::SetAppVolume. 45 | - (void) setVolume:(SInt32)volume 46 | forAppWithProcessID:(pid_t)processID 47 | bundleID:(NSString* __nullable)bundleID; 48 | 49 | // See BGMBackgroundMusicDevice::SetPanVolume. 50 | - (void) setPanPosition:(SInt32)pan 51 | forAppWithProcessID:(pid_t)processID 52 | bundleID:(NSString* __nullable)bundleID; 53 | 54 | - (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication *)app; 55 | - (void) setVolumeAndPan:(BGMAppVolumeAndPan)volumeAndPan forApp:(NSRunningApplication*)app; 56 | 57 | @end 58 | 59 | #pragma clang assume_nonnull end 60 | 61 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMAppWatcher.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMAppWatcher.h 18 | // BGMApp 19 | // 20 | // Copyright © 2019 Kyle Neideck 21 | // 22 | // Calls callback functions when a given application is launched or terminated. Starts watching 23 | // after being initialised, stops after being destroyed. 24 | // 25 | 26 | // System Includes 27 | #import 28 | 29 | 30 | #pragma clang assume_nonnull begin 31 | 32 | @interface BGMAppWatcher : NSObject 33 | 34 | // appLaunched will be called when the application is launched and appTerminated will be called when 35 | // it's terminated. Background apps, status bar apps, etc. are ignored. 36 | - (instancetype) initWithBundleID:(NSString*)bundleID 37 | appLaunched:(void(^)(void))appLaunched 38 | appTerminated:(void(^)(void))appTerminated; 39 | 40 | // With this constructor, when an application is launched or terminated, isMatchingBundleID will be 41 | // called first to decide whether or not the callback should be called. 42 | - (instancetype) initWithAppLaunched:(void(^)(void))appLaunched 43 | appTerminated:(void(^)(void))appTerminated 44 | isMatchingBundleID:(BOOL(^)(NSString* appBundleID))isMatchingBundleID; 45 | 46 | @end 47 | 48 | #pragma clang assume_nonnull end 49 | 50 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMAutoPauseMenuItem.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMAutoPauseMenuItem.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Local Includes 24 | #import "BGMAutoPauseMusic.h" 25 | #import "BGMMusicPlayers.h" 26 | #import "BGMUserDefaults.h" 27 | 28 | // System Includes 29 | #import 30 | 31 | 32 | #pragma clang assume_nonnull begin 33 | 34 | @interface BGMAutoPauseMenuItem : NSObject 35 | 36 | - (instancetype) initWithMenuItem:(NSMenuItem*)item 37 | autoPauseMusic:(BGMAutoPauseMusic*)autoPause 38 | musicPlayers:(BGMMusicPlayers*)players 39 | userDefaults:(BGMUserDefaults*)defaults; 40 | 41 | // Handle events passed along by the delegate (NSMenuDelegate) of the menu containing this menu item. 42 | - (void) parentMenuNeedsUpdate; 43 | - (void) parentMenuItemWillHighlight:(NSMenuItem* __nullable)item; 44 | 45 | @end 46 | 47 | #pragma clang assume_nonnull end 48 | 49 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMAutoPauseMusic.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMAutoPauseMusic.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | // When enabled, BGMAutoPauseMusic listens for notifications from BGMDevice to tell when music is playing and 23 | // pauses the music player if other audio starts. 24 | // 25 | 26 | // Local Includes 27 | #import "BGMAudioDeviceManager.h" 28 | #import "BGMMusicPlayers.h" 29 | 30 | // System Includes 31 | #import 32 | 33 | 34 | #pragma clang assume_nonnull begin 35 | 36 | @interface BGMAutoPauseMusic : NSObject 37 | 38 | - (id) initWithAudioDevices:(BGMAudioDeviceManager*)inAudioDevices musicPlayers:(BGMMusicPlayers*)inMusicPlayers; 39 | 40 | - (void) enable; 41 | - (void) disable; 42 | 43 | @end 44 | 45 | #pragma clang assume_nonnull end 46 | 47 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMDebugLoggingMenuItem.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMDebugLoggingMenuItem.h 18 | // BGMApp 19 | // 20 | // Copyright © 2020 Kyle Neideck 21 | // 22 | // A menu item in the main menu that enables/disables debug logging. Only visible if you hold the 23 | // option down when you click the status bar icon to reveal the main menu. 24 | // 25 | // TODO: It would be better to have this menu item in the Preferences menu (maybe in an Advanced 26 | // section) and always visible, but first we'd need to add something that tells the user how 27 | // to view the log messages. Or better yet, something that automatically opens them. 28 | // 29 | 30 | // System Includes 31 | #import 32 | 33 | 34 | #pragma clang assume_nonnull begin 35 | 36 | @interface BGMDebugLoggingMenuItem : NSObject 37 | 38 | - (instancetype) initWithMenuItem:(NSMenuItem*)menuItem; 39 | 40 | // True if the main menu is showing hidden items/options because the user held the option key when 41 | // they clicked the icon. This class makes the debug logging menu item visible if this property has 42 | // been set true or if debug logging is enabled. 43 | @property (nonatomic) BOOL menuShowingExtraOptions; 44 | 45 | @end 46 | 47 | #pragma clang assume_nonnull end 48 | 49 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMDebugLoggingMenuItem.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMDebugLoggingMenuItem.m 18 | // BGMApp 19 | // 20 | // Copyright © 2020 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #import "BGMDebugLoggingMenuItem.h" 25 | 26 | // PublicUtility Includes 27 | #import "BGMDebugLogging.h" 28 | #import "CADebugMacros.h" 29 | 30 | 31 | #pragma clang assume_nonnull begin 32 | 33 | @implementation BGMDebugLoggingMenuItem { 34 | NSMenuItem* _menuItem; 35 | BOOL _menuShowingExtraOptions; 36 | } 37 | 38 | - (instancetype) initWithMenuItem:(NSMenuItem*)menuItem { 39 | if ((self = [super init])) { 40 | _menuItem = menuItem; 41 | _menuItem.state = 42 | BGMDebugLoggingIsEnabled() ? NSControlStateValueOn : NSControlStateValueOff; 43 | 44 | [self setMenuShowingExtraOptions:NO]; 45 | 46 | // Enable/disable debug logging when the menu item is clicked. 47 | menuItem.target = self; 48 | menuItem.action = @selector(toggleDebugLogging); 49 | } 50 | 51 | return self; 52 | } 53 | 54 | - (void) setMenuShowingExtraOptions:(BOOL)showingExtra { 55 | _menuShowingExtraOptions = showingExtra; 56 | _menuItem.hidden = !BGMDebugLoggingIsEnabled() && !showingExtra; 57 | 58 | DebugMsg("BGMDebugLoggingMenuItem::menuShowingExtraOptions: %s the menu item", 59 | _menuItem.hidden ? "Hiding" : "Showing"); 60 | } 61 | 62 | - (void) toggleDebugLogging { 63 | BGMSetDebugLoggingEnabled(!BGMDebugLoggingIsEnabled()); 64 | _menuItem.state = BGMDebugLoggingIsEnabled() ? NSControlStateValueOn : NSControlStateValueOff; 65 | 66 | DebugMsg("BGMDebugLoggingMenuItem::toggleDebugLogging: Debug logging %s", 67 | BGMDebugLoggingIsEnabled() ? "enabled" : "disabled"); 68 | } 69 | 70 | @end 71 | 72 | #pragma clang assume_nonnull end 73 | 74 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMOutputDeviceMenuSection.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMOutputDeviceMenuSection.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016, 2018 Kyle Neideck 21 | // 22 | 23 | // Local Includes 24 | #import "BGMAudioDeviceManager.h" 25 | #import "BGMPreferredOutputDevices.h" 26 | 27 | // System Includes 28 | #import 29 | 30 | 31 | #pragma clang assume_nonnull begin 32 | 33 | @interface BGMOutputDeviceMenuSection : NSObject 34 | 35 | - (instancetype) initWithBGMMenu:(NSMenu*)inBGMMenu 36 | audioDevices:(BGMAudioDeviceManager*)inAudioDevices 37 | preferredDevices:(BGMPreferredOutputDevices*)inPreferredDevices; 38 | 39 | // To be called when BGMApp has been set to use a different output device. For example, when a new 40 | // device is connected and BGMPreferredOutputDevices decides BGMApp should switch to it. 41 | - (void) outputDeviceDidChange; 42 | 43 | @end 44 | 45 | #pragma clang assume_nonnull end 46 | 47 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMOutputVolumeMenuItem.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMOutputVolumeMenuItem.h 18 | // BGMApp 19 | // 20 | // Copyright © 2017 Kyle Neideck 21 | // 22 | 23 | // Local Includes 24 | #import "BGMAudioDeviceManager.h" 25 | 26 | // System Includes 27 | #import 28 | 29 | 30 | #pragma clang assume_nonnull begin 31 | 32 | @interface BGMOutputVolumeMenuItem : NSMenuItem 33 | 34 | // A menu item with a slider for controlling the volume of the output device. Similar to the one in 35 | // macOS's Volume menu extra. 36 | // 37 | // view, slider and deviceLabel are the UI elements from MainMenu.xib. 38 | - (instancetype) initWithAudioDevices:(BGMAudioDeviceManager*)devices 39 | view:(NSView*)view 40 | slider:(NSSlider*)slider 41 | deviceLabel:(NSTextField*)label; 42 | 43 | - (void) outputDeviceDidChange; 44 | 45 | @end 46 | 47 | #pragma clang assume_nonnull end 48 | 49 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMPreferredOutputDevices.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMPreferredOutputDevices.h 18 | // BGMApp 19 | // 20 | // Copyright © 2018 Kyle Neideck 21 | // 22 | // Tries to change BGMApp's output device when the user plugs in or unplugs an audio device, in the 23 | // same way macOS would change its default device if Background Music wasn't running. 24 | // 25 | // For example, if you plug in some USB headphones, make them your default device and then unplug 26 | // them, macOS will change its default device to the previous default device. Then, if you plug 27 | // them back in, macOS will make them the default device again. 28 | // 29 | // This class isn't always able to figure out what macOS would do, in which case it leaves BGMApp's 30 | // output device as it is. 31 | // 32 | 33 | // Local Includes 34 | #import "BGMAudioDeviceManager.h" 35 | #import "BGMUserDefaults.h" 36 | 37 | // System Includes 38 | #import 39 | #import 40 | 41 | 42 | #pragma clang assume_nonnull begin 43 | 44 | @interface BGMPreferredOutputDevices : NSObject 45 | 46 | // Starts responding to device connections/disconnections immediately. Stops if/when the instance is 47 | // deallocated. 48 | - (instancetype) initWithDevices:(BGMAudioDeviceManager*)devices 49 | userDefaults:(BGMUserDefaults*)userDefaults; 50 | 51 | // Returns the most-preferred device that's currently connected. If no preferred devices are 52 | // connected, returns the current output device. If the current output device has been disconnected, 53 | // returns an arbitrary device. 54 | // 55 | // If none of the connected devices can be used as the output device, or if it can't find a device 56 | // to use because the HAL returned errors when queried, returns kAudioObjectUnknown. 57 | - (AudioObjectID) findPreferredDevice; 58 | 59 | - (void) userChangedOutputDeviceTo:(AudioObjectID)device; 60 | 61 | @end 62 | 63 | #pragma clang assume_nonnull end 64 | 65 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMStatusBarItem.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMStatusBarItem.h 18 | // BGMApp 19 | // 20 | // Copyright © 2019, 2020 Kyle Neideck 21 | // 22 | // The button in the system status bar (the bar with volume, battery, clock, etc.) to show the main 23 | // menu for the app. These are called "menu bar extras" in the Human Interface Guidelines. 24 | // 25 | 26 | // Local Includes 27 | #import "BGMAudioDeviceManager.h" 28 | #import "BGMDebugLoggingMenuItem.h" 29 | 30 | // System Includes 31 | #import 32 | 33 | // Forward Declarations 34 | @class BGMUserDefaults; 35 | 36 | 37 | #pragma clang assume_nonnull begin 38 | 39 | typedef NS_ENUM(NSInteger, BGMStatusBarIcon) { 40 | BGMFermataStatusBarIcon = 0, 41 | BGMVolumeStatusBarIcon 42 | }; 43 | 44 | static BGMStatusBarIcon const kBGMStatusBarIconMinValue = BGMFermataStatusBarIcon; 45 | static BGMStatusBarIcon const kBGMStatusBarIconMaxValue = BGMVolumeStatusBarIcon; 46 | static BGMStatusBarIcon const kBGMStatusBarIconDefaultValue = BGMFermataStatusBarIcon; 47 | 48 | @interface BGMStatusBarItem : NSObject 49 | 50 | - (instancetype) initWithMenu:(NSMenu*)bgmMenu 51 | audioDevices:(BGMAudioDeviceManager*)devices 52 | userDefaults:(BGMUserDefaults*)defaults; 53 | 54 | // Set this to BGMFermataStatusBarIcon to change the icon to the Background Music logo. 55 | // 56 | // Set this to BGMFermataStatusBarIcon to change the icon to a volume icon. This icon has the 57 | // advantage of indicating the volume level, but we can't make it the default because it looks the 58 | // same as the icon for the macOS volume status bar item. 59 | @property BGMStatusBarIcon icon; 60 | 61 | // If the user holds down the option key when they click the status bar icon, this menu item will be 62 | // shown in the main menu. 63 | - (void) setDebugLoggingMenuItem:(BGMDebugLoggingMenuItem*)menuItem; 64 | 65 | @end 66 | 67 | #pragma clang assume_nonnull end 68 | 69 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMSystemSoundsVolume.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMSystemSoundsVolume.h 18 | // BGMApp 19 | // 20 | // Copyright © 2017 Kyle Neideck 21 | // 22 | // The menu item with the volume slider that controls system-related sounds. The slider is used to 23 | // set the volume of the instance of BGMDevice that system sounds are played on, i.e. the audio 24 | // device returned by BGMBackgroundMusicDevice::GetUISoundsBGMDeviceInstance. 25 | // 26 | // System sounds are any sounds played using the audio device macOS is set to use as the device 27 | // "for system related sound from the alert sound to digital call progress". See 28 | // kAudioHardwarePropertyDefaultSystemOutputDevice in AudioHardware.h. They can be played by any 29 | // app, though most apps use systemsoundserverd to play their system sounds, which means BGMDriver 30 | // can't tell which app is actually playing the sounds. 31 | // 32 | 33 | // Local Includes 34 | #import "BGMAudioDevice.h" 35 | 36 | // System Includes 37 | #import 38 | 39 | 40 | #pragma clang assume_nonnull begin 41 | 42 | @interface BGMSystemSoundsVolume : NSObject 43 | 44 | // The volume level of uiSoundsDevice will be used to set the slider's initial position and will be 45 | // updated when the user moves the slider. view and slider are the UI elements from MainMenu.xib. 46 | - (instancetype) initWithUISoundsDevice:(BGMAudioDevice)uiSoundsDevice 47 | view:(NSView*)view 48 | slider:(NSSlider*)slider; 49 | 50 | // The menu item with the volume slider for system sounds. 51 | @property (readonly) NSMenuItem* menuItem; 52 | 53 | @end 54 | 55 | #pragma clang assume_nonnull end 56 | 57 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMSystemSoundsVolume.mm: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMSystemSoundsVolume.mm 18 | // BGMApp 19 | // 20 | // Copyright © 2017 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #import "BGMSystemSoundsVolume.h" 25 | 26 | // Local Includes 27 | #import "BGM_Types.h" 28 | #import "BGM_Utils.h" 29 | 30 | 31 | #pragma clang assume_nonnull begin 32 | 33 | // TODO: It's a bit confusing that this slider's default position is all the way right, but the App 34 | // Volumes sliders default to 50%. After you move the slider there's no way to tell how to put 35 | // it back to its normal position. 36 | 37 | NSString* const kMenuItemToolTip = 38 | @"Alerts, notification sounds, etc. Usually short. Can be played by any app."; 39 | 40 | @implementation BGMSystemSoundsVolume { 41 | BGMAudioDevice uiSoundsDevice; 42 | NSSlider* volumeSlider; 43 | } 44 | 45 | - (instancetype) initWithUISoundsDevice:(BGMAudioDevice)inUISoundsDevice 46 | view:(NSView*)inView 47 | slider:(NSSlider*)inSlider { 48 | if ((self = [super init])) { 49 | uiSoundsDevice = inUISoundsDevice; 50 | volumeSlider = inSlider; 51 | 52 | _menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; 53 | _menuItem.toolTip = kMenuItemToolTip; 54 | 55 | // Apply our custom view from MainMenu.xib. It's very similar to the one for app volumes. 56 | _menuItem.view = inView; 57 | 58 | try { 59 | volumeSlider.floatValue = 60 | uiSoundsDevice.GetVolumeControlScalarValue(kAudioObjectPropertyScopeOutput, 61 | kMasterChannel); 62 | } catch (const CAException& e) { 63 | BGMLogException(e); 64 | volumeSlider.floatValue = 1.0f; // Full volume 65 | } 66 | 67 | volumeSlider.target = self; 68 | volumeSlider.action = @selector(systemSoundsSliderChanged:); 69 | } 70 | 71 | return self; 72 | } 73 | 74 | - (void) systemSoundsSliderChanged:(id)sender { 75 | #pragma unused(sender) 76 | 77 | float sliderLevel = volumeSlider.floatValue; 78 | 79 | BGMAssert((sliderLevel >= 0.0f) && (sliderLevel <= 1.0f), "Invalid value from slider"); 80 | DebugMsg("BGMSystemSoundsVolume::systemSoundsSliderChanged: UI sounds volume: %f", sliderLevel); 81 | 82 | BGMLogAndSwallowExceptions("BGMSystemSoundsVolume::systemSoundsSliderChanged", ([&] { 83 | uiSoundsDevice.SetVolumeControlScalarValue(kAudioObjectPropertyScopeOutput, 84 | kMasterChannel, 85 | sliderLevel); 86 | })); 87 | } 88 | 89 | @end 90 | 91 | #pragma clang assume_nonnull end 92 | 93 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMTermination.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMTermination.h 18 | // BGMApp 19 | // 20 | // Copyright © 2017 Kyle Neideck 21 | // 22 | // Cleans up if BGMApp crashes because of an uncaught C++ or Objective-C exception, or is sent 23 | // SIGINT/SIGTERM/SIGQUIT. Currently, it just changes the default output device from BGMDevice to 24 | // the real output device and records debug info for some types of crashes. 25 | // 26 | // BGMXPCHelper also changes the default device if BGMApp disconnects and leaves BGMDevice as the 27 | // default. This handles cases like segfaults where it wouldn't be safe to clean up from the 28 | // crashing process. 29 | // 30 | 31 | #ifndef BGMApp__BGMTermination 32 | #define BGMApp__BGMTermination 33 | 34 | // Local Includes 35 | #import "BGMAudioDeviceManager.h" 36 | 37 | // PublicUtility Includes 38 | #import "CAPThread.h" 39 | 40 | // STL Includes 41 | #import 42 | 43 | 44 | #pragma clang assume_nonnull begin 45 | 46 | class BGMTermination 47 | { 48 | 49 | public: 50 | /*! 51 | Starts a thread that will clean up before exiting if BGMApp receives SIGINT, SIGTERM or 52 | SIGQUIT. Sets a similar clean up function to run if BGMApp terminates due to an uncaught 53 | exception. 54 | */ 55 | static void SetUpTerminationCleanUp(BGMAudioDeviceManager* inAudioDevices); 56 | 57 | /*! Some commented out ways to have BGMApp crash for testing. Does nothing if unmodified. */ 58 | static void TestCrash() __attribute__((noinline)); 59 | 60 | private: 61 | static void StartExitSignalsThread(); 62 | 63 | static void CleanUpAudioDevices(); 64 | 65 | /*! Adds some info about the uncaught exception that caused a crash to the crash report. */ 66 | static void AddCurrentExceptionToCrashReport(); 67 | 68 | /*! The entry point for sExitSignalsThread. */ 69 | static void* __nullable ExitSignalsProc(void* __nullable ignored); 70 | 71 | /*! The thread that handles SIGQUIT, SIGTERM and SIGINT. Never destroyed. */ 72 | static CAPThread* const sExitSignalsThread; 73 | static sigset_t sExitSignals; 74 | 75 | /*! The function that handles std::terminate by default. */ 76 | static std::terminate_handler sOriginalTerminateHandler; 77 | 78 | /*! The audio device manager. (Must be static to be accessed in our std::terminate_handler.) */ 79 | static BGMAudioDeviceManager* __nullable sAudioDevices; 80 | 81 | }; 82 | 83 | #pragma clang assume_nonnull end 84 | 85 | #endif /* BGMApp__BGMTermination */ 86 | 87 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMUserDefaults.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMUserDefaults.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016-2019 Kyle Neideck 21 | // 22 | // A simple wrapper around our use of NSUserDefaults. Used to store the preferences/state that only 23 | // apply to BGMApp. The others are stored by BGMDriver. 24 | // 25 | // Private data will be stored in the user's keychain instead of user defaults. 26 | // 27 | 28 | // Local Includes 29 | #import "BGMStatusBarItem.h" 30 | 31 | // System Includes 32 | #import 33 | 34 | 35 | #pragma clang assume_nonnull begin 36 | 37 | @interface BGMUserDefaults : NSObject 38 | 39 | // If inDefaults is nil, settings are not loaded from or saved to disk, which is useful for testing. 40 | - (instancetype) initWithDefaults:(NSUserDefaults* __nullable)inDefaults; 41 | 42 | // The musicPlayerID (see BGMMusicPlayer.h), as a string, of the music player selected by the user. 43 | // Must be either null or a string that can be parsed by NSUUID. 44 | @property NSString* __nullable selectedMusicPlayerID; 45 | 46 | @property BOOL autoPauseMusicEnabled; 47 | 48 | // The UIDs of the output devices most recently selected by the user. The most-recently selected 49 | // device is at index 0. See BGMPreferredOutputDevices. 50 | @property NSArray* preferredDeviceUIDs; 51 | 52 | // The (type of) icon to show in the button in the status bar. (The button the user clicks to open 53 | // BGMApp's main menu.) 54 | @property BGMStatusBarIcon statusBarIcon; 55 | 56 | // The auth code we're required to send when connecting to GPMDP. Stored in the keychain. Reading 57 | // this property is thread-safe, but writing it isn't. 58 | // 59 | // Returns nil if no code is found or if reading fails. If writing fails, an error is logged, but no 60 | // exception is thrown. 61 | @property NSString* __nullable googlePlayMusicDesktopPlayerPermanentAuthCode; 62 | 63 | @end 64 | 65 | #pragma clang assume_nonnull end 66 | 67 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMVolumeChangeListener.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMVolumeChangeListener.h 18 | // BGMApp 19 | // 20 | // Copyright © 2019 Kyle Neideck 21 | // 22 | 23 | // Local Includes 24 | #include "BGMBackgroundMusicDevice.h" 25 | 26 | // PublicUtility Includes 27 | #import "CAPropertyAddress.h" 28 | 29 | // STL Includes 30 | #include 31 | 32 | // System Includes 33 | #include 34 | 35 | 36 | #pragma clang assume_nonnull begin 37 | 38 | class BGMVolumeChangeListener 39 | { 40 | 41 | public: 42 | /*! 43 | * @param device Listens for notifications about this device. 44 | * @param handler The function to call when the device's volume (or mute) changes. Called on the 45 | * main queue. 46 | */ 47 | BGMVolumeChangeListener(BGMAudioDevice device, std::function handler); 48 | virtual ~BGMVolumeChangeListener(); 49 | BGMVolumeChangeListener(const BGMVolumeChangeListener&) = delete; 50 | BGMVolumeChangeListener& operator=(const BGMVolumeChangeListener&) = delete; 51 | 52 | private: 53 | AudioObjectPropertyListenerBlock mListenerBlock; 54 | BGMAudioDevice mDevice; 55 | 56 | }; 57 | 58 | #pragma clang assume_nonnull end 59 | 60 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/BGMXPCListener.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMXPCListener.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | // Connects to BGMXPCHelper via XPC. When BGMDriver wants BGMApp to do something it can call one of BGMHelper's 23 | // XPC methods, which passes the request along to this class. 24 | // 25 | 26 | // Local Includes 27 | #import "BGMAudioDeviceManager.h" 28 | #import "BGMXPCProtocols.h" 29 | 30 | // System Includes 31 | #import 32 | 33 | 34 | #pragma clang assume_nonnull begin 35 | 36 | @interface BGMXPCListener : NSObject 37 | 38 | - (id) initWithAudioDevices:(BGMAudioDeviceManager*)devices helperConnectionErrorHandler:(void (^)(NSError* error))errorHandler; 39 | 40 | - (void) initHelperConnection; 41 | 42 | @end 43 | 44 | #pragma clang assume_nonnull end 45 | 46 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AirPlayIcon.imageset/AirPlay.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/AirPlayIcon.imageset/AirPlay.pdf -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AirPlayIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "AirPlay.pdf", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "appicon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "appicon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "appicon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "appicon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "appicon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "appicon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "appicon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "appicon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "appicon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "appicon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_1024.png -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_128.png -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_16.png -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_256.png -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_32.png -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_512.png -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_64.png -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/FermataIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "FermataIcon.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/FermataIcon.imageset/FermataIcon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/FermataIcon.imageset/FermataIcon.pdf -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/Volume0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "Volume0.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/Volume0.imageset/Volume0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/Volume0.imageset/Volume0.pdf -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/Volume1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "Volume1.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/Volume1.imageset/Volume1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/Volume1.imageset/Volume1.pdf -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/Volume2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "Volume2.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/Volume2.imageset/Volume2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/Volume2.imageset/Volume2.pdf -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/Volume3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "Volume3.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Images.xcassets/Volume3.imageset/Volume3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/BGMApp/Images.xcassets/Volume3.imageset/Volume3.pdf -------------------------------------------------------------------------------- /BGMApp/BGMApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AudioHardwarePowerHint 6 | None 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.4.3 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0.0 25 | LSApplicationCategoryType 26 | public.app-category.utilities 27 | LSMinimumSystemVersion 28 | $(MACOSX_DEPLOYMENT_TARGET) 29 | LSUIElement 30 | 31 | NSAppleEventsUsageDescription 32 | Background Music needs to control your music player app if you want it to automatically pause your music. 33 | NSAppleScriptEnabled 34 | 35 | NSHumanReadableCopyright 36 | Copyright © 2016-2024 Background Music contributors 37 | NSMainNibFile 38 | MainMenu 39 | NSMicrophoneUsageDescription 40 | The "Background Music" virtual audio device sends system audio to Background Music (the app) through a virtual input device. 41 | NSPrincipalClass 42 | NSApplication 43 | NSServices 44 | 45 | 46 | 47 | OSAScriptingDefinition 48 | BGMApp.sdef 49 | 53 | NSSupportsAutomaticGraphicsSwitching 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMDecibel.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMDecibel.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Superclass/Protocol Import 24 | #import "BGMMusicPlayer.h" 25 | 26 | 27 | @interface BGMDecibel : BGMMusicPlayerBase 28 | 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMDecibel.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMDecibel.m 18 | // BGMApp 19 | // 20 | // Copyright © 2016-2018 Kyle Neideck 21 | // Copyright © 2016 Tanner Hoke 22 | // 23 | 24 | // Self Include 25 | #import "BGMDecibel.h" 26 | 27 | // Auto-generated Scripting Bridge header 28 | #import "Decibel.h" 29 | 30 | // Local Includes 31 | #import "BGMScriptingBridge.h" 32 | 33 | // PublicUtility Includes 34 | #import "CADebugMacros.h" 35 | 36 | 37 | #pragma clang assume_nonnull begin 38 | 39 | @implementation BGMDecibel { 40 | BGMScriptingBridge* scriptingBridge; 41 | } 42 | 43 | - (instancetype) init { 44 | if ((self = [super initWithMusicPlayerID:[BGMMusicPlayerBase makeID:@"A9790CD5-4886-47C7-9FFC-DD70743CF2BF"] 45 | name:@"Decibel" 46 | bundleID:@"org.sbooth.Decibel"])) { 47 | scriptingBridge = [[BGMScriptingBridge alloc] initWithMusicPlayer:self]; 48 | } 49 | 50 | return self; 51 | } 52 | 53 | - (DecibelApplication* __nullable) decibel { 54 | return (DecibelApplication* __nullable)scriptingBridge.application; 55 | } 56 | 57 | - (void) wasSelected { 58 | [super wasSelected]; 59 | [scriptingBridge ensurePermission]; 60 | } 61 | 62 | - (BOOL) isRunning { 63 | return self.decibel.running; 64 | } 65 | 66 | - (BOOL) isPlaying { 67 | return self.running && self.decibel.playing; 68 | } 69 | 70 | - (BOOL) isPaused { 71 | // We don't want to return true when Decibel is stopped, rather than paused. At least for me, Decibel 72 | // returns -1 for playbackTime and playbackPosition when it's neither playing nor paused. 73 | BOOL probablyNotStopped = 74 | self.decibel.playbackTime >= 0 || self.decibel.playbackPosition >= 0; 75 | 76 | return self.running && !self.decibel.playing && probablyNotStopped; 77 | } 78 | 79 | - (BOOL) pause { 80 | // isPlaying checks isRunning, so we don't need to check it here and waste an Apple event 81 | BOOL wasPlaying = self.playing; 82 | 83 | if (wasPlaying) { 84 | DebugMsg("BGMDecibel::pause: Pausing Decibel"); 85 | [self.decibel pause]; 86 | } 87 | 88 | return wasPlaying; 89 | } 90 | 91 | - (BOOL) unpause { 92 | // isPaused checks isRunning, so we don't need to check it here and waste an Apple event 93 | BOOL wasPaused = self.paused; 94 | 95 | if (wasPaused) { 96 | DebugMsg("BGMDecibel::unpause: Unpausing Decibel"); 97 | [self.decibel play]; 98 | } 99 | 100 | return wasPaused; 101 | } 102 | 103 | @end 104 | 105 | #pragma clang assume_nonnull end 106 | 107 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMGooglePlayMusicDesktopPlayer.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMGooglePlayMusicDesktopPlayer.h 18 | // BGMApp 19 | // 20 | // Copyright © 2019 Kyle Neideck 21 | // 22 | // We have a lot more code for GPMDP than most music players largely because GPMDP has a WebSockets 23 | // API and because the user has to enter a code from GPMDP to allow BGMApp to control it. 24 | // Currently, the other music players all have AppleScript APIs, so for them the OS asks the user 25 | // for permission on our behalf automatically and handles the whole process for us. 26 | // 27 | // This class implements the usual BGMMusicPlayer methods and handles the UI for authenticating 28 | // with GPMDP. BGMGooglePlayMusicDesktopPlayerConnection manages the connection to GPMDP and hides 29 | // the details of its API. 30 | // 31 | 32 | // Superclass/Protocol Import 33 | #import "BGMMusicPlayer.h" 34 | 35 | 36 | #pragma clang assume_nonnull begin 37 | 38 | API_AVAILABLE(macos(10.10)) 39 | @interface BGMGooglePlayMusicDesktopPlayer : BGMMusicPlayerBase 40 | 41 | + (NSArray>*) createInstancesWithDefaults:(BGMUserDefaults*)userDefaults; 42 | 43 | @end 44 | 45 | #pragma clang assume_nonnull end 46 | 47 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMGooglePlayMusicDesktopPlayerConnection.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMGooglePlayMusicDesktopPlayerConnection.h 18 | // BGMApp 19 | // 20 | // Copyright © 2019 Kyle Neideck 21 | // 22 | 23 | // Local Includes 24 | #import "BGMUserDefaults.h" 25 | 26 | // System Includes 27 | #import 28 | #import 29 | 30 | 31 | #pragma clang assume_nonnull begin 32 | 33 | API_AVAILABLE(macos(10.10)) 34 | @interface BGMGooglePlayMusicDesktopPlayerConnection : NSObject 35 | 36 | // authRequiredHandler: A UI callback that asks the user for the auth code GPMDP will display. 37 | // Returns the auth code they entered, or nil. 38 | // connectionErrorHandler: A UI callback that shows a connection error message. 39 | // apiVersionMismatchHandler: A UI callback that shows a warning dialog explaining that GPMDP 40 | // reported an API version that we don't support yet. 41 | - (instancetype) initWithUserDefaults:(BGMUserDefaults*)defaults 42 | authRequiredHandler:(NSString* __nullable (^)(void))authHandler 43 | connectionErrorHandler:(void (^)(void))errorHandler 44 | apiVersionMismatchHandler:(void (^)(NSString* reportedAPIVersion))apiVersionHandler; 45 | 46 | // Returns before the connection has been fully established. The playing and paused properties will 47 | // remain false until the connection is complete, but playPause can be called at any time after 48 | // calling this method. 49 | // 50 | // If the connection fails, it will be retried after a one second delay, up to the number of times 51 | // given. 52 | - (void) connectWithRetries:(int)retries; 53 | - (void) disconnect; 54 | 55 | // Tell GPMDP to play if it's paused or pause if it's playing. 56 | - (void) playPause; 57 | 58 | @property (readonly) BOOL playing; 59 | @property (readonly) BOOL paused; 60 | 61 | @end 62 | 63 | #pragma clang assume_nonnull end 64 | 65 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMHermes.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMHermes.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Superclass/Protocol Import 24 | #import "BGMMusicPlayer.h" 25 | 26 | 27 | @interface BGMHermes : BGMMusicPlayerBase 28 | 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMHermes.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMHermes.m 18 | // BGMApp 19 | // 20 | // Copyright © 2016-2018 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #import "BGMHermes.h" 25 | 26 | // Auto-generated Scripting Bridge header 27 | #import "Hermes.h" 28 | 29 | // Local Includes 30 | #import "BGMScriptingBridge.h" 31 | 32 | // PublicUtility Includes 33 | #import "CADebugMacros.h" 34 | 35 | 36 | #pragma clang assume_nonnull begin 37 | 38 | @implementation BGMHermes { 39 | BGMScriptingBridge* scriptingBridge; 40 | } 41 | 42 | - (instancetype) init { 43 | // If you're copying this class, replace the ID string with a new one generated by uuidgen. (Command line tool.) 44 | if ((self = [super initWithMusicPlayerID:[BGMMusicPlayerBase makeID:@"0CDC67B0-56D3-4D94-BC06-6E380D8F5E34"] 45 | name:@"Hermes" 46 | bundleID:@"com.alexcrichton.Hermes"])) { 47 | scriptingBridge = [[BGMScriptingBridge alloc] initWithMusicPlayer:self]; 48 | } 49 | 50 | return self; 51 | } 52 | 53 | - (HermesApplication* __nullable) hermes { 54 | return (HermesApplication* __nullable)scriptingBridge.application; 55 | } 56 | 57 | - (void) wasSelected { 58 | [super wasSelected]; 59 | [scriptingBridge ensurePermission]; 60 | } 61 | 62 | - (BOOL) isRunning { 63 | // Note that this will return NO if is self.hermes is nil (i.e. Hermes isn't running). 64 | return self.hermes.running; 65 | } 66 | 67 | // isPlaying and isPaused check self.running first just in case Hermes is closed but self.hermes hasn't become 68 | // nil yet. In that case, reading self.hermes.playerState could make Scripting Bridge open Hermes. 69 | 70 | - (BOOL) isPlaying { 71 | return self.running && (self.hermes.playbackState == HermesPlayerStatesPlaying); 72 | } 73 | 74 | - (BOOL) isPaused { 75 | return self.running && (self.hermes.playbackState == HermesPlayerStatesPaused); 76 | } 77 | 78 | - (BOOL) pause { 79 | // isPlaying checks isRunning, so we don't need to check it here and waste an Apple event 80 | BOOL wasPlaying = self.playing; 81 | 82 | if (wasPlaying) { 83 | DebugMsg("BGMHermes::pause: Pausing Hermes"); 84 | [self.hermes pause]; 85 | } 86 | 87 | return wasPlaying; 88 | } 89 | 90 | - (BOOL) unpause { 91 | // isPaused checks isRunning, so we don't need to check it here and waste an Apple event 92 | BOOL wasPaused = self.paused; 93 | 94 | if (wasPaused) { 95 | DebugMsg("BGMHermes::unpause: Unpausing Hermes"); 96 | [self.hermes play]; 97 | } 98 | 99 | return wasPaused; 100 | } 101 | 102 | @end 103 | 104 | #pragma clang assume_nonnull end 105 | 106 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMMusic.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMMusic.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016, 2019 Kyle Neideck 21 | // Copyright © 2019 theLMGN 22 | // 23 | 24 | // Superclass/Protocol Import 25 | #import "BGMMusicPlayer.h" 26 | 27 | 28 | #pragma clang assume_nonnull begin 29 | 30 | @interface BGMMusic : BGMMusicPlayerBase 31 | 32 | @end 33 | 34 | #pragma clang assume_nonnull end 35 | 36 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMMusic.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMMusic.m 18 | // BGMApp 19 | // 20 | // Copyright © 2016-2019 Kyle Neideck, theLMGN 21 | // 22 | 23 | // Self Include 24 | #import "BGMMusic.h" 25 | 26 | // Auto-generated Scripting Bridge header 27 | #import "Music.h" 28 | 29 | // Local Includes 30 | #import "BGMScriptingBridge.h" 31 | 32 | // PublicUtility Includes 33 | #import "CADebugMacros.h" 34 | 35 | 36 | #pragma clang assume_nonnull begin 37 | 38 | @implementation BGMMusic { 39 | BGMScriptingBridge* scriptingBridge; 40 | } 41 | 42 | + (NSUUID*) sharedMusicPlayerID { 43 | NSUUID* __nullable musicPlayerID = 44 | [[NSUUID alloc] initWithUUIDString:@"829B8069-8BD2-481D-BD40-54AB8CDAE228"]; 45 | NSAssert(musicPlayerID, @"BGMMusic::sharedMusicPlayerID: !musicPlayerID"); 46 | return (NSUUID*)musicPlayerID; 47 | } 48 | 49 | - (instancetype) init { 50 | if ((self = [super initWithMusicPlayerID:[BGMMusic sharedMusicPlayerID] 51 | name:@"Music" 52 | bundleID:@"com.apple.Music"])) { 53 | scriptingBridge = [[BGMScriptingBridge alloc] initWithMusicPlayer:self]; 54 | } 55 | 56 | return self; 57 | } 58 | 59 | - (MusicApplication* __nullable) music { 60 | return (MusicApplication*)scriptingBridge.application; 61 | } 62 | 63 | - (void) wasSelected { 64 | [super wasSelected]; 65 | [scriptingBridge ensurePermission]; 66 | } 67 | 68 | - (BOOL) isRunning { 69 | return self.music.running; 70 | } 71 | 72 | // isPlaying and isPaused check self.running first just in case Music is closed but self.music 73 | // hasn't become nil yet. In that case, reading self.music.playerState could make Scripting Bridge 74 | // open Music. 75 | 76 | - (BOOL) isPlaying { 77 | return self.running && (self.music.playerState == MusicEPlSPlaying); 78 | } 79 | 80 | - (BOOL) isPaused { 81 | return self.running && (self.music.playerState == MusicEPlSPaused); 82 | } 83 | 84 | - (BOOL) pause { 85 | // isPlaying checks isRunning, so we don't need to check it here and waste an Apple event 86 | BOOL wasPlaying = self.playing; 87 | 88 | if (wasPlaying) { 89 | DebugMsg("BGMMusic::pause: Pausing Music"); 90 | [self.music pause]; 91 | } 92 | 93 | return wasPlaying; 94 | } 95 | 96 | - (BOOL) unpause { 97 | // isPaused checks isRunning, so we don't need to check it here and waste an Apple event 98 | BOOL wasPaused = self.paused; 99 | 100 | if (wasPaused) { 101 | DebugMsg("BGMMusic::unpause: Unpausing Music"); 102 | [self.music playpause]; 103 | } 104 | 105 | return wasPaused; 106 | } 107 | 108 | @end 109 | 110 | #pragma clang assume_nonnull end 111 | 112 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMMusicPlayers.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMMusicPlayers.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016, 2019 Kyle Neideck 21 | // 22 | // Holds the music players (i.e. BGMMusicPlayer objects) available in BGMApp. Also keeps track of 23 | // which music player is currently selected by the user. 24 | // 25 | 26 | // Local Includes 27 | #import "BGMAudioDeviceManager.h" 28 | #import "BGMMusicPlayer.h" 29 | #import "BGMUserDefaults.h" 30 | 31 | // System Includes 32 | #import 33 | 34 | 35 | #pragma clang assume_nonnull begin 36 | 37 | @interface BGMMusicPlayers : NSObject 38 | 39 | // Calls initWithAudioDevices:musicPlayers: with sensible defaults. 40 | - (instancetype) initWithAudioDevices:(BGMAudioDeviceManager*)devices 41 | userDefaults:(BGMUserDefaults*)defaults; 42 | 43 | // defaultMusicPlayerID is the musicPlayerID (see BGMMusicPlayer.h) of the music player that should be 44 | // selected by default. 45 | // 46 | // The createInstancesWithDefaults method of each class in musicPlayerClasses will be called and 47 | // the results will be stored in the musicPlayers property. 48 | - (instancetype) initWithAudioDevices:(BGMAudioDeviceManager*)devices 49 | defaultMusicPlayerID:(NSUUID*)defaultMusicPlayerID 50 | musicPlayerClasses:(NSArray>*)musicPlayerClasses 51 | userDefaults:(BGMUserDefaults*)defaults; 52 | 53 | @property (readonly) NSArray>* musicPlayers; 54 | 55 | // The music player currently selected in the preferences menu. BGMDevice is informed when this property 56 | // is changed. 57 | @property id selectedMusicPlayer; 58 | 59 | @end 60 | 61 | #pragma clang assume_nonnull end 62 | 63 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMScriptingBridge.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMScriptingBridge.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016, 2018 Kyle Neideck 21 | // 22 | // A wrapper around Scripting Bridge's SBApplication that tries to avoid ever launching the application. 23 | // 24 | // We use Scripting Bridge to communicate with music player apps, which we never want to launch 25 | // ourselves. But creating an SBApplication for an app, or sending messages/events to an existing one, 26 | // can launch the app. 27 | // 28 | // As a workaround, this class has an SBApplication property, application (see below), which is nil 29 | // unless the music player app is running. That way messages sent while the app is closed are ignored. 30 | // 31 | 32 | // Local Includes 33 | #import "BGMMusicPlayer.h" 34 | 35 | // System Includes 36 | #import 37 | #import 38 | 39 | 40 | #pragma clang assume_nonnull begin 41 | 42 | @interface BGMScriptingBridge : NSObject 43 | 44 | // Only keeps a weak ref to musicPlayer. 45 | - (instancetype) initWithMusicPlayer:(id)musicPlayer; 46 | 47 | // If the music player application is running, this property is the Scripting Bridge object representing 48 | // it. If not, it's set to nil. Used to send Apple events to the music player app. 49 | @property (readonly) __kindof SBApplication* __nullable application; 50 | 51 | // macOS 10.14 requires the user's permission to send Apple Events. If the music player that owns 52 | // this object (i.e. the one passed to initWithMusicPlayer) is currently the selected music player 53 | // and the user hasn't already given us permission to send it Apple Events, this method asks the 54 | // user for permission. 55 | - (void) ensurePermission; 56 | 57 | // SBApplicationDelegate 58 | 59 | // On 10.11, SBApplicationDelegate.h declares eventDidFail with a non-null return type, but the docs 60 | // specifically say that returning nil is allowed. 61 | #pragma clang diagnostic push 62 | #pragma clang diagnostic ignored "-Wnullability" 63 | - (id __nullable) eventDidFail:(const AppleEvent*)event withError:(NSError*)error; 64 | #pragma clang diagnostic pop 65 | 66 | @end 67 | 68 | #pragma clang assume_nonnull end 69 | 70 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMSpotify.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMSpotify.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Superclass/Protocol Import 24 | #import "BGMMusicPlayer.h" 25 | 26 | 27 | @interface BGMSpotify : BGMMusicPlayerBase 28 | 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMSwinsian.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMSwinsian.h 18 | // BGMApp 19 | // 20 | // Copyright © 2018 Kyle Neideck 21 | // 22 | 23 | // Superclass/Protocol Import 24 | #import "BGMMusicPlayer.h" 25 | 26 | 27 | #pragma clang assume_nonnull begin 28 | 29 | @interface BGMSwinsian : BGMMusicPlayerBase 30 | 31 | @end 32 | 33 | #pragma clang assume_nonnull end 34 | 35 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMSwinsian.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMSwinsian.m 18 | // BGMApp 19 | // 20 | // Copyright © 2018 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #import "BGMSwinsian.h" 25 | 26 | // Auto-generated Scripting Bridge header 27 | #import "Swinsian.h" 28 | 29 | // Local Includes 30 | #import "BGMScriptingBridge.h" 31 | 32 | // PublicUtility Includes 33 | #import "CADebugMacros.h" 34 | 35 | 36 | #pragma clang assume_nonnull begin 37 | 38 | @implementation BGMSwinsian { 39 | BGMScriptingBridge* scriptingBridge; 40 | } 41 | 42 | - (instancetype) init { 43 | // If you're copying this class, replace the ID string with a new one generated by uuidgen (the 44 | // command line tool). 45 | NSUUID* musicPlayerID = [BGMMusicPlayerBase makeID:@"B74D18F6-DFF7-4D88-B719-429CFF98CFFA"]; 46 | 47 | if ((self = [super initWithMusicPlayerID:musicPlayerID 48 | name:@"Swinsian" 49 | bundleID:@"com.swinsian.Swinsian"])) { 50 | scriptingBridge = [[BGMScriptingBridge alloc] initWithMusicPlayer:self]; 51 | } 52 | 53 | return self; 54 | } 55 | 56 | - (SwinsianApplication* __nullable) swinsian { 57 | return (SwinsianApplication* __nullable)scriptingBridge.application; 58 | } 59 | 60 | - (void) wasSelected { 61 | [super wasSelected]; 62 | [scriptingBridge ensurePermission]; 63 | } 64 | 65 | - (BOOL) isRunning { 66 | // Note that this will return NO if is self.swinsian is nil (i.e. Swinsian isn't running). 67 | return self.swinsian.running; 68 | } 69 | 70 | // isPlaying and isPaused check self.running first just in case Swinsian is closed but self.swinsian 71 | // hasn't become nil yet. In that case, reading self.swinsian.playerState could make Scripting 72 | // Bridge open Swinsian. 73 | 74 | - (BOOL) isPlaying { 75 | return self.running && (self.swinsian.playerState == SwinsianPlayerStatePlaying); 76 | } 77 | 78 | - (BOOL) isPaused { 79 | return self.running && (self.swinsian.playerState == SwinsianPlayerStatePaused); 80 | } 81 | 82 | - (BOOL) pause { 83 | // isPlaying checks isRunning, so we don't need to check it here and waste an Apple event. 84 | BOOL wasPlaying = self.playing; 85 | 86 | if (wasPlaying) { 87 | DebugMsg("BGMSwinsian::pause: Pausing Swinsian"); 88 | [self.swinsian pause]; 89 | } 90 | 91 | return wasPlaying; 92 | } 93 | 94 | - (BOOL) unpause { 95 | // isPaused checks isRunning, so we don't need to check it here and waste an Apple event. 96 | BOOL wasPaused = self.paused; 97 | 98 | if (wasPaused) { 99 | DebugMsg("BGMSwinsian::unpause: Unpausing Swinsian"); 100 | [self.swinsian play]; 101 | } 102 | 103 | return wasPaused; 104 | } 105 | 106 | @end 107 | 108 | #pragma clang assume_nonnull end 109 | 110 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMVLC.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMVLC.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Superclass/Protocol Import 24 | #import "BGMMusicPlayer.h" 25 | 26 | 27 | @interface BGMVLC : BGMMusicPlayerBase 28 | 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMVOX.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMVOX.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Superclass/Protocol Import 24 | #import "BGMMusicPlayer.h" 25 | 26 | 27 | @interface BGMVOX : BGMMusicPlayerBase 28 | 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMVOX.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMVOX.m 18 | // BGMApp 19 | // 20 | // Copyright © 2016-2018 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #import "BGMVOX.h" 25 | 26 | // Auto-generated Scripting Bridge header 27 | #import "VOX.h" 28 | 29 | // Local Includes 30 | #import "BGMScriptingBridge.h" 31 | 32 | // PublicUtility Includes 33 | #import "CADebugMacros.h" 34 | 35 | 36 | #pragma clang assume_nonnull begin 37 | 38 | @implementation BGMVOX { 39 | BGMScriptingBridge* scriptingBridge; 40 | } 41 | 42 | - (instancetype) init { 43 | if ((self = [super initWithMusicPlayerID:[BGMMusicPlayerBase makeID:@"26498C5D-C18B-4689-8B41-9DA91A78FFAD"] 44 | name:@"VOX" 45 | bundleID:@"com.coppertino.Vox"])) { 46 | scriptingBridge = [[BGMScriptingBridge alloc] initWithMusicPlayer:self]; 47 | } 48 | 49 | return self; 50 | } 51 | 52 | - (VoxApplication* __nullable) vox { 53 | return (VoxApplication*)scriptingBridge.application; 54 | } 55 | 56 | - (void) wasSelected { 57 | [super wasSelected]; 58 | [scriptingBridge ensurePermission]; 59 | } 60 | 61 | - (BOOL) isRunning { 62 | return self.vox.running; 63 | } 64 | 65 | // isPlaying and isPaused check self.running first just in case VOX is closed but self.vox hasn't become 66 | // nil yet. In that case, reading self.vox.playerState could make Scripting Bridge open VOX. 67 | // 68 | // VOX's comment for its playerState property says "playing = 1, paused = 0". 69 | 70 | - (BOOL) isPlaying { 71 | return self.running && (self.vox.playerState == 1); 72 | } 73 | 74 | - (BOOL) isPaused { 75 | return self.running && (self.vox.playerState == 0); 76 | } 77 | 78 | - (BOOL) pause { 79 | // isPlaying checks isRunning, so we don't need to check it here and waste an Apple event 80 | BOOL wasPlaying = self.playing; 81 | 82 | if (wasPlaying) { 83 | DebugMsg("BGMVOX::pause: Pausing VOX"); 84 | [self.vox pause]; 85 | } 86 | 87 | return wasPlaying; 88 | } 89 | 90 | - (BOOL) unpause { 91 | // isPaused checks isRunning, so we don't need to check it here and waste an Apple event 92 | BOOL wasPaused = self.paused; 93 | 94 | if (wasPaused) { 95 | DebugMsg("BGMVOX::unpause: Unpausing VOX"); 96 | [self.vox playpause]; 97 | } 98 | 99 | return wasPaused; 100 | } 101 | 102 | @end 103 | 104 | #pragma clang assume_nonnull end 105 | 106 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMiTunes.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMiTunes.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Superclass/Protocol Import 24 | #import "BGMMusicPlayer.h" 25 | 26 | 27 | @interface BGMiTunes : BGMMusicPlayerBase 28 | 29 | // The music player ID (see BGMMusicPlayer.h) used by BGMiTunes instances. (Though BGMApp only ever creates one instance of 30 | // BGMiTunes, sharedMusicPlayerID is exposed so iTunes can be set as the default music player.) 31 | + (NSUUID*) sharedMusicPlayerID; 32 | 33 | @end 34 | 35 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/BGMiTunes.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMiTunes.m 18 | // BGMApp 19 | // 20 | // Copyright © 2016-2018 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #import "BGMiTunes.h" 25 | 26 | // Auto-generated Scripting Bridge header 27 | #import "iTunes.h" 28 | 29 | // Local Includes 30 | #import "BGMScriptingBridge.h" 31 | 32 | // PublicUtility Includes 33 | #import "CADebugMacros.h" 34 | 35 | 36 | #pragma clang assume_nonnull begin 37 | 38 | @implementation BGMiTunes { 39 | BGMScriptingBridge* scriptingBridge; 40 | } 41 | 42 | + (NSUUID*) sharedMusicPlayerID { 43 | NSUUID* __nullable musicPlayerID = [[NSUUID alloc] initWithUUIDString:@"7B62B5BF-CF90-4938-84E3-F16DEDC3F608"]; 44 | NSAssert(musicPlayerID, @"BGMiTunes::sharedMusicPlayerID: !musicPlayerID"); 45 | return (NSUUID*)musicPlayerID; 46 | } 47 | 48 | - (instancetype) init { 49 | if ((self = [super initWithMusicPlayerID:[BGMiTunes sharedMusicPlayerID] 50 | name:@"iTunes" 51 | bundleID:@"com.apple.iTunes"])) { 52 | scriptingBridge = [[BGMScriptingBridge alloc] initWithMusicPlayer:self]; 53 | } 54 | 55 | return self; 56 | } 57 | 58 | - (iTunesApplication* __nullable) iTunes { 59 | return (iTunesApplication*)scriptingBridge.application; 60 | } 61 | 62 | - (void) wasSelected { 63 | [super wasSelected]; 64 | [scriptingBridge ensurePermission]; 65 | } 66 | 67 | - (BOOL) isRunning { 68 | return self.iTunes.running; 69 | } 70 | 71 | // isPlaying and isPaused check self.running first just in case iTunes is closed but self.iTunes hasn't become 72 | // nil yet. In that case, reading self.iTunes.playerState could make Scripting Bridge open iTunes. 73 | 74 | - (BOOL) isPlaying { 75 | return self.running && (self.iTunes.playerState == iTunesEPlSPlaying); 76 | } 77 | 78 | - (BOOL) isPaused { 79 | return self.running && (self.iTunes.playerState == iTunesEPlSPaused); 80 | } 81 | 82 | - (BOOL) pause { 83 | // isPlaying checks isRunning, so we don't need to check it here and waste an Apple event 84 | BOOL wasPlaying = self.playing; 85 | 86 | if (wasPlaying) { 87 | DebugMsg("BGMiTunes::pause: Pausing iTunes"); 88 | [self.iTunes pause]; 89 | } 90 | 91 | return wasPlaying; 92 | } 93 | 94 | - (BOOL) unpause { 95 | // isPaused checks isRunning, so we don't need to check it here and waste an Apple event 96 | BOOL wasPaused = self.paused; 97 | 98 | if (wasPaused) { 99 | DebugMsg("BGMiTunes::unpause: Unpausing iTunes"); 100 | [self.iTunes playpause]; 101 | } 102 | 103 | return wasPaused; 104 | } 105 | 106 | @end 107 | 108 | #pragma clang assume_nonnull end 109 | 110 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/Hermes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Hermes.h 3 | * 4 | * Generated with 5 | * sdef /Applications/Hermes.app | sdp -fh --basename Hermes 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | 12 | @class HermesApplication, HermesSong, HermesStation; 13 | 14 | // Legal player states 15 | enum HermesPlayerStates { 16 | HermesPlayerStatesStopped = 'stop' /* Player is stopped */, 17 | HermesPlayerStatesPlaying = 'play' /* Player is playing */, 18 | HermesPlayerStatesPaused = 'paus' /* Player is paused */ 19 | }; 20 | typedef enum HermesPlayerStates HermesPlayerStates; 21 | 22 | 23 | 24 | /* 25 | * Hermes Suite 26 | */ 27 | 28 | // The Pandora player. 29 | @interface HermesApplication : SBApplication 30 | 31 | - (SBElementArray *) stations; 32 | 33 | @property NSInteger playbackVolume; // The current playback volume (0–100). 34 | @property HermesPlayerStates playbackState; // The current playback state. 35 | @property (readonly) double playbackPosition; // The current song’s playback position, in seconds. 36 | @property (readonly) double currentSongDuration; // The duration (length) of the current song, in seconds. 37 | @property (copy) HermesStation *currentStation; // The currently selected Pandora station. 38 | @property (copy, readonly) HermesSong *currentSong; // The currently playing (or paused) Pandora song (WARNING: This is an invalid reference in current versions of Hermes; you must access the current song’s properties individually or as a group directly instead.) 39 | 40 | - (void) playpause; // Play the current song if it is paused; pause the current song if it is playing. 41 | - (void) pause; // Pause the currently playing song. 42 | - (void) play; // Resume playing the current song. 43 | - (void) nextSong; // Skip to the next song on the current station. 44 | - (void) thumbsUp; // Tell Pandora you like the current song. 45 | - (void) thumbsDown; // Tell Pandora you don’t like the current song. 46 | - (void) tiredOfSong; // Tell Pandora you’re tired of the current song. 47 | - (void) increaseVolume; // Increase the playback volume. 48 | - (void) decreaseVolume; // Decrease the playback volume. 49 | - (void) maximizeVolume; // Set the playback volume to its maximum level. 50 | - (void) mute; // Mutes playback, saving the current volume level. 51 | - (void) unmute; // Restores the volume to the level prior to muting. 52 | 53 | @end 54 | 55 | // A Pandora song (track). 56 | @interface HermesSong : SBObject 57 | 58 | @property (copy, readonly) NSString *title; // The song’s title. 59 | @property (copy, readonly) NSString *artist; // The song’s artist. 60 | @property (copy, readonly) NSString *album; // The song’s album. 61 | @property (copy, readonly) NSString *artworkURL; // An image URL for the album’s cover artwork. 62 | @property (readonly) NSInteger rating; // The song’s numeric rating. 63 | @property (copy, readonly) NSString *albumURL; // A Pandora URL for more information on the album. 64 | @property (copy, readonly) NSString *artistURL; // A Pandora URL for more information on the artist. 65 | @property (copy, readonly) NSString *trackURL; // A Pandora URL for more information on the track. 66 | 67 | 68 | @end 69 | 70 | // A Pandora station. 71 | @interface HermesStation : SBObject 72 | 73 | @property (copy, readonly) NSString *name; // The station’s name. 74 | @property (copy, readonly) NSString *stationID; // The station’s ID. 75 | 76 | 77 | @end 78 | 79 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/Spotify.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Spotify.h 3 | * 4 | * Generated with 5 | * sdef /Applications/Spotify.app | sdp -fh --basename Spotify 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | 12 | @class SpotifyApplication, SpotifyTrack, SpotifyApplication; 13 | 14 | enum SpotifyEPlS { 15 | SpotifyEPlSStopped = 'kPSS', 16 | SpotifyEPlSPlaying = 'kPSP', 17 | SpotifyEPlSPaused = 'kPSp' 18 | }; 19 | typedef enum SpotifyEPlS SpotifyEPlS; 20 | 21 | 22 | 23 | /* 24 | * Spotify Suite 25 | */ 26 | 27 | // The Spotify application. 28 | @interface SpotifyApplication : SBApplication 29 | 30 | @property (copy, readonly) SpotifyTrack *currentTrack; // The current playing track. 31 | @property NSInteger soundVolume; // The sound output volume (0 = minimum, 100 = maximum) 32 | @property (readonly) SpotifyEPlS playerState; // Is Spotify stopped, paused, or playing? 33 | @property double playerPosition; // The player’s position within the currently playing track in seconds. 34 | @property (readonly) BOOL repeatingEnabled; // Is repeating enabled in the current playback context? 35 | @property BOOL repeating; // Is repeating on or off? 36 | @property (readonly) BOOL shufflingEnabled; // Is shuffling enabled in the current playback context? 37 | @property BOOL shuffling; // Is shuffling on or off? 38 | 39 | - (void) nextTrack; // Skip to the next track. 40 | - (void) previousTrack; // Skip to the previous track. 41 | - (void) playpause; // Toggle play/pause. 42 | - (void) pause; // Pause playback. 43 | - (void) play; // Resume playback. 44 | - (void) playTrack:(NSString *)x inContext:(NSString *)inContext; // Start playback of a track in the given context. 45 | 46 | @end 47 | 48 | // A Spotify track. 49 | @interface SpotifyTrack : SBObject 50 | 51 | @property (copy, readonly) NSString *artist; // The artist of the track. 52 | @property (copy, readonly) NSString *album; // The album of the track. 53 | @property (readonly) NSInteger discNumber; // The disc number of the track. 54 | @property (readonly) NSInteger duration; // The length of the track in seconds. 55 | @property (readonly) NSInteger playedCount; // The number of times this track has been played. 56 | @property (readonly) NSInteger trackNumber; // The index of the track in its album. 57 | @property (readonly) BOOL starred; // Is the track starred? 58 | @property (readonly) NSInteger popularity; // How popular is this track? 0-100 59 | - (NSString *) id; // The ID of the item. 60 | @property (copy, readonly) NSString *name; // The name of the track. 61 | @property (copy, readonly) NSImage *artwork; // The track's album cover. 62 | @property (copy, readonly) NSString *albumArtist; // That album artist of the track. 63 | @property (copy) NSString *spotifyUrl; // The URL of the track. 64 | 65 | 66 | @end 67 | 68 | 69 | 70 | /* 71 | * Standard Suite 72 | */ 73 | 74 | // The application's top level scripting object. 75 | @interface SpotifyApplication (StandardSuite) 76 | 77 | @property (copy, readonly) NSString *name; // The name of the application. 78 | @property (readonly) BOOL frontmost; // Is this the frontmost (active) application? 79 | @property (copy, readonly) NSString *version; // The version of the application. 80 | 81 | @end 82 | 83 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Music Players/VOX.h: -------------------------------------------------------------------------------- 1 | /* 2 | * VOX.h 3 | * 4 | * Generated with 5 | * sdef /Applications/VOX.app | sdp -fh --basename VOX 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | 12 | @class VoxApplication, VoxApplication; 13 | 14 | 15 | 16 | /* 17 | * Standard Suite 18 | */ 19 | 20 | // The application's top level scripting object. 21 | @interface VoxApplication : SBApplication 22 | 23 | @property (copy, readonly) NSString *name; // The name of the application. 24 | @property (readonly) BOOL frontmost; // Is this the frontmost (active) application? 25 | @property (copy, readonly) NSString *version; // The version of the application. 26 | 27 | - (void) quit; // Quit an application. 28 | - (void) pause; // Pause playback. 29 | - (void) play; // Begin playback. 30 | - (void) playpause; // Toggle playback between playing and paused. 31 | - (void) next; // Skip to the next track in the playlist. 32 | - (void) previous; // Skip to the previous track in the playlist. 33 | - (void) shuffle; // Shuffle the tracks in the playlist. 34 | - (void) playUrl:(NSString *)x; // Play specified URL. 35 | - (void) addUrl:(NSString *)x; // Add specified URL to playlist 36 | - (void) rewindForward; // Rewind current track forward. 37 | - (void) rewindForwardFast; // Rewind current track forward fast. 38 | - (void) rewindBackward; // Rewind current track backward. 39 | - (void) rewindBackwardFast; // Rewind current track backward fast. 40 | - (void) increasVolume; // Increase volume. 41 | - (void) decreaseVolume; // Decrease volume. 42 | - (void) showHidePlaylist; // Show/Hide playlist. 43 | 44 | @end 45 | 46 | 47 | 48 | /* 49 | * Vox Suite 50 | */ 51 | 52 | // The application's top-level scripting object. 53 | @interface VoxApplication (VoxSuite) 54 | 55 | @property (copy, readonly) NSData *tiffArtworkData; // Current track artwork data in TIFF format. 56 | @property (copy, readonly) NSImage *artworkImage; // Current track artwork as an image. 57 | @property (readonly) NSInteger playerState; // Player state (playing = 1, paused = 0) 58 | @property (copy, readonly) NSString *track; // Current track title. 59 | @property (copy, readonly) NSString *trackUrl; // Current track URL. 60 | @property (copy, readonly) NSString *artist; // Current track artist. 61 | @property (copy, readonly) NSString *albumArtist; // Current track album artist. 62 | @property (copy, readonly) NSString *album; // Current track album. 63 | @property (copy, readonly) NSString *uniqueID; // Unique identifier for the current track. 64 | @property double currentTime; // The current playback position. 65 | @property (readonly) double totalTime; // The total time of the currently playing track. 66 | @property double playerVolume; // Player volume (0.0 to 1.0) 67 | @property NSInteger repeatState; // Player repeat state (none = 0, repeat one = 1, repeat all = 2) 68 | 69 | @end 70 | 71 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Preferences/BGMAboutPanel.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMAboutPanel.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | // This class manages the "About Background Music" window. 23 | // 24 | 25 | // System Includes 26 | #import 27 | 28 | 29 | NS_ASSUME_NONNULL_BEGIN 30 | 31 | @interface BGMAboutPanel : NSObject 32 | 33 | - (instancetype)initWithPanel:(NSPanel*)inAboutPanel licenseView:(NSTextView*)inLicenseView; 34 | - (void) show; 35 | 36 | @end 37 | 38 | 39 | @interface BGMLinkField : NSTextField 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | 44 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Preferences/BGMAutoPauseMusicPrefs.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMAutoPauseMusicPrefs.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Local Includes 24 | #import "BGMAudioDeviceManager.h" 25 | #import "BGMMusicPlayers.h" 26 | 27 | // System Includes 28 | #import 29 | 30 | 31 | #pragma clang assume_nonnull begin 32 | 33 | @interface BGMAutoPauseMusicPrefs : NSObject 34 | 35 | - (id) initWithPreferencesMenu:(NSMenu*)inPrefsMenu 36 | audioDevices:(BGMAudioDeviceManager*)inAudioDevices 37 | musicPlayers:(BGMMusicPlayers*)inMusicPlayers; 38 | 39 | @end 40 | 41 | #pragma clang assume_nonnull end 42 | 43 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Preferences/BGMPreferencesMenu.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMPreferencesMenu.h 18 | // BGMApp 19 | // 20 | // Copyright © 2016, 2018, 2019 Kyle Neideck 21 | // 22 | // Handles the preferences menu UI. The user's preference changes are often passed directly to the driver rather 23 | // than to other BGMApp classes. 24 | // 25 | 26 | // Local Includes 27 | #import "BGMAudioDeviceManager.h" 28 | #import "BGMMusicPlayers.h" 29 | #import "BGMStatusBarItem.h" 30 | 31 | // System Includes 32 | #import 33 | 34 | 35 | NS_ASSUME_NONNULL_BEGIN 36 | 37 | @interface BGMPreferencesMenu : NSObject 38 | 39 | - (id) initWithBGMMenu:(NSMenu*)inBGMMenu 40 | audioDevices:(BGMAudioDeviceManager*)inAudioDevices 41 | musicPlayers:(BGMMusicPlayers*)inMusicPlayers 42 | statusBarItem:(BGMStatusBarItem*)inStatusBarItem 43 | aboutPanel:(NSPanel*)inAboutPanel 44 | aboutPanelLicenseView:(NSTextView*)inAboutPanelLicenseView; 45 | 46 | @end 47 | 48 | NS_ASSUME_NONNULL_END 49 | 50 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Scripting/BGMASApplication.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMASApplication.h 18 | // BGMApp 19 | // 20 | // Copyright © 2021 Marcus Wu 21 | // Copyright © 2021 Kyle Neideck 22 | // 23 | // An AppleScript class for volume and pan settings for running applications. 24 | // 25 | 26 | 27 | // Local Includes 28 | #import "BGMAppVolumesController.h" 29 | 30 | // System Includes 31 | #import 32 | #import 33 | 34 | 35 | NS_ASSUME_NONNULL_BEGIN 36 | 37 | @interface BGMASApplication : NSObject 38 | 39 | - (instancetype) initWithApplication:(NSRunningApplication*)app 40 | volumeController:(BGMAppVolumesController*)volumeController 41 | parentSpecifier:(NSScriptObjectSpecifier* __nullable)parentSpecifier 42 | index:(int)i; 43 | 44 | @property (readonly) NSString* name; 45 | @property (readonly) NSString* bundleID; 46 | @property int volume; 47 | @property int pan; 48 | @end 49 | 50 | NS_ASSUME_NONNULL_END 51 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Scripting/BGMASApplication.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMASApplication.m 18 | // BGMApp 19 | // 20 | // Copyright © 2021 Marcus Wu 21 | // Copyright © 2021 Kyle Neideck 22 | // 23 | 24 | // Self Include 25 | #import "BGMASApplication.h" 26 | 27 | // Local Includes 28 | #import "BGM_Types.h" 29 | 30 | @implementation BGMASApplication { 31 | NSScriptObjectSpecifier* parentSpecifier; 32 | NSRunningApplication *application; 33 | BGMAppVolumesController* appVolumesController; 34 | int index; 35 | } 36 | 37 | - (instancetype) initWithApplication:(NSRunningApplication*)app 38 | volumeController:(BGMAppVolumesController*)volumeController 39 | parentSpecifier:(NSScriptObjectSpecifier* __nullable)parent 40 | index:(int)i { 41 | if ((self = [super init])) { 42 | parentSpecifier = parent; 43 | application = app; 44 | appVolumesController = volumeController; 45 | index = i; 46 | } 47 | 48 | return self; 49 | } 50 | 51 | - (NSString*) name { 52 | return [NSString stringWithFormat:@"%@", [application localizedName]]; 53 | } 54 | 55 | - (NSString*) bundleID { 56 | return [NSString stringWithFormat:@"%@", [application bundleIdentifier]]; 57 | } 58 | 59 | - (int) volume { 60 | return [appVolumesController getVolumeAndPanForApp:application].volume; 61 | } 62 | 63 | - (void) setVolume:(int)vol { 64 | BGMAppVolumeAndPan volume = { 65 | .volume = vol, 66 | .pan = kAppPanNoValue 67 | }; 68 | [appVolumesController setVolumeAndPan:volume forApp:application]; 69 | } 70 | 71 | - (int) pan { 72 | return [appVolumesController getVolumeAndPanForApp:application].pan; 73 | } 74 | 75 | - (void) setPan:(int)pan { 76 | BGMAppVolumeAndPan thePan = { 77 | .volume = -1, 78 | .pan = pan 79 | }; 80 | [appVolumesController setVolumeAndPan:thePan forApp:application]; 81 | } 82 | 83 | - (NSScriptObjectSpecifier* __nullable) objectSpecifier { 84 | NSScriptClassDescription* parentClassDescription = [parentSpecifier keyClassDescription]; 85 | return [[NSNameSpecifier alloc] initWithContainerClassDescription:parentClassDescription 86 | containerSpecifier:parentSpecifier 87 | key:@"applications" 88 | name:self.name]; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Scripting/BGMASOutputDevice.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMASOutputDevice.h 18 | // BGMApp 19 | // 20 | // Copyright © 2017 Kyle Neideck 21 | // 22 | // An AppleScript class for the output devices that can be selected in the preferences menu. 23 | // 24 | 25 | // Local Includes 26 | #import "BGMAudioDeviceManager.h" 27 | 28 | // System Includes 29 | #import 30 | 31 | 32 | #pragma clang assume_nonnull begin 33 | 34 | @interface BGMASOutputDevice : NSObject 35 | 36 | - (instancetype) initWithAudioObjectID:(AudioObjectID)objID 37 | audioDevices:(BGMAudioDeviceManager*)devices 38 | parentSpecifier:(NSScriptObjectSpecifier* __nullable)parentSpecifier; 39 | 40 | @property (readonly) NSString* name; 41 | @property BOOL selected; // is this the device to be used for audio output? 42 | 43 | @end 44 | 45 | #pragma clang assume_nonnull end 46 | 47 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Scripting/BGMASOutputDevice.mm: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMASOutputDevice.mm 18 | // BGMApp 19 | // 20 | // Copyright © 2017 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #import "BGMASOutputDevice.h" 25 | 26 | // Local Includes 27 | #import "BGMAudioDevice.h" 28 | 29 | // PublicUtility Includes 30 | #import "CADebugMacros.h" 31 | 32 | 33 | #pragma clang assume_nonnull begin 34 | 35 | @implementation BGMASOutputDevice { 36 | NSScriptObjectSpecifier* parentSpecifier; 37 | BGMAudioDevice device; 38 | BGMAudioDeviceManager* audioDevices; 39 | } 40 | 41 | - (instancetype) initWithAudioObjectID:(AudioObjectID)objID 42 | audioDevices:(BGMAudioDeviceManager*)devices 43 | parentSpecifier:(NSScriptObjectSpecifier* __nullable)parent { 44 | if ((self = [super init])) { 45 | parentSpecifier = parent; 46 | device = objID; 47 | audioDevices = devices; 48 | } 49 | 50 | return self; 51 | } 52 | 53 | - (NSString*) name { 54 | return (NSString*)CFBridgingRelease(device.CopyName()); 55 | } 56 | 57 | - (BOOL) selected { 58 | return [audioDevices isOutputDevice:device]; 59 | } 60 | 61 | - (void) setSelected:(BOOL)selected { 62 | if (selected && ![self selected]) { 63 | DebugMsg("BGMASOutputDevice::setSelected: A script is setting output device to %s", 64 | [[self name] UTF8String]); 65 | 66 | NSError* err = [audioDevices setOutputDeviceWithID:device revertOnFailure:YES]; 67 | (void)err; // TODO: Return an error to the script somehow if this isn't nil. Also, should 68 | // we return an error if the script tries to set this property to false? 69 | } 70 | } 71 | 72 | - (NSScriptObjectSpecifier* __nullable) objectSpecifier { 73 | NSScriptClassDescription* parentClassDescription = [parentSpecifier keyClassDescription]; 74 | return [[NSNameSpecifier alloc] initWithContainerClassDescription:parentClassDescription 75 | containerSpecifier:parentSpecifier 76 | key:@"output devices" 77 | name:self.name]; 78 | } 79 | 80 | @end 81 | 82 | #pragma clang assume_nonnull end 83 | 84 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/Scripting/BGMAppDelegate+AppleScript.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMAppDelegate+AppleScript.h 18 | // BGMApp 19 | // 20 | // Copyright © 2017 Kyle Neideck 21 | // Copyright © 2021 Marcus Wu 22 | // 23 | 24 | #import "BGMAppDelegate.h" 25 | 26 | // Local Includes 27 | #import "BGMAudioDeviceManager.h" 28 | #import "BGMAppVolumesController.h" 29 | 30 | // Local Includes 31 | #import "BGMASOutputDevice.h" 32 | #import "BGMASApplication.h" 33 | 34 | // System Includes 35 | #import 36 | 37 | 38 | #pragma clang assume_nonnull begin 39 | 40 | @interface BGMAppDelegate (AppleScript) 41 | 42 | - (BOOL) application:(NSApplication*)sender delegateHandlesKey:(NSString*)key; 43 | 44 | @property BGMASOutputDevice* selectedOutputDevice; 45 | @property (readonly) NSArray* outputDevices; 46 | @property double mainVolume; 47 | @property (readonly) NSArray* applications; 48 | 49 | @end 50 | 51 | #pragma clang assume_nonnull end 52 | 53 | -------------------------------------------------------------------------------- /BGMApp/BGMApp/main.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // main.m 18 | // BGMApp 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // System Includes 24 | #import 25 | 26 | 27 | int main(int argc, const char * argv[]) { 28 | // Start BGMApp. 29 | return NSApplicationMain(argc, argv); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UITests/BGMApp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BGMApp.h 3 | * 4 | * Generated with 5 | * sdef "/Applications/Background Music.app" | sdp -fh --basename BGMApp 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | 12 | @class BGMAppOutputDevice, BGMAppApplication; 13 | 14 | 15 | 16 | /* 17 | * Background Music 18 | */ 19 | 20 | // A hardware device that can play audio 21 | @interface BGMAppOutputDevice : SBObject 22 | 23 | @property (copy, readonly) NSString *name; // The name of the output device. 24 | @property BOOL selected; // Is this the device to be used for audio output? 25 | 26 | @end 27 | 28 | // The application program 29 | @interface BGMAppApplication : SBApplication 30 | 31 | - (SBElementArray *) outputDevices; 32 | 33 | @property (copy) BGMAppOutputDevice *selectedOutputDevice; // The device to be used for audio output 34 | 35 | @end 36 | 37 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UITests/BGMAppUITests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UITests/skip-ui-tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This file is part of Background Music. 4 | # 5 | # Background Music is free software: you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License as 7 | # published by the Free Software Foundation, either version 2 of the 8 | # License, or (at your option) any later version. 9 | # 10 | # Background Music is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with Background Music. If not, see . 17 | 18 | # 19 | # skip-ui-tests.py 20 | # BGMAppUITests 21 | # 22 | # Copyright (c) 2017, 2024 Kyle Neideck 23 | # 24 | # Disables the UI tests. This is mainly useful on CI systems that don't support UI tests. 25 | # 26 | 27 | import xml.etree.ElementTree as ET 28 | 29 | SCHEME_FILE = "BGMApp/BGMApp.xcodeproj/xcshareddata/xcschemes/Background Music.xcscheme" 30 | UI_REF_XPATH = ".//BuildableReference[@BlueprintName='BGMAppUITests']/.." 31 | 32 | # Parse the Xcode scheme. 33 | tree = ET.parse(SCHEME_FILE) 34 | 35 | # Set the TestableReference for the UI tests to skipped. 36 | tree.getroot().findall(UI_REF_XPATH)[0].set("skipped", "YES") 37 | 38 | # Save the scheme. 39 | tree.write(SCHEME_FILE) 40 | 41 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UnitTests/BGMAppUnitTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioDevice.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // MockAudioDevice.cpp 18 | // BGMAppUnitTests 19 | // 20 | // Copyright © 2020 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #include "MockAudioDevice.h" 25 | 26 | // BGM Includes 27 | #include "BGM_Types.h" 28 | 29 | // STL Includes 30 | #include 31 | 32 | 33 | MockAudioDevice::MockAudioDevice(const std::string& inUID) 34 | : 35 | mUID(inUID), 36 | mNominalSampleRate(44100.0), 37 | mIOBufferSize(512), 38 | MockAudioObject(static_cast(std::hash{}(inUID))) 39 | { 40 | } 41 | 42 | CACFString MockAudioDevice::GetPlayerBundleID() const 43 | { 44 | if(mUID != kBGMDeviceUID) 45 | { 46 | throw "Only BGMDevice has kAudioDeviceCustomPropertyMusicPlayerBundleID"; 47 | } 48 | 49 | return mPlayerBundleID; 50 | } 51 | 52 | void MockAudioDevice::SetPlayerBundleID(const CACFString& inPlayerBundleID) 53 | { 54 | if(mUID != kBGMDeviceUID) 55 | { 56 | throw "Only BGMDevice has kAudioDeviceCustomPropertyMusicPlayerBundleID"; 57 | } 58 | 59 | mPlayerBundleID = inPlayerBundleID; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioDevice.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // MockAudioObject.h 18 | // BGMAppUnitTests 19 | // 20 | // Copyright © 2020 Kyle Neideck 21 | // 22 | 23 | #ifndef BGMAppUnitTests__MockAudioDevice 24 | #define BGMAppUnitTests__MockAudioDevice 25 | 26 | // Superclass Includes 27 | #include "MockAudioObject.h" 28 | 29 | // STL Includes 30 | #include 31 | 32 | 33 | /*! 34 | * A mock audio device in our mock CoreAudio HAL. In the HAL's API class hierarchy, the base class 35 | * for audio devices, kAudioDeviceClassID, is the audio objects class, kAudioObjectClassID. 36 | * 37 | * The unit tests generally use instances of this class to verify the HAL is being queried correctly 38 | * and to control the responses that the code they're testing will receive from the mock HAL. 39 | */ 40 | class MockAudioDevice 41 | : 42 | public MockAudioObject 43 | { 44 | 45 | public: 46 | MockAudioDevice(const std::string& inUID); 47 | 48 | /*! 49 | * @return This device's music player bundle ID property. 50 | * @throws If this device isn't a mock of BGMDevice. 51 | */ 52 | CACFString GetPlayerBundleID() const; 53 | /*! 54 | * Set this device's music player bundle ID property. 55 | * @throws If this device isn't a mock of BGMDevice. 56 | */ 57 | void SetPlayerBundleID(const CACFString& inPlayerBundleID); 58 | 59 | /*! 60 | * The device's UID. The UID is a persistent token used to identify a particular audio device 61 | * across boot sessions. 62 | */ 63 | const std::string mUID; 64 | Float64 mNominalSampleRate; 65 | UInt32 mIOBufferSize; 66 | 67 | private: 68 | CACFString mPlayerBundleID { "" }; 69 | 70 | }; 71 | 72 | #endif /* BGMAppUnitTests__MockAudioDevice */ 73 | 74 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObject.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // MockAudioObject.cpp 18 | // BGMAppUnitTests 19 | // 20 | // Copyright © 2020 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #include "MockAudioObject.h" 25 | 26 | 27 | MockAudioObject::MockAudioObject(AudioObjectID inAudioObjectID) 28 | : 29 | mAudioObjectID(inAudioObjectID) 30 | { 31 | } 32 | 33 | AudioObjectID MockAudioObject::GetObjectID() const 34 | { 35 | return mAudioObjectID; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObject.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // MockAudioObject.h 18 | // BGMAppUnitTests 19 | // 20 | // Copyright © 2020 Kyle Neideck 21 | // 22 | 23 | #ifndef BGMAppUnitTests__MockAudioObject 24 | #define BGMAppUnitTests__MockAudioObject 25 | 26 | // PublicUtility Includes 27 | #include "CACFString.h" 28 | 29 | // STL Includes 30 | #include 31 | 32 | // System Includes 33 | #include 34 | 35 | 36 | /*! 37 | * The base class for mock audio objects in our mock CoreAudio HAL. Maps to kAudioObjectClassID 38 | * (AudioHardwareBase.h) in the HAL's API class hierarchy. 39 | */ 40 | class MockAudioObject 41 | { 42 | 43 | public: 44 | MockAudioObject(AudioObjectID inAudioObjectID); 45 | virtual ~MockAudioObject() = default; 46 | 47 | AudioObjectID GetObjectID() const; 48 | 49 | /*! 50 | * The properties that callers have added listeners for (and haven't since removed). See 51 | * CAHALAudioObject::AddPropertyListener and CAHALAudioObject::RemovePropertyListener. 52 | */ 53 | std::set mPropertiesWithListeners; 54 | 55 | private: 56 | AudioObjectID mAudioObjectID; 57 | 58 | }; 59 | 60 | #endif /* BGMAppUnitTests__MockAudioObject */ 61 | 62 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // MockAudioObjects.h 18 | // BGMAppUnitTests 19 | // 20 | // Copyright © 2020 Kyle Neideck 21 | // 22 | 23 | #ifndef BGMAppUnitTests__MockAudioObjects 24 | #define BGMAppUnitTests__MockAudioObjects 25 | 26 | // Local Includes 27 | #include "MockAudioObject.h" 28 | #include "MockAudioDevice.h" 29 | 30 | // STL Includes 31 | #include 32 | #include 33 | #include 34 | 35 | // System Includes 36 | #include 37 | 38 | 39 | class MockAudioObjects 40 | { 41 | 42 | public: 43 | /*! 44 | * Create a mock audio device in the mock CoreAudio HAL. 45 | * 46 | * The mock device will then be accessible using GetAudioObject and GetAudioDevice. The 47 | * Mock_CAHAL* implementations will access the mock device when they query the mock HAL. 48 | * 49 | * Unit tests can check the mock device to verify the code they're testing has called the mocked 50 | * CAHAL classes correctly. They can also modify the mock device to control the Mock_CAHAL* 51 | * implementations, e.g. to have CAHALAudioDevice::IsAlive return false so the test can cover 52 | * the case where a device is being removed from the system. 53 | * 54 | * @param inUID The UID string to give the device. The UID is a persistent token used to 55 | * identify a particular audio device across boot sessions. 56 | * @return The mock device. 57 | */ 58 | static std::shared_ptr CreateMockDevice(const std::string& inUID); 59 | 60 | /*! 61 | * Remove all mock audio objects from the mock HAL. (Currently, mock devices are the only mock 62 | * objects that can be created.) 63 | */ 64 | static void DestroyMocks(); 65 | 66 | /*! Get a mock audio object by its ID. */ 67 | static std::shared_ptr GetAudioObject(AudioObjectID inAudioObjectID); 68 | 69 | /*! Get a mock audio device by its ID. */ 70 | static std::shared_ptr GetAudioDevice(AudioObjectID inAudioDeviceID); 71 | /*! Get a mock audio device by its UID. */ 72 | static std::shared_ptr GetAudioDevice(const std::string& inUID); 73 | /*! Get a mock audio device by its UID. */ 74 | static std::shared_ptr GetAudioDevice(CFStringRef inUID); 75 | 76 | private: 77 | typedef std::map> MockDeviceMap; 78 | typedef std::map> MockDeviceMapByUID; 79 | 80 | static std::shared_ptr GetAudioDeviceOrNull(AudioObjectID inAudioDeviceID); 81 | 82 | /*! Maps IDs to mocked audio devices. */ 83 | static MockDeviceMap sDevices; 84 | /*! Maps UIDs (ID strings) to mocked audio devices. */ 85 | static MockDeviceMapByUID sDevicesByUID; 86 | 87 | }; 88 | 89 | #endif /* BGMAppUnitTests__MockAudioObjects */ 90 | 91 | -------------------------------------------------------------------------------- /BGMApp/BGMAppTests/UnitTests/Mocks/Mock_CAHALAudioSystemObject.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // Mock_CAHALAudioSystemObject.cpp 18 | // BGMAppUnitTests 19 | // 20 | // Copyright © 2017, 2020 Kyle Neideck 21 | // 22 | 23 | // Self include 24 | #include "CAHALAudioSystemObject.h" 25 | 26 | // BGM Includes 27 | #include "BGM_Types.h" 28 | 29 | // Local Includes 30 | #include "MockAudioObjects.h" 31 | 32 | 33 | CAHALAudioSystemObject::CAHALAudioSystemObject() 34 | : 35 | CAHALAudioObject(kAudioObjectSystemObject) 36 | { 37 | } 38 | 39 | CAHALAudioSystemObject::~CAHALAudioSystemObject() 40 | { 41 | } 42 | 43 | AudioObjectID CAHALAudioSystemObject::GetAudioDeviceForUID(CFStringRef inUID) const 44 | { 45 | auto device = MockAudioObjects::GetAudioDevice(inUID); 46 | 47 | if(device) 48 | { 49 | return device->GetObjectID(); 50 | } 51 | 52 | return kAudioObjectUnknown; 53 | } 54 | 55 | #pragma mark Unimplemented Methods 56 | 57 | #pragma clang diagnostic ignored "-Wunused-parameter" 58 | 59 | UInt32 CAHALAudioSystemObject::GetNumberAudioDevices() const 60 | { 61 | Throw(new CAException(kAudio_UnimplementedError)); 62 | } 63 | 64 | void CAHALAudioSystemObject::GetAudioDevices(UInt32& ioNumberAudioDevices, AudioObjectID* outAudioDevices) const 65 | { 66 | Throw(new CAException(kAudio_UnimplementedError)); 67 | } 68 | 69 | AudioObjectID CAHALAudioSystemObject::GetAudioDeviceAtIndex(UInt32 inIndex) const 70 | { 71 | Throw(new CAException(kAudio_UnimplementedError)); 72 | } 73 | 74 | void CAHALAudioSystemObject::LogBasicDeviceInfo() 75 | { 76 | Throw(new CAException(kAudio_UnimplementedError)); 77 | } 78 | 79 | AudioObjectID CAHALAudioSystemObject::GetDefaultAudioDevice(bool inIsInput, bool inIsSystem) const 80 | { 81 | Throw(new CAException(kAudio_UnimplementedError)); 82 | } 83 | 84 | void CAHALAudioSystemObject::SetDefaultAudioDevice(bool inIsInput, bool inIsSystem, AudioObjectID inNewDefaultDevice) 85 | { 86 | Throw(new CAException(kAudio_UnimplementedError)); 87 | } 88 | 89 | AudioObjectID CAHALAudioSystemObject::GetAudioPlugInForBundleID(CFStringRef inUID) const 90 | { 91 | Throw(new CAException(kAudio_UnimplementedError)); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /BGMApp/BGMXPCHelper/BGMXPCHelperService.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMXPCHelperService.h 18 | // BGMXPCHelper 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Local Includes 24 | #import "BGMXPCProtocols.h" 25 | 26 | // System Includes 27 | #import 28 | 29 | 30 | // This object implements the protocol which we have defined. It provides the actual behavior for the service. It is 31 | // 'exported' by the service to make it available to the process hosting the service over an NSXPCConnection. 32 | @interface BGMXPCHelperService : NSObject 33 | 34 | - (id) initWithConnection:newConnection; 35 | 36 | @end 37 | 38 | -------------------------------------------------------------------------------- /BGMApp/BGMXPCHelper/BGMXPCListenerDelegate.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMXPCListenerDelegate.h 18 | // BGMXPCHelper 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // System Includes 24 | #import 25 | 26 | 27 | #pragma clang assume_nonnull begin 28 | 29 | @interface BGMXPCListenerDelegate : NSObject 30 | 31 | // The UID of the _coreaudiod user, which BGMDriver runs under. This is used in debug builds, usually to warn if a remote method is 32 | // called by BGMApp when it's only meant to be called by BGMDriver, or vice versa. 33 | + (uid_t) _coreaudiodUID; 34 | 35 | - (BOOL) listener:(NSXPCListener*)listener shouldAcceptNewConnection:(NSXPCConnection*)newConnection; 36 | 37 | @end 38 | 39 | #pragma clang assume_nonnull end 40 | 41 | -------------------------------------------------------------------------------- /BGMApp/BGMXPCHelper/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | BGMXPCHelper 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 0.4.3 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2016-2024 Background Music contributors 27 | XPCService 28 | 29 | ServiceType 30 | User 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /BGMApp/BGMXPCHelper/com.bearisdriving.BGM.XPCHelper.plist.template: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | Label 12 | com.bearisdriving.BGM.XPCHelper 13 | 14 | ProgramArguments 15 | 16 | 17 | {{PATH_TO_BGMXPCHELPER}}/{{BGMXPCHELPER_EXECUTABLE_PATH}} 18 | 19 | 20 | MachServices 21 | 22 | com.bearisdriving.BGM.XPCHelper 23 | 24 | 25 | 26 | ProcessType 27 | Adaptive 28 | 29 | 32 | 33 | UserName 34 | {{BGMXPCHELPER_USER_NAME}} 35 | GroupName 36 | {{BGMXPCHELPER_GROUP_NAME}} 37 | 38 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /BGMApp/BGMXPCHelperTests/BGMXPCHelperTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /BGMApp/BGMXPCHelperTests/BGMXPCHelperTests.m: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMXPCHelperTests.m 18 | // BGMXPCHelperTests 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Local Includes 24 | #import "BGM_TestUtils.h" 25 | #import "BGMXPCProtocols.h" 26 | 27 | // System Includes 28 | #import 29 | 30 | 31 | #pragma clang assume_nonnull begin 32 | 33 | // To run these tests, BGMXPCHelper has to be installed and its launchd job enabled. 34 | 35 | @interface BGMXPCHelperTests : XCTestCase 36 | @end 37 | 38 | @implementation BGMXPCHelperTests { 39 | NSXPCConnection* connection; 40 | } 41 | 42 | - (void) setUp { 43 | [super setUp]; 44 | 45 | connection = [[NSXPCConnection alloc] initWithMachServiceName:kBGMXPCHelperMachServiceName 46 | options:NSXPCConnectionPrivileged]; 47 | 48 | connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(BGMXPCHelperXPCProtocol)]; 49 | [connection resume]; 50 | } 51 | 52 | - (void) tearDown { 53 | [connection invalidate]; 54 | 55 | [super tearDown]; 56 | } 57 | 58 | - (void) testStartOutputDeviceWithoutBGMAppConnected { 59 | dispatch_semaphore_t replySemaphore = dispatch_semaphore_create(0); 60 | 61 | // Unregister BGMXPCHelper's connection to BGMApp in case BGMApp didn't shutdown cleanly the last time it ran. 62 | [[connection remoteObjectProxy] unregisterAsBGMApp]; 63 | 64 | [[connection remoteObjectProxy] startBGMAppPlayThroughSyncWithReply:^(NSError* reply) { 65 | XCTAssertEqual([reply code], 66 | kBGMXPC_MessageFailure, 67 | @"Check that BGMApp isn't running, which would cause this failure"); 68 | 69 | dispatch_semaphore_signal(replySemaphore); 70 | } forUISoundsDevice:NO]; 71 | 72 | // Very long timeout to make it less likely to fail in CI builds when there's high contention. 73 | if (0 != dispatch_semaphore_wait(replySemaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * 60 * NSEC_PER_SEC))) { 74 | XCTFail(@"Timed out waiting for BGMXPCHelper"); 75 | } 76 | } 77 | 78 | @end 79 | 80 | #pragma clang assume_nonnull end 81 | 82 | -------------------------------------------------------------------------------- /BGMApp/OptimizationProfiles/BGMApp.profdata: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMApp/OptimizationProfiles/BGMApp.profdata -------------------------------------------------------------------------------- /BGMApp/PublicUtility/BGMDebugLogging.c: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMDebugLogging.c 18 | // PublicUtility 19 | // 20 | // Copyright © 2020, 2024 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #include "BGMDebugLogging.h" 25 | 26 | 27 | #pragma clang assume_nonnull begin 28 | 29 | // It's probably not ideal to use a global variable for this, but it's a lot easier. 30 | #if DEBUG || CoreAudio_Debug 31 | // Enable debug logging by default in debug builds. 32 | int gDebugLoggingIsEnabled = 1; 33 | #else 34 | int gDebugLoggingIsEnabled = 0; 35 | #endif 36 | 37 | // We don't bother synchronising accesses of gDebugLoggingIsEnabled because it isn't really 38 | // necessary and would complicate code that accesses it on realtime threads. 39 | int BGMDebugLoggingIsEnabled(void) 40 | { 41 | return gDebugLoggingIsEnabled; 42 | } 43 | 44 | void BGMSetDebugLoggingEnabled(int inEnabled) 45 | { 46 | gDebugLoggingIsEnabled = inEnabled; 47 | } 48 | 49 | #pragma clang assume_nonnull end 50 | 51 | -------------------------------------------------------------------------------- /BGMApp/PublicUtility/BGMDebugLogging.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMDebugLogging.h 18 | // PublicUtility 19 | // 20 | // Copyright © 2020 Kyle Neideck 21 | // 22 | // Functions to globally enable/disable debug logging, i.e. more detailed logging to help diagnose 23 | // bugs. If debug logging is enabled, the DebugMsg macro from CADebugMacros.h (and possibly others) 24 | // will log messages. If not, it won't do anything. 25 | // 26 | // If the preprocessor macro CoreAudio_UseSysLog is true, which is currently the case for all build 27 | // variants (see BGMApp/BGMApp.xcodeproj/project.pbxproj and 28 | // BGMDriver/BGMDriver.xcodeproj/project.pbxproj), those messages will be logged using syslog and 29 | // can be read using Console.app. Try searching for "background music", "bgm" or "coreaudiod". 30 | // 31 | // Debug logging is enabled by default in debug builds, but in release builds you have to enable it 32 | // by option-clicking the status bar icon and then checking the Debug Logging menu item. Enabling 33 | // debug logging probably won't cause glitches, but we don't try to guarantee that and it's not 34 | // well tested. 35 | // 36 | 37 | #ifndef PublicUtility__BGMDebugLogging 38 | #define PublicUtility__BGMDebugLogging 39 | 40 | #pragma clang assume_nonnull begin 41 | 42 | /*! 43 | * @return Non-zero if debug logging is globally enabled. (Probably -- it's not synchronised.) 44 | * Real-time safe. 45 | */ 46 | #if defined(__cplusplus) 47 | extern "C" 48 | #endif 49 | int BGMDebugLoggingIsEnabled(void); 50 | 51 | /*! 52 | * @param inEnabled Non-zero to globally enable debug logging, zero to disable it. The change might 53 | * not be visible to other threads immediately. 54 | */ 55 | #if defined(__cplusplus) 56 | extern "C" 57 | #endif 58 | void BGMSetDebugLoggingEnabled(int inEnabled); 59 | 60 | #pragma clang assume_nonnull end 61 | 62 | #endif /* PublicUtility__BGMDebugLogging */ 63 | 64 | -------------------------------------------------------------------------------- /BGMApp/PublicUtility/CADebugger.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: CADebugger.h 3 | Abstract: Part of CoreAudio Utility Classes 4 | Version: 1.1 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | #if !defined(__CADebugger_h__) 48 | #define __CADebugger_h__ 49 | 50 | //============================================================================= 51 | // Includes 52 | //============================================================================= 53 | 54 | #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) 55 | #include 56 | #else 57 | #include 58 | #endif 59 | 60 | //============================================================================= 61 | // CADebugger 62 | //============================================================================= 63 | 64 | // BGM edit: Added extern "C" so CADebugger (and headers that include it) can be used in Obj-C. 65 | #ifdef __cplusplus 66 | extern "C" { 67 | #endif 68 | 69 | #if TARGET_API_MAC_OSX 70 | extern bool CAIsDebuggerAttached(void); 71 | #endif 72 | extern void CADebuggerStop(void); 73 | 74 | #ifdef __cplusplus 75 | } 76 | #endif 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /BGMDriver/BGMDriver/BGM_Control.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGM_Control.h 18 | // BGMDriver 19 | // 20 | // Copyright © 2017 Kyle Neideck 21 | // 22 | // An AudioObject that represents a user-controllable aspect of a device or stream, such as volume 23 | // or balance. 24 | // 25 | 26 | #ifndef BGMDriver__BGM_Control 27 | #define BGMDriver__BGM_Control 28 | 29 | // Superclass Includes 30 | #include "BGM_Object.h" 31 | 32 | 33 | #pragma clang assume_nonnull begin 34 | 35 | class BGM_Control 36 | : 37 | public BGM_Object 38 | { 39 | 40 | protected: 41 | BGM_Control(AudioObjectID inObjectID, 42 | AudioClassID inClassID, 43 | AudioClassID inBaseClassID, 44 | AudioObjectID inOwnerObjectID, 45 | AudioObjectPropertyScope inScope = 46 | kAudioObjectPropertyScopeOutput, 47 | AudioObjectPropertyElement inElement = 48 | kAudioObjectPropertyElementMaster); 49 | 50 | #pragma mark Property Operations 51 | 52 | public: 53 | virtual bool HasProperty(AudioObjectID inObjectID, 54 | pid_t inClientPID, 55 | const AudioObjectPropertyAddress& inAddress) const; 56 | virtual bool IsPropertySettable(AudioObjectID inObjectID, 57 | pid_t inClientPID, 58 | const AudioObjectPropertyAddress& inAddress) const; 59 | virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID, 60 | pid_t inClientPID, 61 | const AudioObjectPropertyAddress& inAddress, 62 | UInt32 inQualifierDataSize, 63 | const void* inQualifierData) const; 64 | virtual void GetPropertyData(AudioObjectID inObjectID, 65 | pid_t inClientPID, 66 | const AudioObjectPropertyAddress& inAddress, 67 | UInt32 inQualifierDataSize, 68 | const void* inQualifierData, 69 | UInt32 inDataSize, 70 | UInt32& outDataSize, 71 | void* outData) const; 72 | 73 | #pragma mark Implementation 74 | 75 | protected: 76 | void CheckObjectID(AudioObjectID inObjectID) const; 77 | 78 | protected: 79 | const AudioObjectPropertyScope mScope; 80 | const AudioObjectPropertyElement mElement; 81 | 82 | }; 83 | 84 | #pragma clang assume_nonnull end 85 | 86 | #endif /* BGMDriver__BGM_Control */ 87 | 88 | -------------------------------------------------------------------------------- /BGMDriver/BGMDriver/BGM_WrappedAudioEngine.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGM_WrappedAudioEngine.cpp 18 | // BGMDriver 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #include "BGM_WrappedAudioEngine.h" 25 | 26 | 27 | // TODO: Register to be notified when the IO Registry values for these change so we can cache them 28 | 29 | UInt64 BGM_WrappedAudioEngine::GetSampleRate() const 30 | { 31 | return 0; 32 | } 33 | 34 | kern_return_t BGM_WrappedAudioEngine::SetSampleRate(Float64 inNewSampleRate) 35 | { 36 | #pragma unused (inNewSampleRate) 37 | 38 | return 0; 39 | } 40 | 41 | UInt32 BGM_WrappedAudioEngine::GetSampleBufferFrameSize() const 42 | { 43 | return 0; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /BGMDriver/BGMDriver/BGM_WrappedAudioEngine.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGM_WrappedAudioEngine.h 18 | // BGMDriver 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | // The plan for this is to allow devices with IOAudioEngine drivers to be used as the output device 23 | // directly from BGMDriver, rather than going through BGMApp. That way we get roughly the same CPU 24 | // usage and latency as normal, and don't need to worry about pausing BGMApp's IO when no clients 25 | // are doing IO. It also lets BGMDriver mostly continue working without BGMApp running. I've written 26 | // a very experimental version that mostly works but the code needs a lot of clean up so I haven't 27 | // added it to this project yet. 28 | // 29 | 30 | #ifndef __BGMDriver__BGM_WrappedAudioEngine__ 31 | #define __BGMDriver__BGM_WrappedAudioEngine__ 32 | 33 | #include 34 | #include 35 | 36 | 37 | class BGM_WrappedAudioEngine 38 | { 39 | 40 | public: 41 | UInt64 GetSampleRate() const; 42 | kern_return_t SetSampleRate(Float64 inNewSampleRate); 43 | UInt32 GetSampleBufferFrameSize() const; 44 | 45 | }; 46 | 47 | #endif /* __BGMDriver__BGM_WrappedAudioEngine__ */ 48 | 49 | -------------------------------------------------------------------------------- /BGMDriver/BGMDriver/BGM_XPCHelper.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGM_XPCHelper.h 18 | // BGMDriver 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | #ifndef BGMDriver__BGM_XPCHelper 24 | #define BGMDriver__BGM_XPCHelper 25 | 26 | // System Includes 27 | #include 28 | 29 | #if defined(__cplusplus) 30 | extern "C" { 31 | #endif 32 | 33 | // On failure, returns one of the kBGMXPC_* error codes, or the error code received from BGMXPCHelper. Returns kBGMXPC_Success otherwise. 34 | UInt64 StartBGMAppPlayThroughSync(bool inIsForUISoundsDevice); 35 | 36 | #if defined(__cplusplus) 37 | } 38 | #endif 39 | 40 | #endif /* BGMDriver__BGM_XPCHelper */ 41 | 42 | -------------------------------------------------------------------------------- /BGMDriver/BGMDriver/DeviceClients/BGM_Client.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGM_Client.cpp 18 | // BGMDriver 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | // Self Include 24 | #include "BGM_Client.h" 25 | 26 | 27 | BGM_Client::BGM_Client(const AudioServerPlugInClientInfo* inClientInfo) 28 | : 29 | mClientID(inClientInfo->mClientID), 30 | mProcessID(inClientInfo->mProcessID), 31 | mIsNativeEndian(inClientInfo->mIsNativeEndian), 32 | mBundleID(inClientInfo->mBundleID) 33 | { 34 | // The bundle ID ref we were passed is only valid until our plugin returns control to the HAL, so we need to retain 35 | // it. (CACFString will handle the rest of its ownership/destruction.) 36 | if(inClientInfo->mBundleID != NULL) 37 | { 38 | CFRetain(inClientInfo->mBundleID); 39 | } 40 | } 41 | 42 | void BGM_Client::Copy(const BGM_Client& inClient) 43 | { 44 | mClientID = inClient.mClientID; 45 | mProcessID = inClient.mProcessID; 46 | mBundleID = inClient.mBundleID; 47 | mIsNativeEndian = inClient.mIsNativeEndian; 48 | mDoingIO = inClient.mDoingIO; 49 | mIsMusicPlayer = inClient.mIsMusicPlayer; 50 | mRelativeVolume = inClient.mRelativeVolume; 51 | mPanPosition = inClient.mPanPosition; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /BGMDriver/BGMDriver/DeviceClients/BGM_Client.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGM_Client.h 18 | // BGMDriver 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | 23 | #ifndef __BGMDriver__BGM_Client__ 24 | #define __BGMDriver__BGM_Client__ 25 | 26 | // PublicUtility Includes 27 | #include "CACFString.h" 28 | 29 | // System Includes 30 | #include 31 | 32 | 33 | #pragma clang assume_nonnull begin 34 | 35 | //================================================================================================== 36 | // BGM_Client 37 | // 38 | // Client meaning a client (of the host) of the BGMDevice, i.e. an app registered with the HAL, 39 | // generally so it can do IO at some point. 40 | //================================================================================================== 41 | 42 | class BGM_Client 43 | { 44 | 45 | public: 46 | BGM_Client() = default; 47 | BGM_Client(const AudioServerPlugInClientInfo* inClientInfo); 48 | ~BGM_Client() = default; 49 | BGM_Client(const BGM_Client& inClient) { Copy(inClient); }; 50 | BGM_Client& operator=(const BGM_Client& inClient) { Copy(inClient); return *this; } 51 | 52 | private: 53 | void Copy(const BGM_Client& inClient); 54 | 55 | public: 56 | // These fields are duplicated from AudioServerPlugInClientInfo (except the mBundleID CFStringRef is 57 | // wrapped in a CACFString here). 58 | UInt32 mClientID; 59 | pid_t mProcessID; 60 | Boolean mIsNativeEndian = true; 61 | CACFString mBundleID; 62 | 63 | // Becomes true when the client triggers the plugin host to call StartIO or to begin 64 | // kAudioServerPlugInIOOperationThread, and false again on StopIO or when 65 | // kAudioServerPlugInIOOperationThread ends 66 | bool mDoingIO = false; 67 | 68 | // True if BGMApp has set this client as belonging to the music player app 69 | bool mIsMusicPlayer = false; 70 | 71 | // The client's volume relative to other clients. In the range [0.0, 4.0], defaults to 1.0 (unchanged). 72 | // mRelativeVolumeCurve is applied to this value when it's set. 73 | Float32 mRelativeVolume = 1.0; 74 | 75 | // The client's pan position, in the range [-100, 100] where -100 is left and 100 is right 76 | SInt32 mPanPosition = 0; 77 | 78 | }; 79 | 80 | #pragma clang assume_nonnull end 81 | 82 | #endif /* __BGMDriver__BGM_Client__ */ 83 | 84 | -------------------------------------------------------------------------------- /BGMDriver/BGMDriver/DeviceClients/BGM_ClientTasks.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGM_ClientTasks.h 18 | // BGMDriver 19 | // 20 | // Copyright © 2016 Kyle Neideck 21 | // 22 | // The interface between the client classes (BGM_Client, BGM_Clients and BGM_ClientMap) and BGM_TaskQueue. 23 | // 24 | 25 | #ifndef __BGMDriver__BGM_ClientTasks__ 26 | #define __BGMDriver__BGM_ClientTasks__ 27 | 28 | // Local Includes 29 | #include "BGM_Clients.h" 30 | #include "BGM_ClientMap.h" 31 | 32 | 33 | // Forward Declarations 34 | class BGM_TaskQueue; 35 | 36 | 37 | #pragma clang assume_nonnull begin 38 | 39 | class BGM_ClientTasks 40 | { 41 | 42 | friend class BGM_TaskQueue; 43 | 44 | private: 45 | static bool StartIONonRT(BGM_Clients* inClients, UInt32 inClientID) { return inClients->StartIONonRT(inClientID); } 46 | static bool StopIONonRT(BGM_Clients* inClients, UInt32 inClientID) { return inClients->StopIONonRT(inClientID); } 47 | 48 | static void SwapInShadowMapsRT(BGM_ClientMap* inClientMap) { inClientMap->SwapInShadowMapsRT(); } 49 | 50 | }; 51 | 52 | #pragma clang assume_nonnull end 53 | 54 | #endif /* __BGMDriver__BGM_ClientTasks__ */ 55 | 56 | -------------------------------------------------------------------------------- /BGMDriver/BGMDriver/DeviceIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/BGMDriver/BGMDriver/DeviceIcon.icns -------------------------------------------------------------------------------- /BGMDriver/BGMDriver/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AudioServerPlugIn_MachServices 6 | 7 | com.bearisdriving.BGM.XPCHelper 8 | 9 | CFBundleDevelopmentRegion 10 | en 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | BNDL 21 | CFBundleShortVersionString 22 | 0.4.3 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0.0 27 | CFPlugInFactories 28 | 29 | C957AD43-DACA-4A40-8850-3BA8CE28FAF9 30 | BGM_Create 31 | 32 | CFPlugInTypes 33 | 34 | 443ABAB8-E7B3-491A-B985-BEB9187030DB 35 | 36 | C957AD43-DACA-4A40-8850-3BA8CE28FAF9 37 | 38 | 39 | NSHumanReadableCopyright 40 | Copyright © 2016-2024 Background Music contributors 41 | NSPrincipalClass 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /BGMDriver/BGMDriverTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /BGMDriver/PublicUtility/CADebugger.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: CADebugger.h 3 | Abstract: Part of CoreAudio Utility Classes 4 | Version: 1.1 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | #if !defined(__CADebugger_h__) 48 | #define __CADebugger_h__ 49 | 50 | //============================================================================= 51 | // Includes 52 | //============================================================================= 53 | 54 | #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) 55 | #include 56 | #else 57 | #include 58 | #endif 59 | 60 | //============================================================================= 61 | // CADebugger 62 | //============================================================================= 63 | 64 | #if TARGET_API_MAC_OSX 65 | extern bool CAIsDebuggerAttached(void); 66 | #endif 67 | extern void CADebuggerStop(void); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Contributing 4 | 5 | Firstly, thanks for reading this. Pull requests, bug reports, feature requests, questions, etc. are all very welcome -- 6 | including ones from non-developers. 7 | 8 | ## Issues 9 | 10 | You'll probably want to update to the latest version of the code before creating an issue. The easiest way is to just 11 | run the installation command from [README.md](/README.md#install) again. (But `git pull && ./build_and_install.sh` is 12 | faster.) 13 | 14 | For bug reports about `build_and_install.sh`, please include your `build_and_install.log`. It should be saved in the 15 | directory `build_and_install.sh` is in. 16 | 17 | It might also be helpful to include logs in bug reports about Background Music itself. Those logs go to syslog by 18 | default, so you can use Console.app to read them. (It might help to search for "BGM" or "Background Music".) 19 | 20 | You also might not get any log messages at all. Normally (i.e. in release builds) Background Music only logs errors and 21 | warnings. We're still working on adding optional debug-level logging to release builds. 22 | 23 | If you feel like being really helpful, you could reproduce your bug with a debug build and include the debug logs, which 24 | are much more detailed. But don't feel obligated to. To install a debug build, use `./build_and_install.sh -d`. 25 | 26 | If you make an issue and you're interested in implementing/fixing it yourself, mention that in the issue so we can 27 | confirm you're on the right track, assign the issue to you and so on. 28 | 29 | ## Code 30 | 31 | The code is mostly C++ and Objective-C. But don't worry if you don't know those languages--I don't either. Or Core 32 | Audio, for that matter. Also don't worry if you're not sure your code is right. 33 | 34 | No dependencies so far. (Though you're welcome to add some.) 35 | 36 | The best place to start is probably [DEVELOPING.md](/DEVELOPING.md), which has an overview of the project and 37 | instructions for building, debugging, etc. It's kind of long, though, and not very interesting, so you might prefer to 38 | go straight into the code. In that case, you'll probably want to start with 39 | [BGMAppDelegate.mm](/BGMApp/BGMApp/BGMAppDelegate.mm). 40 | 41 | If you get stuck or have questions about the project, feel free to open an issue. You could also [email 42 | me](mailto:kyle@bearisdriving.com) or try [#backgroundmusic on 43 | Freenode](https://webchat.freenode.net/?channels=backgroundmusic). 44 | 45 | If you have questions related to Core Audio, the [Core Audio mailing 46 | list](https://lists.apple.com/archives/coreaudio-api) is very useful. There's also the [Core Audio 47 | Overview](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html) 48 | and the [Core Audio 49 | Glossary](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioGlossary/Glossary/core_audio_glossary.html). 50 | 51 | If you remember to, add a copyright notice with your name to any source files you change substantially. Let us know in 52 | the PR if you've intentionally not added one so we know not to add one for you. 53 | 54 | 55 | -------------------------------------------------------------------------------- /Images/FermataIcon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/Images/FermataIcon.pdf -------------------------------------------------------------------------------- /Images/FermataIcon.tex: -------------------------------------------------------------------------------- 1 | % This file is part of Background Music. 2 | % 3 | % Background Music is free software: you can redistribute it and/or 4 | % modify it under the terms of the GNU General Public License as 5 | % published by the Free Software Foundation, either version 2 of the 6 | % License, or (at your option) any later version. 7 | % 8 | % Background Music is distributed in the hope that it will be useful, 9 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | % GNU General Public License for more details. 12 | % 13 | % You should have received a copy of the GNU General Public License 14 | % along with Background Music. If not, see . 15 | 16 | % 17 | % FermataIcon.tex 18 | % Copyright © 2016 Kyle Neideck 19 | % 20 | % The app/device/status bar icon for BGMApp 21 | % 22 | % Build with XeTeX: 23 | % xelatex FermataIcon.tex 24 | % 25 | % Then run generate_icon_pngs.sh 26 | % 27 | % Not sure why it doesn't build properly with regular LaTeX. 28 | % 29 | 30 | \documentclass{article} 31 | \thispagestyle{empty} 32 | 33 | \usepackage{tikz} 34 | \usepackage[paperwidth=100mm, paperheight=100mm, margin=0mm]{geometry} 35 | 36 | \begin{document} 37 | \begin{center} 38 | \begin{tikzpicture}[remember picture,overlay] 39 | 40 | % A path that follows the edges of the current page 41 | \tikzstyle{reverseclip}=[insert path={(current page.north east) -- 42 | (current page.south east) -- 43 | (current page.south west) -- 44 | (current page.north west) -- 45 | (current page.north east)} 46 | ] 47 | 48 | \begin{scope} 49 | \begin{pgfinterruptboundingbox} 50 | \path [clip] (0,-62.6mm) circle (33mm) [reverseclip]; 51 | \end{pgfinterruptboundingbox} 52 | 53 | \fill[black] (0,-50mm) circle (44mm); 54 | \end{scope} 55 | 56 | \begin{scope} 57 | \fill[black] (0,-68.5mm) circle (26mm); 58 | \end{scope} 59 | 60 | \end{tikzpicture} 61 | \end{center} 62 | \end{document} 63 | 64 | -------------------------------------------------------------------------------- /Images/README/FermataIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/Images/README/FermataIcon.png -------------------------------------------------------------------------------- /Images/README/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/Images/README/Screenshot.png -------------------------------------------------------------------------------- /Images/README/pkg-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyleneideck/BackgroundMusic/3d4e1bff10e18b43c83bfc81f4ed9cb056c16a70/Images/README/pkg-icon.png -------------------------------------------------------------------------------- /Images/VolumeIcons.tex: -------------------------------------------------------------------------------- 1 | % This file is part of Background Music. 2 | % 3 | % Background Music is free software: you can redistribute it and/or 4 | % modify it under the terms of the GNU General Public License as 5 | % published by the Free Software Foundation, either version 2 of the 6 | % License, or (at your option) any later version. 7 | % 8 | % Background Music is distributed in the hope that it will be useful, 9 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | % GNU General Public License for more details. 12 | % 13 | % You should have received a copy of the GNU General Public License 14 | % along with Background Music. If not, see . 15 | 16 | % 17 | % VolumeIcons.tex 18 | % 19 | % Build with XeTeX: 20 | % xelatex -jobname=Volume0 '\def\UseOption{}\input{VolumeIcons.tex}' 21 | % xelatex -jobname=Volume1 '\def\UseOption{w1}\input{VolumeIcons.tex}' 22 | % xelatex -jobname=Volume2 '\def\UseOption{w1,w2}\input{VolumeIcons.tex}' 23 | % xelatex -jobname=Volume3 '\def\UseOption{w1,w2,w3}\input{VolumeIcons.tex}' 24 | % for n in 0 1 2 3; do mv Volume$n.pdf ../BGMApp/BGMApp/Images.xcassets/Volume$n.imageset/; done 25 | % 26 | % Might build correctly with regular LaTeX. I haven't tried it. 27 | % 28 | 29 | \documentclass[tikz]{standalone} 30 | \usepackage{tikz} 31 | % "dummyOption" prevents "Package optional Warning: No options were selected, 32 | % so all optional text will be printed" when building Volume0.pdf. 33 | \usepackage[dummyOption]{optional} 34 | 35 | \begin{document} 36 | \begin{tikzpicture} 37 | 38 | % Speaker (Rounded box and triangle) 39 | \fill[rounded corners=5mm] 40 | (0mm, 62.5mm) rectangle (25mm, 37.5mm) {}; 41 | \draw[rounded corners=2.5mm,fill=black] 42 | (3mm, 50mm)--(34mm, 76.5mm)--(34mm, 23.5mm)--cycle; 43 | 44 | % First sound wave (Curved line) 45 | \opt{w1}{ 46 | \draw[line width=4.3mm,line cap=round] 47 | (44mm, 36.5mm) to[out=46,in=-46] (44mm, 63.5mm); 48 | } 49 | 50 | % Second sound wave (Curved line) 51 | \opt{w2}{ 52 | \draw[line width=4.3mm,line cap=round] 53 | (57.5mm, 27.5mm) to[out=46,in=-46] (57.5mm, 72.5mm); 54 | } 55 | 56 | % Third sound wave (Curved line) 57 | \opt{w3}{ 58 | \draw[line width=4.3mm,line cap=round] 59 | (72mm, 18.5mm) to[out=46,in=-46] (72mm, 81.5mm); 60 | } 61 | 62 | % Always draw a transparent copy of the third wave so the images will all have 63 | % the same width. 64 | \draw[line width=4.3mm,line cap=round,opacity=0] 65 | (72mm, 18.5mm) to[out=46,in=-46] (72mm, 81.5mm); 66 | 67 | \end{tikzpicture} 68 | \end{document} 69 | 70 | -------------------------------------------------------------------------------- /Images/generate_icon_pngs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # vim: tw=0: 3 | 4 | # This file is part of Background Music. 5 | # 6 | # Background Music is free software: you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License as 8 | # published by the Free Software Foundation, either version 2 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # Background Music is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Background Music. If not, see . 18 | 19 | # 20 | # generate_icon_pngs.sh 21 | # Copyright © 2016 Kyle Neideck 22 | # 23 | # Creates the app and status bar icons for BGMApp in Images.xcassets, and DeviceIcon.icns 24 | # for BGMDriver, from FermataIcon.pdf 25 | # 26 | # Requires ImageMagick (for iconizer.sh) 27 | # 28 | 29 | # Safe mode 30 | set -euo pipefail 31 | IFS=$'\n\t' 32 | 33 | echo Copying FermataIcon.pdf into FermataIcon.imageset for the status bar icon 34 | echo ---- 35 | 36 | (set -x; cp FermataIcon.pdf ../BGMApp/BGMApp/Images.xcassets/FermataIcon.imageset/) 37 | 38 | echo 39 | echo Generating app icon for BGMApp 40 | echo ---- 41 | 42 | cp ../BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/Contents.json \ 43 | ../BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/Contents.json.brb 44 | 45 | sh iconizer.sh FermataIcon.pdf ../BGMApp/BGMApp 46 | 47 | # Delete unused sizes 48 | cd ../BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/ 49 | 50 | rm appicon_114.png appicon_144.png appicon_180.png appicon_80.png appicon_100.png appicon_120.png appicon_152.png appicon_40.png appicon_57.png appicon_72.png appicon_87.png appicon_29.png appicon_50.png appicon_58.png appicon_76.png 51 | 52 | mv Contents.json.brb Contents.json 53 | 54 | cd - > /dev/null 55 | 56 | echo 57 | echo Generating DeviceIcon.icns for BGMDriver 58 | echo ---- 59 | 60 | set -x 61 | 62 | cp -r ../BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset ../BGMDriver/BGMDriver/DeviceIcon.iconset 63 | 64 | cd ../BGMDriver/BGMDriver/DeviceIcon.iconset 65 | 66 | mv appicon_1024.png icon_512x512@2x.png 67 | mv appicon_512.png icon_512x512.png 68 | cp icon_512x512.png icon_256x256@2x.png 69 | mv appicon_256.png icon_256x256.png 70 | cp icon_256x256.png icon_128x128@2x.png 71 | mv appicon_128.png icon_128x128.png 72 | mv appicon_64.png icon_32x32@2x.png 73 | mv appicon_32.png icon_32x32.png 74 | cp icon_32x32.png icon_16x16@2x.png 75 | mv appicon_16.png icon_16x16.png 76 | 77 | cd - 78 | 79 | iconutil -c icns -o ../BGMDriver/BGMDriver/DeviceIcon.icns ../BGMDriver/BGMDriver/DeviceIcon.iconset 80 | 81 | # Fail if the .icns wasn't created 82 | ls ../BGMDriver/BGMDriver/DeviceIcon.icns 83 | 84 | rm -r ../BGMDriver/BGMDriver/DeviceIcon.iconset 85 | 86 | 87 | -------------------------------------------------------------------------------- /LICENSE-Apple-Sample-Code: -------------------------------------------------------------------------------- 1 | Background Music includes code from Core Audio User-Space Driver 2 | Examples, see 3 | , 4 | which was provided with the following copyright notice and the license 5 | below. 6 | 7 | Copyright (C) 2013 Apple Inc. All Rights Reserved. 8 | 9 | Background Music includes code from Core Audio Utility Classes, see 10 | , 11 | which was provided with the following copyright notice and the license 12 | below. 13 | 14 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 15 | 16 | ------------------------------------------------------------------------ 17 | 18 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 19 | Inc. ("Apple") in consideration of your agreement to the following 20 | terms, and your use, installation, modification or redistribution of 21 | this Apple software constitutes acceptance of these terms. If you do 22 | not agree with these terms, please do not use, install, modify or 23 | redistribute this Apple software. 24 | 25 | In consideration of your agreement to abide by the following terms, and 26 | subject to these terms, Apple grants you a personal, non-exclusive 27 | license, under Apple's copyrights in this original Apple software (the 28 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 29 | Software, with or without modifications, in source and/or binary forms; 30 | provided that if you redistribute the Apple Software in its entirety and 31 | without modifications, you must retain this notice and the following 32 | text and disclaimers in all such redistributions of the Apple Software. 33 | Neither the name, trademarks, service marks or logos of Apple Inc. may 34 | be used to endorse or promote products derived from the Apple Software 35 | without specific prior written permission from Apple. Except as 36 | expressly stated in this notice, no other rights or licenses, express or 37 | implied, are granted by Apple herein, including but not limited to any 38 | patent rights that may be infringed by your derivative works or by other 39 | works in which the Apple Software may be incorporated. 40 | 41 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 42 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 43 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 44 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 45 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 46 | 47 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 48 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 49 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 50 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 51 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 52 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 53 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 54 | POSSIBILITY OF SUCH DAMAGE. 55 | 56 | 57 | -------------------------------------------------------------------------------- /MANUAL-INSTALL.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Manual Build and Install 4 | 5 | - Install the virtual audio device `Background Music Device.driver` to `/Library/Audio/Plug-Ins/HAL`. 6 | 7 | ```shell 8 | sudo xcodebuild -project BGMDriver/BGMDriver.xcodeproj \ 9 | -target "PublicUtility" \ 10 | RUN_CLANG_STATIC_ANALYZER=0 \ 11 | clean build 12 | sudo xcodebuild -project BGMDriver/BGMDriver.xcodeproj \ 13 | -target "Background Music Device" \ 14 | RUN_CLANG_STATIC_ANALYZER=0 \ 15 | DSTROOT="/" \ 16 | clean install 17 | ``` 18 | - Install the XPC helper. 19 | 20 | ```shell 21 | sudo xcodebuild -project BGMApp/BGMApp.xcodeproj \ 22 | -target BGMXPCHelper \ 23 | RUN_CLANG_STATIC_ANALYZER=0 \ 24 | DSTROOT="/" \ 25 | INSTALL_PATH="$(BGMApp/BGMXPCHelper/safe_install_dir.sh)" \ 26 | clean install 27 | ``` 28 | - Install `Background Music.app` to `/Applications` (or wherever). 29 | 30 | ```shell 31 | sudo xcodebuild -project BGMApp/BGMApp.xcodeproj \ 32 | -target "Background Music" \ 33 | RUN_CLANG_STATIC_ANALYZER=0 \ 34 | DSTROOT="/" \ 35 | clean install 36 | ``` 37 | - Restart `coreaudiod`:
38 | (Audio will stop working until the next step, so you might want to pause any running audio apps.) 39 | 40 | ```shell 41 | sudo killall coreaudiod 42 | ``` 43 | 44 | or, if that fails 45 | 46 | ```shell 47 | sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod 48 | ``` 49 | - Run `Background Music.app`. 50 | 51 | 52 | -------------------------------------------------------------------------------- /MANUAL-UNINSTALL.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Manual Uninstall 4 | 5 | - Delete `Background Music.app` from `/Applications`. 6 | - Delete `Background Music Device.driver` from `/Library/Audio/Plug-Ins/HAL`. 7 | - Pause apps that are playing audio, if you can. 8 | - Restart `coreaudiod`:
9 | (Open `/Applications/Utilities/Terminal.app` and paste the following at the prompt.) 10 | 11 | ```shell 12 | sudo killall coreaudiod 13 | ``` 14 | or, if that fails 15 | 16 | ```shell 17 | sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod 18 | ``` 19 | - Go to the Sound section in System Settings and change your default output device at least once. (If you only have 20 | one device now, either use `Audio MIDI Setup.app` to create a temporary aggregate device, restart any audio apps that 21 | have stopped working or just restart your system.) 22 | 23 | ## Troubleshooting 24 | 25 | If you still have the Background Music audio device, try using `Terminal.app` to make sure you've deleted its files: 26 | 27 | ```shell 28 | sudo ls /Library/Audio/Plug-Ins/HAL 29 | ``` 30 | 31 | If you see `Background Music Device.driver` in the output of that command, use this command to actually delete it: 32 | 33 | ```shell 34 | sudo rm -rf "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver" 35 | ``` 36 | 37 | Then restart `coreaudiod` again. If that still doesn't work, restart your computer. If that doesn't work, feel free to 38 | open an issue. Include the output of `sudo ls /Library/Audio/Plug-Ins/HAL`. 39 | 40 | ## Optional 41 | 42 | - Delete `BGMXPCHelper.xpc` from `/usr/local/libexec` or possibly `/Library/Application Support/Background Music`. 43 | - Unregister BGMXPCHelper. 44 | - If you're using OS X 10.11 or later: 45 | 46 | ```shell 47 | sudo launchctl bootout system /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist 48 | ``` 49 | - If you're using an earlier version of OS X: 50 | 51 | ```shell 52 | sudo launchctl unload /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist 53 | ``` 54 | - Delete BGMXPCHelper's launchd.plist. 55 | 56 | ```shell 57 | sudo rm /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist 58 | ``` 59 | - Delete BGMXPCHelper's user and group. 60 | 61 | ```shell 62 | sudo dscl . -delete /Users/_BGMXPCHelper 63 | sudo dscl . -delete /Groups/_BGMXPCHelper 64 | ``` 65 | 66 | 67 | -------------------------------------------------------------------------------- /SharedSource/BGMXPCProtocols.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGMXPCProtocols.h 18 | // SharedSource 19 | // 20 | // Copyright © 2016, 2017 Kyle Neideck 21 | // 22 | 23 | // Local Includes 24 | #include "BGM_Types.h" 25 | 26 | // System Includes 27 | #import 28 | 29 | 30 | #pragma clang assume_nonnull begin 31 | 32 | static NSString* kBGMXPCHelperMachServiceName = @kBGMXPCHelperBundleID; 33 | 34 | // The protocol that BGMXPCHelper will vend as its XPC API. 35 | @protocol BGMXPCHelperXPCProtocol 36 | 37 | // Tells BGMXPCHelper that the caller is BGMApp and passes a listener endpoint that BGMXPCHelper can use to create connections to BGMApp. 38 | // BGMXPCHelper may also pass the endpoint on to BGMDriver so it can do the same. 39 | - (void) registerAsBGMAppWithListenerEndpoint:(NSXPCListenerEndpoint*)endpoint reply:(void (^)(void))reply; 40 | - (void) unregisterAsBGMApp; 41 | 42 | // BGMDriver calls this remote method when it wants BGMApp to start IO. BGMXPCHelper passes the message along and then passes the response 43 | // back. This allows BGMDriver to wait for the audio hardware to start up, which means it can let the HAL know when it's safe to start 44 | // sending us audio data from the client. 45 | // 46 | // If BGMApp can be reached, the error it returns will be passed the reply block. Otherwise, the reply block will be passed an error with 47 | // one of the kBGMXPC_* error codes. It may have an underlying error using one of the NSXPCConnection* error codes from FoundationErrors.h. 48 | - (void) startBGMAppPlayThroughSyncWithReply:(void (^)(NSError*))reply forUISoundsDevice:(BOOL)isUI; 49 | 50 | // BGMXPCHelper will set the system's default output device to deviceID if it loses its connection 51 | // to BGMApp and BGMApp has left BGMDevice as the default device. It waits for a short time first to 52 | // give BGMApp a chance to fix the connection. 53 | // 54 | // This is so BGMDevice isn't left as the default device if BGMApp crashes or otherwise terminates 55 | // abnormally. If audio is played to BGMDevice and BGMApp isn't running, the user won't hear it. 56 | - (void) setOutputDeviceToMakeDefaultOnAbnormalTermination:(AudioObjectID)deviceID; 57 | 58 | @end 59 | 60 | 61 | // The protocol that BGMApp will vend as its XPC API. 62 | @protocol BGMAppXPCProtocol 63 | 64 | - (void) startPlayThroughSyncWithReply:(void (^)(NSError*))reply forUISoundsDevice:(BOOL)isUI; 65 | 66 | @end 67 | 68 | #pragma clang assume_nonnull end 69 | 70 | -------------------------------------------------------------------------------- /SharedSource/BGM_TestUtils.h: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // BGM_TestUtils.h 18 | // SharedSource 19 | // 20 | // Copyright © 2016, 2021 Kyle Neideck 21 | // 22 | 23 | #ifndef __SharedSource__BGM_TestUtils__ 24 | #define __SharedSource__BGM_TestUtils__ 25 | 26 | // Test Framework 27 | #import 28 | 29 | #if defined(__cplusplus) 30 | 31 | // STL Includes 32 | #include 33 | 34 | 35 | // Fails the test if f doesn't throw ExpectedException when run. 36 | // (The "self" argument is required by XCTFail, presumably so it can report the context.) 37 | template 38 | void BGMShouldThrow(XCTestCase* self, const std::function& f) 39 | { 40 | #pragma unused (self) 41 | try 42 | { 43 | f(); 44 | XCTFail(); 45 | } 46 | catch (ExpectedException) 47 | { } 48 | } 49 | 50 | #endif /* defined(__cplusplus) */ 51 | 52 | #endif /* __SharedSource__BGM_TestUtils__ */ 53 | 54 | -------------------------------------------------------------------------------- /SharedSource/Scripts/set-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This file is part of Background Music. 4 | # 5 | # Background Music is free software: you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License as 7 | # published by the Free Software Foundation, either version 2 of the 8 | # License, or (at your option) any later version. 9 | # 10 | # Background Music is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with Background Music. If not, see . 17 | 18 | # 19 | # set-version.sh 20 | # SharedSource 21 | # 22 | # Copyright © 2020 Kyle Neideck 23 | # 24 | # Append the git HEAD short ID to the build version for SNAPSHOT and DEBUG builds. For example, 25 | # this might change the version string from "0.4.0" to "0.4.0-SNAPSHOT-abc0123". 26 | # 27 | # Thanks to Václav Slavík for the initial version of this: 28 | # . 29 | # 30 | # TODO: Update CFBundleVersion as well? 31 | # 32 | 33 | # If HEAD isn't tagged, or has "SNAPSHOT" or "DEBUG" in the tag name, this is a snapshot build. 34 | # If HEAD is tagged more than once, use the most recent. 35 | TAG=$(/usr/bin/git tag --points-at HEAD --sort='-taggerdate' 2>/dev/null | head -n 1) 36 | 37 | if [[ $? -eq 0 ]] && ( [[ "${TAG}" == "" ]] || \ 38 | [[ "${TAG}" =~ .*SNAPSHOT.* ]] || \ 39 | [[ "${TAG}" =~ .*DEBUG.* ]] ); then 40 | head_short_id=$(/usr/bin/git rev-list HEAD --max-count=1 --abbrev-commit) 41 | info_plist="${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}" 42 | 43 | if [[ "${CONFIGURATION}" != "Release" ]]; then 44 | build_type="DEBUG" 45 | else 46 | build_type="SNAPSHOT" 47 | fi 48 | 49 | if [[ -f "$info_plist" ]]; then 50 | current_version=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "${info_plist}") 51 | base_version=$(/usr/libexec/PlistBuddy -c "Print :BGMBundleVersionBase" "${info_plist}" 2>/dev/null) 52 | 53 | if [[ $? -ne 0 ]] || [[ "${base_version}" == "" ]]; then 54 | base_version="${current_version}" 55 | /usr/libexec/PlistBuddy -c "Add :BGMBundleVersionBase string ${base_version}" "${info_plist}" 56 | fi 57 | 58 | new_version="${base_version}-${build_type}-${head_short_id}" 59 | 60 | if [[ "${new_version}" != "${current_version}" ]]; then # Only touch the file if we need to. 61 | /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${new_version}" "${info_plist}" 62 | fi 63 | fi 64 | fi 65 | 66 | -------------------------------------------------------------------------------- /pkg/Distribution.xml.template: -------------------------------------------------------------------------------- 1 | 2 | 3 | Background Music {{VERSION}} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Installer.pkg 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /pkg/ListInputDevices.swift: -------------------------------------------------------------------------------- 1 | // This file is part of Background Music. 2 | // 3 | // Background Music is free software: you can redistribute it and/or 4 | // modify it under the terms of the GNU General Public License as 5 | // published by the Free Software Foundation, either version 2 of the 6 | // License, or (at your option) any later version. 7 | // 8 | // Background Music is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Background Music. If not, see . 15 | 16 | // 17 | // ListInputDevices.swift 18 | // 19 | // Copyright © 2022 Kyle Neideck 20 | // 21 | // The postinstall script uses this to check that BGMDevice was installed successfully. 22 | // 23 | 24 | import AVFoundation 25 | 26 | var devices: Array 27 | 28 | if #available(macOS 10.15, *) { 29 | devices = AVCaptureDevice.DiscoverySession( 30 | deviceTypes: [ .builtInMicrophone, .externalUnknown ], 31 | mediaType: .audio, 32 | position: .unspecified 33 | ).devices 34 | } else { 35 | devices = AVCaptureDevice.devices(for: .audio) 36 | } 37 | 38 | print(devices.map { 39 | (device: AVCaptureDevice) -> Array in 40 | [device.uniqueID, device.modelID, device.localizedName] 41 | }) 42 | -------------------------------------------------------------------------------- /pkg/README.md: -------------------------------------------------------------------------------- 1 | Files used by [package.sh](../package.sh), the script that creates the .pkg installer. 2 | -------------------------------------------------------------------------------- /pkg/pkgbuild.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | RootRelativeBundlePath Applications/Background Music.app 7 | BundleHasStrictIdentifier 8 | BundleIsRelocatable 9 | BundleIsVersionChecked 10 | BundleOverwriteAction upgrade 11 | 12 | 13 | RootRelativeBundlePath Library/Audio/Plug-Ins/HAL/Background Music Device.driver 14 | BundleHasStrictIdentifier 15 | BundleIsRelocatable 16 | BundleIsVersionChecked 17 | BundleOverwriteAction upgrade 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /pkg/preinstall: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # vim: tw=100: 3 | 4 | # This file is part of Background Music. 5 | # 6 | # Background Music is free software: you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License as 8 | # published by the Free Software Foundation, either version 2 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # Background Music is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Background Music. If not, see . 18 | 19 | # 20 | # preinstall 21 | # 22 | # Copyright © 2017 Kyle Neideck 23 | # 24 | 25 | PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH 26 | 27 | # Show a warning if we can't find a safe dir to install BGMXPCHelper in. Should be very unlikely. 28 | xpc_helper_path="$(bash safe_install_dir.sh -y)" 29 | 30 | if [ "$(bash safe_install_dir.sh "$xpc_helper_path")" != "1" ]; then 31 | # TODO: This message could be more user friendly. 32 | msg="We need to install a helper app called BGMXPCHelper to ${xpc_helper_path}, but it may not " 33 | msg+="be secure to do so.\n\n" 34 | msg+="${xpc_helper_path} and all of its parent directories should be owned by 'root', with the " 35 | msg+="group 'wheel', and have permissions 755 (rwxr-xr-x).\n\n" 36 | msg+="Background Music will still work if you decide to continue." 37 | 38 | if [ "$COMMAND_LINE_INSTALL" == "1" ]; then 39 | # As far as I can tell, we can't print anything the user is likely to see. 40 | logger "WARNING: $msg" 41 | exit 1 42 | else 43 | if ! osascript <. 18 | 19 | # 20 | # uninstall.sh 21 | # 22 | # Copyright © 2016 Nick Jacques 23 | # Copyright © 2016, 2017 Kyle Neideck 24 | # 25 | # Removes BGMApp, BGMDriver and BGMXPCHelper from the system. 26 | # 27 | 28 | # Halt on errors. 29 | set -e 30 | 31 | PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH 32 | 33 | bold=$(tput bold) 34 | normal=$(tput sgr0) 35 | 36 | # Warn if running as root. 37 | if [[ $(id -u) -eq 0 ]]; then 38 | echo "$(tput setaf 11)WARNING$(tput sgr0): This script is not intended to be run as root. Run" \ 39 | "it normally and it'll sudo when it needs to." >&2 40 | echo "" 41 | fi 42 | 43 | echo "${bold}You are about to uninstall Background Music.${normal}" 44 | echo "Please pause all audio before continuing." 45 | echo "" 46 | read -p "Continue (y/N)? " user_prompt 47 | 48 | if [ "$user_prompt" == "y" ] || [ "$user_prompt" == "Y" ]; then 49 | # Run from the dir containing this script. 50 | cd "$( dirname "${BASH_SOURCE[0]}" )" 51 | 52 | if [ -f "BGMApp/BGMApp/_uninstall-non-interactive.sh" ]; then 53 | # Running from the source directory. 54 | bash "BGMApp/BGMApp/_uninstall-non-interactive.sh" 55 | elif [ -f "_uninstall-non-interactive.sh" ]; then 56 | # Probably running from Background Music.app/Contents/Resources. 57 | bash "_uninstall-non-interactive.sh" 58 | else 59 | echo "${bold}ERROR: Could not find _uninstall-non-interactive.sh${normal}" >&2 60 | exit 1 61 | fi 62 | 63 | # Invalidate sudo ticket 64 | sudo -k 65 | 66 | # Open System Settings and go to Sound > Output. 67 | osascript -e 'tell application id "com.apple.systempreferences" 68 | activate 69 | reveal anchor "output" of pane id "com.apple.preference.sound" 70 | end tell' >/dev/null || true 71 | echo "" 72 | 73 | else 74 | echo "Uninstall cancelled." 75 | fi 76 | 77 | 78 | --------------------------------------------------------------------------------