ref: 1ef59f6dbd62ea361c9416e878a6c8d0a8f8b93a
parent: 5cf2a66d5a71fad0128b2f9d0b088cea743c0d60
author: Sigrid Haflínudóttir <[email protected]>
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);