shithub: zuke

Download patch

ref: 524e32b1cd45a332c58b8cd681662f815628deee
parent: 13eb015acabef04e4b983dee43c376318c6726c0
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Thu May 28 14:06:46 EDT 2020

add shuffled mode

--- a/README.md
+++ b/README.md
@@ -39,15 +39,16 @@
 +  volume up
 
 left/right                  seek backwards/forward (10 seconds step)
-,/.                         seek backwards/forward (one minute step)
-up/down/pgup/pgdn/home/end  move within the playlist
+, .                         seek backwards/forward (one minute step)
+up down pgup pgdn home end  move within the playlist
 o                           move to the currently playing track
 enter                       play the selected track
 
->      skip next
-<      skip prev
-s      stop
-p      pause/resume
+> b    skip next
+< z    skip prev
+v      stop
+p c    pause/resume
+s      toggle shuffle
 q/del  quit
 
 /  search forward
--- /dev/null
+++ b/shuffle.c
@@ -1,0 +1,92 @@
+#include "shuffle.h"
+
+void
+shuffle_init(Shuffle *s, int (*rndrange)(int max_excluding), int total, int first)
+{
+	assert(first < total && total > 0);
+	s->m = s->n = total;
+	s->i = 0;
+	s->xi = s->x0 = first;
+
+	if(total < 4){
+		s->a = 1;
+		s->c = 3;
+		s->m = 7;
+		return;
+	}
+
+	s->m += 1;
+	s->m |= s->m >> 1;
+	s->m |= s->m >> 2;
+	s->m |= s->m >> 4;
+	s->m |= s->m >> 8;
+	s->m |= s->m >> 16;
+
+	s->a = 1 + rndrange(s->m/4)*4;     /* 1 ≤ a < m   && a mod 4 = 1 */
+	s->c = 3 + rndrange((s->m-2)/2)*2; /* 3 ≤ c < m-1 && c mod 2 = 1 */
+}
+
+void
+shuffle_resize(Shuffle *s, int total)
+{
+	assert(total > 0);
+	s->n = total;
+}
+
+static int
+next(Shuffle *s)
+{
+	int res;
+
+	res = s->xi;
+	if(s->n < 2){
+		/* if it's less than two items, just use the first one (if any) */
+		s->x0 = s->xi = s->i = 0;
+		return 0;
+	}else if(s->x0 >= s->n){
+		/* if items were removed up to this one -- update for a period */
+		s->x0 = 0;
+	}
+
+	for(;;){
+		s->xi = (s->a*s->xi + s->c) & s->m;
+		if(s->xi < s->n){
+			s->i++;
+			if(s->xi == s->x0)
+				s->i = 0;
+			break;
+		}
+	}
+
+	return res;
+}
+
+int
+shuffle_one(Shuffle *s, int index)
+{
+	if(s->n < 1 || index < 0)
+		return index;
+	index %= s->n;
+	while(s->i < s->n && s->i != index)
+		next(s);
+
+	return next(s);
+}
+
+int
+shuffle_for(Shuffle *s, int index)
+{
+	int i, r;
+
+	if(index < 0 || index >= s->n)
+		return index;
+	for(i = 0; i < s->n; i++){
+		r = s->i;
+		if(next(s) == index){
+			next(s);
+			return r;
+		}
+	}
+
+	return index;
+}
--- /dev/null
+++ b/shuffle.h
@@ -1,0 +1,11 @@
+typedef struct Shuffle Shuffle;
+
+struct Shuffle {
+	int i, n;
+	int m, a, c, x0, xi;
+};
+
+void shuffle_init(Shuffle *s, int (*rndrange)(int max_excluding), int total, int first);
+void shuffle_resize(Shuffle *s, int total);
+int shuffle_one(Shuffle *s, int index);
+int shuffle_for(Shuffle *s, int index);
--- a/zuke.c
+++ b/zuke.c
@@ -1,4 +1,5 @@
 #include "theme.c"
+#include "shuffle.c"
 #include <mouse.h>
 #include <keyboard.h>
 #include <ctype.h>
@@ -57,6 +58,8 @@
 static int entering;
 static int colwidth[3];
 static int mincolwidth[3];
+static Shuffle shuffle;
+static int shuffled;
 static char *covers[] = {"folder", "cover", "Cover", "scans/CD", "Scans/Front", "Covers/Front"};
 
 static char *menu3i[] = {
@@ -111,6 +114,12 @@
 		colwidth[i] = (Dx(screen->r) - 8) * mincolwidth[i] / total;
 }
 
+static Meta *
+getmeta(int i)
+{
+	return &pl[shuffled ? shuffle_one(&shuffle, i) : i];
+}
+
 static void
 redraw(int full)
 {
@@ -187,17 +196,17 @@
 			p.x = left + 2;
 			sel.max.x = p.x + colwidth[0];
 			replclipr(screen, 0, sel);
-			string(screen, p, col, sp, f, pl[i].artist[0]);
+			string(screen, p, col, sp, f, getmeta(i)->artist[0]);
 			p.x += colwidth[0] + 8;
 			sel.min.x = p.x;
 			sel.max.x = p.x + colwidth[1];
 			replclipr(screen, 0, sel);
-			string(screen, p, col, sp, f, pl[i].album);
+			string(screen, p, col, sp, f, getmeta(i)->album);
 			p.x += colwidth[1] + 8;
 			sel.min.x = p.x;
 			sel.max.x = p.x + colwidth[2];
 			replclipr(screen, 0, sel);
-			string(screen, p, col, sp, f, pl[i].title);
+			string(screen, p, col, sp, f, getmeta(i)->title);
 
 			replclipr(screen, 0, r);
 
@@ -214,9 +223,9 @@
 	}
 
 	if(pcurplaying >= 0)
