Skip to content

Commit

Permalink
Add an album shuffle mode
Browse files Browse the repository at this point in the history
This is not implemented for playlists as currently they are not
album-aware. I think this is a reasonable limitation for now, as
playlists also don't offer AAA modes.

Closes cmus#641
  • Loading branch information
gavtroy committed May 30, 2021
1 parent c511abe commit edc5a07
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 62 deletions.
19 changes: 14 additions & 5 deletions Doc/cmus.txt
Original file line number Diff line number Diff line change
Expand Up @@ -654,8 +654,8 @@ source <filename>
Reads and executes commands from <filename>.

toggle <option>
Toggles the value of a toggle-able option (all booleans and tristate
*aaa_mode*).
Toggles the value of a toggle-able option (all booleans and the options
*shuffle*, *aaa_mode*, and *replaygain*).

tqueue [NUM]
Queues NUM (default 1) random tracks from the library. See also
Expand Down Expand Up @@ -1149,9 +1149,18 @@ show_playback_position (true)
show_remaining_time (false)
Display remaining time instead of elapsed time.

shuffle (false)
Play in shuffled order. Shuffle works in the library views (1-2) and
playlist view (3).
shuffle (off) [off, tracks, albums]
@li *off*
Play all tracks in order. See also *lib_sort* and *pl_sort*.
@li *tracks*
Play all tracks in the library or playlist in a shuffled order.
@li *albums*
Play each library album to completion before shuffling to the first track
of another library album. In playlists this option falls back to *tracks*
behaviour.

Filters and *aaa_mode* can be used to limit the items available for play.
Note that *shuffle*=`albums` will have no effect with *aaa_mode*=`album`.

skip_track_info (false)
Don't load metadata when adding tracks. Useful when using network file
Expand Down
22 changes: 17 additions & 5 deletions command_mode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1584,19 +1584,31 @@ static void cmd_win_activate(char *arg)
struct rb_root *shuffle_root = NULL;

if (cur_view == TREE_VIEW || cur_view == SORTED_VIEW) {
if (lib_cur_track)
previous = &lib_cur_track->simple_track.shuffle_info;
shuffle_root = &lib_shuffle_root;
if (shuffle == SHUFFLE_TRACKS) {
if (lib_cur_track)
previous = &lib_cur_track->simple_track.shuffle_info;
shuffle_root = &lib_shuffle_root;
} else if (shuffle == SHUFFLE_ALBUMS) {
if (lib_cur_track)
previous = &lib_cur_track->album->shuffle_info;
shuffle_root = &lib_album_shuffle_root;
}
}

switch (cur_view) {
case TREE_VIEW:
info = tree_activate_selected();
next = &lib_cur_track->simple_track.shuffle_info;
if (shuffle == SHUFFLE_TRACKS)
next = &lib_cur_track->simple_track.shuffle_info;
else if (shuffle == SHUFFLE_ALBUMS)
next = &lib_cur_track->album->shuffle_info;
break;
case SORTED_VIEW:
info = sorted_activate_selected();
next = &lib_cur_track->simple_track.shuffle_info;
if (shuffle == SHUFFLE_TRACKS)
next = &lib_cur_track->simple_track.shuffle_info;
else if (shuffle == SHUFFLE_ALBUMS)
next = &lib_cur_track->album->shuffle_info;
break;
case PLAYLIST_VIEW:
info = pl_play_selected_row();
Expand Down
149 changes: 127 additions & 22 deletions lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum aaa_mode aaa_mode = AAA_MODE_ALL;
char *lib_live_filter = NULL;

struct rb_root lib_shuffle_root;
struct rb_root lib_album_shuffle_root;
static struct expr *filter = NULL;
static struct expr *add_filter = NULL;
static int remove_from_hash = 1;
Expand Down Expand Up @@ -77,6 +78,16 @@ static void shuffle_add(struct tree_track *track)
shuffle_list_add(&track->simple_track.shuffle_info, &lib_shuffle_root, track->album);
}

static void album_shuffle_list_add(struct album *album)
{
shuffle_list_add(&album->shuffle_info, &lib_album_shuffle_root, album);
}

static void album_shuffle_list_remove(struct album *album)
{
rb_erase(&album->shuffle_info.tree_node, &lib_album_shuffle_root);
}

static void views_add_track(struct track_info *ti)
{
struct tree_track *track = xnew(struct tree_track, 1);
Expand All @@ -87,7 +98,7 @@ static void views_add_track(struct track_info *ti)
/* both the hash table and views have refs */
track_info_ref(ti);

tree_add_track(track);
tree_add_track(track, album_shuffle_list_add);
shuffle_add(track);
editable_add(&lib_editable, (struct simple_track *)track);
}
Expand Down Expand Up @@ -217,21 +228,29 @@ static int aaa_mode_filter(const struct album *album)
return 1;
}

