shithub: neindaw

Download patch

ref: 7c285b049475f2139c234f1ce7a760e564fbee49
parent: 0acd2208855ac7e5c1e2499614c50828a1ace0de
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Sun Dec 29 22:57:08 EST 2019

somewhat first working version

--- a/README.md
+++ b/README.md
@@ -3,7 +3,38 @@
 Tools to compile [FAUST](https://faust.grame.fr) DSP code to a 9p
 server, provide UI to control DSP, etc.
 
-WIP, nothing to look at yet
+## Testing
+
+So far only one instrument provided, which is a kick drum.
+
+```
+% ./6.out -m /n/kick
+% cd /n/kick
+
+# allocate one instance
+% cd `{cat clone}
+
+# check the A oscillator frequency
+# the order is: type key value initial min max step
+# it's different for different UI elements
+% cat K*/a/frequency/ctl
+vslider	0	100.000000	100.000000	10.000000	200.000000	5.000000
+
+# raise frequency to 160Hz
+% echo 160 > K*/a/f*/ctl
+
+# toggle the gate, we need to compute once in between for it to actually be processed
+# then listen to the sound
+% echo 0 > 'Kick Drum/control/gate/ctl' && \
+	dd -if data -of /dev/null -count 1 -quiet 1 && \
+	echo 1 > 'Kick Drum/control/gate/ctl' && \
+	audio/pcmconv -i f32c2r44100 -l 128000 < data > /dev/audio
+
+# increase the delay of A oscillator
+% echo 0.2 > 'Kick Drum/a/delay/ctl' # make it
+
+# now you can repeat the listen command to hear the difference
+```
 
 ## Description
 
--- a/arch.c
+++ b/arch.c
@@ -1,7 +1,4 @@
-/*
-Plan 9 C architecture for Faust.  This provides a file system with UI
-elements exported as files and directories.
-*/
+/* mydsp Plan 9 C architecture for Faust. */
 
 #include <u.h>
 #include <libc.h>
@@ -30,29 +27,9 @@
 	.compute = computemydsp,
 };
 
-static void
-usage(char *prog)
+void *
+class_init(int rate)
 {
-	print("usage: %s [-s SAMPLE_RATE]\n", prog);
-	exits("usage");
-}
-
-void
-main(int argc, char **argv)
-{
-	int sample_rate;
-
-	sample_rate = 44100;
-	ARGBEGIN{
-	case 's':
-		sample_rate = atoi(ARGF());
-		break;
-	default:
-		usage(argv[0]);
-	}ARGEND
-
-	classInitmydsp(sample_rate);
-	build_fs(&dspf);
-
-	exits(nil);
+	classInitmydsp(rate);
+	return &dspf;
 }
--- /dev/null
+++ b/aux.h
@@ -1,0 +1,52 @@
+enum {
+	Xctl,
+	Xmetadata,
+	Xclone,
+	Xdsp,
+	Xdspctl,
+	Xdspdata,
+	UITBox,
+	UIHBox,
+	UIVBox,
+	UIButton,
+	UICheck,
+	UIVSlider,
+	UIHSlider,
+	UINum,
+	UIHBarGraph,
+	UIVBarGraph,
+	Xuictl,
+};
+
+typedef struct Aux Aux;
+typedef struct Auxdsp Auxdsp;
+typedef struct UI UI;
+
+struct Auxdsp {
+	void *dsp;
+	FAUSTFLOAT **in, **out;
+	int numin, numout;
+	int inmax, outmax;
+};
+
+struct Aux {
+	int type;
+	int id;
+	Auxdsp dsp;
+	int ctl;
+	int data;
+	UI *ui;
+};
+
+struct UI {
+	const char *key;
+	const char *value;
+	const char *label;
+	FAUSTFLOAT *zone;
+	FAUSTFLOAT init;
+	FAUSTFLOAT min;
+	FAUSTFLOAT max;
+	FAUSTFLOAT step;
+	char *(*readstr)(Aux *a, UI *ui, int type, char *s, int sz);
+	int (*write)(Aux *a, UI *ui, int type, char *s);
+};
--- a/dspf.h
+++ b/dspf.h
@@ -11,4 +11,4 @@
 	void (*compute)(DSP *dsp, int count, FAUSTFLOAT **in, FAUSTFLOAT **out);
 }DSPf;
 
-void build_fs(void *dspf);
+void *class_init(int rate);
--- a/fs.c
+++ b/fs.c
@@ -1,18 +1,109 @@
 #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"
 
-typedef struct {
-	void *metaInterface;
-	void (*declare)(void *metaInterface, const char *key, const char *value);
-}MetaGlue;
+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);
+	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;
@@ -35,12 +126,148 @@
 }
 
 void
-build_fs(void *dspf)
+fsopen(Req *r)
 {
-	DSPf *f;
+	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:
+		o = auxtype2obj(&a->type);
+		if (o->ui->readstr != nil)
+			readstr(r, o->ui->readstr(o, o->ui, Xuictl, b, sizeof(b)));
+		respond(r, nil);
+		break;
+	case Xdspdata:
+		o = auxtype2obj(&a->type);
+		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 Xctl: /* FIXME reset, changing sampling rate, etc */
+	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;
 
-	f = dspf;
+	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;
-	f->metadata(&mg);
+	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);
 }
--- a/gen.sh
+++ b/gen.sh
@@ -2,6 +2,11 @@
 set -e
 
 for i in *.dsp; do
-	class=$(grep -o declare.*name.* kick_drum.dsp | head -1 | sed 's/^[^"]*//g;s/[^A-Za-z0-9]//g')
-	faust -lang c -a arch.c -cn $class $@ $i -o /dev/stdout | sed 's/new\([^(]*\)() /new\1(void) /' > $i.c
+	class=$(grep -o declare.*name.* $i | head -1 | sed 's/^[^"]*//g;s/[^A-Za-z0-9]//g')
+	# in C () is not the same as (void) so fix it
+	# mark "dsp", "samplingFreq", and "inputs" args as USED
+	faust -lang c -a arch.c -cn $class $@ $i -o /dev/stdout \
+		| sed 's/new\([^(]*\)() /new\1(void) /' \
+		| sed 's/'$class'[A-Z0]*\* dsp.*{/&\n\tUSED(dsp);/g' \
+		| sed 's/ \(samplingFreq\|inputs\).*{/&\n\tUSED(\1);/g' > $i.c
 done
--- /dev/null
+++ b/kick_drum.dsp.c
@@ -1,0 +1,461 @@
+/* ------------------------------------------------------------
+name: "Kick Drum"
+Code generated with Faust 2.5.23 (https://faust.grame.fr)
+Compilation options: c, -scal -ftz 0
+------------------------------------------------------------ */
+
+#ifndef  __KickDrum_H__
+#define  __KickDrum_H__
+
+/* KickDrum Plan 9 C architecture for Faust. */
+
+#include <u.h>
+#include <libc.h>
+#include "uiglue.h"
+
+#define max(x,y) (((x) > (y)) ? (x) : (y))
+#define min(x,y) (((x) < (y)) ? (x) : (y))
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif 
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <math.h>
+#include <stdlib.h>
+
+
+typedef struct {
+	
+	int iRec0[2];
+	
+} KickDrumSIG0;
+
+static KickDrumSIG0* newKickDrumSIG0(void) { return (KickDrumSIG0*)malloc(sizeof(KickDrumSIG0)); }
+static void deleteKickDrumSIG0(KickDrumSIG0* dsp) {
+	USED(dsp); free(dsp); }
+
+int getNumInputsKickDrumSIG0(KickDrumSIG0* dsp) {
+	USED(dsp);
+	return 0;
+	
+}
+int getNumOutputsKickDrumSIG0(KickDrumSIG0* dsp) {
+	USED(dsp);
+	return 1;
+	
+}
+int getInputRateKickDrumSIG0(KickDrumSIG0* dsp, int channel) {
+	USED(dsp);
+	int rate;
+	switch (channel) {
+		default: {
+			rate = -1;
+			break;
+		}
+		
+	}
+	return rate;
+	
+}
+int getOutputRateKickDrumSIG0(KickDrumSIG0* dsp, int channel) {
+	USED(dsp);
+	int rate;
+	switch (channel) {
+		case 0: {
+			rate = 0;
+			break;
+		}
+		default: {
+			rate = -1;
+			break;
+		}
+		
+	}
+	return rate;
+	
+}
+
+static void instanceInitKickDrumSIG0(KickDrumSIG0* dsp, int samplingFreq) {
+	USED(samplingFreq);
+	USED(dsp);
+	/* C99 loop */
+	{
+		int l0;
+		for (l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
+			dsp->iRec0[l0] = 0;
+			
+		}
+		
+	}
+	
+}
+
+static void fillKickDrumSIG0(KickDrumSIG0* dsp, int count, float* output) {
+	USED(dsp);
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			dsp->iRec0[0] = (dsp->iRec0[1] + 1);
+			output[i] = sinf((9.58738019e-05f * (float)(dsp->iRec0[0] + -1)));
+			dsp->iRec0[1] = dsp->iRec0[0];
+			
+		}
+		
+	}
+	
+};
+
+static float ftbl0KickDrumSIG0[65536];
+
+#ifndef FAUSTCLASS 
+#define FAUSTCLASS KickDrum
+#endif
+#ifdef __APPLE__ 
+#define exp10f __exp10f
+#define exp10 __exp10
+#endif
+
+typedef struct {
+	
+	int fSamplingFreq;
+	float fConst0;
+	float fConst1;
+	FAUSTFLOAT fVslider0;
+	FAUSTFLOAT fVslider1;
+	FAUSTFLOAT fCheckbox0;
+	FAUSTFLOAT fButton0;
+	float fVec0[2];
+	FAUSTFLOAT fVslider2;
+	FAUSTFLOAT fVslider3;
+	float fVec1[2];
+	float fRec2[2];
+	float fConst2;
+	float fRec1[2];
+	FAUSTFLOAT fVslider4;
+	float fRec3[2];
+	FAUSTFLOAT fVslider5;
+	FAUSTFLOAT fVslider6;
+	float fRec5[2];
+	float fRec4[2];
+	
+} KickDrum;
+
+KickDrum* newKickDrum(void) { 
+	KickDrum* dsp = (KickDrum*)malloc(sizeof(KickDrum));
+	return dsp;
+}
+
+void deleteKickDrum(KickDrum* dsp) {
+	USED(dsp); 
+	free(dsp);
+}
+
+void metadataKickDrum(MetaGlue* m) { 
+	m->declare(m->metaInterface, "basics.lib/name", "Faust Basic Element Library");
+	m->declare(m->metaInterface, "basics.lib/version", "0.0");
+	m->declare(m->metaInterface, "envelopes.lib/author", "GRAME");
+	m->declare(m->metaInterface, "envelopes.lib/copyright", "GRAME");
+	m->declare(m->metaInterface, "envelopes.lib/license", "LGPL with exception");
+	m->declare(m->metaInterface, "envelopes.lib/name", "Faust Envelope Library");
+	m->declare(m->metaInterface, "envelopes.lib/version", "0.0");
+	m->declare(m->metaInterface, "filename", "kick_drum");
+	m->declare(m->metaInterface, "group", "synthesis");
+	m->declare(m->metaInterface, "maths.lib/author", "GRAME");
+	m->declare(m->metaInterface, "maths.lib/copyright", "GRAME");
+	m->declare(m->metaInterface, "maths.lib/license", "LGPL with exception");
+	m->declare(m->metaInterface, "maths.lib/name", "Faust Math Library");
+	m->declare(m->metaInterface, "maths.lib/version", "2.1");
+	m->declare(m->metaInterface, "name", "Kick Drum");
+	m->declare(m->metaInterface, "oscillators.lib/name", "Faust Oscillator Library");
+	m->declare(m->metaInterface, "oscillators.lib/version", "0.0");
+}
+
+int getSampleRateKickDrum(KickDrum* dsp) {
+	USED(dsp); return dsp->fSamplingFreq; }
+
+int getNumInputsKickDrum(KickDrum* dsp) {
+	USED(dsp);
+	return 0;
+	
+}
+int getNumOutputsKickDrum(KickDrum* dsp) {
+	USED(dsp);
+	return 2;
+	
+}
+int getInputRateKickDrum(KickDrum* dsp, int channel) {
+	USED(dsp);
+	int rate;
+	switch (channel) {
+		default: {
+			rate = -1;
+			break;
+		}
+		
+	}
+	return rate;
+	
+}
+int getOutputRateKickDrum(KickDrum* dsp, int channel) {
+	USED(dsp);
+	int rate;
+	switch (channel) {
+		case 0: {
+			rate = 1;
+			break;
+		}
+		case 1: {
+			rate = 1;
+			break;
+		}
+		default: {
+			rate = -1;
+			break;
+		}
+		
+	}
+	return rate;
+	
+}
+
+void classInitKickDrum(int samplingFreq) {
+	USED(samplingFreq);
+	KickDrumSIG0* sig0 = newKickDrumSIG0();
+	instanceInitKickDrumSIG0(sig0, samplingFreq);
+	fillKickDrumSIG0(sig0, 65536, ftbl0KickDrumSIG0);
+	deleteKickDrumSIG0(sig0);
+	
+}
+
+void instanceResetUserInterfaceKickDrum(KickDrum* dsp) {
+	USED(dsp);
+	dsp->fVslider0 = (FAUSTFLOAT)100.0f;
+	dsp->fVslider1 = (FAUSTFLOAT)200.0f;
+	dsp->fCheckbox0 = (FAUSTFLOAT)0.0f;
+	dsp->fButton0 = (FAUSTFLOAT)0.0f;
+	dsp->fVslider2 = (FAUSTFLOAT)0.001f;
+	dsp->fVslider3 = (FAUSTFLOAT)0.001f;
+	dsp->fVslider4 = (FAUSTFLOAT)0.001f;
+	dsp->fVslider5 = (FAUSTFLOAT)0.001f;
+	dsp->fVslider6 = (FAUSTFLOAT)0.01f;
+	
+}
+
+void instanceClearKickDrum(KickDrum* dsp) {
+	USED(dsp);
+	/* C99 loop */
+	{
+		int l1;
+		for (l1 = 0; (l1 < 2); l1 = (l1 + 1)) {
+			dsp->fVec0[l1] = 0.0f;
+			
+		}
+		
+	}
+	/* C99 loop */
+	{
+		int l2;
+		for (l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
+			dsp->fVec1[l2] = 0.0f;
+			
+		}
+		
+	}
+	/* C99 loop */
+	{
+		int l3;
+		for (l3 = 0; (l3 < 2); l3 = (l3 + 1)) {
+			dsp->fRec2[l3] = 0.0f;
+			
+		}
+		
+	}
+	/* C99 loop */
+	{
+		int l4;
+		for (l4 = 0; (l4 < 2); l4 = (l4 + 1)) {
+			dsp->fRec1[l4] = 0.0f;
+			
+		}
+		
+	}
+	/* C99 loop */
+	{
+		int l5;
+		for (l5 = 0; (l5 < 2); l5 = (l5 + 1)) {
+			dsp->fRec3[l5] = 0.0f;
+			
+		}
+		
+	}
+	/* C99 loop */
+	{
+		int l6;
+		for (l6 = 0; (l6 < 2); l6 = (l6 + 1)) {
+			dsp->fRec5[l6] = 0.0f;
+			
+		}
+		
+	}
+	/* C99 loop */
+	{
+		int l7;
+		for (l7 = 0; (l7 < 2); l7 = (l7 + 1)) {
+			dsp->fRec4[l7] = 0.0f;
+			
+		}
+		
+	}
+	
+}
+
+void instanceConstantsKickDrum(KickDrum* dsp, int samplingFreq) {
+	USED(samplingFreq);
+	USED(dsp);
+	dsp->fSamplingFreq = samplingFreq;
+	dsp->fConst0 = min(192000.0f, max(1.0f, (float)dsp->fSamplingFreq));
+	dsp->fConst1 = (1.0f / dsp->fConst0);
+	dsp->fConst2 = (1.0f / dsp->fConst0);
+	
+}
+
+void instanceInitKickDrum(KickDrum* dsp, int samplingFreq) {
+	USED(samplingFreq);
+	USED(dsp);
+	instanceConstantsKickDrum(dsp, samplingFreq);
+	instanceResetUserInterfaceKickDrum(dsp);
+	instanceClearKickDrum(dsp);
+}
+
+void initKickDrum(KickDrum* dsp, int samplingFreq) {
+	USED(samplingFreq);
+	USED(dsp);
+	classInitKickDrum(samplingFreq);
+	instanceInitKickDrum(dsp, samplingFreq);
+}
+
+void buildUserInterfaceKickDrum(KickDrum* dsp, UIGlue* ui_interface) {
+	USED(dsp);
+	ui_interface->openVerticalBox(ui_interface->uiInterface, "Kick Drum");
+	ui_interface->openHorizontalBox(ui_interface->uiInterface, "a");
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fVslider0, "0", "");
+	ui_interface->addVerticalSlider(ui_interface->uiInterface, "frequency", &dsp->fVslider0, 100.0f, 10.0f, 200.0f, 5.0f);
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fVslider5, "1", "");
+	ui_interface->addVerticalSlider(ui_interface->uiInterface, "attack", &dsp->fVslider5, 0.00100000005f, 9.99999975e-06f, 0.200000003f, 0.00100000005f);
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fVslider6, "2", "");
+	ui_interface->addVerticalSlider(ui_interface->uiInterface, "delay", &dsp->fVslider6, 0.00999999978f, 9.99999975e-06f, 1.0f, 0.00100000005f);
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fVslider4, "3", "");
+	ui_interface->addVerticalSlider(ui_interface->uiInterface, "release", &dsp->fVslider4, 0.00100000005f, 9.99999975e-06f, 1.0f, 0.00100000005f);
+	ui_interface->closeBox(ui_interface->uiInterface);
+	ui_interface->openHorizontalBox(ui_interface->uiInterface, "b");
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fVslider2, "0", "");
+	ui_interface->addVerticalSlider(ui_interface->uiInterface, "attack", &dsp->fVslider2, 0.00100000005f, 9.99999975e-06f, 0.200000003f, 0.00100000005f);
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fVslider1, "1", "");
+	ui_interface->addVerticalSlider(ui_interface->uiInterface, "frequency", &dsp->fVslider1, 200.0f, -400.0f, 400.0f, 5.0f);
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fVslider3, "2", "");
+	ui_interface->addVerticalSlider(ui_interface->uiInterface, "release", &dsp->fVslider3, 0.00100000005f, 9.99999975e-06f, 0.200000003f, 0.00100000005f);
+	ui_interface->closeBox(ui_interface->uiInterface);
+	ui_interface->openHorizontalBox(ui_interface->uiInterface, "control");
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fButton0, "0", "");
+	ui_interface->addButton(ui_interface->uiInterface, "gate", &dsp->fButton0);
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fCheckbox0, "1", "");
+	ui_interface->addCheckButton(ui_interface->uiInterface, "b enable", &dsp->fCheckbox0);
+	ui_interface->closeBox(ui_interface->uiInterface);
+	ui_interface->closeBox(ui_interface->uiInterface);
+	
+}
+
+void computeKickDrum(KickDrum* dsp, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+	USED(inputs);
+	USED(dsp);
+	FAUSTFLOAT* output0 = outputs[0];
+	FAUSTFLOAT* output1 = outputs[1];
+	float fSlow0 = (float)dsp->fVslider0;
+	float fSlow1 = ((float)dsp->fVslider1 * (float)dsp->fCheckbox0);
+	float fSlow2 = (float)dsp->fButton0;
+	float fSlow3 = (float)dsp->fVslider2;
+	float fSlow4 = (fSlow3 + (float)dsp->fVslider3);
+	float fSlow5 = (dsp->fConst0 * fSlow4);
+	float fSlow6 = (dsp->fConst0 * fSlow3);
+	float fSlow7 = (1.0f / (0.0f - (dsp->fConst0 * (fSlow3 - fSlow4))));
+	float fSlow8 = (dsp->fConst2 / fSlow3);
+	int iSlow9 = (fSlow2 > 0.0f);
+	int iSlow10 = (iSlow9 > 0);
+	float fSlow11 = (float)dsp->fVslider4;
+	float fSlow12 = (dsp->fConst0 * fSlow11);
+	int iSlow13 = ((fSlow2 == 0.0f) > 0);
+	float fSlow14 = (float)dsp->fVslider5;
+	float fSlow15 = (fSlow14 + (float)dsp->fVslider6);
+	float fSlow16 = (dsp->fConst0 * fSlow15);
+	float fSlow17 = (dsp->fConst0 * fSlow14);
+	float fSlow18 = (9.99999997e-07f * fSlow2);
+	float fSlow19 = ((fSlow18 + -1.0f) / (0.0f - (dsp->fConst0 * (fSlow14 - fSlow15))));
+	float fSlow20 = (dsp->fConst2 / fSlow14);
+	float fSlow21 = (dsp->fConst2 / fSlow11);
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			dsp->fVec0[0] = fSlow2;
+			dsp->fVec1[0] = fSlow4;
+			dsp->fRec2[0] = ((((fSlow2 - dsp->fVec0[1]) > 0.0f) > 0)?0.0f:min(fSlow5, ((dsp->fRec2[1] + (dsp->fConst0 * (fSlow4 - dsp->fVec1[1]))) + 1.0f)));
+			int iTemp0 = (dsp->fRec2[0] < fSlow6);
+			float fTemp1 = (dsp->fRec1[1] + (dsp->fConst1 * (fSlow0 + (fSlow1 * (iTemp0?((dsp->fRec2[0] < 0.0f)?0.0f:(iTemp0?(fSlow8 * dsp->fRec2[0]):1.0f)):((dsp->fRec2[0] < fSlow5)?((fSlow7 * (0.0f - (dsp->fRec2[0] - fSlow6))) + 1.0f):0.0f))))));
+			dsp->fRec1[0] = (fTemp1 - floorf(fTemp1));
+			dsp->fRec3[0] = (iSlow10?0.0f:min(fSlow12, (dsp->fRec3[1] + 1.0f)));
+			dsp->fRec5[0] = (iSlow13?0.0f:min(fSlow16, (dsp->fRec5[1] + 1.0f)));
+			int iTemp2 = (dsp->fRec5[0] < fSlow17);
+			dsp->fRec4[0] = (iSlow9?((float)iSlow9 * (iTemp2?((dsp->fRec5[0] < 0.0f)?0.0f:(iTemp2?(fSlow20 * dsp->fRec5[0]):1.0f)):((dsp->fRec5[0] < fSlow16)?((fSlow19 * (dsp->fRec5[0] - fSlow17)) + 1.0f):fSlow18))):dsp->fRec4[1]);
+			float fTemp3 = (ftbl0KickDrumSIG0[(int)(65536.0f * dsp->fRec1[0])] * ((dsp->fRec3[0] < 0.0f)?dsp->fRec4[0]:((dsp->fRec3[0] < fSlow12)?(dsp->fRec4[0] + (fSlow21 * (dsp->fRec3[0] * (0.0f - dsp->fRec4[0])))):0.0f)));
+			output0[i] = (FAUSTFLOAT)fTemp3;
+			output1[i] = (FAUSTFLOAT)fTemp3;
+			dsp->fVec0[1] = dsp->fVec0[0];
+			dsp->fVec1[1] = dsp->fVec1[0];
+			dsp->fRec2[1] = dsp->fRec2[0];
+			dsp->fRec1[1] = dsp->fRec1[0];
+			dsp->fRec3[1] = dsp->fRec3[0];
+			dsp->fRec5[1] = dsp->fRec5[0];
+			dsp->fRec4[1] = dsp->fRec4[0];
+			
+		}
+		
+	}
+	
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#define DSP KickDrum
+
+#include "dspf.h"
+
+static DSPf dspf = {
+	.new = newKickDrum,
+	.init = instanceInitKickDrum,
+	.delete = deleteKickDrum,
+	.metadata = metadataKickDrum,
+	.num_in = getNumInputsKickDrum,
+	.num_out = getNumOutputsKickDrum,
+	.clear = instanceClearKickDrum,
+	.reset_ui = instanceResetUserInterfaceKickDrum,
+	.build_ui = buildUserInterfaceKickDrum,
+	.compute = computeKickDrum,
+};
+
+void *
+class_init(int rate)
+{
+	classInitKickDrum(rate);
+	return &dspf;
+}
+
+#endif
--- a/mkfile
+++ b/mkfile
@@ -5,6 +5,7 @@
 OFILES=\
 	fs.$O\
 	kick_drum.dsp.$O\
+	uiglue.$O\
 
 default:V:	all
 
--- /dev/null
+++ b/uiglue.c
@@ -1,0 +1,225 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "uiglue.h"
+#include "aux.h"
+
+static FAUSTFLOAT *declzone;
+static const char *declkey;
+static const char *declvalue;
+
+static char *
+ui_readstr(Aux *a, UI *ui, int type, char *s, int sz)
+{
+	if (type != Xuictl)
+		sysfatal("unknown ui file");
+
+	switch (a->type) {
+	case UITBox: snprint(s, sz, "tbox\n"); return s;
+	case UIHBox: snprint(s, sz, "hbox\n"); return s;
+	case UIVBox: snprint(s, sz, "vbox\n"); return s;
+	case UIButton: snprint(s, sz, "button\t%s\t%d\n", ui->key, !!*ui->zone); return s;
+	case UICheck: snprint(s, sz, "check\t%s\t%d\n", ui->key, !!*ui->zone); return s;
+	case UIVSlider: snprint(s, sz, "vslider\t%s\t%f\t%f\t%f\t%f\t%f\n", ui->key, *ui->zone, ui->init, ui->min, ui->max, ui->step); return s;
+	case UIHSlider: snprint(s, sz, "hslider\t%s\t%f\t%f\t%f\t%f\t%f\n", ui->key, *ui->zone, ui->init, ui->min, ui->max, ui->step); return s;
+	case UINum: snprint(s, sz, "num\t%s\t%f\t%f\t%f\t%f\t%f\n", ui->key, *ui->zone, ui->init, ui->min, ui->max, ui->step); return s;
+	case UIHBarGraph: snprint(s, sz, "hbargraph\t%s\t%f\t%f\t%f\n", ui->key, *ui->zone, ui->min, ui->max); return s;
+	case UIVBarGraph: snprint(s, sz, "vbargraph\t%s\t%f\t%f\t%f\n", ui->key, *ui->zone, ui->min, ui->max); return s;
+	}
+	sysfatal("unknown ui type %d", a->type);
+	return nil;
+}
+
+static int
+ui_write(Aux *a, UI *ui, int type, char *s)
+{
+	float v;
+
+	if (type != Xuictl)
+		sysfatal("unknown ui file");
+
+	/* FIXME optional argument should specify at which frame to apply the change */
+
+	v = 0.0f;
+	if (strncmp(s, "reset", 5) == 0) { /* FIXME reset for a box should reset ALL controls inside it */
+		v = ui->init;
+	} else if (strncmp(s, "add", 3) == 0) {
+		if (ui->zone != nil)
+			v = *ui->zone + atof(s+3);
+	} else if (strncmp(s, "sub", 3) == 0) {
+		if (ui->zone != nil)
+			v = *ui->zone - atof(s+3);
+	} else {
+		v = atof(s);
+	}
+
+	if (ui->zone != nil) {
+		if (a->type == UIButton || a->type == UICheck)
+			v = !!v;
+		else if (*ui->zone < ui->min)
+			v = ui->min;
+		else if (*ui->zone > ui->max)
+			v = ui->max;
+		*ui->zone = v;
+	}
+
+	return 0;
+}
+
+static UI *
+newui(File *f, const char *label, int type)
+{
+	Aux *a;
+
+	a = calloc(1, sizeof(*a)+sizeof(UI));
+	a->ui = (UI*)(a+1);
+	a->ctl = Xuictl;
+	a->type = type;
+	a->ui->label = label;
+	a->ui->readstr = ui_readstr;
+	a->ui->write = ui_write;
+	if ((uiglue.uiInterface = createfile(f, label, nil, DMDIR|0775, a)) == nil)
+		sysfatal("failed to create ui: %r");
+	if (createfile(uiglue.uiInterface, "ctl", nil, 0664, &a->ctl) == nil)
+		sysfatal("failed to create ui ctl: %r");
+
+	return a->ui;
+}
+
+static void
+ui_tbox(void *f, const char *label)
+{
+	newui(f, label, UITBox);
+}
+
+static void
+ui_hbox(void *f, const char *label)
+{
+	newui(f, label, UIHBox);
+}
+
+static void
+ui_vbox(void *f, const char *label)
+{
+	newui(f, label, UIVBox);
+}
+
+static void
+ui_close(void *file)
+{
+	File *f;
+
+	f = file;
+	uiglue.uiInterface = f->parent;
+}
+
+static UI *
+ui_define(File *f, int type, const char *label, FAUSTFLOAT *zone)
+{
+	UI *ui;
+
+	if (zone != declzone)
+		sysfatal("zone mismatch");
+	ui = newui(f, label, type);
+	ui->zone = declzone;
+	ui->key = declkey;
+	ui->value = declvalue;
+	uiglue.uiInterface = f;
+
+	return ui;
+}
+
+static void
+ui_button(void *f, const char *label, FAUSTFLOAT *zone)
+{
+	ui_define(f, UIButton, label, zone);
+}
+
+static void
+ui_check(void *f, const char *label, FAUSTFLOAT *zone)
+{
+	ui_define(f, UICheck, label, zone);
+}
+
+static void
+ui_vslider(void *f, const char *label, FAUSTFLOAT *zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+	UI *ui;
+
+	ui = ui_define(f, UIVSlider, label, zone);
+	ui->init = init;
+	ui->min = min;
+	ui->max = max;
+	ui->step = step;
+}
+
+static void
+ui_hslider(void *f, const char *label, FAUSTFLOAT *zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+	UI *ui;
+
+	ui = ui_define(f, UIHSlider, label, zone);
+	ui->init = init;
+	ui->min = min;
+	ui->max = max;
+	ui->step = step;
+}
+
+static void
+ui_num(void *f, const char *label, FAUSTFLOAT *zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+	UI *ui;
+
+	ui = ui_define(f, UINum, label, zone);
+	ui->init = init;
+	ui->min = min;
+	ui->max = max;
+	ui->step = step;
+}
+
+static void
+ui_hbargraph(void *f, const char *label, FAUSTFLOAT *zone, FAUSTFLOAT min, FAUSTFLOAT max)
+{
+	UI *ui;
+
+	ui = ui_define(f, UIHBarGraph, label, zone);
+	ui->min = min;
+	ui->max = max;
+}
+
+static void
+ui_vbargraph(void *f, const char *label, FAUSTFLOAT *zone, FAUSTFLOAT min, FAUSTFLOAT max)
+{
+	UI *ui;
+
+	ui = ui_define(f, UIVBarGraph, label, zone);
+	ui->min = min;
+	ui->max = max;
+}
+
+static void
+ui_declare(void *f, FAUSTFLOAT *zone, const char *key, const char *value)
+{
+	USED(f);
+
+	declzone = zone;
+	declkey = key;
+	declvalue = value;
+}
+
+UIGlue uiglue = {
+	.openTabBox = ui_tbox,
+	.openHorizontalBox = ui_hbox,
+	.openVerticalBox = ui_vbox,
+	.closeBox = ui_close,
+	.addButton = ui_button,
+	.addCheckButton = ui_check,
+	.addVerticalSlider = ui_vslider,
+	.addHorizontalSlider = ui_hslider,
+	.addNumEntry = ui_num,
+	.addHorizontalBargraph = ui_hbargraph,
+	.addVerticalBargraph = ui_vbargraph,
+	.declare = ui_declare,
+};
--- a/uiglue.h
+++ b/uiglue.h
@@ -2,10 +2,13 @@
 #define FAUSTFLOAT float
 #endif
 
-typedef struct {
+typedef struct UIGlue UIGlue;
+typedef struct MetaGlue MetaGlue;
+
+struct UIGlue {
 	void *uiInterface;
 	void (*openTabBox)(void *uiInterface, const char *label);
-	void (*openHorizontalBox) (void *uiInterface, const char *label);
+	void (*openHorizontalBox)(void *uiInterface, const char *label);
 	void (*openVerticalBox)(void *uiInterface, const char *label);
 	void (*closeBox)(void *uiInterface);
 	void (*addButton)(void *uiInterface, const char *label, FAUSTFLOAT *zone);
@@ -16,9 +19,11 @@
 	void (*addHorizontalBargraph)(void *uiInterface, const char *label, FAUSTFLOAT *zone, FAUSTFLOAT min, FAUSTFLOAT max);
 	void (*addVerticalBargraph)(void *uiInterface, const char *label, FAUSTFLOAT *zone, FAUSTFLOAT min, FAUSTFLOAT max);
 	void (*declare)(void *uiInterface, FAUSTFLOAT *zone, const char *key, const char *value);
-}UIGlue;
+};
 
-typedef struct {
+struct MetaGlue {
 	void *metaInterface;
 	void (*declare)(void *metaInterface, const char *key, const char *value);
-}MetaGlue;
+};
+
+extern UIGlue uiglue;