Skip to content

Commit

Permalink
Add workaround for FaceTime volume.
Browse files Browse the repository at this point in the history
FaceTime plays call audio using a daemon called avconferenced, so
BGMDriver can't tell where the audio is actually coming from. As a
hopefully temporary fix, BGMApp now just sets avconferenced's volume to
match FaceTime's. See #139.

Also,
 - set a tooltip and accessibility label for BGMApp's status bar item
   (the thing you click to show the main menu), and
 - some minor refactoring.
  • Loading branch information
kyleneideck committed Feb 24, 2018
1 parent 287bae0 commit 944fc11
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 94 deletions.
78 changes: 50 additions & 28 deletions BGMApp/BGMApp/BGMAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// BGMAppDelegate.mm
// BGMApp
//
// Copyright © 2016, 2017 Kyle Neideck
// Copyright © 2016-2018 Kyle Neideck
//

// Self Includes
Expand All @@ -43,7 +43,9 @@

#pragma clang assume_nonnull begin

static float const kStatusBarIconPadding = 0.25;
static float const kStatusBarIconPadding = 0.25;
static NSString* const kOptNoPersistentData = @"--no-persistent-data";
static NSString* const kOptShowDockIcon = @"--show-dock-icon";

@implementation BGMAppDelegate {
// The button in the system status bar (the bar with volume, battery, clock, etc.) to show the main menu
Expand All @@ -65,8 +67,9 @@ @implementation BGMAppDelegate {
@synthesize audioDevices = audioDevices;

- (void) awakeFromNib {
// Show BGMApp in the dock, if the command-line option for that was passed. This is used by the UI tests.
if ([NSProcessInfo.processInfo.arguments indexOfObject:@"--show-dock-icon"] != NSNotFound) {
// Show BGMApp in the dock, if the command-line option for that was passed. This is used by the
// UI tests.
if ([NSProcessInfo.processInfo.arguments indexOfObject:kOptShowDockIcon] != NSNotFound) {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
}

Expand All @@ -78,13 +81,24 @@ - (void) awakeFromNib {
// Set up the status bar item. (The thing you click to show BGMApp's UI.)
- (void) initStatusBarItem {
statusBarItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];

// Set the icon
NSImage* icon = [NSImage imageNamed:@"FermataIcon"];

// NSStatusItem doesn't have the "button" property on OS X 10.9.
BOOL buttonAvailable = (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_10);

// Set the title/tooltip to "Background Music".
statusBarItem.title = [NSRunningApplication currentApplication].localizedName;
statusBarItem.toolTip = statusBarItem.title;

if (buttonAvailable) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
statusBarItem.button.accessibilityLabel = statusBarItem.title;
#pragma clang diagnostic pop
}

// Set the icon.
NSImage* icon = [NSImage imageNamed:@"FermataIcon"];

if (icon != nil) {
NSRect statusBarItemFrame;

Expand Down Expand Up @@ -151,45 +165,26 @@ - (void) applicationDidFinishLaunching:(NSNotification*)aNotification {
BGMTermination::SetUpTerminationCleanUp(audioDevices);

// Set up the rest of the UI and other external interfaces.

BGMUserDefaults* userDefaults = [self createUserDefaults];

musicPlayers = [[BGMMusicPlayers alloc] initWithAudioDevices:audioDevices
userDefaults:userDefaults];

autoPauseMusic = [[BGMAutoPauseMusic alloc] initWithAudioDevices:audioDevices
musicPlayers:musicPlayers];

autoPauseMenuItem = [[BGMAutoPauseMenuItem alloc] initWithMenuItem:self.autoPauseMenuItemUnwrapped
autoPauseMusic:autoPauseMusic
musicPlayers:musicPlayers
userDefaults:userDefaults];
[self setUpMainMenu:userDefaults];

xpcListener = [[BGMXPCListener alloc] initWithAudioDevices:audioDevices
helperConnectionErrorHandler:^(NSError* error) {
NSLog(@"BGMAppDelegate::applicationDidFinishLaunching: (helperConnectionErrorHandler) "
"BGMXPCHelper connection error: %@",
error);

[self showXPCHelperErrorMessage:error];
}];

[self initVolumesMenuSection];

prefsMenu = [[BGMPreferencesMenu alloc] initWithBGMMenu:self.bgmMenu
audioDevices:audioDevices
musicPlayers:musicPlayers
aboutPanel:self.aboutPanel
aboutPanelLicenseView:self.aboutPanelLicenseView];

// Handle events about the main menu. (See the NSMenuDelegate methods below.)
self.bgmMenu.delegate = self;
}

- (BGMUserDefaults*) createUserDefaults {
BOOL persistentDefaults = [NSProcessInfo.processInfo.arguments indexOfObject:@"--no-persistent-data"] == NSNotFound;
NSUserDefaults* wrappedDefaults = persistentDefaults ? [NSUserDefaults standardUserDefaults] : nil;
return [[BGMUserDefaults alloc] initWithDefaults:wrappedDefaults];
}

// Returns NO if (and only if) BGMApp is about to terminate because of a fatal error.
Expand All @@ -214,6 +209,32 @@ - (BOOL) initAudioDeviceManager {
return YES;
}

- (void) setUpMainMenu:(BGMUserDefaults*)userDefaults {
autoPauseMenuItem =
[[BGMAutoPauseMenuItem alloc] initWithMenuItem:self.autoPauseMenuItemUnwrapped
autoPauseMusic:autoPauseMusic
musicPlayers:musicPlayers
userDefaults:userDefaults];

[self initVolumesMenuSection];

prefsMenu = [[BGMPreferencesMenu alloc] initWithBGMMenu:self.bgmMenu
audioDevices:audioDevices
musicPlayers:musicPlayers
aboutPanel:self.aboutPanel
aboutPanelLicenseView:self.aboutPanelLicenseView];

// Handle events about the main menu. (See the NSMenuDelegate methods below.)
self.bgmMenu.delegate = self;
}

- (BGMUserDefaults*) createUserDefaults {
BOOL persistentDefaults =
[NSProcessInfo.processInfo.arguments indexOfObject:kOptNoPersistentData] == NSNotFound;
NSUserDefaults* wrappedDefaults = persistentDefaults ? [NSUserDefaults standardUserDefaults] : nil;
return [[BGMUserDefaults alloc] initWithDefaults:wrappedDefaults];
}

- (void) initVolumesMenuSection {
// Create the menu item with the (main) output volume slider.
BGMOutputVolumeMenuItem* outputVolume =
Expand Down Expand Up @@ -249,6 +270,7 @@ - (void) applicationWillTerminate:(NSNotification*)aNotification {

DebugMsg("BGMAppDelegate::applicationWillTerminate");

// Change the user's default output device back.
NSError* error = [audioDevices unsetBGMDeviceAsOSDefault];

if (error) {
Expand Down
10 changes: 6 additions & 4 deletions BGMApp/BGMApp/BGMAppVolumes.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,19 @@
//

// Local Includes
#import "BGMAudioDeviceManager.h"
#import "BGMAppVolumesController.h"

// System Includes
#import <Cocoa/Cocoa.h>


#pragma clang assume_nonnull begin

@interface BGMAppVolumes : NSObject

- (id) initWithMenu:(NSMenu*)menu
appVolumeView:(NSView*)view
audioDevices:(BGMAudioDeviceManager*)audioDevices;
- (id) initWithController:(BGMAppVolumesController*)inController
bgmMenu:(NSMenu*)inMenu
appVolumeView:(NSView*)inView;

// Pass -1 for initialVolume or initialPan to leave the volume/pan at its default level.
- (void) insertMenuItemForApp:(NSRunningApplication*)app
Expand All @@ -51,6 +52,7 @@

- (void) setUpWithApp:(NSRunningApplication*)app
context:(BGMAppVolumes*)ctx
controller:(BGMAppVolumesController*)ctrl
menuItem:(NSMenuItem*)item;

@end
Expand Down
76 changes: 41 additions & 35 deletions BGMApp/BGMApp/BGMAppVolumes.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// BGMAppVolumes.m
// BGMApp
//
// Copyright © 2016, 2017 Kyle Neideck
// Copyright © 2016-2018 Kyle Neideck
// Copyright © 2017 Andrew Tonner
//

Expand All @@ -38,27 +38,27 @@
static NSString* const kMoreAppsMenuTitle = @"More Apps";

@implementation BGMAppVolumes {
BGMAppVolumesController* controller;

NSMenu* bgmMenu;
NSMenu* moreAppsMenu;

NSView* appVolumeView;
CGFloat appVolumeViewFullHeight;

BGMAudioDeviceManager* audioDevices;

// The number of menu items this class has added to bgmMenu. Doesn't include the More Apps menu.
NSInteger numMenuItems;
}

- (id) initWithMenu:(NSMenu*)menu
appVolumeView:(NSView*)view
audioDevices:(BGMAudioDeviceManager*)devices {
- (id) initWithController:(BGMAppVolumesController*)inController
bgmMenu:(NSMenu*)inMenu
appVolumeView:(NSView*)inView {
if ((self = [super init])) {
bgmMenu = menu;
controller = inController;
bgmMenu = inMenu;
moreAppsMenu = [[NSMenu alloc] initWithTitle:kMoreAppsMenuTitle];
appVolumeView = view;
appVolumeView = inView;
appVolumeViewFullHeight = appVolumeView.frame.size.height;
audioDevices = devices;
numMenuItems = 0;

// Add the More Apps menu to the main menu.
Expand All @@ -81,12 +81,6 @@ - (id) initWithMenu:(NSMenu*)menu
return self;
}

// This method allows the Interface Builder Custom Classes for controls (below) to send their values
// directly to BGMDevice. Not public to other classes.
- (BGMAudioDeviceManager*) audioDevices {
return audioDevices;
}

#pragma mark UI Modifications

- (void) insertMenuItemForApp:(NSRunningApplication*)app
Expand All @@ -99,6 +93,7 @@ - (void) insertMenuItemForApp:(NSRunningApplication*)app
if ([subview conformsToProtocol:@protocol(BGMAppVolumeMenuItemSubview)]) {
[(NSView<BGMAppVolumeMenuItemSubview>*)subview setUpWithApp:app
context:self
controller:controller
menuItem:appVolItem];
}
}
Expand Down Expand Up @@ -276,8 +271,11 @@ - (void) removeAllAppVolumeMenuItems {

@implementation BGMAVM_AppIcon

- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx menuItem:(NSMenuItem*)menuItem {
#pragma unused (ctx, menuItem)
- (void) setUpWithApp:(NSRunningApplication*)app
context:(BGMAppVolumes*)ctx
controller:(BGMAppVolumesController*)ctrl
menuItem:(NSMenuItem*)menuItem {
#pragma unused (ctx, ctrl, menuItem)

self.image = app.icon;

Expand All @@ -296,8 +294,11 @@ - (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx men

@implementation BGMAVM_AppNameLabel

- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx menuItem:(NSMenuItem*)menuItem {
#pragma unused (ctx, menuItem)
- (void) setUpWithApp:(NSRunningApplication*)app
context:(BGMAppVolumes*)ctx
controller:(BGMAppVolumesController*)ctrl
menuItem:(NSMenuItem*)menuItem {
#pragma unused (ctx, ctrl, menuItem)

NSString* name = app.localizedName ? (NSString*)app.localizedName : @"";
self.stringValue = name;
Expand All @@ -307,8 +308,11 @@ - (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx men

@implementation BGMAVM_ShowMoreControlsButton

- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx menuItem:(NSMenuItem*)menuItem {
#pragma unused (app)
- (void) setUpWithApp:(NSRunningApplication*)app
context:(BGMAppVolumes*)ctx
controller:(BGMAppVolumesController*)ctrl
menuItem:(NSMenuItem*)menuItem {
#pragma unused (app, ctrl)

// Set up the button that show/hide the extra controls (currently only a pan slider) for the app.
self.cell.representedObject = menuItem;
Expand Down Expand Up @@ -336,13 +340,16 @@ @implementation BGMAVM_VolumeSlider {
// Will be set to -1 for apps without a pid
pid_t appProcessID;
NSString* __nullable appBundleID;
BGMAppVolumes* context;
BGMAppVolumesController* controller;
}

- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx menuItem:(NSMenuItem*)menuItem {
#pragma unused (menuItem)
- (void) setUpWithApp:(NSRunningApplication*)app
context:(BGMAppVolumes*)ctx
controller:(BGMAppVolumesController*)ctrl
menuItem:(NSMenuItem*)menuItem {
#pragma unused (ctx, menuItem)

context = ctx;
controller = ctrl;

self.target = self;
self.action = @selector(appVolumeChanged);
Expand Down Expand Up @@ -388,9 +395,7 @@ - (void) appVolumeChanged {

// The values from our sliders are in
// [kAppRelativeVolumeMinRawValue, kAppRelativeVolumeMaxRawValue] already.
[context.audioDevices setVolume:self.intValue
forAppWithProcessID:appProcessID
bundleID:appBundleID];
[controller setVolume:self.intValue forAppWithProcessID:appProcessID bundleID:appBundleID];
}

@end
Expand All @@ -399,13 +404,16 @@ @implementation BGMAVM_PanSlider {
// Will be set to -1 for apps without a pid
pid_t appProcessID;
NSString* __nullable appBundleID;
BGMAppVolumes* context;
BGMAppVolumesController* controller;
}

- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx menuItem:(NSMenuItem*)menuItem {
#pragma unused (menuItem)
- (void) setUpWithApp:(NSRunningApplication*)app
context:(BGMAppVolumes*)ctx
controller:(BGMAppVolumesController*)ctrl
menuItem:(NSMenuItem*)menuItem {
#pragma unused (ctx, menuItem)

context = ctx;
controller = ctrl;

self.target = self;
self.action = @selector(appPanPositionChanged);
Expand Down Expand Up @@ -434,9 +442,7 @@ - (void) appPanPositionChanged {
DebugMsg("BGMAppVolumes::appPanPositionChanged: App pan position for %s changed to %d", appBundleID.UTF8String, self.intValue);

// The values from our sliders are in [kAppPanLeftRawValue, kAppPanRightRawValue] already.
[context.audioDevices setPanPosition:self.intValue
forAppWithProcessID:appProcessID
bundleID:appBundleID];
[controller setPanPosition:self.intValue forAppWithProcessID:appProcessID bundleID:appBundleID];
}

@end
Expand Down
10 changes: 10 additions & 0 deletions BGMApp/BGMApp/BGMAppVolumesController.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@
appVolumeView:(NSView*)view
audioDevices:(BGMAudioDeviceManager*)audioDevices;

// See BGMBackgroundMusicDevice::SetAppVolume.
- (void) setVolume:(SInt32)volume
forAppWithProcessID:(pid_t)processID
bundleID:(NSString* __nullable)bundleID;

// See BGMBackgroundMusicDevice::SetPanVolume.
- (void) setPanPosition:(SInt32)pan
forAppWithProcessID:(pid_t)processID
bundleID:(NSString* __nullable)bundleID;

@end

#pragma clang assume_nonnull end
Expand Down
Loading

0 comments on commit 944fc11

Please sign in to comment.