shithub: mpl

Download patch

ref: a848106feaaba815d72a871bf412b3fcd65338c0
parent: a1aa7038ba62a46b1173b609e4c884b1919d3949
author: Jacob Moody <jsmoody@iastate.edu>
date: Wed Sep 11 10:30:17 EDT 2019

Parse files into Song and Album structs
Seperate draw function to use Album
Refactor

--- a/dat.h
+++ b/dat.h
@@ -33,9 +33,17 @@
 
 typedef struct VorbisMeta VorbisMeta;
 struct VorbisMeta{
+	/* Raw values */
 	uint ncom;
 	Rune **key;
 	Rune **val;
+
+	/* Common Tags */
+	Rune *title;
+	Rune *artist;
+	Rune *album;
+	int year;
+	int tracknumber;
 };
 
 typedef struct FlacPic FlacPic;
@@ -45,7 +53,6 @@
 		Point p;
 		uvlong size;
 		uchar *data;
-		Image *i;
 };
 
 typedef struct FlacMeta FlacMeta;
@@ -52,4 +59,31 @@
 struct FlacMeta{
 	VorbisMeta *com;
 	FlacPic *pic;
-};
\ No newline at end of file
+};
+
+
+enum metatype{
+	FLAC,
+	MP3,
+	VORBIS,
+};
+
+typedef struct Song Song;
+struct Song{
+	enum metatype type;
+	union {
+		FlacMeta *fmeta;
+		VorbisMeta *vmeta;
+		ID3v1 *idmeta;
+	};
+	char *path;
+};
+
+typedef struct Album Album;
+struct Album{
+	char *path;
+	Rune *name;
+	Image *cover;
+	int nsong;
+	Song **songs;
+};
--- a/dec.c
+++ b/dec.c
@@ -55,7 +55,7 @@
 		if(nbrecv(c, &msg) != 0)
 			switch(msg){
 			case STOP:
-				threadexits("Stopped");
+				threadexits(nil);
 				break;
 			case PAUSE:
 				//Block until we get a START message
@@ -68,15 +68,15 @@
 	}
 }
 
