shithub: cuefs

Download patch

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