static int cur_album_filter(const struct album *album)
{
return CUR_ALBUM == album;
}

/* set next/prev (tree) {{{ */

static struct tree_track *normal_get_next(void)
static struct tree_track *normal_get_next(enum aaa_mode aaa, bool allow_repeat)
{
if (lib_cur_track == NULL)
if (lib_cur_track == NULL) {
if (!allow_repeat)
return NULL;
return normal_get_first();
}

/* not last track of the album? */
if (rb_next(&lib_cur_track->tree_node)) {
/* next track of the current album */
return to_tree_track(rb_next(&lib_cur_track->tree_node));
}

if (aaa_mode == AAA_MODE_ALBUM) {
if (!repeat)
if (aaa == AAA_MODE_ALBUM) {
if (!allow_repeat || !repeat)
return NULL;
/* first track of the current album */
return album_first_track(CUR_ALBUM);
Expand All @@ -243,8 +262,8 @@ static struct tree_track *normal_get_next(void)
return album_first_track(to_album(rb_next(&CUR_ALBUM->tree_node)));
}

if (aaa_mode == AAA_MODE_ARTIST) {
if (!repeat)
if (aaa == AAA_MODE_ARTIST) {
if (!allow_repeat || !repeat)
return NULL;
/* first track of the first album of the current artist */
return artist_first_track(CUR_ARTIST);
Expand All @@ -256,26 +275,29 @@ static struct tree_track *normal_get_next(void)
return artist_first_track(to_artist(rb_next(&CUR_ARTIST->tree_node)));
}

if (!repeat)
if (!allow_repeat || !repeat)
return NULL;

/* first track */
return normal_get_first();
}

static struct tree_track *normal_get_prev(void)
static struct tree_track *normal_get_prev(enum aaa_mode aaa, bool allow_repeat)
{
if (lib_cur_track == NULL)
if (lib_cur_track == NULL) {
if (!allow_repeat)
return NULL;
return normal_get_last();
}

/* not first track of the album? */
if (rb_prev(&lib_cur_track->tree_node)) {
/* prev track of the album */
return to_tree_track(rb_prev(&lib_cur_track->tree_node));
}

if (aaa_mode == AAA_MODE_ALBUM) {
if (!repeat)
if (aaa == AAA_MODE_ALBUM) {
if (!allow_repeat || !repeat)
return NULL;
/* last track of the album */
return album_last_track(CUR_ALBUM);
Expand All @@ -287,8 +309,8 @@ static struct tree_track *normal_get_prev(void)
return album_last_track(to_album(rb_prev(&CUR_ALBUM->tree_node)));
}

if (aaa_mode == AAA_MODE_ARTIST) {
if (!repeat)
if (aaa == AAA_MODE_ARTIST) {
if (!allow_repeat || !repeat)
return NULL;
/* last track of the last album of the artist */
return album_last_track(to_album(rb_last(&CUR_ARTIST->album_root)));
Expand All @@ -300,18 +322,79 @@ static struct tree_track *normal_get_prev(void)
return artist_last_track(to_artist(rb_prev(&CUR_ARTIST->tree_node)));
}

if (!repeat)
if (!allow_repeat || !repeat)
return NULL;

/* last track */
return normal_get_last();
}

static struct tree_track *shuffle_album_get_next(void)
{
struct shuffle_info *shuffle_info = NULL;
struct album *album;

if (lib_cur_track != NULL)
shuffle_info = &lib_cur_track->album->shuffle_info;
album = (struct album *)shuffle_list_get_next(&lib_album_shuffle_root,
shuffle_info, aaa_mode_filter);
if (album != NULL)
return album_first_track(album);
return NULL;
}

static struct tree_track *shuffle_album_get_prev(void)
{
struct shuffle_info *shuffle_info = NULL;
struct album *album;

if (lib_cur_track != NULL)
shuffle_info = &lib_cur_track->album->shuffle_info;
album = (struct album *)shuffle_list_get_prev(&lib_album_shuffle_root,
shuffle_info, aaa_mode_filter);
if (album != NULL)
return album_last_track(album);
return NULL;
}

static struct tree_track *sorted_album_first_track(struct tree_track *track)
{
struct tree_track *prev = track;

while (true) {
prev = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
(struct simple_track *)prev, NULL, false);
if (prev == NULL)
return track;
if (prev->album == track->album)
track = prev;
}
}

static struct tree_track *sorted_album_last_track(struct tree_track *track)
{
struct tree_track *next = track;

while (true) {
next = (struct tree_track *)simple_list_get_next(&lib_editable.head,
(struct simple_track *)next, NULL, false);
if (next == NULL)
return track;
if (next->album == track->album)
track = next;
}
}

/* set next/prev (tree) }}} */

void lib_reshuffle(void)
{
shuffle_list_reshuffle(&lib_shuffle_root);
shuffle_list_reshuffle(&lib_album_shuffle_root);
}

void lib_sort_artists(void) {
tree_sort_artists(album_shuffle_list_add, album_shuffle_list_remove);
}

static void free_lib_track(struct editable *e, struct list_head *item)
Expand All @@ -326,7 +409,7 @@ static void free_lib_track(struct editable *e, struct list_head *item)
hash_remove(ti);

rb_erase(&track->simple_track.shuffle_info.tree_node, &lib_shuffle_root);
tree_remove(track);
tree_remove(track, album_shuffle_list_remove);

track_info_unref(ti);
free(track);
Expand Down Expand Up @@ -365,14 +448,25 @@ struct track_info *lib_goto_next(void)
BUG_ON(lib_cur_track != NULL);
return NULL;
}
if (shuffle) {
if (shuffle == SHUFFLE_TRACKS) {
track = (struct tree_track *)shuffle_list_get_next(&lib_shuffle_root,
(struct shuffle_info *)lib_cur_track, aaa_mode_filter);
} else if (shuffle == SHUFFLE_ALBUMS) {
if (play_sorted)
track = (struct tree_track *)simple_list_get_next(&lib_editable.head,
(struct simple_track *)lib_cur_track, cur_album_filter, false);
else
track = normal_get_next(AAA_MODE_ALBUM, false);
if (track == NULL) {
track = shuffle_album_get_next();
if (play_sorted)
track = sorted_album_first_track(track);
}
} else if (play_sorted) {
track = (struct tree_track *)simple_list_get_next(&lib_editable.head,
(struct simple_track *)lib_cur_track, aaa_mode_filter);
(struct simple_track *)lib_cur_track, aaa_mode_filter, true);
} else {
track = normal_get_next();
track = normal_get_next(aaa_mode, true);
}
return lib_set_track(track);
}
Expand All @@ -385,14 +479,25 @@ struct track_info *lib_goto_prev(void)
BUG_ON(lib_cur_track != NULL);
return NULL;
}
if (shuffle) {
if (shuffle == SHUFFLE_TRACKS) {
track = (struct tree_track *)shuffle_list_get_prev(&lib_shuffle_root,
(struct shuffle_info *)lib_cur_track, aaa_mode_filter);
} else if (shuffle == SHUFFLE_ALBUMS) {
if (play_sorted)
track = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
(struct simple_track *)lib_cur_track, cur_album_filter, false);
else
track = normal_get_prev(AAA_MODE_ALBUM, false);
if (track == NULL) {
track = shuffle_album_get_prev();
if (play_sorted)
track = sorted_album_last_track(track);
}
} else if (play_sorted) {
track = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
(struct simple_track *)lib_cur_track, aaa_mode_filter);
(struct simple_track *)lib_cur_track, aaa_mode_filter, true);
} else {
track = normal_get_prev();
track = normal_get_prev(aaa_mode, true);
}
return lib_set_track(track);
}
Expand Down
10 changes: 7 additions & 3 deletions lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ static inline struct tree_track *to_tree_track(const struct rb_node *node)


