shithub: mpl

Download patch

ref: f0bb0451afe4b3d4719c6b2446dfa8034c9eb4fc
parent: a848106feaaba815d72a871bf412b3fcd65338c0
author: Jacob Moody <jsmoody@iastate.edu>
date: Thu Sep 12 11:09:11 EDT 2019

Refactor dec to use more channels
Plug dec proc leak
Move to next song on eof

--- a/dat.h
+++ b/dat.h
@@ -2,17 +2,7 @@
 	START,
 	STOP,
 	PAUSE,
-};
-
-/*
-* Dec represents a decoder process.
-* ctl is the control channel for pausing, starting, and stoping the proc.
-*/
-typedef struct Dec Dec;
-struct Dec{
-	int		decpid;
-	int		ctlpid;
-	Channel	*ctl;
+	NEXT,
 };
 
 /*
--- a/dec.c
+++ b/dec.c
@@ -16,7 +16,7 @@
 typedef struct {
 	int inpipe;
 	Channel *ctl;
-} Ctlarg;
+} Writearg;
 
 void
 decodeproc(void *arg)
@@ -28,11 +28,13 @@
 	dup(nfd, 0);
 	dup(nfd, 2);
 	dup(a->outpipe, 1);
+	close(nfd);
+	close(a->outpipe);
 	procexecl(a->cpid, "/bin/play", "play", "-o", "/fd/1", a->file, nil);
 }
 
 void
-ctlproc(void *arg)
+writethread(void *arg)
 {
 	int afd;
 	int bufsize;
@@ -40,14 +42,13 @@
 	enum decmsg msg;
 	char *buf;
 	
-	Ctlarg *a = arg;
+	Writearg *a = arg;
 	Channel *c = a->ctl;
 	int inpipe = a->inpipe;
-	free(a);
 
 	afd = open("/dev/audio", OWRITE);
 	if(afd < 0)
-		threadexits("could not open audio device");
+		sysfatal("could not open audio device");
 	bufsize = iounit(afd);
 	buf = emalloc(bufsize);
 
@@ -55,10 +56,12 @@
 		if(nbrecv(c, &msg) != 0)
 			switch(msg){
 			case STOP:
+				free(buf);
+				close(afd);
 				threadexits(nil);
 				break;
 			case PAUSE:
-				//Block until we get a START message
+				/* Block until we get a START message */
 				while(msg != START)
 					recv(c, &msg);
 				break;
@@ -65,35 +68,118 @@
 			}
 		n = read(inpipe, buf, bufsize);
 		write(afd, buf, n);
+		yield();
 	}
 }
 
+enum{
+	QUEUE,
+	CTL,
+	WAIT,
+};
+
 void
-playfile(Dec *d, char *file)
+ctlproc(void *arg)
 {
-	Decodearg *a;
-	Ctlarg *c;
+	Channel **chans = arg;
+	Channel *q = chans[0];
+	Channel *c = chans[1];
+	Channel *pop = chans[2];
+	Channel *w = threadwaitchan();
+	free(chans);
+
+	char *path;
+	enum decmsg msg;
+	Waitmsg *wmsg;
+
+	Decodearg a;
+	Writearg wr;
 	int p[2];
+	/* This allows for the main thread to kill the decoder on exit */
+	extern int decpid = -1;
 
-	if(d->ctl != nil)
-		chanfree(d->ctl);
-	
+	Alt alts[] = {
+		{q, &path, CHANRCV},
+		{c, &msg,  CHANRCV},
+		{w, &wmsg, CHANRCV},
+		{nil, nil, CHANEND},
+	};
+
 	pipe(p);
-	a = emalloc(sizeof(Decodearg));
-	a->cpid = chancreate(sizeof(int), 0);
-	a->outpipe = p[1];
-	a->file = file;
-	procrfork(decodeproc, a, 8192, RFFDG);
-	recv(a->cpid, &(d->decpid));
-	chanfree(a->cpid);
-	free(a);
 
-	c = emalloc(sizeof(Ctlarg));
-	c->ctl = d->ctl = chancreate(sizeof(enum decmsg), 0);
-	c->inpipe = p[0];
-	d->ctlpid = procrfork(ctlproc, c, 8192, RFFDG);
-	/* Other proc frees c */
+	a.cpid = chancreate(sizeof(int), 0);
+	a.outpipe = p[0];
 
+	wr.ctl = chancreate(sizeof(enum decmsg), 0);
+	wr.inpipe = p[1];
+
+	/* Start first song to stop blocks on writethread read */
+	a.file = recvp(q);
+	procrfork(decodeproc, &a, 8192, RFFDG);
+	recv(a.cpid, &decpid);
+	threadcreate(writethread, &wr, 8192);
+
+	for(;;){
+		switch(alt(alts)){
+			case WAIT:
+				if(strstr(wmsg->msg, "eof")){
+					decpid = -1;
+					send(pop, nil);
+				}
+				free(wmsg);
+				break;
+			case CTL:
+				if(msg == NEXT){
+					killgrp(decpid);
+					decpid = -1;
+					send(pop, nil);
+				}else
+					send(wr.ctl, &msg);
+				break;
+			case QUEUE:
+				a.file = path;
+				if(decpid != -1)
+					killgrp(decpid);
+				procrfork(decodeproc, &a, 8192, RFFDG);
+				recv(a.cpid, &decpid);
+				break;
+			default:
+				goto cleanup;
+		}
+	}
+
+
+cleanup:
+	if(decpid != -1)
+		killgrp(decpid);
+	chanfree(wr.ctl);
+	chanfree(a.cpid);
 	close(p[0]);
 	close(p[1]);
+
 }
