Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions BGMApp/BGMApp/BGMAutoPauseMenuItem.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@

#pragma clang assume_nonnull begin

static NSString* const kMenuItemTitleFormat = @"Auto-pause %@";
static NSString* const kMenuItemTitleFormatPause = @"Auto-pause %@";
static NSString* const kMenuItemTitleFormatDuck = @"Auto-duck %@";
static NSString* const kMenuItemDisabledToolTipFormat = @"%@ doesn't appear to be running.";

// Wait time to disable/enable the auto-pause menu item, in seconds.
Expand Down Expand Up @@ -123,7 +124,8 @@ - (void) updateMenuItemTitle {
- (void) updateMenuItemTitleWithHighlight:(BOOL)highlight {
// Set the title of the Auto-pause Music menu item, including the name of the selected music player.
NSString* musicPlayerName = musicPlayers.selectedMusicPlayer.name;
menuItem.title = [NSString stringWithFormat:kMenuItemTitleFormat, musicPlayerName];
NSString* titleFormat = userDefaults.autoDuckMusic ? kMenuItemTitleFormatDuck : kMenuItemTitleFormatPause;
menuItem.title = [NSString stringWithFormat:titleFormat, musicPlayerName];

// Make the Auto-pause Music menu item appear disabled if the application is not running.
//
Expand Down
285 changes: 241 additions & 44 deletions BGMApp/BGMApp/BGMAutoPauseMusic.mm

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions BGMApp/BGMApp/BGMUserDefaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
@property NSString* __nullable selectedMusicPlayerID;

@property BOOL autoPauseMusicEnabled;
@property BOOL autoDuckMusic;
@property NSUInteger autoDuckPercent;

// The UIDs of the output devices most recently selected by the user. The most-recently selected
// device is at index 0. See BGMPreferredOutputDevices.
Expand Down
26 changes: 25 additions & 1 deletion BGMApp/BGMApp/BGMUserDefaults.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

// Keys
static NSString* const kDefaultKeyAutoPauseMusicEnabled = @"AutoPauseMusicEnabled";
static NSString* const kDefaultKeyAutoDuckMusic = @"AutoDuckMusic";
static NSString* const kDefaultKeyAutoDuckPercent = @"AutoDuckPercent";
static NSString* const kDefaultKeySelectedMusicPlayerID = @"SelectedMusicPlayerID";
static NSString* const kDefaultKeyPreferredDeviceUIDs = @"PreferredDeviceUIDs";
static NSString* const kDefaultKeyStatusBarIcon = @"StatusBarIcon";
Expand Down Expand Up @@ -62,7 +64,9 @@ - (instancetype) initWithDefaults:(NSUserDefaults* __nullable)inDefaults {
NSDictionary* defaultsDict = @{
kDefaultKeyAutoPauseMusicEnabled: @YES,
kDefaultKeyPauseDelayMS: @1500,
kDefaultKeyMaxUnpauseDelayMS: @3500
kDefaultKeyMaxUnpauseDelayMS: @3500,
kDefaultKeyAutoDuckMusic: @NO,
kDefaultKeyAutoDuckPercent: @30
};

if (defaults) {
Expand Down Expand Up @@ -95,6 +99,26 @@ - (void) setAutoPauseMusicEnabled:(BOOL)autoPauseMusicEnabled {
[self setBool:kDefaultKeyAutoPauseMusicEnabled to:autoPauseMusicEnabled];
}

#pragma mark Auto-duck

- (BOOL) autoDuckMusic {
return [self getBool:kDefaultKeyAutoDuckMusic];
}

- (void) setAutoDuckMusic:(BOOL)autoDuckMusic {
[self setBool:kDefaultKeyAutoDuckMusic to:autoDuckMusic];
}

- (NSUInteger) autoDuckPercent {
NSInteger percent = [self getInt:kDefaultKeyAutoDuckPercent or:30];
return (NSUInteger)MAX(0, MIN(100, percent));
}

- (void) setAutoDuckPercent:(NSUInteger)autoDuckPercent {
NSUInteger clampedPercent = MAX(0, MIN(100, autoDuckPercent));
[self setInt:kDefaultKeyAutoDuckPercent to:(NSInteger)clampedPercent];
}

#pragma mark Auto-pause Delays

- (NSUInteger) pauseDelayMS {
Expand Down
3 changes: 2 additions & 1 deletion BGMApp/BGMApp/Preferences/BGMAutoPauseMusicPrefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@

- (id) initWithPreferencesMenu:(NSMenu*)inPrefsMenu
audioDevices:(BGMAudioDeviceManager*)inAudioDevices
musicPlayers:(BGMMusicPlayers*)inMusicPlayers;
musicPlayers:(BGMMusicPlayers*)inMusicPlayers
userDefaults:(BGMUserDefaults*)inUserDefaults;

@end

Expand Down
23 changes: 22 additions & 1 deletion BGMApp/BGMApp/Preferences/BGMAutoPauseMusicPrefs.mm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
// Local Includes
#import "BGM_Types.h"
#import "BGMMusicPlayer.h"
#import "BGMUserDefaults.h"


#pragma clang assume_nonnull begin
Expand All @@ -36,17 +37,20 @@
@implementation BGMAutoPauseMusicPrefs {
BGMAudioDeviceManager* audioDevices;
BGMMusicPlayers* musicPlayers;
BGMUserDefaults* userDefaults;
NSMenu* prefsMenu;
NSArray<NSMenuItem*>* musicPlayerMenuItems;
}

- (id) initWithPreferencesMenu:(NSMenu*)inPrefsMenu
audioDevices:(BGMAudioDeviceManager*)inAudioDevices
musicPlayers:(BGMMusicPlayers*)inMusicPlayers {
musicPlayers:(BGMMusicPlayers*)inMusicPlayers
userDefaults:(BGMUserDefaults*)inUserDefaults {
if ((self = [super init])) {
prefsMenu = inPrefsMenu;
audioDevices = inAudioDevices;
musicPlayers = inMusicPlayers;
userDefaults = inUserDefaults;

musicPlayerMenuItems = @[];

Expand Down Expand Up @@ -96,6 +100,17 @@ - (void) initPreferencesMenuSection {
menuItem.target = self;
menuItem.indentationLevel = 1;
}

// Add "Duck instead of pause" menu item below the music players
NSInteger duckMusicItemIndex = musicPlayerItemsIndex + musicPlayers.musicPlayers.count;
NSMenuItem* duckMusicMenuItem = [prefsMenu insertItemWithTitle:@"Duck instead of pause"
action:@selector(handleDuckMusicChange:)
keyEquivalent:@""
atIndex:duckMusicItemIndex];
duckMusicMenuItem.representedObject = nil;
duckMusicMenuItem.target = self;
duckMusicMenuItem.indentationLevel = 1;
duckMusicMenuItem.state = userDefaults.autoDuckMusic ? NSOnState : NSOffState;
}

- (void) handleMusicPlayerChange:(NSMenuItem*)sender {
Expand All @@ -112,6 +127,12 @@ - (void) handleMusicPlayerChange:(NSMenuItem*)sender {
}
}

- (void) handleDuckMusicChange:(NSMenuItem*)sender {
BOOL nextState = (sender.state == NSOffState);
sender.state = nextState ? NSOnState : NSOffState;
userDefaults.autoDuckMusic = nextState;
}

@end

#pragma clang assume_nonnull end
Expand Down
91 changes: 89 additions & 2 deletions BGMApp/BGMApp/Preferences/BGMPreferencesMenu.mm
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ @implementation BGMPreferencesMenu {
NSTextField* pauseDelayLabel;
NSTextField* maxUnpauseDelayLabel;
BGMUserDefaults* userDefaults;

// Ducking preferences
NSMenuItem* duckVolumeMenuItem;
NSSlider* duckVolumeSlider;
NSTextField* duckVolumeLabel;
}

- (id) initWithBGMMenu:(NSMenu*)inBGMMenu
Expand All @@ -73,7 +78,8 @@ - (id) initWithBGMMenu:(NSMenu*)inBGMMenu

autoPauseMusicPrefs = [[BGMAutoPauseMusicPrefs alloc] initWithPreferencesMenu:prefsMenu
audioDevices:inAudioDevices
musicPlayers:inMusicPlayers];
musicPlayers:inMusicPlayers
userDefaults:inUserDefaults];

aboutPanel = [[BGMAboutPanel alloc] initWithPanel:inAboutPanel licenseView:inAboutPanelLicenseView];

Expand All @@ -100,6 +106,11 @@ - (id) initWithBGMMenu:(NSMenu*)inBGMMenu
// Set up delay preferences
userDefaults = inUserDefaults;
[self setupDelayPreferences:prefsMenu];

[userDefaults addObserver:self
forKeyPath:@"autoDuckMusic"
options:NSKeyValueObservingOptionNew
context:nil];
}

return self;
Expand Down Expand Up @@ -194,8 +205,36 @@ - (void) setupDelayPreferences:(NSMenu*)prefsMenu {

maxUnpauseDelayMenuItem.view = maxUnpauseDelayView;
[prefsMenu addItem:maxUnpauseDelayMenuItem];

// Create ducking volume menu item with slider
duckVolumeMenuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
NSView* duckVolumeView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 280, 25)];

// Ducking volume label
NSTextField* duckVolumeTitleLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 5, 110, 15)];
duckVolumeTitleLabel.stringValue = @"Ducking Volume:";
duckVolumeTitleLabel.editable = NO;
duckVolumeTitleLabel.bordered = NO;
duckVolumeTitleLabel.backgroundColor = [NSColor clearColor];
duckVolumeTitleLabel.font = [NSFont menuFontOfSize:13];
[duckVolumeView addSubview:duckVolumeTitleLabel];

// Ducking volume slider
duckVolumeSlider = [[NSSlider alloc] initWithFrame:NSMakeRect(115, 5, 100, 15)];
[duckVolumeView addSubview:duckVolumeSlider];

// Ducking volume value label
duckVolumeLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(220, 5, 55, 15)];
duckVolumeLabel.editable = NO;
duckVolumeLabel.bordered = NO;
duckVolumeLabel.backgroundColor = [NSColor clearColor];
duckVolumeLabel.font = [NSFont menuFontOfSize:11];
[duckVolumeView addSubview:duckVolumeLabel];

// Initialize the delay sliders with current values and targets
duckVolumeMenuItem.view = duckVolumeView;
[prefsMenu addItem:duckVolumeMenuItem];

// Initialize the sliders with current values and targets
[self initDelaySliders];
}