-Dec*
-spawndecoder(char *file)
+void
+playfile(Dec *d, char *file)
 {
-	Dec *d;
 	Decodearg *a;
 	Ctlarg *c;
 	int p[2];
 
-	d = emalloc(sizeof(Dec));
+	if(d->ctl != nil)
+		chanfree(d->ctl);
 	
 	pipe(p);
 	a = emalloc(sizeof(Decodearg));
@@ -96,6 +96,4 @@
 
 	close(p[0]);
 	close(p[1]);
-
-	return d;
-}
\ No newline at end of file
+}
--- /dev/null
+++ b/dir.c
@@ -1,0 +1,150 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+
+#include "dat.h"
+#include "fncs.h"
+
+Song*
+file2song(char *path, int needpic)
+{
+	char *dot;
+	Song *s;
+	int fd;
+
+	dot = strrchr(path, '.');
+	if(dot == nil)
+		return nil;
+	dot+=1;
+
+	s = emalloc(sizeof(Song));
+	if(strcmp(dot, "flac") == 0){
+		s->type = FLAC;
+		goto done;
+	}
+	if(strcmp(dot, "mp3") == 0){
+		s->type = MP3;
+		goto done;
+	}
+	if(strcmp(dot, "ogg") == 0){
+		s->type = VORBIS;
+		goto done;
+	}
+	/* Unsupported file suffix */
+	goto error;
+
+done:
+	fd = open(path, OREAD);
+	if(fd < 0)
+		goto error;
+
+	switch(s->type){
+	case FLAC:
+		s->fmeta = readflacmeta(fd, needpic);
+		break;
+	case MP3:
+		s->idmeta = readid3(fd);
+		break;
+	case VORBIS:
+		/* TODO parse raw ogg file */
+		break;
+	}
+	close(fd);
+	/* We can check the pointer without having to worry about which one it is */
+	if(s->fmeta == nil)
+		goto error;
+
+	return s;
+
+error:
+	free(s);
+	return nil;
+}
+
+Album*
+dir2album(char *path)
+{
+	Album *a;
+	Dir *files;
+	int fd;
+	long n;
+	long i;
+	char *dot;
+	Rune *albumtitle;
+	char buf[512];
+	int songcount = 0;
+	int needpic = 1;
+
+	fd = open(path, OREAD);
+	if(fd < 0)
+		return nil;
+
+	n = dirreadall(fd, &files);
+	close(fd);
+	if(n <= 0)
+		return nil;
+
+	a = emalloc(sizeof(Album));
+
+	/* Do a single pass to find cover.^(jp^(eg g) png) */
+	for(i=0;i<n;i++){
+		dot = strstr(path, "cover.");
+		if(dot == nil)
+			continue;
+		dot+=1;
+		snprint(buf, 512, "%s/%s", path, files[i].name);
+		fd = open(buf, OREAD);
+		if(fd<0)
+			continue;
+
+		a->cover = convpic(fd, dot);
+		if(a->cover != nil){
+			needpic = 0;
+			break;
+		}
+	}
+
+	/* Greedy alloc to start, we will trim down later */
+	a->nsong = n;
+	a->songs = emalloc(sizeof(Song*) * n);
+
+	for(i=0;i<n;i++){
+		snprint(buf, 512, "%s/%s", path, files[i].name);
+		a->songs[songcount] = file2song(buf, needpic);
+		if(a->songs[songcount] == nil)
+			continue;
+		if(a->name == nil){
+			switch(a->songs[songcount]->type){
+			case FLAC:
+				albumtitle = a->songs[songcount]->fmeta->com->album;
+				break;
+			case MP3:
+				albumtitle = a->songs[songcount]->idmeta->album;
+				break;
+			case VORBIS:
+				albumtitle = a->songs[songcount]->vmeta->album;
+				break;
+			default:
+				albumtitle = nil;
+			}
+			if(albumtitle != nil)
+				a->name = runesmprint("%S",  albumtitle);
+		}
+		a->songs[songcount]->path = strdup(buf);
+		if(needpic == 1 && a->songs[songcount]->type == FLAC && a->songs[songcount]->fmeta->pic != nil){
+			FlacPic *p = a->songs[songcount]->fmeta->pic;
+			a->cover = convpicbuf(p->data, p->size, p->mime);
+			if(a->cover == nil)
+				quit("dir2album: Could not convert image");
+			needpic--;
+		}
+		songcount++;
+	}
+
+	a->nsong = songcount;
+	a->songs = realloc(a->songs, sizeof(Song*) * songcount);
+
+	free(files);
+	return a;
+}
\ No newline at end of file
--- /dev/null
+++ b/draw.c
@@ -1,0 +1,166 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+
+#include "dat.h"
+#include "fncs.h"
+
+typedef struct{
+	int fd;
+	uvlong size;
+	uchar *data;
+} WriteArg;
+
+void
+picwriteproc(void *arg)
+{
+	int i, n;
+	WriteArg *a = arg;
+	int fd = a->fd;
+	uchar *data = a->data;
+	uvlong towrite = a->size;
+
+	free(a);
+	for(i = 0;towrite>0;){
+		n = write(fd, data+i, towrite);
+		i+=n;
+		towrite-=n;
+	}
+}
+
+typedef struct{
+	int fdin;
+	int fdout;
+	Channel *cpid;
+	char *cmd;
+} ExecArg;
+
+void
+picexecproc(void *arg)
+{
+	ExecArg *a = arg;
+	dup(a->fdin, 0);
+	dup(a->fdout, 1);
+	procexecl(a->cpid, a->cmd, a->cmd, "-c", nil);
+}
+
+void
+picresizeproc(void *arg)
+{
+	ExecArg *a = arg;
+	dup(a->fdin, 0);
+	dup(a->fdout, 1);
+	procexecl(a->cpid, "/bin/resize", "resize", "-x", "256", "-y", "256", nil);
+}
+
+char*
+mime2bin(char *mime)
+{
+	char *start = strchr(mime, '/');
+	if(start!=nil)
+		start+=1;
+	else
+		start = mime;
+
+	if(strcmp(start, "png") == 0)
+		return strdup("/bin/png");
+	if(strcmp(start, "jpeg") == 0 || strcmp(start, "jpg") == 0)
+		return strdup("/bin/jpg");
+
+	return nil;
+}
+
+Image*
+convpic(int fd, char *mime)
+{
+	ExecArg *e;
+	Image *i;
+	int convout[2];
+	int resize[2];
+
+	pipe(convout);
+	pipe(resize);
+
+	e = emalloc(sizeof(ExecArg));
+	e->cpid = chancreate(sizeof(int), 0);
+	e->fdin = fd;
+	e->fdout = convout[0];
+	e->cmd = mime2bin(mime);
+	procrfork(picexecproc, e, 8192, RFFDG);
+	recv(e->cpid, nil);
+	chanfree(e->cpid);
+	free(e->cmd);
+
+	e->cpid = chancreate(sizeof(int), 0);
+	e->fdin = convout[1];
+	e->fdout = resize[0];
+	procrfork(picresizeproc, e, 8192, RFFDG);
+	recv(e->cpid, nil);
+	chanfree(e->cpid);
+	free(e);
+
+	i = readimage(display, resize[1], 0);
+	close(convout[0]);
+	close(convout[1]);
+	close(resize[0]);
+	close(resize[1]);
+
+	return i;
+}
+
+Image*
+convpicbuf(uchar *buf, uvlong size, char *mime)
+{
+	WriteArg *w;
+	int p[2];
+	Image *i;
+
+	pipe(p);
+
+	w = emalloc(sizeof(WriteArg));
+	w->fd = p[0];
+	w->data = buf;
+	w->size = size;
+	procrfork(picwriteproc, w, 8192, RFFDG);
+	/* other proc frees w */
+
+	i = convpic(p[1], mime);
+
+	close(p[0]);
+	close(p[1]);
+
+	return i;
+}
+
+void
+drawalbum(Album *a, Image *textcolor, Image *active, int cursong)
+{
+	uint i;
+	Point p = screen->r.min;
+	Font *f = screen->display->defaultfont;
+	Rune *tracktitle = nil;
+
+	draw(screen, screen->r, a->cover, nil, ZP);
+	p.x += a->cover->r.max.x;
+
+	runestring(screen, p, textcolor, ZP, f, a->name);
+	p.y += f->height * 2;
+
+	for(i=0;i<a->nsong;i++){
+		switch(a->songs[i]->type){
+		case FLAC:
+			tracktitle = a->songs[i]->fmeta->com->title;
+			break;
+		case MP3:
+			tracktitle = a->songs[i]->idmeta->title;
+			break;
+		case VORBIS:
+			tracktitle = a->songs[i]->vmeta->title;
+			break;
+		}
+		runestring(screen, p, i == cursong ? active : textcolor, ZP, f, tracktitle);
+		p.y += f->height;
+	}
+}
+
--- a/flac.c
+++ b/flac.c
@@ -17,7 +17,7 @@
 };
 
 FlacPic*
