shithub: neindaw

ref: 547b21bde31539b21453bfa7a9f8a13b345c7fc5
dir: /fs.c/

View raw version
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "uiglue.h"
typedef struct DSP DSP;
#include "dspf.h"
#include "aux.h"

enum {
	Inmax = 2048, /* float = 8192 bytes */
	Outmax = 2048, /* float = 8192 bytes */
};

static Aux rootaux[] = {
	[Xctl] = {.type = Xctl},
	[Xmetadata] = {.type = Xmetadata},
	[Xclone] = {.type = Xclone},
};
static Aux *objs[32];
static char *meta = nil;
static int metalen = 0;
static int rate = 44100;
static DSPf *dspf;
extern Srv fs;

static char Elocked[] = "file locked";

static void *
auxtype2obj(int *type)
{
	switch (*type) {
	case Xdspctl:
	case Xuictl:
		return (uchar*)type - offsetof(Aux, ctl);
	case Xdspdata:
		return (uchar*)type - offsetof(Aux, data);
	case Xuimeta:
		return (uchar*)type - offsetof(Aux, metadata);
	default:
		sysfatal("trying to get aux out of type %d", *type);
	}

	return nil;
}

static Aux *
newobj(char *name)
{
	File *f;
	Aux *o;
	int i;

	for (i = 0, o = nil; o == nil && i < nelem(objs); i++) {
		if (objs[i] == nil){
			o = objs[i] = calloc(1, sizeof(*o));
			break;
		}
	}
	if (o == nil)
		return nil;

	o->id = i;
	o->type = Xdsp;
	o->ctl = Xdspctl;
	o->data = Xdspdata;
	o->dsp.dsp = dspf->new();
	sprint(name, "%d", o->id);

	o->dsp.in = o->dsp.out = nil;
	if ((o->dsp.numin = dspf->num_in(o->dsp.dsp)) > 0) {
		o->dsp.in = malloc(sizeof(*o->dsp.in) * o->dsp.numin);
		o->dsp.inmax = Inmax;
		for (i = 0; i < o->dsp.numin; i++)
			o->dsp.in[i] = malloc(sizeof(**o->dsp.in) * o->dsp.inmax);
	}
	if ((o->dsp.numout = dspf->num_out(o->dsp.dsp)) > 0) {
		o->dsp.out = malloc(sizeof(*o->dsp.out) * o->dsp.numout);
		o->dsp.outmax = Outmax;
		for (i = 0; i < o->dsp.numout; i++)
			o->dsp.out[i] = malloc(sizeof(**o->dsp.out) * o->dsp.outmax);
	}

	f = createfile(fs.tree->root, name, nil, DMDIR|0775, o);
	createfile(f, "ctl", nil, 0664, &o->ctl);
	createfile(f, "data", nil, 0664, &o->data);
	dspf->init(o->dsp.dsp, rate);
	uiglue.uiInterface = f;
	dspf->build_ui(o->dsp.dsp, &uiglue);

	return o;
}

static void
freeobj(Aux *o)
{
	if (o == nil)
		return;

	objs[o->id] = nil;
	dspf->delete(o->dsp.dsp);
	free(o->dsp.in);
	free(o->dsp.out);
	free(o);
}

static void
addmeta(void *metaInterface, const char *k, const char *v)
{
	int klen, vlen;

	USED(metaInterface);

	if (strchr(k, '/') != nil) /* ignore library-specific meta */ 
		return;

	klen = strlen(k);
	vlen = strlen(v);
	meta = realloc(meta, metalen + klen + 1 + vlen + 2);
	strcpy(meta+metalen, k);
	metalen += klen;
	meta[metalen++] = '\t';
	strcpy(meta+metalen, v);
	metalen += vlen;
	meta[metalen++] = '\n';
	meta[metalen] = 0;
}

void
fsopen(Req *r)
{
	respond(r, nil);
}

