shithub: neindaw

Download patch

ref: fea33a2fc4eca3b9a84ec8c549f37ad99a55c2ef
parent: 8747033fb429a67053e229b30c323ce1c5a25a54
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Tue Aug 24 05:36:40 EDT 2021

implement autovoicing on cross-point (mainly for fast repeating samples); limit the number of autovoices

--- a/dsp/kick_drum.c
+++ b/dsp/kick_drum.c
@@ -298,6 +298,7 @@
 	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->declare(ui_interface->uiInterface, &dsp->fButton0, "crossvoice", "");
 	ui_interface->addButton(ui_interface->uiInterface, "Gate", &dsp->fButton0);
 	ui_interface->closeBox(ui_interface->uiInterface);
 	ui_interface->closeBox(ui_interface->uiInterface);
--- a/dsp/kick_drum.dsp
+++ b/dsp/kick_drum.dsp
@@ -11,6 +11,6 @@
 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[autovoice]");
+gate = button("v:Control/[1]Gate[autovoice][crossvoice]");
 
 process = os.hs_oscsin(aFreq + bFreq*en.ar(bA, bR, gate), gate : ba.impulsify) * gain * en.ar(aA, aR, gate);
--- a/fs.c
+++ b/fs.c
@@ -12,6 +12,7 @@
 
 enum {
 	Maxobjs = 64,
+	Maxvoice = 16,
 };
 
 static Aux *objs[Maxobjs];
@@ -116,13 +117,31 @@
 auxreaddsp(Aux *a, float *b, int n)
 {
 	State *s;
-	float *m, level;
-	int i, j, x;
+	float *m, level, least;
+	int i, j, cross, silent, total, quiet;
 
 	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;
+	cross = 0;
+	total = n;
+	if (!s->silent) {
+		silent = 1;
+		n = fs->dsp.read(s->voice[0], b, n);
+		total = n;
+		for (i = 0; i < n; i++) {
+			if (b[i] != 0.0f) {
+				silent = 0;
+				/* not silent, don't care for crossing? leave early */
+				if (!s->crossvoice)
+					break;
+			}
+			/* find the cross point */
+			if (s->crossvoice && i > 0 && cross < 1 && b[i] <= 0.0f && b[i-1] > 0.0f) {
+				cross = i;
+				break;
+			}
+		}
+		s->silent = silent;
+	}
 
 	/* simple case: error, no data, or just one voice */
 	if (n < 1 || s->nvoice < 2)
@@ -139,21 +158,64 @@
 		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);
+	if (s->silent)
+		memset(b, 0, sizeof(*b)*n);
+	else if (s->crossvoice && cross < 1) /* no cross point? wait for it */
+		return total;
 
+	/* multiple voices and crossvoicing with a cross point known? shut up the main voice */
+	if (cross > 0){
+		s->silent = 1;
+		memset(b+cross, 0, sizeof(*b)*(n-cross));
+		/* start mixing into the crosspoint */
+		b += cross;
+		m += cross;
+		n -= cross;
+	}
+
+	quiet = -1;
+	least = 999.0;
+	for (i = 1; i < s->nvoice && n > 1; i++) {
+		if (s->crossvoice && i > 1 && cross < 1) /* no cross point? wait for it */
+			break;
+
+		n = fs->dsp.read(s->voice[i], m, 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];
+		cross = 0;
+		for (j = 0; j < n; j++) { /* FIXME this could be faster */
+			if (s->crossvoice && i+1 < s->nvoice && cross < 1 && j > 0) {
+				/* crossvoicing AND we have at least one more on top, see if we can shut *this* one up */
+				if (m[j] <= 0.0f && m[j-1] > 0.0f) {
+					cross = j;
+					level = 0.0f; /* shut up forever */
+					b += j;
+					m += j;
+					n -= j;
+					break;
+				}
+			}
+			level = MAX(level, m[j]);
+			b[j] += m[j];
 		}
+
 		if (level <= 0.0f) { /* this one is silent, delete it */
 			fs->dsp.free(s->voice[i]);
 			s->voice[i] = nil;
+			/* freed one, don't try to free more */
+			least = -999.0f;
+			quiet = -1;
+		} else if (level < least) {
+			quiet = i;
+			least = level;
 		}
 	}
 
+	/* free up a voice if there are too many */
+	if (s->nvoice >= Maxvoice && quiet >= 0) {
+		fs->dsp.free(s->voice[quiet]);
+		s->voice[quiet] = nil;
+	}
+
 	/* relocate the voices */
 	for (i = j = 1; i < s->nvoice; i++) {
 		if (s->voice[i] != nil)
@@ -161,7 +223,7 @@
 	}
 	s->nvoice = j;
 
-	return n;
+	return total;
 }
 
 static void
@@ -239,8 +301,10 @@
 	}
 
 	s = a->state;
+	s->crossvoice |= a->ui->crossvoice;
+
 	/* 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) {
+	if (a->ui->autovoice && (!s->silent || s->nvoice > 1) && fs->dsp.clone != nil && fs->dsp.state != nil) {
 		/* now do the impossible */
 		so = fs->dsp.state(s->voice[0], &sz);
 		tmp = malloc(sz);
@@ -263,6 +327,7 @@
 	}
 
 	/* in any other case, just write to the original */
+	s->silent = 0;
 	return a->ui->writestr(a->ui, type, data);
 }
 
--- a/fs.h
+++ b/fs.h
@@ -62,6 +62,7 @@
 	Auxdsp **voice;
 	int nvoice;
 
+	int crossvoice; /* copied from metadata on ctl write (gate) */
 	int silent; /* that's about the first voice only */
 };
 
--- a/ui.c
+++ b/ui.c
@@ -13,6 +13,7 @@
 	Meta *meta;
 	int nummeta;
 	int autovoice;
+	int crossvoice;
 }decl;
 
 File *uif;
@@ -36,7 +37,10 @@
 			return s;
 		case UIButton:
 		case UICheckBox:
-			snprint(s, sz, "%s\t%g%s\n", t, *ui->zone, ui->autovoice ? "\tautovoice" : "");
+			snprint(s, sz, "%s\t%g%s%s\n", t, *ui->zone,
+				ui->autovoice ? "\tautovoice" : "",
+				ui->crossvoice ? "\tcrossvoice" : ""
+			);
 			return s;
 		case UIVSlider:
 		case UIHSlider:
@@ -130,6 +134,7 @@
 	a->state = uis;
 	a->ui->zone = decl.zone;
 	a->ui->autovoice = decl.autovoice;
+	a->ui->crossvoice = decl.crossvoice;
 	a->ui->label = label;
 	a->ui->readstr = ui_readstr;
 	a->ui->writestr = ui_writestr;
@@ -148,6 +153,7 @@
 	decl.meta = nil;
 	decl.nummeta = 0;
 	decl.autovoice = 0;
+	decl.crossvoice = 0;
 
 	if (type == UITGroup || type == UIHGroup || type == UIVGroup)
 		uif = d;
@@ -293,4 +299,6 @@
 	decl.nummeta++;
 	if (strcmp(key, "autovoice") == 0)
 		decl.autovoice = 1;
+	else if (strcmp(key, "crossvoice") == 0)
+		decl.crossvoice = 1;
 }
--- a/ui.h
+++ b/ui.h
@@ -19,6 +19,7 @@
 	int nummeta;
 
 	int autovoice;
+	int crossvoice;
 
 	/* optional */
 	char *(*readstr)(UI *ui, int auxtype, char *s, int sz);