ref: 053d3b16498c601e2af3023f2011e9fa6bf84843
dir: /fs.c/
#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 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 void shutup(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 void freeobj(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 void fsopen(Req *r) { respond(r, nil); } #define CROSS(a,b) (a <= 0.0f && a <= b && b >= 0.0f) static int auxreaddsp(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 void auxreset(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 void fsread(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 int auxwrite(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 void fswrite(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 void fsdestroyfile(File *f) { Aux *a; if ((a = f->aux) == nil) return; switch (a->type) { case Xdsp: case Xui: freeobj(a); f->aux = nil; break; } } void fsinit(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])); }