Shuffle by albums in mocp

Hi,

i use mocp for a long time, but i missed shuffle by albums feature. This patch adds [ALBUM] item (next to [SHUFFLE], [NEXT], [REPEAT], etc...) toggled by CTRL + a key. When the [ALBUM] is activated, key for 'play next/prev' is jumping on the albums instead of jumping on the tracks. Now when the [SHUFFLE] and [ALBUM] is activated and we add files to playlist, playlist will be shuffled by albums.

Patch is adhcoc solution and it is not optimized, but relatively works :-). My big metal collection process 2 minutes, but then plays ;-).

In troubles I recomended to delete mocp cache.

From: Vaclav Svirga
Date: Tue Jul 3 08:59:32 CEST 2012
Subject: Add support for playback of albums

---
diff -ru moc/audio.c moc/audio.c
--- moc/audio.c 2012-07-03 08:51:40.453897385 +0200
+++ moc/audio.c 2012-07-03 08:48:15.125323921 +0200
@@ -297,6 +297,7 @@
static void go_to_another_file ()
{
int shuffle = options_get_int ("Shuffle");
+ int album = options_get_int ("Album");
int go_next = (play_next || options_get_int("AutoNext"));
int curr_playing_curr_pos;
/* XXX: Shouldn't play_next be protected by mutex? */
@@ -334,8 +335,13 @@

if (plist_count(&playlist)
&& !plist_count(&shuffled_plist)) {
- plist_cat (&shuffled_plist, &playlist);
- plist_shuffle (&shuffled_plist);
+ if(album)
+ plist_shuffle_album (&playlist, &shuffled_plist);
+ else
+ {
+ plist_cat (&shuffled_plist, &playlist);
+ plist_shuffle (&shuffled_plist);
+ }

if (curr_playing_fname)
plist_swap_first_fname (&shuffled_plist,
@@ -365,8 +371,12 @@
started_playing_in_queue = 0;
}
else
- curr_playing = plist_prev (curr_plist,
- curr_playing_curr_pos);
+ {
+ if(play_prev && album)
+ curr_playing = plist_prev_album(curr_plist, curr_playing_curr_pos);
+ else
+ curr_playing = plist_prev(curr_plist, curr_playing_curr_pos);
+ }

if (curr_playing == -1) {
if (options_get_int("Repeat"))
@@ -385,14 +395,23 @@
started_playing_in_queue = 0;
}
else
- curr_playing = plist_next (curr_plist,
- curr_playing_curr_pos);
+ {
+ if(play_next && album)
+ curr_playing = plist_next_album(curr_plist, curr_playing_curr_pos);
+ else
+ curr_playing = plist_next(curr_plist, curr_playing_curr_pos);
+ }

if (curr_playing == -1 && options_get_int("Repeat")) {
if (shuffle) {
plist_clear (&shuffled_plist);
- plist_cat (&shuffled_plist, &playlist);
- plist_shuffle (&shuffled_plist);
+ if(album)
+ plist_shuffle_album (&playlist, &shuffled_plist);
+ else
+ {
+ plist_cat (&shuffled_plist, &playlist);
+ plist_shuffle (&shuffled_plist);
+ }
}
curr_playing = plist_next (curr_plist, -1);
logit ("Going back to the first item.");
@@ -562,8 +581,13 @@
}
else if (options_get_int("Shuffle")) {
plist_clear (&shuffled_plist);
- plist_cat (&shuffled_plist, &playlist);
- plist_shuffle (&shuffled_plist);
+ if(options_get_int("Album"))
+ plist_shuffle_album(&playlist, &shuffled_plist);
+ else
+ {
+ plist_cat (&shuffled_plist, &playlist);
+ plist_shuffle(&shuffled_plist);
+ }
plist_swap_first_fname (&shuffled_plist, fname);

curr_plist = &shuffled_plist;
diff -ru moc/interface.c moc/interface.c
--- moc/interface.c 2012-07-03 08:51:40.441897824 +0200
+++ moc/interface.c 2012-07-03 08:49:18.383036093 +0200
@@ -3110,6 +3110,9 @@
case KEY_CMD_TOGGLE_SHUFFLE:
toggle_option ("Shuffle");
break;
+ case KEY_CMD_TOGGLE_ALBUM:
+ toggle_option ("Album");
+ break;
case KEY_CMD_TOGGLE_REPEAT:
toggle_option ("Repeat");
break;
@@ -4087,6 +4090,8 @@
tok = "AutoNext";
else if(!strncmp (tok, "repeat", 7) || !strncmp (tok, "r", 2))
tok = "Repeat";
+ else if(!strncmp (tok, "album", 6) || !strncmp (tok, "a", 2))
+ tok = "Album";
else {
fprintf (stderr, "Unknown option '%s'\n", tok);
break;
diff -ru moc/interface_elements.c moc/interface_elements.c
--- moc/interface_elements.c 2012-07-03 08:51:40.441897824 +0200
+++ moc/interface_elements.c 2012-07-03 03:13:34.076264867 +0200
@@ -238,6 +238,7 @@
int state_shuffle;
int state_repeat;
int state_next;
+ int state_album;
int state_net;

int bitrate; /* in kbps */
@@ -2694,6 +2695,7 @@
w->state_shuffle = 0;
w->state_repeat = 0;
w->state_next = 0;
+ w->state_album = 0;
w->state_play = STATE_STOP;
w->state_net = 0;

@@ -3101,6 +3103,7 @@
info_win_draw_switch (w, 53, 2, "SHUFFLE", w->state_shuffle);
info_win_draw_switch (w, 63, 2, "REPEAT", w->state_repeat);
info_win_draw_switch (w, 72, 2, "NEXT", w->state_next);
+ info_win_draw_switch (w, 79, 2, "ALBUM", w->state_album);
}

static void info_win_make_entry (struct info_win *w, const enum entry_type type)
@@ -3309,6 +3312,8 @@
w->state_next = value;
else if (!strcasecmp(name, "Net"))
w->state_net = value;
+ else if (!strcasecmp(name, "Album"))
+ w->state_album = value;
else
abort ();

diff -ru moc/keys.c moc/keys.c
--- moc/keys.c 2012-07-03 08:51:40.461897095 +0200
+++ moc/keys.c 2012-07-03 08:57:02.690266980 +0200
@@ -178,6 +178,14 @@
1
},
{
+ KEY_CMD_TOGGLE_ALBUM,
+ "toggle_album",
+ "Toggle Album",
+ CON_MENU,
+ { CTRL('a'), -1 },
+ 1
+ },
+ {
KEY_CMD_TOGGLE_REPEAT,
"toggle_repeat",
"Toggle Repeat",
diff -ru moc/keys.h moc/keys.h
--- moc/keys.h 2012-07-03 08:51:40.445897675 +0200
+++ moc/keys.h 2012-07-03 03:25:04.019287993 +0200
@@ -53,6 +53,7 @@
KEY_CMD_GO_DIR,
KEY_CMD_GO_DIR_UP,
KEY_CMD_TOGGLE_SHUFFLE,
+ KEY_CMD_TOGGLE_ALBUM,
KEY_CMD_NEXT_SEARCH,
KEY_CMD_CANCEL,
KEY_CMD_GO_URL,
diff -ru moc/main.c moc/main.c
--- moc/main.c 2012-07-03 08:51:40.449897534 +0200
+++ moc/main.c 2012-07-03 07:41:37.338294818 +0200
@@ -319,9 +319,9 @@
"-e --recursively Alias for -a.\n"
"-k --seek N Seek by N seconds (can be negative).\n"
"-j --jump N{%%,s} Jump to some position of the current track.\n"
-"-o --on Turn on a control (shuffle, autonext, repeat).\n"
-"-u --off Turn off a control (shuffle, autonext, repeat).\n"
-"-t --toggle Toggle a control (shuffle, autonext, repeat).\n"
+"-o --on Turn on a control (shuffle, autonext, repeat, album).\n"
+"-u --off Turn off a control (shuffle, autonext, repeat, album).\n"
+"-t --toggle Toggle a control (shuffle, autonext, repeat, album).\n"
, prg_name);
}

diff -ru moc/options.c moc/options.c
--- moc/options.c 2012-07-03 08:51:40.449897534 +0200
+++ moc/options.c 2012-07-03 03:17:54.122851022 +0200
@@ -560,6 +560,7 @@
add_bool ("ShowStreamErrors", false);
add_bool ("Repeat", false);
add_bool ("Shuffle", false);
+ add_bool ("Album", false);
add_bool ("AutoNext", true);
add_symb ("Sort", "FileName", CHECK_SYMBOL(1), "FileName");
add_str ("FormatString",
diff -ru moc/playlist.c moc/playlist.c
--- moc/playlist.c 2012-07-03 08:51:40.461897095 +0200
+++ moc/playlist.c 2012-07-03 08:51:08.039069897 +0200
@@ -285,6 +285,33 @@
return i < plist->num ? i : -1;
}

+int plist_next_album (struct plist *plist, int num)
+{
+ char calbum[1000] = "";
+ struct file_tags *tags = read_file_tags(plist->items[num].file, NULL, TAGS_COMMENTS);
+ if(tags && tags->filled & TAGS_COMMENTS && tags->album)
+ {
+ strncpy(calbum, tags->album, 1000);
+ tags_free(tags);
+ }
+ int n = num;
+ while(1)
+ {
+ n = plist_next(plist, n);
+ if(n == -1) return -1;
+ tags = read_file_tags(plist->items[n].file, NULL, TAGS_COMMENTS);
+ if(tags && tags->filled & TAGS_COMMENTS && tags->album)
+ {
+ if(strcmp(calbum, tags->album) != 0)
+ {
+ tags_free(tags);
+ return n;
+ }
+ tags_free(tags);
+ }
+ }
+}
+
/* Get the number of the previous item on the list (skipping deleted items).
* If num == -1, get the first item.
* Return -1 if it is the beginning of the playlist.
@@ -302,6 +329,33 @@
return i >= 0 ? i : -1;
}

+int plist_prev_album (struct plist *plist, int num)
+{
+ char calbum[1000] = "";
+ struct file_tags *tags = read_file_tags(plist->items[num].file, NULL, TAGS_COMMENTS);
+ if(tags && tags->filled & TAGS_COMMENTS && tags->album)
+ {
+ strncpy(calbum, tags->album, 1000);
+ tags_free(tags);
+ }
+ int n = num;
+ while(1)
+ {
+ n = plist_prev(plist, n);
+ if(n == -1) return -1;
+ tags = read_file_tags(plist->items[n].file, NULL, TAGS_COMMENTS);
+ if(tags && tags->filled & TAGS_COMMENTS && tags->album)
+ {
+ if(strcmp(calbum, tags->album) != 0)
+ {
+ tags_free(tags);
+ return n;
+ }
+ tags_free(tags);
+ }
+ }
+}
+
void plist_free_item_fields (struct plist_item *item)
{
if (item->file) {
@@ -805,6 +859,70 @@
rb_insert (&plist->search_tree, (void *)i);
}

+void plist_shuffle_album (struct plist *s_plist, struct plist *d_plist)
+{
+ plist_t_item_ix i;
+ plist_t_item_ix albums[50000];
+ albums[0] = 0;
+ int album_c = 1;
+ struct file_tags *tags;
+ char album[1000] = "";
+
+ //create index of albums
+ for(i = 0; i < s_plist->num; i++)
+ {
+ assert(i != 50000);
+ tags = read_file_tags(s_plist->items.file, NULL, TAGS_COMMENTS);
+ if(tags && tags->filled & TAGS_COMMENTS && tags->album)
+ {
+ if(i == 0)
+ {
+ strncpy(album, tags->album, 1000);
+ }
+ else if(strncmp(album, tags->album, 1000) != 0)
+ {
+ albums[album_c] = i;
+ album_c++;
+ strncpy(album, tags->album, 1000);
+ }
+ tags_free(tags);
+ }
+ }
+
+ //carete shuffle index of albums
+ int albums_s[50000];
+ for(i = 0; i < album_c; i++) albums_s = i;
+ int x,n;
+ for(i = 0; i < album_c; i++)
+ {
+ x = albums_s;
+ n = (rand()/(float)RAND_MAX) * (album_c - 1);
+ albums_s = albums_s[n];
+ albums_s[n] = x;
+ }
+
+ //copy items by shuffled albums
+ plist_t_item_ix first_i, last_i;
+ plist_t_item_ix j;
+ for(i = 0; i < album_c; i++)
+ {
+ first_i = albums[albums_s];
+ last_i = albums_s == album_c - 1 ? s_plist->num - 1 : albums[albums_s + 1] - 1;
+
+ for(j = first_i; j <= last_i; j++)
+ {
+ if (!plist_deleted(s_plist, j) && plist_find_fname(d_plist, s_plist->items[j].file) == -1)
+ {
+ plist_add_from_item (d_plist, &s_plist->items[j]);
+ }
+ }
+ }
+ rb_clear (&d_plist->search_tree);
+
+ for (i = 0; i < d_plist->num; i++)
+ rb_insert (&d_plist->search_tree, (void *)i);
+}
+
/* Swap the first item on the playlist with the item with file fname. */
void plist_swap_first_fname (struct plist *plist, const char *fname)
{
diff -ru moc/playlist.h moc/playlist.h
--- moc/playlist.h 2012-07-03 08:51:40.449897534 +0200
+++ moc/playlist.h 2012-07-03 07:48:28.255370553 +0200
@@ -74,7 +74,9 @@
int plist_add_from_item (struct plist *plist, const struct plist_item *item);
char *plist_get_file (const struct plist *plist, int i);
int plist_next (struct plist *plist, int num);
+int plist_next_album (struct plist *plist, int num);
int plist_prev (struct plist *plist, int num);
+int plist_prev_album (struct plist *plist, int num);
void plist_clear (struct plist *plist);
void plist_delete (struct plist *plist, const int num);
void plist_free (struct plist *plist);
@@ -102,6 +104,7 @@
int get_item_time (const struct plist *plist, const int i);
int plist_total_time (const struct plist *plisti, int *all_files);
void plist_shuffle (struct plist *plist);
+void plist_shuffle_album (struct plist *s_plist, struct plist *d_plist);
void plist_swap_first_fname (struct plist *plist, const char *fname);
struct plist_item *plist_new_item ();
void plist_free_item_fields (struct plist_item *item);
diff -ru moc/server.c moc/server.c
--- moc/server.c 2012-07-03 08:51:40.441897824 +0200
+++ moc/server.c 2012-07-03 08:53:36.929692524 +0200
@@ -759,6 +759,7 @@
return !strcasecmp(name, "ShowStreamErrors")
|| !strcasecmp(name, "Repeat")
|| !strcasecmp(name, "Shuffle")
+ || !strcasecmp(name, "Album")
|| !strcasecmp(name, "AutoNext");
}

I forgot to write that patch is for svn version 2.5.0-alpha4, revision 2427M.