-		snprint(tmp, sizeof(tmp), "%P/%P %d%%", (int)(byteswritten/Bps), pl[pcurplaying].duration/1000, volume);
+		snprint(tmp, sizeof(tmp), "%s%P/%P %d%%", shuffled ? "∫ " : "", (int)(byteswritten/Bps), getmeta(pcurplaying)->duration/1000, volume);
 	else
-		snprint(tmp, sizeof(tmp), "%d%%", volume);
+		snprint(tmp, sizeof(tmp), "%s%d%%", shuffled ? "∫ " : "", volume);
 	r = screen->r;
 	r.min.x = r.max.x - stringwidth(f, tmp) - 4;
 	r.min.y = r.max.y - f->height - 4;
@@ -257,7 +266,7 @@
 
 	threadsetname("cover");
 	player = player_;
-	m = &pl[player->pcur];
+	m = getmeta(player->pcur);
 	pid = -1;
 	ch = player->img;
 	fd = -1;
@@ -405,7 +414,7 @@
 	pid = -1;
 
 restart:
-	if((fd = open(pl[player->pcur].path, OREAD)) < 0){
+	if((fd = open(getmeta(player->pcur)->path, OREAD)) < 0){
 		fprint(2, "%r\n");
 		sendul(player->ev, Everror);
 		goto freeplayer;
@@ -417,7 +426,7 @@
 		dup(fd, 0); close(fd);
 		dup(open("/dev/null", OWRITE), 2);
 		close(p[1]);
-		snprint(cmd, sizeof(cmd), "/bin/audio/%sdec", pl[player->pcur].filefmt);
+		snprint(cmd, sizeof(cmd), "/bin/audio/%sdec", getmeta(player->pcur)->filefmt);
 		execl(cmd, cmd, nil);
 		sysfatal("execl: %r");
 	}
@@ -672,7 +681,7 @@
 	char *s, *snext;
 	static char buf[48];
 	static int sz;
-	int inc;
+	int inc, i;
 
 	inc = (d == '/' || d == 'n') ? 1 : -1;
 	if(d == '/' || d == '?'){
@@ -680,10 +689,11 @@
 		sz = enter(inc > 0 ? "forward:" : "backward:", buf, sizeof(buf), mctl, kctl, nil);
 		entering = 0;
 	}
-	if(sz < 1 || (inc > 0 && pcur >= plnum-1) || (inc < 0 && pcur < 1))
+	i = getmeta(pcur) - pl;
+	if(sz < 1 || (inc > 0 && i >= plnum-1) || (inc < 0 && i < 1))
 		return;
 
-	s = pl[pcur + (inc > 0 ? 0 : -1)].path;
+	s = pl[i + (inc > 0 ? 0 : -1)].path;
 	s += strlen(s) + 1;
 	for(; s > plraw && s < plraw+plrawsize-sz; s += inc){
 		if(cistrncmp(s, buf, sz) != 0)
@@ -697,7 +707,7 @@
 		}
 		for(s--; s != plraw; s--){
 			if(memcmp(s, "\0# ", 3) == 0 && isdigit(s[3])){
-				pcur = atoi(s+3);
+				pcur = shuffled ? shuffle_for(&shuffle, atoi(s+3)) : atoi(s+3);
 				redraw(1);
 				return;
 			}
@@ -753,6 +763,20 @@
 	close(f);
 }
 
+static void
+toggleshuffle(void)
+{
+	shuffled = !shuffled;
+	if(shuffled){
+		shuffle_init(&shuffle, nrand, plnum, pcurplaying < 0 ? pcur : pcurplaying);
+		pcur = shuffle_for(&shuffle, pcur);
+		pcurplaying = shuffle_for(&shuffle, pcurplaying);
+	}else{
+		pcur = shuffle_one(&shuffle, pcur);
+		pcurplaying = shuffle_one(&shuffle, pcurplaying);
+	}
+}
+
 void
 threadmain(int argc, char **argv)
 {
@@ -892,14 +916,17 @@
 				playercurr = newplayer(pcur, 1);
 				start(playercurr);
 				break;
-			case 'q': case Kdel:
+			case 'q':
+			case Kdel:
 				stop(playercurr);
 				goto end;
+			case 'i':
 			case 'o':
 				if(pcur == pcurplaying)
 					oldpcur = -1;
 				pcur = pcurplaying;
 				break;
+			case 'b':
 			case '>':
 				if(playercurr == nil)
 					break;
@@ -911,6 +938,7 @@
 				start(playercurr);
 				redraw(1);
 				break;
+			case 'z':
 			case '<':
 				if(playercurr == nil)
 					break;
@@ -930,7 +958,7 @@
 				chvolume(+1);
 				redraw(0);
 				break;
-			case 's':
+			case 'v':
 				stop(playercurr);
 				playercurr = nil;
 				pcurplaying = -1;
@@ -938,10 +966,18 @@
 				cover = nil;
 				redraw(1);
 				break;
+			case 's':
+				toggleshuffle();
+				redraw(1);
+				break;
+			case 'c':
 			case 'p':
 				toggle(playercurr);
 				break;
-			case '/': case '?': case 'n': case 'N':
+			case '/':
+			case '?':
+			case 'n':
+			case 'N':
 				search(key);
 				break;
 			}