ref: ab23e1cbffa4bea97a99d58fea58b13e1958c9f8
parent: ca81298ff007e930fb84a57df875664b6a81fe0d
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Thu Jan 14 07:10:38 EST 2021
rework playlist plumbing
--- a/README.md
+++ b/README.md
@@ -36,9 +36,6 @@
audio/mkplist /n/music | audio/zuke
-*NOTE*: don't forget to pass the *full* path to `audio/mkplist`, not a
-relative one.
-
## Plumbing
Plumbing music files and playlists is supported via "audio" port. New
@@ -45,7 +42,7 @@
files can't be added to the current playlist just yet.
type is text
- data matches '.+\.(mp3|MP3|ogg|OGG|flac|FLAC|wav|WAV|au|AU|mid|MID|mus|MUS|m3u|M3U|pls|PLS|plist)$'
+ data matches '.+\.(mp3|MP3|ogg|OGG|flac|FLAC|wav|WAV|au|AU|mid|MID|mus|MUS|m3u|M3U|pls|PLS|it|IT|plist)$'
arg isfile $0
plumb to audio
plumb start window -scroll play $file
--- a/zuke.c
+++ b/zuke.c
@@ -15,6 +15,7 @@
#define CLAMP(x,min,max) MAX(min, MIN(max, x))
typedef struct Player Player;
+typedef struct Playlist Playlist;
enum
{
@@ -42,15 +43,20 @@
int pcur;
};
+struct Playlist
+{
+ Meta *m;
+ int n;
+ char *raw;
+ int rawsz;
+};
+
int mainstacksize = 32768;
static int debug;
static int audio = -1;
-static Meta *pl;
-static int plnum;
-static char *plraw;
-static int plrawsize;
static int volume;
+static Playlist *pl;
static Player *playernext;
static Player *playercurr;
static vlong byteswritten;
@@ -164,9 +170,9 @@
if(mincolwidth[0] == 0){
for(i = 0; cols[i] != 0; i++)
mincolwidth[i] = 1;
- for(n = 0; n < plnum; n++){
+ for(n = 0; n < pl->n; n++){
for(i = 0; cols[i] != 0; i++){
- if((x = stringwidth(f, getcol(&pl[n], cols[i]))) > mincolwidth[i])
+ if((x = stringwidth(f, getcol(pl->m+n, cols[i]))) > mincolwidth[i])
mincolwidth[i] = x;
}
}
@@ -194,7 +200,7 @@
static Meta *
getmeta(int i)
{
- return &pl[shuffle != nil ? shuffle[i] : i];
+ return &pl->m[shuffle != nil ? shuffle[i] : i];
}
static void
@@ -216,7 +222,7 @@
lockdisplay(display);
updatescrollsz();
left = screen->r.min.x;
- if(scrollsz < plnum) /* adjust for scrollbar */
+ if(scrollsz < pl->n) /* adjust for scrollbar */
left += Scrollwidth + 1;
if(full){
@@ -224,7 +230,7 @@
yield();
adjustcolumns();
- if(scrollsz < plnum){ /* scrollbar */
+ if(scrollsz < pl->n){ /* scrollbar */
p.x = sp.x = screen->r.min.x + Scrollwidth;
p.y = screen->r.min.y;
sp.y = screen->r.max.y;
@@ -236,7 +242,7 @@
if(scroll < 1)
scrollcenter = 0;
else
- scrollcenter = (Dy(screen->r)-Scrollheight*5/4)*scroll / (plnum - scrollsz);
+ scrollcenter = (Dy(screen->r)-Scrollheight*5/4)*scroll / (pl->n - scrollsz);
r.min.y += scrollcenter + Scrollheight/4;
r.max.y = r.min.y + Scrollheight;
draw(screen, r, colors[Dblow].im, nil, ZP);
@@ -257,7 +263,7 @@
p.y = screen->r.min.y + 2;
yield();
- for(i = scroll; i < plnum; i++, p.y += f->height){
+ for(i = scroll; i < pl->n; i++, p.y += f->height){
if(i < 0)
continue;
if(p.y > screen->r.max.y)
@@ -503,7 +509,7 @@
return nil;
done:
- if(pcur < plnum-1 && playernext == nil && loadnext)
+ if(pcur < pl->n-1 && playernext == nil && loadnext)
playernext = newplayer(pcur+1, 0);
return player;
@@ -642,7 +648,7 @@
if(c != Cseekrel || boffset >= getmeta(pcurplaying)->duration/1000*Bps){
next:
playercurr = nil;
- playercurr = newplayer((player->pcur+1) % plnum, 1);
+ playercurr = newplayer((player->pcur+1) % pl->n, 1);
start(playercurr);
goto stop;
}
@@ -690,35 +696,55 @@
{
int i;
- for(i = 0; i < plnum; i++)
- printmeta(&out, pl+i);
+ for(i = 0; i < pl->n; i++)
+ printmeta(&out, pl->m+i);
}
static void
-readplistnew(void)
+freeplist(Playlist *pl)
{
+ if(pl != nil){
+ free(pl->m);
+ free(pl->raw);
+ }
+ free(pl);
+}
+
+static Playlist *
+readplistnew(char *raw)
+{
char *s, *e, *a[5];
+ Playlist *pl;
Meta *m;
int plsz;
m = nil;
+ if((pl = calloc(1, sizeof(*pl))) == nil){
+nomem:
+ freeplist(pl);
+ werrstr("no memory");
+ return nil;
+ }
+
plsz = 0;
- for(s = plraw;; s = e){
+ pl->raw = raw;
+ for(s = pl->raw;; s = e){
if((e = strchr(s, '\n')) == nil)
break;
- if(plsz - plnum < 32){
+ if(plsz - pl->n < 32){
plsz = (plsz + 32)*2;
- if((pl = realloc(pl, sizeof(Meta)*plsz)) == nil)
- sysfatal("memory");
- memset(pl+plnum, 0, sizeof(Meta)*(plsz-plnum));
+ if((m = realloc(pl->m, sizeof(Meta)*plsz)) == nil)
+ goto nomem;
+ pl->m = m;
+ memset(pl->m+pl->n, 0, sizeof(Meta)*(plsz-pl->n));
}
- m = pl + plnum;
+ m = pl->m + pl->n;
s += 2;
*e++ = 0;
switch(s[-2]){
case 0:
if(m->path != nil)
- plnum++;
+ pl->n++;
m = nil;
break;
case Pimage:
@@ -745,28 +771,35 @@
}
}
if(m != nil && m->path != nil)
- plnum++;
+ pl->n++;
+
+ return pl;
}
-static void
+static Playlist *
readplist(int fd)
{
Meta *m;
+ Playlist *pl;
char *s, *e, *endrec;
int i, n, sz, alloc, tagsz, intval;
- plnum = 0;
- pl = nil;
-
s = nil;
for(alloc = sz = 0;;){
alloc += 65536;
- if((s = realloc(s, alloc)) == nil)
- sysfatal("no memory");
+ if((e = realloc(s, alloc)) == nil){
+nomem:
+ werrstr("no memory");
+ free(s);
+ return nil;
+ }
+ s = e;
for(n = 0; sz < alloc; sz += n){
n = readn(fd, s+sz, alloc-sz);
- if(n < 0)
- sysfatal("%r");
+ if(n < 0){
+ free(s);
+ return nil;
+ }
if(n == 0)
break;
}
@@ -773,37 +806,56 @@
if(n == 0)
break;
}
+ s[sz-1] = 0;
- plraw = s;
- plrawsize = sz;
- plraw[plrawsize-1] = 0;
- if(sz < 4 || s[0] != '#' || s[1] != ' ' || !isdigit(s[2]) || (s = memchr(plraw, '\n', sz)) == nil){
- readplistnew();
- return;
+ if(sz < 4 || s[0] != '#' || s[1] != ' ' || !isdigit(s[2]) || (s = memchr(s, '\n', sz)) == nil){
+ if((pl = readplistnew(s)) == nil)
+ return nil;
+ goto end;
}
+ if((pl = calloc(1, sizeof(*pl))) == nil)
+ goto nomem;
+ if((pl->m = calloc(pl->n, sizeof(Meta))) == nil){
+ werrstr("no memory");
+error:
+ freeplist(pl);
+ return nil;
+ }
+ pl->raw = s;
+ pl->rawsz = sz;
+ pl->n = atoi(pl->raw+2);
+
s++; /* at the start of the first record */
- plnum = atoi(plraw+2);
- pl = calloc(plnum, sizeof(Meta));
- for(i = 0; i < plnum; i++, s = endrec){
- if(plraw+plrawsize < s+10)
- sysfatal("truncated playlist");
- if(s[0] != '#' || s[1] != ' ' || !isdigit(s[2]))
- sysfatal("invalid record");
- if((n = strtol(s+2, &e, 10)) < 0 || n > plnum)
- sysfatal("invalid track index");
- if(pl[n].path != nil)
- sysfatal("duplicate track index");
+ for(i = 0; i < pl->n; i++, s = endrec){
+ if(pl->raw+pl->rawsz < s+10){
+ werrstr("truncated playlist");
+ goto error;
+ }
+ if(s[0] != '#' || s[1] != ' ' || !isdigit(s[2])){
+ werrstr("invalid record");
+ goto error;
+ }
+ if((n = strtol(s+2, &e, 10)) < 0 || n > pl->n){
+ werrstr("invalid track index");
+ goto error;
+ }
+ if(pl->m[n].path != nil){
+ werrstr("duplicate track index");
+ goto error;
+ }
s[-1] = 0;
sz = strtol(e, &s, 10);
*s++ = 0; /* skip '\n' */
- if(s+sz > plraw+plrawsize)
- sysfatal("truncated playlist");
+ if(s+sz > pl->raw+pl->rawsz){
+ werrstr("truncated playlist");
+ goto error;
+ }
s[sz-1] = 0; /* '\n'→'\0' to mark the end of the record */
endrec = s+sz;
- m = &pl[n];
+ m = pl->m+n;
for(;;){
if(s[0] == Pimage){
@@ -824,8 +876,10 @@
s = strchr(s+2, '\n') + 1;
}else{
tagsz = strtol(s+1, &e, 10);
- if(e+tagsz >= plraw+plrawsize)
- sysfatal("truncated playlist");
+ if(e+tagsz >= pl->raw+pl->rawsz){
+ werrstr("truncated playlist");
+ goto error;
+ }
e++; /* point to tag value */
e[tagsz] = 0; /* '\n'→'\0' to mark the end of the tag value */
if(s[0] == Palbum) m->album = e;
@@ -833,14 +887,28 @@
else if(s[0] == Ptitle) m->title = e;
else if(s[0] == Pdate) m->date = e;
else if(s[0] == Ptrack) m->track = e;
- else sysfatal("unknown tag type %c", s[0]);
+ else{
+ werrstr("unknown tag type %c", s[0]);
+ goto error;
+ }
s = e + tagsz + 1;
}
s[-1] = 0;
}
- if(m->filefmt == nil)
- sysfatal("old playlist format?\nplease re-run audio/mkplist");
+ if(m->filefmt == nil){
+ fprint(2, "old playlist format? please re-run audio/mkplist or use zuke -G (see man page)\n");
+ werrstr("unknown file format");
+ goto error;
+ }
}
+
+end:
+ if(pl->n < 1){
+ werrstr("empty playlist");
+ goto error;
+ }
+
+ return pl;
}
static void
@@ -867,7 +935,7 @@
return;
cycle = 1;
- for(i = pcur+inc; i >= 0 && i < plnum;){
+ for(i = pcur+inc; i >= 0 && i < pl->n;){
m = getmeta(i);
for(a = 0; a < m->numartist; a++){
if(cistrstr(m->artist[a], buf) != nil)
@@ -882,15 +950,15 @@
onemore:
i += inc;
}
- if(i >= 0 && i < plnum){
+ if(i >= 0 && i < pl->n){
pcur = i;
recenter();
redraw(1);
}else if(cycle && i+inc < 0){
cycle = 0;
- i = plnum;
+ i = pl->n;
goto onemore;
- }else if(cycle && i+inc >= plnum){
+ }else if(cycle && i+inc >= pl->n){
cycle = 0;
i = -1;
goto onemore;
@@ -950,11 +1018,11 @@
int i, m, xi, a, c, pcurnew, pcurplayingnew;
if(shuffle == nil){
- if(plnum < 2)
+ if(pl->n < 2)
return;
- m = plnum;
- if(plnum < 4){
+ m = pl->n;
+ if(pl->n < 4){
a = 1;
c = 3;
m = 7;
@@ -969,12 +1037,12 @@
c = 3 + nrand((m-2)/2)*2; /* 3 ≤ c < m-1 && c mod 2 = 1 */
}
- shuffle = malloc(plnum*sizeof(*shuffle));
+ shuffle = malloc(pl->n*sizeof(*shuffle));
xi = pcurplaying < 0 ? pcur : pcurplaying;
pcurplayingnew = -1;
pcurnew = 0;
- for(i = 0; i < plnum;){
- if(xi < plnum){
+ for(i = 0; i < pl->n;){
+ if(xi < pl->n){
if(pcur == xi)
pcurnew = i;
if(pcurplaying == xi)
@@ -994,7 +1062,7 @@
}
stop(playernext);
- if(pcur < plnum-1)
+ if(pcur < pl->n-1)
playernext = newplayer(pcur+1, 0);
}
@@ -1002,6 +1070,7 @@
plumbaudio(void *)
{
int i, f, pf;
+ Playlist *p;
Plumbmsg *m;
char *s, *e;
@@ -1013,16 +1082,18 @@
s = smprint("%s/%s", m->wdir, m->data);
if((e = strrchr(s, '.')) != nil && strcmp(e, ".plist") == 0 && (pf = open(s, OREAD)) >= 0){
- stop(playercurr);
- free(pl);
- free(plraw);
- plnum = 0;
- readplist(pf);
+ p = readplist(pf);
close(pf);
+ if(p == nil)
+ continue;
+
+ freeplist(pl);
+ pl = p;
+ memset(mincolwidth, 0, sizeof(mincolwidth)); /* readjust columns */
sendul(playc, 0);
}else{
- for(i = 0; i < plnum; i++){
- if(strcmp(pl[i].path, s) == 0){
+ for(i = 0; i < pl->n; i++){
+ if(strcmp(pl->m[i].path, s) == 0){
sendul(playc, i);
break;
}
@@ -1051,8 +1122,13 @@
Rune key;
Mouse m;
ulong ind;
- Alt a[] =
- {
+ enum {
+ Emouse,
+ Eresize,
+ Ekey,
+ Eplay,
+ };
+ Alt a[] = {
{ nil, &m, CHANRCV },
{ nil, nil, CHANRCV },
{ nil, &key, CHANRCV },
@@ -1083,12 +1159,11 @@
break;
}ARGEND;
- readplist(0);
- close(0);
- if(plnum < 1){
- fprint(2, "empty playlist\n");
- sysfatal("empty");
+ if((pl = readplist(0)) == nil){
+ fprint(2, "playlist: %r\n");
+ sysfatal("playlist error");
}
+ close(0);
if(nogui){
Binit(&out, 1, OWRITE);
@@ -1122,7 +1197,7 @@
threadsetname("zuke");
if(shuffled){
- pcur = nrand(plnum);
+ pcur = nrand(pl->n);
toggleshuffle();
recenter();
}
@@ -1142,7 +1217,7 @@
redraw(0);
switch(alt(a)){
- case 0:
+ case Emouse:
if(ptinrect(m.xy, seekbar)){
seekoff = getmeta(pcurplaying)->duration * (double)(m.xy.x-1-seekbar.min.x) / (double)Dx(seekbar);
if(seekoff < 0)
@@ -1161,7 +1236,7 @@
redraw(1);
break;
}else if(m.buttons == 16){
- scroll = MIN(scroll+scrollsz/4+1, plnum-scrollsz-1);
+ scroll = MIN(scroll+scrollsz/4+1, pl->n-scrollsz-1);
redraw(1);
break;
}
@@ -1181,7 +1256,7 @@
redraw(1);
break;
}else if(m.buttons == 4){
- scroll = MIN(scroll+n+1, plnum-scrollsz-1);
+ scroll = MIN(scroll+n+1, pl->n-scrollsz-1);
redraw(1);
break;
}else if(m.buttons == 2){
@@ -1199,14 +1274,14 @@
}
if(scrolling){
- if(scrollsz >= plnum)
+ if(scrollsz >= pl->n)
break;
- scroll = (m.xy.y - screen->r.min.y - Scrollheight/4)*(plnum-scrollsz) / (Dy(screen->r)-Scrollheight/2);
- scroll = CLAMP(scroll, 0, plnum-scrollsz-1);
+ scroll = (m.xy.y - screen->r.min.y - Scrollheight/4)*(pl->n-scrollsz) / (Dy(screen->r)-Scrollheight/2);
+ scroll = CLAMP(scroll, 0, pl->n-scrollsz-1);
redraw(1);
}else if(m.buttons == 1 || m.buttons == 2){
n += scroll;
- if(n < plnum){
+ if(n < pl->n){
pcur = n;
if(m.buttons == 2){
stop(playercurr);
@@ -1216,12 +1291,12 @@
}
}
break;
- case 1: /* resize */
+ case Eresize: /* resize */
if(getwindow(display, Refnone) < 0)
sysfatal("getwindow: %r");
redraw(1);
break;
- case 2:
+ case Ekey:
switch(key){
case Kleft:
seekrel(playercurr, -(double)Seek);
@@ -1248,7 +1323,7 @@
pcur += scrollsz;
break;
case Kend:
- pcur = plnum-1;
+ pcur = pl->n-1;
break;
case Khome:
pcur = 0;
@@ -1275,7 +1350,7 @@
if(playercurr == nil)
break;
pnew = pcurplaying;
- if(++pnew >= plnum)
+ if(++pnew >= pl->n)
pnew = 0;
stop(playercurr);
playercurr = newplayer(pnew, 1);
@@ -1288,7 +1363,7 @@
break;
pnew = pcurplaying;
if(--pnew < 0)
- pnew = plnum-1;
+ pnew = pl->n-1;
stop(playercurr);
playercurr = newplayer(pnew, 1);
start(playercurr);
@@ -1329,18 +1404,21 @@
break;
}
break;
- case 3:
+ case Eplay:
pcur = ind;
- goto playcur;
+ recenter();
+ if(playercurr != nil)
+ goto playcur;
+ break;
}
if(pcur != oldpcur){
- pcur = CLAMP(pcur, 0, plnum-1);
+ pcur = CLAMP(pcur, 0, pl->n-1);
if(pcur < scroll)
scroll = pcur;
else if(pcur > scroll + scrollsz)
scroll = pcur - scrollsz;
- scroll = CLAMP(scroll, 0, plnum-scrollsz);
+ scroll = CLAMP(scroll, 0, pl->n-scrollsz);
if(pcur != oldpcur)
redraw(1);