Sigrid Solveig Haflínudóttirfs: move a macro up the file11bee085 years agoHistory
418 lines · 8.2 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <fcall.h>#include <thread.h>#include <9p.h>#include "common.h"#include "ui.h"#include "fs.h" #define MAX(a,b) ((a)>=(b)?(a):(b))#define MIN(a,b) ((a)<=(b)?(a):(b))#define CROSS(a,b) (a <= 0.0f && a <= b && b >= 0.0f)#define Silence 0.00003f enum {	Maxobjs = 64,}; static Aux *objs[Maxobjs]; static Aux rootaux[] = {	[Xctl] = {.type = Xctl},	[Xmetadata] = {.type = Xmetadata},	[Xclone] = {.type = Xclone},}; extern File *uif;extern State *uis; static Fs *fs; static voidshutup(State *s, Voice *v){	if (s->voice != v && v->dsp != nil) {		fs->dsp.free(v->dsp);		v->dsp = nil;	}	v->state = Vsilent;} static Voice *newvoice(State *s, Auxdsp *dsp){	Voice *v, *f;	int i; 	f = s->voice;	for (i = 0, v = s->voice; i < nelem(s->voice); i++, v++) {		if (v->dsp == nil) {			f = v;			break;		}		if (v->state == Vplaying && f->samples < v->samples)			f = v;	} 	shutup(s, f);	f->dsp = dsp; 	return f;} static Aux *newobj(char *name){	File *f;	Aux *o;	Voice *v;	int i, mode; 	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; 	sprint(name, "%d", o->id);	if ((f = createfile(fs->srv.tree->root, name, nil, DMDIR|0775, o)) == nil)		return nil;	closefile(createfile(f, "ctl", nil, 0664, &o->ctl));	mode = 0;	if (fs->dsp.read != nil)		mode |= 0444;	if (fs->dsp.write != nil)		mode |= 0222;	closefile(createfile(f, "data", nil, mode, &o->data)); 	uif = f;	o->state = uis = calloc(1, sizeof(State));	v = newvoice(o->state, fs->dsp.new(&o->numin, &o->numout, &o->rate));	v->state = Vsilent;	closefile(f); 	return o;} static voidfreeobj(Aux *o){	int i; 	if (o == nil)		return; 	if (o->type == Xdsp) {		objs[o->id] = nil;		for (i = 0; i < nelem(o->state->voice); i++) {			if (o->state->voice[i].dsp != nil)				fs->dsp.free(o->state->voice[i].dsp);		}		free(o->state);	} 	free(o);} 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 voidfsopen(Req *r){	respond(r, nil);} static intauxreaddsp(Aux *a, float *b, int total){	int i, j, n, cross, nzeromax;	Voice *v;	float *m;	State *s; 	s = a->state; 	/* make sure we have a buffer for mixing */	m = s->mixer;	if (s->mixersz < total) {		if ((m = realloc(m, total*sizeof(float))) != nil) {			s->mixer = m;			s->mixersz = total;		} else {			total = s->mixersz;		}	} 	/* silence first */	memset(b, 0, sizeof(*b)*total); 	v = s->voice;	cross = -1;	a->numvoices = 0;	nzeromax = a->rate / 5; /* 1/5th of a second, assuming it's mono */	for (i = 0, n = total; i < nelem(s->voice) && n > 0; i++, v++) {		if (v->dsp == nil)			continue; 		switch (v->state) {		case Vsilent: /* nothing to do here, ignore it */			continue;		case Vstarting: /* should start playing it */			if (s->crossvoice && i > 0 && v[-1].state != Vsilent) {				/* always start another voice at zero crossing to avoid clicks & pops */				if (cross < 0) /* no crossing yet? give up for now */					goto done;				memset(b+cross, 0, sizeof(*b)*(n - cross)); /* silence the prev voice after zero crossing */				cross++; /* leaving one as 0 */				n -= cross;				b += cross;				cross = 0;				shutup(s, &v[-1]);			}			v->state = Vplaying;			v->samples = 0;			v->nzero = 0;			/* slippery floor */		case Vplaying:			if (n < 1)				break;			if (fs->dsp.read(v->dsp, m, n) != n)				return -1;			v->samples += n; 			for (j = 0; j < n; j++) {				b[j] += m[j];				if (s->crossvoice && cross < 0 && j > 0 && CROSS(m[j-1], m[j]))					cross = j;				if (m[j] >= -Silence && m[j] <= Silence)					v->nzero++;				else					v->nzero = 0;			} 			if (v->nzero >= nzeromax) { /* been quite for a while? shut it */				cross = 0;				shutup(s, v);			} else {				a->numvoices++;			}		}	} done: 	/* relocate the voices */	for (i = j = 1; i < nelem(s->voice); i++) {		s->voice[j] = s->voice[i];		if(s->voice[j].dsp != nil)			j++;	}	memset(s->voice+j, 0, sizeof(*s->voice)*(i - j)); 	return total;} static voidauxreset(Aux *a){	int i; 	for (i = 0; i < nelem(a->state->voice); i++) {		if (a->state->voice[i].dsp != nil)			fs->dsp.reset(a->state->voice[i].dsp);	}} static voidfsread(Req *r){	Aux *a, *o;	char b[256]; 	a = r->fid->file->aux;	switch (a->type) {	case Xctl:		respond(r, nil);		break;	case Xdspctl:		o = auxtype2obj(&a->type);		sprint(b, "numin\t%d\nnumout\t%d\nrate\t%d\nnumvoices\t%d\n", o->numin, o->numout, o->rate, o->numvoices);		readstr(r, b);		respond(r, nil);		break;	case Xmetadata:		readstr(r, fs->metadata);		respond(r, nil);		break;	case Xclone:		if (r->ifcall.offset == 0) {			if (newobj(b) != nil) {				readstr(r, b);			} else {				snprint(b, sizeof(b), "no free objects: %r");				respond(r, b);				break;			}		}		respond(r, nil);		break;	case Xuictl:	case Xuimeta:		o = auxtype2obj(&a->type);		if (o->ui->readstr != nil)			readstr(r, o->ui->readstr(o->ui, a->type, b, sizeof(b)));		respond(r, nil);		break;	case Xdspdata:		o = auxtype2obj(&a->type);		r->ofcall.count = auxreaddsp(o, (float*)r->ofcall.data, r->ifcall.count/sizeof(float))*sizeof(float);		respond(r, nil);		break;	default:		respond(r, "not implemented");		break;	}} static intauxwrite(Aux *a, int type, char *data){	State *s;	Auxdsp *clone;	Voice *v;	void *so, *sc;	u8int *tmp;	int i, r, sz, nused; 	if (a->ui->writestr == nil) {		werrstr("not implemented");		return -1;	} 	s = a->state;	s->crossvoice |= a->ui->crossvoice; 	for (i = nused = 0; i < nelem(s->voice); i++) {		if (s->voice[i].state != Vsilent)			nused++;	} 	/* autovoice means every write needs to use (possibly) a new instance */	if (a->ui->autovoice && nused > 0 && fs->dsp.clone != nil && fs->dsp.state != nil) {		/* now do the impossible */		so = fs->dsp.state(s->voice[0].dsp, &sz);		tmp = malloc(sz);		memmove(tmp, so, sz); /* save the original state */		/* write to the original and check if a new voice has to be created */		if ((r = a->ui->writestr(a->ui, type, data)) < 0 || *a->ui->zone == 0.0f) {			free(tmp);			return r;		}		clone = fs->dsp.clone(s->voice[0].dsp); /* clone the original */		sc = fs->dsp.state(clone, &sz);		memmove(sc, so, sz); /* copy the changed state to the clone */		memmove(so, tmp, sz); /* revert the original state */		free(tmp);		/* now we have the original dsp intact, with a cloned dsp actually having the changed state */ 		v = newvoice(s, clone);		v->state = Vstarting; 		return r;	} 	/* in any other case, just write to the original */	s->voice[0].state = Vstarting;	return a->ui->writestr(a->ui, type, data);} static voidfswrite(Req *r){	Aux *a, *o;	char b[256]; 	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';	r->ofcall.count = r->ifcall.count; 	a = r->fid->file->aux;	switch (a->type) {	case Xuictl:		o = auxtype2obj(&a->type);		if (auxwrite(o, a->type, b) >= 0)			respond(r, nil);		else			responderror(r);		break;	case Xdspctl: /* FIXME changing sampling rate */		o = auxtype2obj(&a->type);		if (strncmp(b, "reset", 5) == 0) /* FIXME ui needs to be reset as well */			auxreset(o);		respond(r, nil);		break;	case Xmetadata: /* FIXME should be possible to add new key/value */	default:		respond(r, "not implemented");		break;	}} static voidfsdestroyfile(File *f){	Aux *a; 	if ((a = f->aux) == nil)		return;	switch (a->type) {	case Xdsp:	case Xui:		freeobj(a);		f->aux = nil;		break;	}} voidfsinit(void *fs_){	fs = fs_;	fs->srv.open = fsopen;	fs->srv.read = fsread;	fs->srv.write = fswrite;	fs->srv.tree = alloctree(nil, nil, DMDIR|0775, fsdestroyfile);	closefile(createfile(fs->srv.tree->root, "ctl", nil, 0666, &rootaux[Xctl]));	closefile(createfile(fs->srv.tree->root, "metadata", nil, 0444, &rootaux[Xmetadata]));	closefile(createfile(fs->srv.tree->root, "clone", nil, 0444, &rootaux[Xclone]));}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t