ref: 663bc67e12efaccd4109068b05f8e6d671b7bd23
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))
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;
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;
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;
/* 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;
}
}
}
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\trate\t%d\n", o->numin, o->numout, o->rate);
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]));
}