struct album {
struct shuffle_info shuffle_info;

/* position in album search tree */
struct rb_node tree_node;

Expand Down Expand Up @@ -93,6 +95,7 @@ enum aaa_mode {
extern struct editable lib_editable;
extern struct tree_track *lib_cur_track;
extern struct rb_root lib_shuffle_root;
extern struct rb_root lib_album_shuffle_root;
extern enum aaa_mode aaa_mode;
extern unsigned int play_sorted;
extern char *lib_live_filter;
Expand All @@ -117,6 +120,7 @@ void lib_set_add_filter(struct expr *expr);
int lib_remove(struct track_info *ti);
void lib_clear_store(void);
void lib_reshuffle(void);
void lib_sort_artists(void);
void lib_set_view(int view);
int lib_for_each(int (*cb)(void *data, struct track_info *ti), void *data,
void *opaque);
Expand All @@ -130,9 +134,9 @@ struct track_info *lib_get_cur_stored_track(void);

struct tree_track *tree_get_selected(void);
struct track_info *tree_activate_selected(void);
void tree_sort_artists(void);
void tree_add_track(struct tree_track *track);
void tree_remove(struct tree_track *track);
void tree_sort_artists(void (*add_album_cb)(struct album *), void (*remove_album_cb)(struct album *));
void tree_add_track(struct tree_track *track, void (*add_album_cb)(struct album *));
void tree_remove(struct tree_track *track, void (*remove_album_cb)(struct album *));
void tree_remove_sel(void);
void tree_toggle_active_window(void);
void tree_toggle_expand_artist(void);
Expand Down
Loading

0 comments on commit edc5a07

Please sign in to comment.