-readFlacPic(int fd, vlong offset)
+readflacpic(int fd, vlong offset)
 {
 	uchar buf[1024];
 	uint len;
@@ -66,122 +66,8 @@
 	return pic;
 }
 
-
-typedef struct{
-	int fd;
-	uvlong size;
-	uchar *data;
-} WriteArg;
-
-void
-picwriteproc(void *arg)
-{
-	int i, n;
-	WriteArg *a = arg;
-	int fd = a->fd;
-	uchar *data = a->data;
-	uvlong towrite = a->size;
-
-	free(a);
-	for(i = 0;towrite>0;){
-		n = write(fd, data+i, towrite);
-		i+=n;
-		towrite-=n;
-	}
-}
-
-typedef struct{
-	int fdin;
-	int fdout;
-	Channel *cpid;
-	char *cmd;
-} ExecArg;
-
-void
-picexecproc(void *arg)
-{
-	ExecArg *a = arg;
-	dup(a->fdin, 0);
-	dup(a->fdout, 1);
-	procexecl(a->cpid, a->cmd, a->cmd, "-c", nil);
-}
-
-void
-picresizeproc(void *arg)
-{
-	ExecArg *a = arg;
-	dup(a->fdin, 0);
-	dup(a->fdout, 1);
-	procexecl(a->cpid, "/bin/resize", "resize", "-x", "256", "-y", "256", nil);
-}
-
-char*
-mime2bin(char *mime)
-{
-	char *slash = strchr(mime, '/');
-	if(slash==nil)
-		return nil;
-	slash+=1;
-
-	if(strcmp(slash, "jpeg") == 0)
-		return strdup("/bin/jpg");
-	if(strcmp(slash, "png") == 0)
-		return strdup("/bin/png");
-
-	return nil;
-}
-
-void
-convFlacPic(FlacPic *pic)
-{
-	ExecArg *e;
-	WriteArg *w;
-	int convin[2];
-	int convout[2];
-	int resize[2];
-
-	pipe(convin);
-	pipe(convout);
-	pipe(resize);
-
-	w = emalloc(sizeof(WriteArg));
-	w->fd = convin[0];
-	w->data = pic->data;
-	w->size = pic->size;
-	procrfork(picwriteproc, w, 8192, RFFDG);
-	/* other proc frees w */
-
-	e = emalloc(sizeof(ExecArg));
-	e->cpid = chancreate(sizeof(int), 0);
-	e->fdin = convin[1];
-	e->fdout = convout[0];
-	e->cmd = mime2bin(pic->mime);
-	procrfork(picexecproc, e, 8192, RFFDG);
-	recv(e->cpid, nil);
-	chanfree(e->cpid);
-	free(e->cmd);
-	free(e);
-
-	e = emalloc(sizeof(ExecArg));
-	e->cpid = chancreate(sizeof(int), 0);
-	e->fdin = convout[1];
-	e->fdout = resize[0];
-	procrfork(picresizeproc, e, 8192, RFFDG);
-	recv(e->cpid, nil);
-	chanfree(e->cpid);
-	free(e);
-
-	pic->i = readimage(display, resize[1], 0);
-	close(convin[0]);
-	close(convin[1]);
-	close(convout[0]);
-	close(convout[1]);
-	close(resize[0]);
-	close(resize[1]);
-}
-
 FlacMeta*
