### Install BGM XPC Helper Source: https://github.com/kyleneideck/backgroundmusic/blob/master/MANUAL-INSTALL.md Installs the XPC helper component for Background Music. This uses xcodebuild to build and install the 'BGMXPCHelper' target to a safe installation directory determined by a script. ```shell sudo xcodebuild -project BGMApp/BGMApp.xcodeproj \ -target BGMXPCHelper \ RUN_CLANG_STATIC_ANALYZER=0 \ DSTROOT="/" \ INSTALL_PATH="$(BGMApp/BGMXPCHelper/safe_install_dir.sh)" \ clean install ``` -------------------------------- ### Install Background Music Application Source: https://github.com/kyleneideck/backgroundmusic/blob/master/MANUAL-INSTALL.md Installs the main 'Background Music.app' to the Applications directory. This command uses xcodebuild to build and install the application target. ```shell sudo xcodebuild -project BGMApp/BGMApp.xcodeproj \ -target "Background Music" \ RUN_CLANG_STATIC_ANALYZER=0 \ DSTROOT="/" \ clean install ``` -------------------------------- ### Homebrew Installation and Update for Background Music Source: https://context7.com/kyleneideck/backgroundmusic/llms.txt Documents the process of installing and updating the Background Music application using the Homebrew package manager. Homebrew handles downloading, installation, permission setting, and restarting `coreaudiod`. The `brew install --cask` command is used for initial installation, and `brew upgrade --cask` for updates. ```bash # Install Background Music using Homebrew Cask brew install --cask background-music # Homebrew handles: # - Downloading the latest release # - Installing all components # - Setting appropriate permissions # - Restarting coreaudiod # Update to latest version brew upgrade --cask background-music ``` -------------------------------- ### Build and Install Background Music Application with Xcodebuild Source: https://github.com/kyleneideck/backgroundmusic/blob/master/DEVELOPING.md This snippet demonstrates how to build and install the BGMXPCHelper and the Background Music application using the xcodebuild command-line tool. It includes steps for setting the installation path, cleaning build directories, and fixing ownership permissions. The final command opens the built application. Ensure you have Xcode installed and the project files are available. ```shell sudo xcodebuild -project BGMApp/BGMApp.xcodeproj \ -target BGMXPCHelper \ DSTROOT="/" \ INSTALL_PATH="$(BGMApp/BGMXPCHelper/safe_install_dir.sh)" \ -configuration Debug \ install sudo chown -R $(whoami):staff BGMApp/build # Fix build dir ownership xcodebuild -project BGMApp/BGMApp.xcodeproj \ -configuration Debug open "BGMApp/build/Debug/Background Music.app" ``` -------------------------------- ### Build and Install BGM Driver Source: https://github.com/kyleneideck/backgroundmusic/blob/master/MANUAL-INSTALL.md Builds and installs the 'Background Music Device.driver' using xcodebuild. This process involves cleaning, building the driver, and then installing it to the system's Audio Plug-Ins directory. ```shell sudo xcodebuild -project BGMDriver/BGMDriver.xcodeproj \ -target "PublicUtility" \ RUN_CLANG_STATIC_ANALYZER=0 \ clean build sudo xcodebuild -project BGMDriver/BGMDriver.xcodeproj \ -target "Background Music Device" \ RUN_CLANG_STATIC_ANALYZER=0 \ DSTROOT="/" \ clean install ``` -------------------------------- ### Install Background Music via Local Script Source: https://github.com/kyleneideck/backgroundmusic/blob/master/README.md This method involves cloning or downloading the project, then executing the 'build_and_install.sh' script using '/bin/bash'. Ensure you are in the project's directory before running the script. The script restarts system audio processes upon completion. ```shell /bin/bash build_and_install.sh ``` -------------------------------- ### Record System Audio with QuickTime Player Bash Source: https://context7.com/kyleneideck/backgroundmusic/llms.txt Shell script demonstrating how to launch QuickTime Player and configure it to record system audio using the Background Music device. Includes instructions for recording system audio with microphone simultaneously using an aggregate audio device. This is a manual process guide rather than automated scripting. ```bash # Launch QuickTime Player open -a "QuickTime Player" # In QuickTime Player: # File > New Audio Recording # Click the dropdown arrow (⌄) next to the record button # Select "Background Music" as the input device # Click record to capture system audio # For recording system audio + microphone simultaneously: # 1. Open Audio MIDI Setup (/Applications/Utilities/) # 2. Click the + button, select "Create Aggregate Device" # 3. Check both "Built-in Input" and "Background Music" # 4. Use the aggregate device as input in QuickTime Player ``` -------------------------------- ### Manually Expanding and Installing Background Music .pkg Source: https://github.com/kyleneideck/backgroundmusic/wiki/Installer-Problems? This process involves expanding the .pkg file to access its contents, then manually copying the application, driver, and helper scripts to their correct locations on macOS. It's a fallback for when the standard installer fails. ```shell pkgutil --expand-full BackgroundMusic-0.4.0-SNAPSHOT-b38f6dd.pkg ./bgm-pkg-expanded cd bgm-pkg-expanded/Installer.pkg/ # Install the driver. sudo cp -r Payload/Library/Audio/Plug-Ins/HAL/Background\ Music\ Device.driver /Library/Audio/Plug-Ins/HAL/ # Install the app. cp -r Payload/Applications/Background\ Music.app /Applications/ # Install BGMXPCHelper. cd Scripts/ # This will probably print out some errors. The output explains them a bit. sudo ./postinstall echo $? ``` -------------------------------- ### Shell Script for Packaging Background Music Application Source: https://context7.com/kyleneideck/backgroundmusic/llms.txt Provides shell commands to build and package the Background Music application into a macOS installer (.pkg) file. Supports both release and debug builds. The resulting package includes the application, a device driver, XPC helper, and an uninstall script. Installation is performed using the `installer` command. ```bash # Build and package into .pkg file ./package.sh # Debug build package ./package.sh -d # Output file: BackgroundMusic-.pkg # Package includes: # - Background Music.app # - Background Music Device.driver # - BGMXPCHelper.xpc # - com.bearisdriving.BGM.XPCHelper.plist # - Uninstall script # Install the package sudo installer -pkg BackgroundMusic-0.4.3.pkg -target / # Verify installation ls -la "/Applications/Background Music.app" ls -la "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver" ``` -------------------------------- ### Install Background Music via Terminal Command Source: https://github.com/kyleneideck/backgroundmusic/blob/master/README.md This shell command downloads the latest version of Background Music, builds it, and installs it. It uses 'gzcat' for integrity checking and 'set -eo pipefail' for error handling. The script requires '/bin/bash', 'curl', 'gzcat', and 'tar'. ```shell (set -eo pipefail; URL='https://github.com/kyleneideck/BackgroundMusic/archive/master.tar.gz'; cd $(mktemp -d); echo Downloading $URL to $(pwd); curl -qfL# $URL | gzcat - | tar x && /bin/bash BackgroundMusic-master/build_and_install.sh -w && rm -rf BackgroundMusic-master) ``` -------------------------------- ### Get BGM Device ID using Core Audio Objective-C Source: https://context7.com/kyleneideck/backgroundmusic/llms.txt Retrieves the AudioObjectID for the Background Music virtual audio device by iterating through all system audio devices and matching the device UID. Uses Core Audio framework APIs to query device properties and returns kAudioObjectUnknown if the BGM device is not found. Requires proper error handling for audio property queries. ```objc #import AudioObjectID getBGMDeviceID(void) { AudioObjectID deviceID = kAudioObjectUnknown; // Get all audio devices AudioObjectPropertyAddress addr = { .mSelector = kAudioHardwarePropertyDevices, .mScope = kAudioObjectPropertyScopeGlobal, .mElement = kAudioObjectPropertyElementMaster }; UInt32 dataSize = 0; OSStatus err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &dataSize); if (err != kAudioHardwareNoError) return kAudioObjectUnknown; int deviceCount = dataSize / sizeof(AudioObjectID); AudioObjectID *devices = malloc(dataSize); err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &dataSize, devices); if (err == kAudioHardwareNoError) { for (int i = 0; i < deviceCount; i++) { // Get device UID CFStringRef uid = NULL; dataSize = sizeof(uid); AudioObjectPropertyAddress uidAddr = { .mSelector = kAudioDevicePropertyDeviceUID, .mScope = kAudioObjectPropertyScopeGlobal, .mElement = kAudioObjectPropertyElementMaster }; err = AudioObjectGetPropertyData(devices[i], &uidAddr, 0, NULL, &dataSize, &uid); if (err == kAudioHardwareNoError && uid != NULL) { if (CFStringCompare(uid, CFSTR("BGMDevice"), 0) == kCFCompareEqualTo) { deviceID = devices[i]; CFRelease(uid); break; } CFRelease(uid); } } } free(devices); return deviceID; } ``` -------------------------------- ### Remove BGMXPCHelper Launchd File (Shell) Source: https://github.com/kyleneideck/backgroundmusic/blob/master/MANUAL-UNINSTALL.md Deletes the launchd configuration file for the BGMXPCHelper service. This command requires administrator privileges and targets a specific file in the /Library/LaunchDaemons directory. ```shell sudo rm /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist ``` -------------------------------- ### Restart coreaudiod Service (Shell) Source: https://github.com/kyleneideck/backgroundmusic/blob/master/MANUAL-UNINSTALL.md Restarts the core audio daemon, a necessary step after uninstalling audio-related software. Two commands are provided for different scenarios. This action requires administrator privileges. ```shell sudo killall coreaudiod ``` ```shell sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod ``` -------------------------------- ### Uninstall Background Music Source: https://github.com/kyleneideck/backgroundmusic/blob/master/README.md This process involves navigating to the application's resources directory in the terminal and executing the 'uninstall.sh' script. If 'uninstall.sh' is not found, the project can be re-downloaded. ```shell cd /Applications/Background\ Music.app/Contents/Resources/ bash uninstall.sh ``` -------------------------------- ### Add App Bundle ID Mapping in C++ Source: https://github.com/kyleneideck/backgroundmusic/wiki/App-Volumes-Fixes This code snippet demonstrates how to add a new mapping between an application's bundle ID and the bundle ID used by the Background Music virtual audio driver. It involves creating a new entry in a predefined list within the `BGMBackgroundMusicDevice.cpp` file. Ensure you replace the placeholder bundle IDs with the actual values obtained from debug logs. ```cpp // Safari { "com.apple.Safari", { "com.apple.WebKit.WebContent" } }, ``` -------------------------------- ### Remove BGMXPCHelper User and Group (Shell) Source: https://github.com/kyleneideck/backgroundmusic/blob/master/MANUAL-UNINSTALL.md Deletes the dedicated user and group accounts associated with the BGMXPCHelper service from the system directory. These commands require administrator privileges. ```shell sudo dscl . -delete /Users/_BGMXPCHelper ``` ```shell sudo dscl . -delete /Groups/_BGMXPCHelper ``` -------------------------------- ### Verify and Remove Background Music Driver (Shell) Source: https://github.com/kyleneideck/backgroundmusic/blob/master/MANUAL-UNINSTALL.md Lists the contents of the HAL directory to check for the Background Music driver and provides a command to forcefully remove it if found. This operation requires administrator privileges and targets a specific driver file path. ```shell sudo ls /Library/Audio/Plug-Ins/HAL ``` ```shell sudo rm -rf "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver" ``` -------------------------------- ### Objective-C Custom Music Player Plugin Implementation Source: https://context7.com/kyleneideck/backgroundmusic/llms.txt Implements a custom music player plugin conforming to the BGMMusicPlayer protocol. It allows for creating player instances, checking playback and pause states, and controlling playback via AppleScript. Dependencies include the BGMMusicPlayerBase class and NSWorkspace for checking running applications. Outputs are boolean values indicating success or state. ```objc // CustomMusicPlayer.h #import "BGMMusicPlayer.h" @interface CustomMusicPlayer : BGMMusicPlayerBase + (NSArray>*)createInstancesWithDefaults:(BGMUserDefaults*)userDefaults; @property (readonly) NSUUID* musicPlayerID; @property (readonly) NSString* name; @property (readonly) NSString* bundleID; @property (readonly, getter=isRunning) BOOL running; @property (readonly, getter=isPlaying) BOOL playing; @property (readonly, getter=isPaused) BOOL paused; - (BOOL)pause; - (BOOL)unpause; @end // CustomMusicPlayer.m #import "CustomMusicPlayer.h" @implementation CustomMusicPlayer + (NSArray>*)createInstancesWithDefaults:(BGMUserDefaults*)userDefaults { return @[[[self alloc] init]]; } - (instancetype)init { // Generate UUID with: uuidgen NSUUID* playerID = [BGMMusicPlayerBase makeID:@"12345678-1234-1234-1234-123456789ABC"]; return [super initWithMusicPlayerID:playerID name:@"My Music Player" bundleID:@"com.example.musicplayer"]; } - (BOOL)isRunning { NSArray* runningApps = [[NSWorkspace sharedWorkspace] runningApplications]; for (NSRunningApplication* app in runningApps) { if ([app.bundleIdentifier isEqualToString:self.bundleID]) { return YES; } } return NO; } - (BOOL)isPlaying { if (!self.running) return NO; // Use AppleScript or other means to check playing state // Example using Scripting Bridge or AppleScript NSAppleScript* script = [[NSAppleScript alloc] initWithSource: @"tell application \"My Music Player\" to player state is playing"]; NSDictionary* errorDict; NSAppleEventDescriptor* result = [script executeAndReturnError:&errorDict]; return result.booleanValue; } - (BOOL)isPaused { if (!self.running) return NO; NSAppleScript* script = [[NSAppleScript alloc] initWithSource: @"tell application \"My Music Player\" to player state is paused"]; NSDictionary* errorDict; NSAppleEventDescriptor* result = [script executeAndReturnError:&errorDict]; return result.booleanValue; } - (BOOL)pause { if (!self.isPlaying) return NO; NSAppleScript* script = [[NSAppleScript alloc] initWithSource: @"tell application \"My Music Player\" to pause"]; NSDictionary* errorDict; [script executeAndReturnError:&errorDict]; return errorDict == nil; } - (BOOL)unpause { if (!self.isPaused) return NO; NSAppleScript* script = [[NSAppleScript alloc] initWithSource: @"tell application \"My Music Player\" to play"]; NSDictionary* errorDict; [script executeAndReturnError:&errorDict]; return errorDict == nil; } @end // Register in BGMMusicPlayers.mm initWithAudioDevices: // Add to the array: [[CustomMusicPlayer alloc] init] ``` -------------------------------- ### Set Per-Application Volume using CoreAudio Source: https://context7.com/kyleneideck/backgroundmusic/llms.txt Sets the volume for a specific application process ID (PID) on a custom audio device. It requires the application's PID and a relative volume level (0-100, with 50 being neutral). Dependencies include the CoreAudio framework. ```objc #import // Set volume for a specific application void setAppVolume(pid_t appPID, SInt32 relativeVolume) { AudioObjectID bgmDevice = getBGMDeviceID(); // Create app volume dictionary CFMutableDictionaryRef appVolume = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBackS, &kCFTypeDictionaryValueCallBacks); // Set process ID CFNumberRef pidRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &appPID); CFDictionarySetValue(appVolume, CFSTR("pid"), pidRef); // Set relative volume (0-100, 50 is neutral) CFNumberRef volumeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &relativeVolume); CFDictionarySetValue(appVolume, CFSTR("rvol"), volumeRef); // Create array with single app volume CFArrayRef appVolumes = CFArrayCreate(kCFAllocatorDefault, (const void**)&appVolume, 1, &kCFTypeArrayCallBacks); // Set the property AudioObjectPropertyAddress addr = { .mSelector = 'apvs', // kAudioDeviceCustomPropertyAppVolumes .mScope = kAudioObjectPropertyScopeGlobal, .mElement = kAudioObjectPropertyElementMaster }; UInt32 dataSize = sizeof(CFArrayRef); OSStatus err = AudioObjectSetPropertyData(bgmDevice, &addr, 0, NULL, dataSize, &appVolumes); CFRelease(appVolumes); CFRelease(appVolume); CFRelease(volumeRef); CFRelease(pidRef); if (err != kAudioHardwareNoError) { printf("Failed to set app volume: %d\n", err); } } ``` -------------------------------- ### Unregister BGMXPCHelper (Shell) Source: https://github.com/kyleneideck/backgroundmusic/blob/master/MANUAL-UNINSTALL.md Unregisters the BGMXPCHelper service from the system's launch daemons. Two commands are provided, one for OS X 10.11 and later, and another for earlier versions. This requires administrator privileges. ```shell sudo launchctl bootout system /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist ``` ```shell sudo launchctl unload /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist ``` -------------------------------- ### Configure Music Player for Auto-Pause using CoreAudio Source: https://context7.com/kyleneideck/backgroundmusic/llms.txt Configures the music player application that will be automatically paused by the system. This function requires the bundle identifier of the music player. It utilizes the CoreAudio framework for device property manipulation. ```objc #import // Configure which music player to auto-pause void setMusicPlayer(NSString *bundleID) { AudioObjectID bgmDevice = getBGMDeviceID(); // Set music player bundle ID property AudioObjectPropertyAddress addr = { .mSelector = 'mpbi', // kAudioDeviceCustomPropertyMusicPlayerBundleID .mScope = kAudioObjectPropertyScopeGlobal, .mElement = kAudioObjectPropertyElementMaster }; CFStringRef bundleIDRef = (__bridge CFStringRef)bundleID; UInt32 dataSize = sizeof(CFStringRef); OSStatus err = AudioObjectSetPropertyData(bgmDevice, &addr, 0, NULL, dataSize, &bundleIDRef); if (err != kAudioHardwareNoError) { printf("Failed to set music player: %d\n", err); } } // Example usage setMusicPlayer(@"com.spotify.client"); ``` -------------------------------- ### Monitor Device Audible State Changes using CoreAudio Source: https://context7.com/kyleneideck/backgroundmusic/llms.txt Registers a listener to monitor changes in the audible state of a custom audio device. The listener callback function `audibleStateListener` processes notifications for different states like silent, music-only playing, or other audio playing. It requires the CoreAudio framework. ```objc #import // Listener callback for audible state changes OSStatus audibleStateListener(AudioObjectID device, UInt32 numAddresses, const AudioObjectPropertyAddress *addresses, void *clientData) { for (UInt32 i = 0; i < numAddresses; i++) { if (addresses[i].mSelector == 'daud') { // kAudioDeviceCustomPropertyDeviceAudibleState SInt32 state; UInt32 dataSize = sizeof(state); OSStatus err = AudioObjectGetPropertyData(device, &addresses[i], 0, NULL, &dataSize, &state); if (err == kAudioHardwareNoError) { switch (state) { case 'silt': // kBGMDeviceIsSilent NSLog(@"Device is silent"); break; case 'olym': // kBGMDeviceIsSilentExceptMusic NSLog(@"Only music playing"); break; case 'audi': // kBGMDeviceIsAudible NSLog(@"Other audio playing"); // Pause music player here break; } } } } return kAudioHardwareNoError; } // Register the listener void monitorAudibleState(void) { AudioObjectID bgmDevice = getBGMDeviceID(); AudioObjectPropertyAddress addr = { .mSelector = 'daud', .mScope = kAudioObjectPropertyScopeGlobal, .mElement = kAudioObjectPropertyElementMaster }; OSStatus err = AudioObjectAddPropertyListener(bgmDevice, &addr, audibleStateListener, NULL); if (err != kAudioHardwareNoError) { NSLog(@"Failed to register listener: %d", err); } } ```