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
+}