shithub: neindaw

Download patch

ref: 1ef59f6dbd62ea361c9416e878a6c8d0a8f8b93a
parent: 5cf2a66d5a71fad0128b2f9d0b088cea743c0d60
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Sun Mar 22 20:58:35 EDT 2020

add automatic voice management system on the fs level, for now it's only enabled for the kick drum

--- a/dsp/arch.c
+++ b/dsp/arch.c
@@ -14,6 +14,11 @@
 	return memmove(newmydsp(), dsp, sizeof(*dsp));
 }
 
+static void *statemydsp(mydsp *dsp, int *sz) {
+	*sz = sizeof(*dsp);
+	return dsp;
+}
+
 #define DSP mydsp
 
 #include "dspf.h"
@@ -21,6 +26,7 @@
 static DSPf dspf = {
 	.new = newmydsp,
 	.clone = clonemydsp,
+	.state = statemydsp,
 	.init = instanceInitmydsp,
 	.delete = deletemydsp,
 	.metadata = metadatamydsp,
--- a/dsp/dspf.h
+++ b/dsp/dspf.h
@@ -1,6 +1,7 @@
 typedef struct {
 	DSP *(*new)(void);
 	DSP *(*clone)(DSP *dsp);
+	void *(*state)(DSP *dsp, int *sz);
 	void (*init)(DSP *dsp, int sample_rate);
 	void (*delete)(DSP *dsp);
 	void (*metadata)(MetaGlue *glue);
--- a/dsp/kick_drum.c
+++ b/dsp/kick_drum.c
@@ -107,8 +107,6 @@
 
 typedef struct {
 	FAUSTFLOAT fHslider0;
-	FAUSTFLOAT fButton0;
-	float fVec0[2];
 	int fSampleRate;
 	float fConst0;
 	float fConst1;
@@ -116,6 +114,8 @@
 	FAUSTFLOAT fCheckbox0;
 	FAUSTFLOAT fHslider2;
 	FAUSTFLOAT fHslider3;
+	FAUSTFLOAT fButton0;
+	float fVec0[2];
 	int iRec2[2];
 	FAUSTFLOAT fHslider4;
 	float fRec1[2];
@@ -207,11 +207,11 @@
 void instanceResetUserInterfaceKickDrum(KickDrum* dsp) {
 	USED(dsp);
 	dsp->fHslider0 = (FAUSTFLOAT)1.0f;
-	dsp->fButton0 = (FAUSTFLOAT)0.0f;
 	dsp->fHslider1 = (FAUSTFLOAT)100.0f;
 	dsp->fCheckbox0 = (FAUSTFLOAT)0.0f;
 	dsp->fHslider2 = (FAUSTFLOAT)-5.0f;
 	dsp->fHslider3 = (FAUSTFLOAT)0.001f;
+	dsp->fButton0 = (FAUSTFLOAT)0.0f;
 	dsp->fHslider4 = (FAUSTFLOAT)0.001f;
 	dsp->fHslider5 = (FAUSTFLOAT)0.001f;
 	dsp->fHslider6 = (FAUSTFLOAT)0.001f;
@@ -282,7 +282,7 @@
 	ui_interface->openVerticalBox(ui_interface->uiInterface, "B");
 	ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider2, "0", "");
 	ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider2, "unit", "Hz");
-	ui_interface->addHorizontalSlider(ui_interface->uiInterface, "Frequency", &dsp->fHslider2, -5.0f, -5.0f, 200.0f, 5.0f);
+	ui_interface->addHorizontalSlider(ui_interface->uiInterface, "Frequency", &dsp->fHslider2, -5.0f, -200.0f, 200.0f, 1.0f);
 	ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider3, "1", "");
 	ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider3, "unit", "s");
 	ui_interface->addHorizontalSlider(ui_interface->uiInterface, "Attack", &dsp->fHslider3, 0.00100000005f, 9.99999975e-06f, 0.200000003f, 0.00100000005f);
@@ -297,6 +297,7 @@
 	ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider0, "style", "knob");
 	ui_interface->addHorizontalSlider(ui_interface->uiInterface, "Gain", &dsp->fHslider0, 1.0f, 0.0f, 1.0f, 0.00999999978f);
 	ui_interface->declare(ui_interface->uiInterface, &dsp->fButton0, "1", "");