-readFlacMeta(int fd)
+readflacmeta(int fd, int readpic)
 {
 	uvlong off;
 	char type;
@@ -213,12 +99,13 @@
 		case SEEKTABLE:
 			break;
 		case VORBIS_COMMENT:
-			f->com = parseVorbisMeta(fd, off);
+			f->com = parsevorbismeta(fd, off);
 			break;
 		case CUESHEET:
 			break;
 		case PICTURE:
-			f->pic = readFlacPic(fd, off);
+			if(readpic > 0)
+				f->pic = readflacpic(fd, off);
 			break;
 		}
 		off+=bebtoi(buf, 3);
--- a/fncs.h
+++ b/fncs.h
@@ -1,5 +1,5 @@
 /* dec.c */
-Dec* spawndecoder(char*);
+void	playfile(Dec*,char*);
 
 /* mpl.c */
 void	quit(char*);
@@ -8,14 +8,22 @@
 void*	emalloc(vlong);
 u64int	bebtoi(uchar*,int);
 u64int	lebtoi(uchar*,int);
+void	kill(int);
 
 /* id3.c */
-ID3v1*	readID3v1(int);
-void	destroyID3v1(ID3v1 *id);
+ID3v1*	readid3(int);
+void	destroyid3(ID3v1 *id);
 
 /* vorbis.c */
-VorbisMeta*	parseVorbisMeta(int, uvlong);
+VorbisMeta*	parsevorbismeta(int, uvlong);
 
 /* flac.c */
-FlacMeta*	readFlacMeta(int);
-void		convFlacPic(FlacPic*);
+FlacMeta*	readflacmeta(int, int);
+
+/* draw.c */
+void	drawalbum(Album*, Image*, Image*, int);
+Image*	convpic(int, char*);
+Image*	convpicbuf(uchar*, uvlong, char*);
+
+/* dir.c */
+Album*	dir2album(char*);
\ No newline at end of file
--- a/id3.c
+++ b/id3.c
@@ -7,7 +7,7 @@
 #include "fncs.h"
 
 ID3v1*
-readID3v1(int fd)
+readid3(int fd)
 {
 	Dir *d;
 	ID3v1 *id;
@@ -57,7 +57,7 @@
 }
 
 void
