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);
--
⑨