+	ui_interface->declare(ui_interface->uiInterface, &dsp->fButton0, "autovoice", "");
 	ui_interface->addButton(ui_interface->uiInterface, "Gate", &dsp->fButton0);
 	ui_interface->closeBox(ui_interface->uiInterface);
 	ui_interface->closeBox(ui_interface->uiInterface);
@@ -307,11 +308,11 @@
 	USED(dsp);
 	FAUSTFLOAT* output0 = outputs[0];
 	float fSlow0 = (float)dsp->fHslider0;
-	float fSlow1 = (float)dsp->fButton0;
-	float fSlow2 = (float)dsp->fHslider1;
-	float fSlow3 = ((float)dsp->fCheckbox0 * (float)dsp->fHslider2);
-	float fSlow4 = max(1.0f, (dsp->fConst0 * (float)dsp->fHslider3));
-	float fSlow5 = (1.0f / fSlow4);
+	float fSlow1 = (float)dsp->fHslider1;
+	float fSlow2 = ((float)dsp->fCheckbox0 * (float)dsp->fHslider2);
+	float fSlow3 = max(1.0f, (dsp->fConst0 * (float)dsp->fHslider3));
+	float fSlow4 = (1.0f / fSlow3);
+	float fSlow5 = (float)dsp->fButton0;
 	float fSlow6 = (1.0f / max(1.0f, (dsp->fConst0 * (float)dsp->fHslider4)));
 	float fSlow7 = max(1.0f, (dsp->fConst0 * (float)dsp->fHslider5));
 	float fSlow8 = (1.0f / fSlow7);
