History

rewrite autovoicing to reduce pops/clicks even more

Sigrid Solveig Haflínudóttir committed 5 years ago

Commit 8508e911 parent a85496c2 files · +127 −110Browse files

Unified · Split

2 files
  1. fs.c +111 −107
  2. fs.h +16 −3
fs.c+111−107
@@ -12,7 +12,6 @@ · 11 unmodified lines
1212
1313 enum {
1414 Maxobjs = 64,
15− Maxvoice = 16,
1615 };
1716
1817 static Aux *objs[Maxobjs];
@@ -28,11 +27,44 @@ extern State *uis; · 9 unmodified lines
2827
2928 static Fs *fs;
3029
30+static void
31+shutup(State *s, Voice *v)
32+{
33+ if (s->voice != v) {
34+ fs->dsp.free(v->dsp);
35+ v->dsp = nil;
36+ }
37+ v->state = Vsilent;
38+}
39+
40+static Voice *
41+newvoice(State *s, Auxdsp *dsp)
42+{
43+ Voice *v, *f;
44+ int i;
45+
46+ f = s->voice;
47+ for (i = 0, v = s->voice; i < nelem(s->voice); i++, v++) {
48+ if (v->dsp == nil) {
49+ f = v;
50+ break;
51+ }
52+ if (v->state == Vplaying && f->samples < v->samples)
53+ f = v;
54+ }
55+
56+ f->dsp = dsp;
57+ f->state = Vsilent;
58+
59+ return f;
60+}
61+
3162 static Aux *
3263 newobj(char *name)
3364 {
3465 File *f;
3566 Aux *o;
67+ Voice *v;
3668 int i, mode;
3769
3870 for (i = 0, o = nil; o == nil && i < nelem(objs); i++) {
@@ -62,9 +94,8 @@ newobj(char *name) · 23 unmodified lines
6294
6395 uif = f;
6496 o->state = uis = calloc(1, sizeof(State));
65− o->state->voice = calloc(1, sizeof(Auxdsp*));
66− o->state->voice[o->state->nvoice++] = fs->dsp.new(&o->numin, &o->numout);
67− o->state->silent = 1;
97+ v = newvoice(o->state, fs->dsp.new(&o->numin, &o->numout));
98+ v->state = Vsilent;
6899 closefile(f);
69100
70101 return o;
@@ -80,9 +111,10 @@ freeobj(Aux *o) · 9 unmodified lines
80111
81112 if (o->type == Xdsp) {
82113 objs[o->id] = nil;
83− for (i = 0; i < o->state->nvoice; i++)
84− fs->dsp.free(o->state->voice[i]);
85− free(o->state->voice);
114+ for (i = 0; i < nelem(o->state->voice); i++) {
115+ if (o->state->voice[i].dsp != nil)
116+ fs->dsp.free(o->state->voice[i].dsp);
117+ }
86118 free(o->state);
87119 }
88120
@@ -113,117 +145,80 @@ fsopen(Req *r) · 24 unmodified lines
113145 respond(r, nil);
114146 }
115147
116−#define CROSS(a,b) (a <= 0.0f && a < b)
148+#define CROSS(a,b) (a <= 0.0f && a <= b && b >= 0.0f)
117149
118150 static int
119−auxreaddsp(Aux *a, float *b, int n)
151+auxreaddsp(Aux *a, float *b, int total)
120152 {
153+ int i, j, n, cross;
154+ Voice *v;
155+ float *m;
121156 State *s;
122− float *m, level, least;
123− int i, j, cross, silent, total, quiet;
124157
125158 s = a->state;
126− cross = 0;
127− total = n;
128− if (!s->silent) {
129− silent = 1;
130− n = fs->dsp.read(s->voice[0], b, n);
131− total = n;
132− for (i = 0; i < n; i++) {
133− if (b[i] != 0.0f) {
134− silent = 0;
135− /* not silent, don't care for crossing? leave early */
136− if (!s->crossvoice)
137− break;
138− }
139− /* find the cross point */
140− if (s->crossvoice && i > 0 && cross < 1 && CROSS(b[i-1], b[i])) {
141− cross = i;
142− break;
143− }
144− }
145− s->silent = silent;
146− }
147−
148− /* simple case: error, no data, or just one voice */
149− if (n < 1 || s->nvoice < 2)
150− return n;
151159
152− /* otherwise need to mix all the voices together */
160+ /* make sure we have a buffer for mixing */
153161 m = s->mixer;
154− if (s->mixersz < n) {
155− s->mixersz = n;
156− if ((m = realloc(m, n*sizeof(float))) == nil) {
157− fprint(2, "no memory for mixer, giving up\n");
158− return n;
162+ if (s->mixersz < total) {
163+ if ((m = realloc(m, total*sizeof(float))) != nil) {
164+ s->mixer = m;
165+ s->mixersz = total;
166+ } else {
167+ total = s->mixersz;
159168 }
160− s->mixer = m;
161169 }
162170
163− if (s->silent)
164− memset(b, 0, sizeof(*b)*n);
165− else if (s->crossvoice && cross < 1) /* no cross point? wait for it */
166− return total;
167−
168− /* multiple voices and crossvoicing with a cross point known? shut up the main voice */
169− if (cross > 0){
170− s->silent = 1;
171− memset(b+cross, 0, sizeof(*b)*(n-cross));
172− /* start mixing into the crosspoint */
173− b += cross;
174− m += cross;
175− n -= cross;
176− }
177−
178− quiet = -1;
179− least = 999.0;
180− for (i = 1; i < s->nvoice && n > 1; i++) {
181− if (s->crossvoice && i > 1 && cross < 1) /* no cross point? wait for it */
182− break;
171+ /* silence first */
172+ memset(b, 0, sizeof(*b)*total);
173+
174+ v = s->voice;
175+ cross = -1;
176+ for (i = 0, n = total; i < nelem(s->voice) && n > 0; i++, v++) {
177+ if (v->dsp == nil)
178+ continue;
179+
180+ switch (v->state) {
181+ case Vsilent: /* nothing to do here, ignore it */
182+ continue;
183+ case Vstarting: /* should start playing it */
184+ if (s->crossvoice && i > 0 && v[-1].state != Vsilent) {
185+ /* always start another voice at zero crossing to avoid clicks & pops */
186+ if (cross < 0) /* no crossing yet? give up for now */
187+ goto done;
188+ memset(b+cross, 0, sizeof(*b)*(n - cross)); /* silence the prev voice after zero crossing */
189+ cross++; /* leaving one as 0 */
190+ n -= cross;
191+ b += cross;
192+ cross = 0;
193+ shutup(s, &v[-1]);
194+ }
195+ v->state = Vplaying;
196+ v->samples = 0;
197+ /* slippery floor */
198+ case Vplaying:
199+ if (n < 1)
200+ break;
201+ if (fs->dsp.read(v->dsp, m, n) != n)
202+ return -1;
203+ v->samples += n;
183204
184− n = fs->dsp.read(s->voice[i], m, n);
185− level = 0.0f;
186− cross = 0;
187− for (j = 0; j < n; j++) { /* FIXME this could be faster */
188− if (s->crossvoice && i+1 < s->nvoice && cross < 1 && j > 0) {
189− /* crossvoicing AND we have at least one more on top, see if we can shut *this* one up */
190− if (CROSS(m[j-1], m[j])) {
205+ for (j = 0; j < n; j++) {
206+ b[j] += m[j];
207+ if (s->crossvoice && cross < 0 && j > 0 && CROSS(m[j-1], m[j]))
191208 cross = j;
192− level = 0.0f; /* shut up forever */
193− b += j;
194− m += j;
195− n -= j;
196− break;
197− }
198209 }
199− level = MAX(level, m[j]);
200− b[j] += m[j];
201− }
202−
203− if (level <= 0.0f) { /* this one is silent, delete it */
204− fs->dsp.free(s->voice[i]);
205− s->voice[i] = nil;
206− /* freed one, don't try to free more */
207− least = -999.0f;
208− quiet = -1;
209− } else if (level < least) {
210− quiet = i;
211− least = level;
212210 }
213211 }
214212
215− /* free up a voice if there are too many */
216− if (s->nvoice >= Maxvoice && quiet >= 0) {
217− fs->dsp.free(s->voice[quiet]);
218− s->voice[quiet] = nil;
219− }
213+done:
220214
221215 /* relocate the voices */
222− for (i = j = 1; i < s->nvoice; i++) {
223− if (s->voice[i] != nil)
224− s->voice[j++] = s->voice[i];
216+ for (i = j = 1; i < nelem(s->voice); i++) {
217+ s->voice[j] = s->voice[i];
218+ if(s->voice[j].dsp != nil)
219+ j++;
225220 }
226− s->nvoice = j;
221+ memset(s->voice+j, 0, sizeof(*s->voice)*(i - j));
227222
228223 return total;
229224 }
@@ -233,8 +228,10 @@ auxreset(Aux *a) · 3 unmodified lines
233228 {
234229 int i;
235230
236− for (i = 0; i < a->state->nvoice; i++)
237− fs->dsp.reset(a->state->voice[i]);
231+ for (i = 0; i < nelem(a->state->voice); i++) {
232+ if (a->state->voice[i].dsp != nil)
233+ fs->dsp.reset(a->state->voice[i].dsp);
234+ }
238235 }
239236
240237 static void
@@ -293,9 +290,10 @@ auxwrite(Aux *a, int type, char *data) · 52 unmodified lines
293290 {
294291 State *s;
295292 Auxdsp *clone;
293+ Voice *v;
296294 void *so, *sc;
297295 u8int *tmp;
298− int r, sz;
296+ int i, r, sz, nused;
299297
300298 if (a->ui->writestr == nil) {
301299 werrstr("not implemented");
@@ -305,10 +303,15 @@ auxwrite(Aux *a, int type, char *data) · 3 unmodified lines
305303 s = a->state;
306304 s->crossvoice |= a->ui->crossvoice;
307305
306+ for (i = nused = 0; i < nelem(s->voice); i++) {
307+ if (s->voice[i].state != Vsilent)
308+ nused++;
309+ }
310+
308311 /* autovoice means every write needs to use (possibly) a new instance */
309− if (a->ui->autovoice && (!s->silent || s->nvoice > 1) && fs->dsp.clone != nil && fs->dsp.state != nil) {
312+ if (a->ui->autovoice && nused > 0 && fs->dsp.clone != nil && fs->dsp.state != nil) {
310313 /* now do the impossible */
311− so = fs->dsp.state(s->voice[0], &sz);
314+ so = fs->dsp.state(s->voice[0].dsp, &sz);
312315 tmp = malloc(sz);
313316 memmove(tmp, so, sz); /* save the original state */
314317 /* write to the original and check if a new voice has to be created */
@@ -316,20 +319,21 @@ auxwrite(Aux *a, int type, char *data) · 1 unmodified line
316319 free(tmp);
317320 return r;
318321 }
319− clone = fs->dsp.clone(s->voice[0]); /* clone the original */
322+ clone = fs->dsp.clone(s->voice[0].dsp); /* clone the original */
320323 sc = fs->dsp.state(clone, &sz);
321324 memmove(sc, so, sz); /* copy the changed state to the clone */
322325 memmove(so, tmp, sz); /* revert the original state */
323326 free(tmp);
324327 /* now we have the original dsp intact, with a cloned dsp actually having the changed state */
325328
326− s->voice = realloc(s->voice, (s->nvoice+1)*sizeof(*s->voice));
327− s->voice[s->nvoice++] = clone;
329+ v = newvoice(s, clone);
330+ v->state = Vstarting;
331+
328332 return r;
329333 }
330334
331335 /* in any other case, just write to the original */
332− s->silent = 0;
336+ s->voice[0].state = Vstarting;
333337 return a->ui->writestr(a->ui, type, data);
334338 }
335339
fs.h+16−3
@@ -1,7 +1,16 @@
1+enum {
2+ Vsilent,
3+ Vstarting,
4+ Vplaying,
5+
6+ Maxvoice = 16,
7+};
8+
19 typedef struct Aux Aux;
210 typedef struct Auxdsp Auxdsp;
311 typedef struct Fs Fs;
412 typedef struct State State;
13+typedef struct Voice Voice;
514
615 typedef enum {
716 Xclone,
@@ -55,15 +64,19 @@ struct Fs { · 47 unmodified lines
5564 }dsp;
5665 };
5766
67+struct Voice {
68+ uvlong samples;
69+ Auxdsp *dsp;
70+ int state;
71+};
72+
5873 struct State {
5974 float *mixer;
6075 int mixersz;
6176
62− Auxdsp **voice;
63− int nvoice;
77+ Voice voice[Maxvoice];
6478
6579 int crossvoice; /* copied from metadata on ctl write (gate) */
66− int silent; /* that's about the first voice only */
6780 };
6881
6982 void fsinit(void *fs);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t