-destroyID3v1(ID3v1 *id)
+destroyid3(ID3v1 *id)
 {
 	free(id->title);
 	free(id->artist);
--- a/mkfile
+++ b/mkfile
@@ -8,6 +8,8 @@
 	id3.$O \
 	flac.$O \
 	vorbis.$O \
+	draw.$O \
+	dir.$O \
 
 
 </sys/src/cmd/mkone
--- a/mpl.c
+++ b/mpl.c
@@ -16,11 +16,17 @@
 	NONE
 };
 
-Dec 		*d;
+
 Mousectl 	*mctl;
 Keyboardctl *kctl;
-Image		*cover;
+Album		*a;
+Dec 		d;
+int		cursong;
 
+Image *black;
+Image *red;
+Image *background;
+
 void
 quit(char *err)
 {
@@ -35,9 +41,25 @@
 {
 	if(isnew && getwindow(display, Refnone) < 0)
 		quit("eresized: Can't reattach to window");
+
+	draw(screen, screen->r, background, nil, ZP);
+	drawalbum(a, black, red, cursong);
+	flushimage(display, Refnone);
 }
 
 void
+play(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);
+}
+
+void
 handleaction(Rune kbd)
 {
 	enum decmsg msg;
@@ -47,21 +69,25 @@
 			quit(nil);
 			break;
 		case 'w':
-			draw(screen, screen->r, cover, nil, ZP);
+			eresized(0);
 			break;
-		case 'b':
-			draw(screen, screen->r, display->black, nil, ZP);
-			break;
 		case 'p':
 			msg = PAUSE;
-			send(d->ctl, &msg);
+			send(d.ctl, &msg);
 			break;
 		case 'l':
 			msg = START;
-			send(d->ctl, &msg);
+			send(d.ctl, &msg);
 			break;
+		case 'n':
+			cursong++;
+			play();
+			break;
+		case 'm':
+			cursong--;
+			play();
+			break;
 	}
-	flushimage(display, Refnone);
 }
 
 void
@@ -77,6 +103,7 @@
 	Mouse mouse;
 	Rune kbd;
 	int resize[2];
+	cursong = 0;
 
 	//TODO: Use ARGBEGIN
 	argv0 = argv[0];	
@@ -91,21 +118,17 @@
 	if((kctl = initkeyboard(nil)) == nil)
 		sysfatal("initkeyboard: %r");
 
-	d = spawndecoder(argv[1]);
+	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);
 
-	int fd = open(argv[1], OREAD);
-	if(fd < 0)
-		quit("can not open file");
-	FlacMeta *f = readFlacMeta(fd);
-	if(f == nil)
-		print("failed to parse\n");
-	if(f != nil && f->com == nil)
-		print("failed to parse comm\n");
-
-	convFlacPic(f->pic);
-	if(f->pic->i == nil)
-		quit("Could not load image\n");
-	cover = f->pic->i;
+	a = dir2album(argv[1]);
+	if(a == nil)
+		quit("nil album");
+	if(a->nsong == 0)
+		quit("no songs");
+	playfile(&d, a->songs[0]->path);
 	handleaction('w');
 
 	Alt alts[] = {
--- a/util.c
+++ b/util.c
@@ -42,3 +42,17 @@
 		out = out | buf[i]<<(--n*8);
 	return out;
 }
+
+void
+kill(int pid)
+{
+	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);
+	close(nfd);
+	free(note);
+}
--- a/vorbis.c
+++ b/vorbis.c
@@ -6,8 +6,28 @@
 #include "dat.h"
 #include "fncs.h"
 
+void
+finddefmeta(VorbisMeta *v)
+{
+	uint i;
+	for(i=0;i<v->ncom;i++){
+		if(runestrcmp(v->key[i], L"ALBUM") == 0){
+			v->album = v->val[i];
+			continue;
+		}
+		if(runestrcmp(v->key[i], L"TITLE") == 0){
+			v->title = v->val[i];
+			continue;
+		}
+		if(runestrcmp(v->key[i], L"ARTIST") == 0){
+			v->artist = v->val[i];
+			continue;
+		}
+	}
+}
+
 VorbisMeta*
-parseVorbisMeta(int fd, uvlong offset)
+parsevorbismeta(int fd, uvlong offset)
 {
 	u32int size;
 	uchar buf[1024];
@@ -48,5 +68,6 @@
 		v->val[i] = runesmprint("%s", sep+1);
 	}
 
+	finddefmeta(v);
 	return v;
-}
\ No newline at end of file
+}