Playback: Implement Selection Follows Playback

Option to make selection follow the playback, within the lag of the
output buffer, including if Always Stop After Current or Repeat One is
enabled. Allows easily queueing up a list of tracks in Always Stop mode,
then hitting the Play button again to play the next track. Enabled by
default, but always optional to disable.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-03-03 18:34:40 -08:00
parent 8d848bc745
commit 7719ccf864
6 changed files with 69 additions and 4 deletions

View file

@ -108,6 +108,8 @@
- (void)showPathSuggester;
+ (void)globalShowPathSuggester;
- (void)selectTrack:(id)sender;
- (IBAction)showRubberbandSettings:(id)sender;
+ (void)globalShowRubberbandSettings;

View file

@ -695,6 +695,7 @@ static BOOL consentLastEnabled = NO;
[userDefaultsValuesDict setObject:@(44100) forKey:@"synthSampleRate"];
[userDefaultsValuesDict setObject:@NO forKey:@"alwaysStopAfterCurrent"];
[userDefaultsValuesDict setObject:@YES forKey:@"selectionFollowsPlayback"];
// Register and sync defaults
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
@ -882,4 +883,13 @@ static BOOL consentLastEnabled = NO;
[[SparkleBridge sharedStandardUpdaterController] checkForUpdates:[[NSApplication sharedApplication] delegate]];
}
- (void)selectTrack:(id)sender {
PlaylistEntry *pe = (PlaylistEntry *)sender;
@try {
[playlistView selectRowIndexes:[NSIndexSet indexSetWithIndex:pe.index] byExtendingSelection:NO];
}
@catch(id anException) {
}
}
@end

View file

@ -1715,6 +1715,12 @@
<action selector="scrollToCurrentEntry:" target="207" id="1888"/>
</connections>
</menuItem>
<menuItem title="Selection Follows Playback" id="Q7K-Eu-1Vf">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<binding destination="1689" name="value" keyPath="values.selectionFollowsPlayback" id="pml-0X-qIh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>

View file

@ -1288,11 +1288,20 @@ static void *playlistControllerContext = &playlistControllerContext;
}
- (PlaylistEntry *)getNextEntry:(PlaylistEntry *)pe ignoreRepeatOne:(BOOL)ignoreRepeatOne {
BOOL selectionFollows = [[NSUserDefaults standardUserDefaults] boolForKey:@"selectionFollowsPlayback"];
if(!ignoreRepeatOne && [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysStopAfterCurrent"]) {
if(selectionFollows) {
PlaylistEntry *nextPe = [self getNextEntry:pe ignoreRepeatOne:YES];
[appController selectTrack:nextPe];
}
return nil;
}
if(!ignoreRepeatOne && [self repeat] == RepeatModeRepeatOne) {
if(selectionFollows) {
[appController selectTrack:pe];
}
return pe;
}
@ -1310,11 +1319,19 @@ static void *playlistControllerContext = &playlistControllerContext;
[self commitPersistentStore];
if(selectionFollows) {
[appController selectTrack:pe];
}
return pe;
}
if([self shuffle] != ShuffleOff) {
return [self shuffledEntryAtIndex:(pe.shuffleIndex + 1)];
PlaylistEntry *nextPe = [self shuffledEntryAtIndex:(pe.shuffleIndex + 1)];
if(selectionFollows && nextPe) {
[appController selectTrack:nextPe];
}
return nextPe;
} else {
NSInteger i;
@ -1342,7 +1359,13 @@ static void *playlistControllerContext = &playlistControllerContext;
}
}
return [self entryAtIndex:i];
PlaylistEntry *nextPe = [self entryAtIndex:i];
if(selectionFollows) {
[appController selectTrack:nextPe];
}
return nextPe;
}
}
@ -1361,16 +1384,29 @@ static void *playlistControllerContext = &playlistControllerContext;
}
- (PlaylistEntry *)getPrevEntry:(PlaylistEntry *)pe ignoreRepeatOne:(BOOL)ignoreRepeatOne {
BOOL selectionFollows = [[NSUserDefaults standardUserDefaults] boolForKey:@"selectionFollowsPlayback"];
if(!ignoreRepeatOne && [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysStopAfterCurrent"]) {
if(selectionFollows) {
PlaylistEntry *prevPe = [self getPrevEntry:pe ignoreRepeatOne:YES];
[appController selectTrack:prevPe];
}
return nil;
}
if(!ignoreRepeatOne && [self repeat] == RepeatModeRepeatOne) {
if(selectionFollows) {
[appController selectTrack:pe];
}
return pe;
}
if([self shuffle] != ShuffleOff) {
return [self shuffledEntryAtIndex:(pe.shuffleIndex - 1)];
PlaylistEntry *prevPe = [self shuffledEntryAtIndex:(pe.shuffleIndex - 1)];
if(selectionFollows && prevPe) {
[appController selectTrack:prevPe];
}
return prevPe;
} else {
NSInteger i;
if(pe.index < 0) // Was a current entry, now removed.
@ -1380,7 +1416,12 @@ static void *playlistControllerContext = &playlistControllerContext;
i = pe.index - 1;
}
return [self entryAtIndex:i];
PlaylistEntry *prevPe = [self entryAtIndex:i];
if(selectionFollows) {
[appController selectTrack:prevPe];
}
return prevPe;
}
}

View file

@ -419,6 +419,9 @@
/* Class = "NSMenuItem"; title = "Select Currently Playing"; ObjectID = "1823"; */
"1823.title" = "Select Currently Playing";
/* Class = "NSMenuItem"; title = "Selection Follows Playback"; ObjectID = "Q7K-Eu-1Vf"; */
"Q7K-Eu-1Vf.title" = "Selection Follows Playback";
/* Class = "NSMenuItem"; title = "View"; ObjectID = "1848"; */
"1848.title" = "View";

View file

@ -419,6 +419,9 @@
/* Class = "NSMenuItem"; title = "Select Currently Playing"; ObjectID = "1823"; */
"1823.title" = "Seleccionar reproducción actual";
/* Class = "NSMenuItem"; title = "Selection Follows Playback"; ObjectID = "Q7K-Eu-1Vf"; */
"Q7K-Eu-1Vf.title" = "Selección sigue la reproducción";
/* Class = "NSMenuItem"; title = "View"; ObjectID = "1848"; */
"1848.title" = "Visualización";