Expand All @@ -215,10 +254,19 @@ - (void) initDelaySliders {
maxUnpauseDelaySlider.integerValue = [self msToSliderValue:userDefaults.maxUnpauseDelayMS];
maxUnpauseDelaySlider.target = self;
maxUnpauseDelaySlider.action = @selector(maxUnpauseDelaySliderChanged:);

// Configure ducking volume slider (0% to 100%)
duckVolumeSlider.minValue = 0;
duckVolumeSlider.maxValue = 100;
duckVolumeSlider.integerValue = userDefaults.autoDuckPercent;
duckVolumeSlider.target = self;
duckVolumeSlider.action = @selector(duckVolumeSliderChanged:);

// Update labels with current values
[self updatePauseDelayLabel];
[self updateMaxUnpauseDelayLabel];
[self updateDuckVolumeLabel];
[self updateSliderStates];
}

- (void) pauseDelaySliderChanged:(NSSlider*)sender {
Expand Down Expand Up @@ -284,6 +332,45 @@ - (NSInteger) msToSliderValue:(NSUInteger)ms {
return (NSInteger)MIN(MAX(sliderValue, 0), 100);
}

- (void) dealloc {
try {
[userDefaults removeObserver:self forKeyPath:@"autoDuckMusic" context:nil];
} catch (const std::exception& e) {
}
}

- (void) observeValueForKeyPath:(NSString* __nullable)keyPath
ofObject:(id __nullable)object
change:(NSDictionary* __nullable)change
context:(void* __nullable)context
{
#pragma unused (object, change, context)
if ([keyPath isEqualToString:@"autoDuckMusic"]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateSliderStates];
});
}
}

