ref: 11b150a2bb01fbf09ae7cfee553dda0e8af5334d
dir: /fs.c/
#include <u.h> #include <libc.h> #include <fcall.h> #include <thread.h> #include <9p.h> #include "cuefs.h" typedef struct { Cuesheet* sheet; int outfmt; } Fsprops; typedef struct { int fd, pid; vlong curoff, end; } Decoder; void pcmserve(Entry*, Req*); void (*servefmt[])(Entry*, Req*) = { // [WAVE] = wavserve, [BINARY] = pcmserve, [UNKNOWN] = nil }; char *decoder[] = { [MP3] = "audio/mp3dec", [FLAC] = "audio/flacdec", [WAVE] = "audio/wavdec" }; /* * FIXME find a better way to signal decoder failure, * one that we can answer the Tread with */ Decoder* pipedec(AFile *f, double sec, vlong off, vlong end) { Decoder *ret; int fd[2], afd; char *dec; dec = decoder[f->actual]; debug("decoding %s starting at %f\n", f->name, sec); if(pipe(fd) < 0) sysfatal("pipedec: can't decode: pipe: %r"); ret = emalloc(sizeof(*ret)); ret->fd = fd[0]; ret->curoff = off; ret->end = end; switch(ret->pid = rfork(RFPROC|RFFDG|RFREND|RFNOTEG)) { case 0: if((afd = open(f->name, OREAD)) < 0) sysfatal("pipedec: can't decode: open: %r"); dup(afd, 0); dup(fd[1], 1); close(afd); close(fd[1]); seek(0, 0, 0); { char *argv[] = { dec, "-s", smprint("%f", sec), nil }; debug("command line: "); for(char **a = argv; *a != nil; a++) debug("'%s' ", *a); debug("\n"); if(argv[2] == nil) sysfatal("pipedec: can't decode: smprint: %r"); exec(dec, argv); dec = smprint("/bin/%s", dec); if(dec == nil) sysfatal("pipedec: can't decode: smprint: %r"); exec(dec, argv); sysfatal("pipedec: can't decode: exec: %r"); } break; case -1: sysfatal("pipedec: can't decode: rfork: %r"); } close(fd[1]); return ret; } void closedec(Decoder *dec) { char *path; int fd; if(dec == nil) return; close(dec->fd); if((path = smprint("/proc/%d/notepg", dec->pid)) == nil) sysfatal("smprint: %r"); if((fd = open(path, OWRITE)) < 0) sysfatal("open: %r"); write(fd, "kill", strlen("kill")); close(fd); free(dec); } long readdec(Decoder *dec, void *buf, long count) { long ret, n; debug("readdec: decoder offset = %lld, decoder end = %lld, count = %ld\n", dec->curoff, dec->end, count); n = dec->end - dec->curoff + count; if(n < 0) count += n; debug("reading %ld bytes from pid %d\n", count, dec->pid); ret = read(dec->fd, buf, count); dec->curoff += ret; return ret; } void pcmserve(Entry *e, Req *r) { Decoder *dec; double sec; long end; sec = t2sec(e->starts[0]) + of2sec(44100, 16, 2, r->ifcall.offset); /* * wouldn't be that bad to just read and throw away a little of the * decoded pcm if r->ifcall.offset isn't that far from dec->curoff */ if((dec = r->fid->aux) == nil || dec->curoff != r->ifcall.offset) { /* amount of samples between songs... */ end = (e->next->starts->frames - e->starts->frames) * (44100/75); /* ...*2 channels, 2 bytes per sample */ end *= 2*2; closedec(dec); dec = r->fid->aux = pipedec(e->file, sec, r->ifcall.offset, end); } r->ofcall.count = readdec(dec, r->ofcall.data, r->ifcall.count); respond(r, nil); } void fsopen(Req *r) { respond(r, nil); } void fsclose(Fid *fid) { if(fid->aux != nil) closedec(fid->aux); } void fsread(Req *r) { void (*func)(Entry*, Req*); extern Srv fs; Fsprops *p; p = fs.aux; func = servefmt[p->outfmt]; if(func != nil) func(r->fid->file->aux, r); else respond(r, Eunsupported); } void fsend(Srv *s) { Fsprops *p; p = s->aux; freesheet(p->sheet); free(p); } Srv fs = { .open = fsopen, .read = fsread, .destroyfid = fsclose, .end = fsend }; void cuefsinit(Cuesheet *sheet, char *mtpt) { Fsprops *p; char *s; p = emalloc(sizeof(*p)); p->sheet = sheet; p->outfmt = BINARY; /* STUB */ fs.aux = p; fs.tree = alloctree(nil, nil, DMDIR | 0444, nil); for(Entry *e = sheet->entries; e != nil; e = e->next) { debug("%d: %d\n", e->index, e->starts[0].frames); /* TODO make the format customizable */ s = smprint("%02d - %s.%s", e->index, e->title, formatext(p->outfmt)); strreplace(s, '/', '-'); createfile(fs.tree->root, s, nil, 0444, e); free(s); } postmountsrv(&fs, nil, mtpt, MREPL); }