@@ -320,12 +321,13 @@
 	{
 		int i;
 		for (i = 0; (i < count); i = (i + 1)) {
-			dsp->fVec0[0] = fSlow1;
-			int iTemp0 = (fSlow1 > dsp->fVec0[1]);
-			dsp->iRec2[0] = (iTemp0 + ((fSlow1 <= dsp->fVec0[1]) * (dsp->iRec2[1] + (dsp->iRec2[1] > 0))));
+			dsp->fVec0[0] = fSlow5;
+			int iTemp0 = (fSlow5 > dsp->fVec0[1]);
+			dsp->iRec2[0] = (iTemp0 + ((fSlow5 <= dsp->fVec0[1]) * (dsp->iRec2[1] + (dsp->iRec2[1] > 0))));
 			float fTemp1 = (float)dsp->iRec2[0];
-			float fTemp2 = ((dsp->fRec1[1] * (1.0f - (float)(iTemp0 > 0))) + (dsp->fConst1 * (fSlow2 + (fSlow3 * max(0.0f, min((fSlow5 * fTemp1), (1.0f - (fSlow6 * (fTemp1 - fSlow4)))))))));
-			dsp->fRec1[0] = (fTemp2 - floorf(fTemp2));
+			float fTemp2 = (dsp->fConst1 * (fSlow1 + (fSlow2 * max(0.0f, min((fSlow4 * fTemp1), (1.0f - (fSlow6 * (fTemp1 - fSlow3))))))));
+			float fTemp3 = (dsp->fRec1[1] * ((float)(iTemp0 > 0) + -1.0f));
+			dsp->fRec1[0] = (fTemp2 - (fTemp3 + floorf((fTemp2 - fTemp3))));
 			output0[i] = (FAUSTFLOAT)(fSlow0 * (ftbl0KickDrumSIG0[(int)(65536.0f * dsp->fRec1[0])] * max(0.0f, min((fSlow8 * fTemp1), ((fSlow9 * (fSlow7 - fTemp1)) + 1.0f)))));
 			dsp->fVec0[1] = dsp->fVec0[0];
 			dsp->iRec2[1] = dsp->iRec2[0];
@@ -342,6 +344,11 @@
 	return memmove(newKickDrum(), dsp, sizeof(*dsp));
 }
 
+static void *stateKickDrum(KickDrum *dsp, int *sz) {
+	*sz = sizeof(*dsp);
+	return dsp;
+}
+
 #define DSP KickDrum
 
 #include "dspf.h"
@@ -349,6 +356,7 @@
 static DSPf dspf = {
 	.new = newKickDrum,
 	.clone = cloneKickDrum,
+	.state = stateKickDrum,
 	.init = instanceInitKickDrum,
 	.delete = deleteKickDrum,
 	.metadata = metadataKickDrum,
--- a/dsp/kick_drum.dsp
+++ b/dsp/kick_drum.dsp
@@ -8,9 +8,9 @@
 aR = hslider("v:A/[3]Release[unit:s]", 0.001, 0.00001, 1.0, 0.001);
 bA = hslider("v:B/[1]Attack[unit:s]", 0.001, 0.00001, 0.2, 0.001);
 bR = hslider("v:B/[2]Release[unit:s]", 0.001, 0.00001, 1, 0.001);
-bFreq = checkbox("v:B/[3]Enable") * hslider("v:B/[0]Frequency[unit:Hz]", -5, -5, 200, 5);
+bFreq = checkbox("v:B/[3]Enable") * hslider("v:B/[0]Frequency[unit:Hz]", -5, -200, 200, 1);
 
 gain = hslider("v:Control/[0]Gain[style:knob]", 1, 0, 1, 0.01);
-gate = button("v:Control/[1]Gate");
+gate = button("v:Control/[1]Gate[autovoice]");
 
 process = os.hs_oscsin(aFreq + bFreq*en.ar(bA, bR, gate), gate : ba.impulsify) * gain * en.ar(aA, aR, gate);
--- a/dsp/main.c
+++ b/dsp/main.c
@@ -10,6 +10,7 @@
 #include "fs.h"
 #include "ui.h"
 
+/* FIXME figure out how to get the buffering right */
 enum {
 	Inmax = 2048, /* float = 8192 bytes */
 	Outmax = 2048, /* float = 8192 bytes */
@@ -26,7 +27,7 @@
 static DSPf *dspf;
 
 static Auxdsp *
-dspnew(void)
+dspwrap(void *p)
 {
 	Auxdsp *dsp;
 	int i;
@@ -34,8 +35,7 @@
 	if ((dsp = calloc(1, sizeof(*dsp))) == nil)
 		return nil;
 
-	dsp->dsp = dspf->new();
-
+	dsp->dsp = p;
 	dsp->in = dsp->out = nil;
 	if ((dsp->numin = dspf->num_in(dsp->dsp)) > 0) {
 		dsp->in = malloc(sizeof(*dsp->in) * dsp->numin);
@@ -50,6 +50,15 @@
 			dsp->out[i] = malloc(sizeof(**dsp->out) * dsp->outmax);
 	}
 
+	return dsp;
+}
+
+static Auxdsp *
+dspnew(void)
+{
+	Auxdsp *dsp;
+
+	dsp = dspwrap(dspf->new());
 	dspf->init(dsp->dsp, rate);
 	dspf->build_ui(dsp->dsp, &uiglue);
 
@@ -56,6 +65,29 @@
 	return dsp;
 }
 
+static Auxdsp *
+dspclone(Auxdsp *dsp)
+{
+	Auxdsp *clone;
+	int i;
+
+	clone = dspwrap(dspf->clone(dsp->dsp));
+	if (clone->in != nil) {
+		/* copy input data */
+		/* FIXME need to set the valid data size when the processing is in */
+		for (i = 0; i < dsp->numin; i++)
+			memmove(clone->in[i], dsp->in[i], sizeof(**dsp->in)*dsp->inmax);
+	}
+
+	return clone;
+}
+
+static void *
+dspstate(Auxdsp *dsp, int *sz)
+{
+	return dspf->state(dsp->dsp, sz);
+}
+
 static void
 dspfree(Auxdsp *dsp)
 {
@@ -102,6 +134,8 @@
 static Fs fs = {
 	.dsp = {
 		.new = dspnew,
+		.clone = dspclone,
+		.state = dspstate,
 		.free = dspfree,
 		.reset = dspreset,
 		.read = dspread,
--- a/fs.c
+++ b/fs.c
@@ -7,6 +7,9 @@
 #include "ui.h"
 #include "fs.h"
 
+#define MAX(a,b) ((a)>=(b)?(a):(b))
+#define MIN(a,b) ((a)<=(b)?(a):(b))
+
 enum {
 	Maxobjs = 32,
 };
@@ -20,6 +23,7 @@
 };
 
 extern File *uif;
+extern State *uis;
 
 static Fs *fs;
 
@@ -56,7 +60,10 @@
 	closefile(createfile(f, "data", nil, mode, &o->data));
 
 	uif = f;
-	o->dsp = fs->dsp.new();
+	o->state = uis = calloc(1, sizeof(State));
+	o->state->voice = calloc(1, sizeof(Auxdsp*));
+	o->state->voice[o->state->nvoice++] = fs->dsp.new();
+	o->state->silent = 1;
 	closefile(f);
 
 	return o;
@@ -65,12 +72,17 @@
 static void
 freeobj(Aux *o)
 {
+	int i;
+
 	if (o == nil)
 		return;
 
 	if (o->type == Xdsp) {
 		objs[o->id] = nil;
-		fs->dsp.free(o->dsp);
+		for (i = 0; i < o->state->nvoice; i++)
+			fs->dsp.free(o->state->voice[i]);
+		free(o->state->voice);
+		free(o->state);
 	}
 
 	free(o);
@@ -100,7 +112,68 @@
 	respond(r, nil);
 }
 
+static int
+auxreaddsp(Aux *a, float *b, int n)
+{
+	State *s;
+	float *m, level;
+	int i, j, x;
+
+	s = a->state;
+	n = fs->dsp.read(s->voice[0], b, n);
+	for (i = 0; i < n && b[i] == 0.0f; i++);
+	s->silent = i != n;
+
+	/* simple case: error, no data, or just one voice */
+	if (n < 1 || s->nvoice < 2)
+		return n;
+
+	/* otherwise need to mix all the voices together */
+	m = s->mixer;
+	if (s->mixersz < n) {
+		s->mixersz = n;
+		if ((m = realloc(m, n*sizeof(float))) == nil) {
+			fprint(2, "no memory for mixer, giving up\n");
+			return n;
+		}
+		s->mixer = m;
+	}
+
+	for (i = 1; i < s->nvoice; i++) {
+		if ((x = fs->dsp.read(s->voice[i], m, n)) != n) /* well that's weird... why wouldn't it work? */
+			fprint(2, "voice %d returned %d samples instead of %d\n", i, x, n);
+
+		level = 0.0f;
+		for (x--; x >= 0; x--) { /* FIXME this could be faster */
+			level = MAX(level, m[x]);
+			b[x] = b[x]+m[x] - b[x]*m[x];
+		}
+		if (level <= 0.0f) { /* this one is silent, delete it */
+			fs->dsp.free(s->voice[i]);
+			s->voice[i] = nil;
+		}
+	}
+
+	/* relocate the voices */
+	for (i = j = 1; i < s->nvoice; i++) {
+		if (s->voice[i] != nil)
+			s->voice[j++] = s->voice[i];
+	}
+	s->nvoice = j;
+
+	return n;
+}
+
 static void
+auxreset(Aux *a)
+{
+	int i;
+
+	for (i = 0; i < a->state->nvoice; i++)
+		fs->dsp.reset(a->state->voice[i]);
+}
+
+static void
 fsread(Req *r)
 {
 	Aux *a, *o;
@@ -136,7 +209,7 @@
 		break;
 	case Xdspdata:
 		o = auxtype2obj(&a->type);
-		r->ofcall.count = fs->dsp.read(o->dsp, (float*)r->ofcall.data, r->ifcall.count/sizeof(float))*sizeof(float);
+		r->ofcall.count = auxreaddsp(o, (float*)r->ofcall.data, r->ifcall.count/sizeof(float))*sizeof(float);
 		respond(r, nil);
 		break;
 	default:
@@ -145,6 +218,48 @@
 	}
 }
 
+static int
+auxwrite(Aux *a, int type, char *data)
+{
+	State *s;
+	Auxdsp *clone;
+	void *so, *sc;
+	u8int *tmp;
+	int r, sz;
+
+	if (a->ui->writestr == nil) {
+		werrstr("not implemented");
+		return -1;
+	}
+
+	s = a->state;
+	/* autovoice means every write needs to use (possibly) a new instance */
+	if (a->ui->autovoice && !s->silent && fs->dsp.clone != nil && fs->dsp.state != nil) {
+		/* now do the impossible */
+		so = fs->dsp.state(s->voice[0], &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]); /* 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 */
+
+		s->voice = realloc(s->voice, (s->nvoice+1)*sizeof(*s->voice));
+		s->voice[s->nvoice++] = clone;
+		return r;
+	}
+
+	/* in any other case, just write to the original */
+	return a->ui->writestr(a->ui, type, data);
+}
+
 static void
 fswrite(Req *r)
 {
@@ -164,9 +279,7 @@
 	switch (a->type) {
 	case Xuictl:
 		o = auxtype2obj(&a->type);
-		if (o->ui->writestr == nil)
-			respond(r, "not implemented");
-		else if (o->ui->writestr(o->ui, a->type, b) >= 0)
+		if (auxwrite(o, a->type, b) >= 0)
 			respond(r, nil);
 		else
 			responderror(r);
@@ -174,7 +287,7 @@
 	case Xdspctl: /* FIXME changing sampling rate */
 		o = auxtype2obj(&a->type);
 		if (strncmp(b, "reset", 5) == 0) /* FIXME ui needs to be reset as well */
-			fs->dsp.reset(o->dsp);
+			auxreset(o);
 		respond(r, nil);
 		break;
 	case Xmetadata: /* FIXME should be possible to add new key/value */
--- a/fs.h
+++ b/fs.h
@@ -1,6 +1,7 @@
 typedef struct Aux Aux;
 typedef struct Auxdsp Auxdsp;
 typedef struct Fs Fs;
+typedef struct State State;
 
 typedef enum {
 	Xclone,
@@ -25,8 +26,8 @@
 	int data;
 	int metadata;
 
-	Auxdsp *dsp;
 	struct UI *ui;
+	State *state;
 };
 
 struct Fs {
@@ -38,6 +39,11 @@
 		void (*free)(Auxdsp *dsp);
 		void (*reset)(Auxdsp *dsp);
 
+		/* optional, atm this one is needed only for autovoicing to work */
+		Auxdsp *(*clone)(Auxdsp *dsp);
+		/* optional, for autovoicing. this is ugly */
+		void *(*state)(Auxdsp *dsp, int *sz);
+
 		/* optional, n is always modulo number of channels */
 		int (*read)(Auxdsp *dsp, float *b, int n);
 
@@ -44,6 +50,16 @@
 		/* optional, n is always modulo number of channels */
 		int (*write)(Auxdsp *dsp, float *b, int n);
 	}dsp;
+};
+
+struct State {
+	float *mixer;
+	int mixersz;
+
+	Auxdsp **voice;
+	int nvoice;
+
+	int silent; /* that's about the first voice only */
 };
 
 void fsinit(void *fs);
--- a/ui.c
+++ b/ui.c
@@ -12,9 +12,11 @@
 	float *zone;
 	Meta *meta;
 	int nummeta;
+	int autovoice;
 }decl;
 
 File *uif;
+State *uis;
 
 char *
 ui_readstr(UI *ui, int auxtype, char *s, int sz)
@@ -125,7 +127,9 @@
 	memmove(a->ui->meta, decl.meta, sizeof(Meta)*decl.nummeta);
 	a->ctl = Xuictl;
 	a->metadata = Xuimeta;
+	a->state = uis;
 	a->ui->zone = decl.zone;
+	a->ui->autovoice = decl.autovoice;
 	a->ui->label = label;
 	a->ui->readstr = ui_readstr;
 	a->ui->writestr = ui_writestr;
@@ -143,6 +147,7 @@
 	decl.zone = nil;
 	decl.meta = nil;
 	decl.nummeta = 0;
+	decl.autovoice = 0;
 
 	if (type == UITGroup || type == UIHGroup || type == UIVGroup)
 		uif = d;
@@ -286,4 +291,6 @@
 	decl.meta[decl.nummeta].k = key;
 	decl.meta[decl.nummeta].v = value;
 	decl.nummeta++;
+	if (strcmp(key, "autovoice") == 0)
+		decl.autovoice = 1;
 }
--- a/ui.h
+++ b/ui.h
@@ -18,6 +18,8 @@
 	Meta *meta;
 	int nummeta;
 
+	int autovoice;
+
 	/* optional */
 	char *(*readstr)(UI *ui, int auxtype, char *s, int sz);
 	int (*writestr)(UI *ui, int auxtype, char *s);