- (void) duckVolumeSliderChanged:(NSSlider*)sender {
userDefaults.autoDuckPercent = (NSUInteger)sender.integerValue;
[self updateDuckVolumeLabel];
}

- (void) updateDuckVolumeLabel {
duckVolumeLabel.stringValue = [NSString stringWithFormat:@"%lu%%", (unsigned long)userDefaults.autoDuckPercent];
}

- (void) updateSliderStates {
BOOL duckEnabled = userDefaults.autoDuckMusic;
duckVolumeSlider.enabled = duckEnabled;
if (duckEnabled) {
duckVolumeLabel.textColor = [NSColor controlTextColor];
} else {
duckVolumeLabel.textColor = [NSColor disabledControlTextColor];
}
}

@end

NS_ASSUME_NONNULL_END
Expand Down
16 changes: 16 additions & 0 deletions BGMApp/BGMAppTests/UnitTests/BGMMusicPlayersUnitTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ - (void) setAutoPauseMusicEnabled:(BOOL)autoPauseMusicEnabled {
#pragma unused (autoPauseMusicEnabled)
}

- (BOOL) autoDuckMusic {
return NO;
}

- (void) setAutoDuckMusic:(BOOL)autoDuckMusic {
#pragma unused (autoDuckMusic)
}

- (NSUInteger) autoDuckPercent {
return 30;
}

- (void) setAutoDuckPercent:(NSUInteger)autoDuckPercent {
#pragma unused (autoDuckPercent)
}

@end

// -------------------------------------------------------------------------------------------------
Expand Down