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);