+
+/*
+* Spawns the decoder processes.
+* q is a queue for next songs. chan char*
+* c is for sending control messages. chan enum decmsg
+* nil msg is sent over pop on song change.
+*/
+void
+spawndec(Channel **q, Channel **c, Channel **pop)
+{
+	Channel **chans = emalloc(sizeof(Channel*) * 3);
+
+	if(*q == nil)
+		*q = chancreate(sizeof(char*), 0);
+	if(*c == nil)
+		*c = chancreate(sizeof(enum decmsg), 0);
+	if(*pop == nil)
+		*pop = chancreate(sizeof(char), 0);
+
+	chans[0] = *q;
+	chans[1] = *c;
+	chans[2] = *pop;
+
+	procrfork(ctlproc, chans, 8192, RFFDG);
+}
\ No newline at end of file
--- a/fncs.h
+++ b/fncs.h
@@ -1,5 +1,5 @@
 /* dec.c */
-void	playfile(Dec*,char*);
+void	spawndec(Channel**,Channel**,Channel**);
 
 /* mpl.c */
 void	quit(char*);
@@ -9,6 +9,7 @@
 u64int	bebtoi(uchar*,int);
 u64int	lebtoi(uchar*,int);
 void	kill(int);
+void	killgrp(int);
 
 /* id3.c */
 ID3v1*	readid3(int);
--- a/mpl.c
+++ b/mpl.c
@@ -13,6 +13,7 @@
 	MOUSEC,
 	RESIZEC,
 	KEYC,
+	QUEUEPOP,
 	NONE
 };
 
@@ -19,14 +20,19 @@
 
 Mousectl 	*mctl;
 Keyboardctl *kctl;
+Channel		*queuein;
+Channel		*queueout;
+Channel		*ctl;
 Album		*a;
-Dec 		d;
-int		cursong;
+int			cursong;
+int			decpid;
 
 Image *black;
 Image *red;
 Image *background;
 
+enum decmsg msg;
+
 void
 quit(char *err)
 {
@@ -47,25 +53,21 @@
 	flushimage(display, Refnone);
 }
 
-void
-play(void)
+char*
+nextsong(void)
 {
-	enum decmsg msg = STOP;
-	send(d.ctl, &msg);
-	kill(d.decpid);
 	cursong = cursong < 0 ? a->nsong-1 : cursong;
 	cursong = cursong > a->nsong-1 ? 0 : cursong;
-	playfile(&d, a->songs[cursong]->path);
-	eresized(0);
+	return a->songs[cursong]->path;
 }
 
 void
 handleaction(Rune kbd)
 {
-	enum decmsg msg;
 	switch(kbd){
 		case Kbs:
 		case Kdel:
+			killgrp(decpid);
 			quit(nil);
 			break;
 		case 'w':
@@ -73,19 +75,23 @@
 			break;
 		case 'p':
 			msg = PAUSE;
-			send(d.ctl, &msg);
+			send(ctl, &msg);
 			break;
 		case 'l':
 			msg = START;
-			send(d.ctl, &msg);
+			send(ctl, &msg);
 			break;
 		case 'n':
 			cursong++;
-			play();
+			nextsong();
+			sendp(queuein, nextsong());
+			eresized(0);
 			break;
 		case 'm':
 			cursong--;
-			play();
+			nextsong();
+			sendp(queuein, nextsong());
+			eresized(0);
 			break;
 	}
 }
@@ -104,6 +110,7 @@
 	Rune kbd;
 	int resize[2];
 	cursong = 0;
+	queuein = queueout = ctl = nil;
 
 	//TODO: Use ARGBEGIN
 	argv0 = argv[0];	
@@ -118,7 +125,6 @@
 	if((kctl = initkeyboard(nil)) == nil)
 		sysfatal("initkeyboard: %r");
 
-	memset(&d, 0, sizeof d);
 	red = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DBlue);
 	black = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DBlack);
 	background = allocimagemix(display, DPaleyellow, DPalegreen);
@@ -128,7 +134,8 @@
 		quit("nil album");
 	if(a->nsong == 0)
 		quit("no songs");
-	playfile(&d, a->songs[0]->path);
+	spawndec(&queuein, &ctl, &queueout);
+	send(queuein, &(a->songs[0]->path));
 	handleaction('w');
 
 	Alt alts[] = {
@@ -135,6 +142,7 @@
 		{mctl->c, &mouse, CHANRCV},
 		{mctl->resizec, resize, CHANRCV},
 		{kctl->c, &kbd, CHANRCV},
+		{queueout, nil, CHANRCV},
 		{nil, nil, CHANEND},
 	};
 
@@ -145,6 +153,9 @@
 				break;
 			case RESIZEC:
 				eresized(1);
+				break;
+			case QUEUEPOP:
+				handleaction(L'n');
 				break;
 		}
 	}
--- a/util.c
+++ b/util.c
@@ -49,10 +49,24 @@
 	int nfd;
 	char *note = smprint( "/proc/%d/note", pid);
 	nfd = open(note, OWRITE);
-	//If the file does not exist, it is probably already dead
 	if(nfd<0)
-		return;
-	write(nfd, "kill", 4);
+		sysfatal("proc doesn't exist");
+	if(write(nfd, "kill", 4)!=4)
+		sysfatal("could not write to note");
 	close(nfd);
 	free(note);
 }
+
+void
+killgrp(int pid)
+{
+	int nfd;
+	char *note = smprint( "/proc/%d/notepg", pid);
+	nfd = open(note, OWRITE);
+	if(nfd<0)
+		sysfatal("proc doesn't exist");
+	if(write(nfd, "kill", 4)!=4)
+		sysfatal("could not write to note");
+	close(nfd);
+	free(note);
+}
\ No newline at end of file