void
fsread(Req *r)
{
	Aux *a, *o;
	char b[256];
	FAUSTFLOAT *p;
	int i, j, n, numframes, framesz;

	a = r->fid->file->aux;
	switch (a->type) {
	case Xctl:
		respond(r, nil);
		break;
	case Xmetadata:
		readstr(r, meta);
		respond(r, nil);
		break;
	case Xclone:
		if (r->ifcall.offset == 0) {
			if (newobj(b) != nil) {
				readstr(r, b);
			} else {
				respond(r, "no free objects");
				break;
			}
		}
		respond(r, nil);
		break;
	case Xuictl:
	case Xuimeta:
		o = auxtype2obj(&a->type);
		if (o->ui->readstr != nil)
			readstr(r, o->ui->readstr(o, o->ui, a->type, b, sizeof(b)));
		respond(r, nil);
		break;
	case Xdspdata:
		o = auxtype2obj(&a->type);
		if (r->ifcall.offset == 0) /* clear every time the offset is reset */
			dspf->clear(o->dsp.dsp);
		framesz = o->dsp.numout * sizeof(*p);
		n = r->ifcall.count;
		for (p = (FAUSTFLOAT*)r->ofcall.data; n >= framesz;) {
			numframes = n / framesz;
			if (numframes > o->dsp.outmax)
				numframes = o->dsp.outmax;
			dspf->compute(o->dsp.dsp, numframes, o->dsp.in, o->dsp.out);
			for (i = 0; i < numframes; i++) {
				for (j = 0; j < o->dsp.numout; j++)
					*p++ = o->dsp.out[j][i];
			}
			n -= numframes * framesz;
		}
		r->ofcall.count = r->ifcall.count - n;
		respond(r, nil);
		break;
	default:
		respond(r, "not implemented");
		break;
	}
}

void
fswrite(Req *r)
{
	Aux *a, *o;
	char b[256];
	int st;

	if (r->ifcall.count >= sizeof(b)) {
		respond(r, "can't fit into buffer");
		return;
	}

	memmove(b, r->ifcall.data, r->ifcall.count);
	b[r->ifcall.count] = '\0';

	a = r->fid->file->aux;
	switch (a->type) {
	case Xuictl:
		o = auxtype2obj(&a->type);
		st = o->ui->write != nil ? o->ui->write(o, o->ui, Xuictl, b) : -1;
		respond(r, st == 0 ? nil : "write failed");
		break;
	case Xdspctl: /* FIXME changing sampling rate */
		o = auxtype2obj(&a->type);
		if (strncmp(b, "clear", 5) == 0)
			dspf->clear(o->dsp.dsp);
		else if (strncmp(b, "reset", 5) == 0)
			dspf->reset_ui(o->dsp.dsp);
		else if (strncmp(b, "init", 4) == 0)
			dspf->init(o->dsp.dsp, rate);
		respond(r, nil);
		break;
	case Xmetadata: /* FIXME should be possible to add new key/value */
	default:
		respond(r, "not implemented");
		break;
	}
}

Srv fs = {
	.open = fsopen,
	.read = fsread,
	.write = fswrite,
};

static void
usage(char *prog)
{
	print("usage: %s [-s srv] [-m mtpt] [-r rate]\n", prog);
	exits("usage");
}

void
main(int argc, char **argv)
{
	char *srv, *mtpt;
	MetaGlue mg;

	srv = nil;
	mtpt = nil;
	ARGBEGIN{
	case 'D':
		chatty9p++;
		break;
	case 's':
		srv = EARGF(usage(argv[0]));
		break;
	case 'm':
		mtpt = EARGF(usage(argv[0]));
		break;
	case 'r':
		rate = atoi(EARGF(usage(argv[0])));
		break;
	default:
		usage(argv[0]);
	}ARGEND

	if (srv == nil && mtpt == nil)
		sysfatal("must specify -s or -m option");

	mg.declare = addmeta;
	dspf = class_init(rate);
	dspf->metadata(&mg);

	fs.tree = alloctree(nil, nil, DMDIR|0775, nil);
	closefile(createfile(fs.tree->root, "ctl", nil, 0666, &rootaux[Xctl]));
	closefile(createfile(fs.tree->root, "metadata", nil, 0444, &rootaux[Xmetadata]));
	closefile(createfile(fs.tree->root, "clone", nil, 0444, &rootaux[Xclone]));
	postmountsrv(&fs, srv, mtpt, MREPL);
	exits(nil);
}