ref: dd1bcfaa29cc7a2a00b44af00fc9cc8516196f49
parent: 9c3fa319d80fb14b5da3c29b3e0945961068b0d1
author: Tevo <estevan.cps@gmail.com>
date: Fri Jan 15 20:59:20 EST 2021
Simple flac output(!)
--- a/BUGS
+++ b/BUGS
@@ -1,6 +1,5 @@
• unsure if seeking on a file works correctly
• current treatment of lossy sources is less than ideal
-• can only serve .wav and raw pcm files (no flac encoder)
• everything will get resampled to 44100kHz/16bit/2channel
• the codebase is a mess
• reading concurrently from the same fid might break decoding logic, or be unecessarily slow (does that ever happen?)
--- a/cue.c
+++ b/cue.c
@@ -172,6 +172,7 @@
Entry *new;
new = emallocz(sizeof(*new), 1);
+ new->sheet = c;
new->file = c->curfile;
new->performer = c->performer;
new->index = i;
--- a/cuefs.h
+++ b/cuefs.h
@@ -35,40 +35,47 @@
AAC, FLAC, OGG, OPUS, UNKNOWN
};
-typedef struct
+typedef struct Timestamp Timestamp;
+typedef struct AFile AFile;
+typedef struct Start Start;
+typedef struct Entry Entry;
+typedef struct Cuesheet Cuesheet;
+
+struct Timestamp
{
u64int frames;
-} Timestamp;
+};
-typedef struct AFile
+struct AFile
{
int type, actual;
- struct AFile *next;
+ AFile *next;
char *name;
-} AFile;
+};
-typedef struct Start
+struct Start
{
Timestamp;
u8int index;
- struct Start *next;
-} Start;
+ Start *next;
+};
-typedef struct Entry
+struct Entry
{
+ Cuesheet *sheet;
Start *starts;
AFile *file;
int index;
char *title, *performer;
- struct Entry *next;
-} Entry;
+ Entry *next;
+};
-typedef struct
+struct Cuesheet
{
char *title, *performer;
AFile *files, *curfile;
Entry *entries, *curentry;
-} Cuesheet;
+};
extern Cuesheet *cursheet;
--- a/fs.c
+++ b/fs.c
@@ -47,12 +47,22 @@
u32int datasize; /* LE */
} Wavehdr;
+typedef struct
+{
+ Resource;
+ Decoder *dec;
+ int pollpid, encpid, fd;
+ vlong curoff;
+} Flacenc;
+
void pcmserve(Entry*, Req*);
void wavserve(Entry*, Req*);
+void flacserve(Entry*, Req*);
void (*servefmt[])(Entry*, Req*) =
{
[WAVE] = wavserve,
+ [FLAC] = flacserve,
[BINARY] = pcmserve,
[UNKNOWN] = nil
@@ -69,25 +79,29 @@
};
void
-closedec(Decoder *dec)
+kill(int pid)
{
char *path;
int fd;
- if(dec == nil)
- return;
-
- close(dec->fd);
-
- if((path = smprint("/proc/%d/notepg", dec->pid)) == nil)
+ if((path = smprint("/proc/%d/notepg", pid)) == nil)
sysfatal("smprint: %r");
if((fd = open(path, OWRITE)) < 0)
- goto cleanup; /* open failed, assume it's already dead */
+ goto end;
write(fd, "kill", strlen("kill"));
close(fd);
-
-cleanup:
+end:
free(path);
+}
+
+void
+closedec(Decoder *dec)
+{
+ if(dec == nil)
+ return;
+
+ close(dec->fd);
+ kill(dec->pid);
free(dec);
}
@@ -164,7 +178,7 @@
count += n;
}
- debug("reading %ld bytes from pid %d\n", count, dec->pid);
+ debug("readdec: reading %ld bytes from pid %d\n", count, dec->pid);
ret = read(dec->fd, buf, count);
dec->curoff += ret;
@@ -172,12 +186,29 @@
return ret;
}
+vlong
+entrylen(Entry *e)
+{
+ vlong end;
+
+ if(e->next != nil)
+ {
+ /* amount of samples between songs... */
+ end = (e->next->starts->frames - e->starts->frames) * (44100/75);
+ /* ...*2 channels, 2 bytes per sample */
+ end *= 2*2;
+ }
+ else
+ end = 0;
+
+ return end;
+}
+
Decoder*
reqdec(Entry *e, Req *r, ulong offset)
{
Decoder *dec;
double sec;
- ulong end;
sec = t2sec(e->starts[0]) + of2sec(44100, 16, 2, offset);
@@ -187,17 +218,8 @@
*/
if((dec = r->fid->aux) == nil || dec->curoff != offset)
{
- if(e->next != nil)
- {
- /* amount of samples between songs... */
- end = (e->next->starts->frames - e->starts->frames) * (44100/75);
- /* ...*2 channels, 2 bytes per sample */
- end *= 2*2;
- }
- else
- end = 0;
closedec(dec);
- dec = r->fid->aux = pipedec(e->file, sec, offset, end);
+ dec = r->fid->aux = pipedec(e->file, sec, offset, entrylen(e));
}
return dec;
@@ -272,6 +294,156 @@
}
void
+closeflac(Flacenc *enc)
+{
+ if(enc == nil)
+ return;
+
+ closedec(enc->dec);
+ kill(enc->pollpid);
+ kill(enc->encpid);
+ close(enc->fd);
+
+ free(enc);
+}
+
+int
+polldec(Decoder *dec, int fd)
+{
+ char buf[4096] = {0};
+ int pid;
+
+ switch(pid = rfork(RFPROC|RFFDG|RFREND|RFNOTEG))
+ {
+ case 0:
+ for(int n = -1; n != 0;)
+ {
+ debug("polldec: reading %d from decoder\n", sizeof(buf));
+ n = readdec(dec, buf, sizeof(buf));
+ write(fd, buf, sizeof(buf));
+ debug("polldec: writing %d into poll pipe\n", n);
+ }
+ debug("polldec: decoder finished, exiting\n");
+ closedec(dec);
+ exits(0);
+ case -1:
+ sysfatal("polldec: rfork: %r");
+ }
+
+ close(fd);
+ return pid;
+}
+
+int
+_flacenc(Entry *e, int infd, int outfd)
+{
+ static char *enc = "audio/flacenc";
+ int pid;
+
+ switch(pid = rfork(RFPROC|RFFDG|RFREND|RFNOTEG))
+ {
+ case 0:
+ dup(infd, 0);
+ dup(outfd, 1);
+ close(infd);
+ close(outfd);
+ {
+ /* TODO better metadata handling */
+ char *argv[] =
+ {
+ enc,
+ "-T", smprint("TITLE=%s", e->title),
+ "-T", smprint("ARTIST=%s", e->performer),
+ "-T", smprint("ALBUMARTIST=%s", e->sheet->performer),
+ "-T", smprint("ALBUM=%s", e->sheet->title),
+ nil
+ };
+ exec(enc, argv);
+ enc = smprint("/bin/%s", enc);
+ if(enc == nil)
+ sysfatal("_flacenc: can't encode: smprint: %r");
+ exec(enc, argv);
+ sysfatal("_flacenc: can't encode: exec: %r");
+ }
+ case -1:
+ sysfatal("_flacenc: can't encode: rfork: %r");
+ }
+
+ close(infd);
+ close(outfd);
+ return pid;
+}
+
+Flacenc*
+flacenc(Entry *e)
+{
+ Flacenc *enc;
+ int encfd[2], decfd[2];
+
+ if(pipe(encfd) < 0 || pipe(decfd) < 0)
+ sysfatal("flacenc: pipe: %r");
+
+ enc = emalloc(sizeof(*enc));
+ enc->cleanup = (void(*)(void*))closeflac;
+ enc->fd = encfd[0];
+ enc->dec = pipedec(e->file, t2sec(e->starts[0]), 0, entrylen(e));
+ enc->pollpid = polldec(enc->dec, decfd[1]);
+ enc->encpid = _flacenc(e, decfd[0], encfd[1]);
+
+ return enc;
+}
+
+long
+readflac(Flacenc *enc, void *buf, long count)
+{
+ long ret;
+
+ debug("readflac: reading %ld bytes from poll pipe\n", count);
+ ret = read(enc->fd, buf, count);
+ enc->curoff += ret;
+
+ return ret;
+}
+
+long
+seekflac(Flacenc *enc, vlong offset)
+{
+ char buf[4096];
+
+ if(offset < enc->curoff)
+ return enc->curoff;
+
+ debug("seekflac: %lld → %lld\n", offset);
+
+ for(int todo; (todo = enc->curoff - offset) == 0;)
+ {
+ debug("seekflac: %d to go\n");
+ if(readflac(enc, buf, todo < sizeof(buf) ? todo : sizeof(buf)) == 0)
+ break;
+ }
+
+ return enc->curoff;
+}
+
+void
+flacserve(Entry *e, Req *r)
+{
+ Flacenc *enc;
+
+ if((enc = r->fid->aux) == nil || enc->curoff < r->ifcall.offset)
+ {
+ closeflac(enc);
+ enc = r->fid->aux = flacenc(e);
+ }
+
+ if(enc->curoff != r->ifcall.offset)
+ seekflac(enc, r->ifcall.offset);
+
+ r->ofcall.count = readflac(enc, r->ofcall.data, r->ifcall.count);
+ respond(r, nil);
+}
+
+void
fsopen(Req *r)
{
respond(r, nil);
@@ -330,7 +502,7 @@
p = emalloc(sizeof(*p));
p->sheet = sheet;
- p->outfmt = WAVE; /* STUB */
+ p->outfmt = FLAC; /* STUB */
fs.aux = p;
fs.tree = alloctree(nil, nil, DMDIR | 0444, nil);