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
12 12
13enum {13enum {
14 Maxobjs = 64,14 Maxobjs = 64,
15 Maxvoice = 16,
16};15};
17 16
18static Aux *objs[Maxobjs];17static Aux *objs[Maxobjs];
@@ -28,11 +27,44 @@ extern State *uis; · 9 unmodified lines
28 27
29static Fs *fs;28static Fs *fs;
30 29
30static void
31shutup(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
40static Voice *
41newvoice(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
31static Aux *62static Aux *
32newobj(char *name)63newobj(char *name)
33{64{
34 File *f;65 File *f;
35 Aux *o;66 Aux *o;
67 Voice *v;
36 int i, mode;68 int i, mode;
37 69
38 for (i = 0, o = nil; o == nil && i < nelem(objs); i++) {70 for (i = 0, o = nil; o == nil && i < nelem(objs); i++) {
@@ -62,9 +94,8 @@ newobj(char *name) · 23 unmodified lines
62 94
63 uif = f;95 uif = f;
64 o->state = uis = calloc(1, sizeof(State));96 o->state = uis = calloc(1, sizeof(State));
65 o->state->voice = calloc(1, sizeof(Auxdsp*));97 v = newvoice(o->state, fs->dsp.new(&o->numin, &o->numout));
66 o->state->voice[o->state->nvoice++] = fs->dsp.new(&o->numin, &o->numout);98 v->state = Vsilent;
67 o->state->silent = 1;
68 closefile(f);99 closefile(f);
69 100
70 return o;101 return o;
@@ -80,9 +111,10 @@ freeobj(Aux *o) · 9 unmodified lines
80 111
81 if (o->type == Xdsp) {112 if (o->type == Xdsp) {
82 objs[o->id] = nil;113 objs[o->id] = nil;
83 for (i = 0; i < o->state->nvoice; i++)114 for (i = 0; i < nelem(o->state->voice); i++) {
84 fs->dsp.free(o->state->voice[i]);115 if (o->state->voice[i].dsp != nil)
85 free(o->state->voice);116 fs->dsp.free(o->state->voice[i].dsp);
117 }
86 free(o->state);118 free(o->state);
87 }119 }
88 120
@@ -113,117 +145,80 @@ fsopen(Req *r) · 24 unmodified lines
113 respond(r, nil);145 respond(r, nil);
114}146}
115 147
116#define CROSS(a,b) (a <= 0.0f && a < b)148#define CROSS(a,b) (a <= 0.0f && a <= b && b >= 0.0f)
117 149
118static int150static int
119auxreaddsp(Aux *a, float *b, int n)151auxreaddsp(Aux *a, float *b, int total)
120{152{
153 int i, j, n, cross;
154 Voice *v;
155 float *m;
121 State *s;156 State *s;
122 float *m, level, least;
123 int i, j, cross, silent, total, quiet;
124 157
125 s = a->state;158 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;
151 159
152 /* otherwise need to mix all the voices together */160 /* make sure we have a buffer for mixing */
153 m = s->mixer;161 m = s->mixer;
154 if (s->mixersz < n) {162 if (s->mixersz < total) {
155 s->mixersz = n;163 if ((m = realloc(m, total*sizeof(float))) != nil) {
156 if ((m = realloc(m, n*sizeof(float))) == nil) {164 s->mixer = m;
157 fprint(2, "no memory for mixer, giving up\n");165 s->mixersz = total;
158 return n;166 } else {
167 total = s->mixersz;
159 }168 }
160 s->mixer = m;
161 }169 }
162 170
163 if (s->silent)171 /* silence first */
164 memset(b, 0, sizeof(*b)*n);172 memset(b, 0, sizeof(*b)*total);
165 else if (s->crossvoice && cross < 1) /* no cross point? wait for it */173
166 return total;174 v = s->voice;
167 175 cross = -1;
168 /* multiple voices and crossvoicing with a cross point known? shut up the main voice */176 for (i = 0, n = total; i < nelem(s->voice) && n > 0; i++, v++) {
169 if (cross > 0){177 if (v->dsp == nil)
170 s->silent = 1;178 continue;
171 memset(b+cross, 0, sizeof(*b)*(n-cross));179
172 /* start mixing into the crosspoint */180 switch (v->state) {
173 b += cross;181 case Vsilent: /* nothing to do here, ignore it */
174 m += cross;182 continue;
175 n -= cross;183 case Vstarting: /* should start playing it */
176 }184 if (s->crossvoice && i > 0 && v[-1].state != Vsilent) {
177 185 /* always start another voice at zero crossing to avoid clicks & pops */
178 quiet = -1;186 if (cross < 0) /* no crossing yet? give up for now */
179 least = 999.0;187 goto done;
180 for (i = 1; i < s->nvoice && n > 1; i++) {188 memset(b+cross, 0, sizeof(*b)*(n - cross)); /* silence the prev voice after zero crossing */
181 if (s->crossvoice && i > 1 && cross < 1) /* no cross point? wait for it */189 cross++; /* leaving one as 0 */
182 break;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;
183 204
184 n = fs->dsp.read(s->voice[i], m, n);205 for (j = 0; j < n; j++) {
185 level = 0.0f;206 b[j] += m[j];
186 cross = 0;207 if (s->crossvoice && cross < 0 && j > 0 && CROSS(m[j-1], m[j]))
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])) {
191 cross = j;208 cross = j;
192 level = 0.0f; /* shut up forever */
193 b += j;
194 m += j;
195 n -= j;
196 break;
197 }
198 }209 }
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;
212 }210 }
213 }211 }
214 212
215 /* free up a voice if there are too many */213done:
216 if (s->nvoice >= Maxvoice && quiet >= 0) {
217 fs->dsp.free(s->voice[quiet]);
218 s->voice[quiet] = nil;
219 }
220 214
221 /* relocate the voices */215 /* relocate the voices */
222 for (i = j = 1; i < s->nvoice; i++) {216 for (i = j = 1; i < nelem(s->voice); i++) {
223 if (s->voice[i] != nil)217 s->voice[j] = s->voice[i];
224 s->voice[j++] = s->voice[i];218 if(s->voice[j].dsp != nil)
219 j++;
225 }220 }
226 s->nvoice = j;221 memset(s->voice+j, 0, sizeof(*s->voice)*(i - j));
227 222
228 return total;223 return total;
229}224}
@@ -233,8 +228,10 @@ auxreset(Aux *a) · 3 unmodified lines
233{228{
234 int i;229 int i;
235 230
236 for (i = 0; i < a->state->nvoice; i++)231 for (i = 0; i < nelem(a->state->voice); i++) {
237 fs->dsp.reset(a->state->voice[i]);232 if (a->state->voice[i].dsp != nil)
233 fs->dsp.reset(a->state->voice[i].dsp);
234 }
238}235}
239 236
240static void237static void
@@ -293,9 +290,10 @@ auxwrite(Aux *a, int type, char *data) · 52 unmodified lines
293{290{
294 State *s;291 State *s;
295 Auxdsp *clone;292 Auxdsp *clone;
293 Voice *v;
296 void *so, *sc;294 void *so, *sc;
297 u8int *tmp;295 u8int *tmp;
298 int r, sz;296 int i, r, sz, nused;
299 297
300 if (a->ui->writestr == nil) {298 if (a->ui->writestr == nil) {
301 werrstr("not implemented");299 werrstr("not implemented");
@@ -305,10 +303,15 @@ auxwrite(Aux *a, int type, char *data) · 3 unmodified lines
305 s = a->state;303 s = a->state;
306 s->crossvoice |= a->ui->crossvoice;304 s->crossvoice |= a->ui->crossvoice;
307 305
306 for (i = nused = 0; i < nelem(s->voice); i++) {
307 if (s->voice[i].state != Vsilent)
308 nused++;
309 }
310
308 /* autovoice means every write needs to use (possibly) a new instance */311 /* 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) {
310 /* now do the impossible */313 /* now do the impossible */
311 so = fs->dsp.state(s->voice[0], &sz);314 so = fs->dsp.state(s->voice[0].dsp, &sz);
312 tmp = malloc(sz);315 tmp = malloc(sz);
313 memmove(tmp, so, sz); /* save the original state */316 memmove(tmp, so, sz); /* save the original state */
314 /* write to the original and check if a new voice has to be created */317 /* 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
316 free(tmp);319 free(tmp);
317 return r;320 return r;
318 }321 }
319 clone = fs->dsp.clone(s->voice[0]); /* clone the original */322 clone = fs->dsp.clone(s->voice[0].dsp); /* clone the original */
320 sc = fs->dsp.state(clone, &sz);323 sc = fs->dsp.state(clone, &sz);
321 memmove(sc, so, sz); /* copy the changed state to the clone */324 memmove(sc, so, sz); /* copy the changed state to the clone */
322 memmove(so, tmp, sz); /* revert the original state */325 memmove(so, tmp, sz); /* revert the original state */
323 free(tmp);326 free(tmp);
324 /* now we have the original dsp intact, with a cloned dsp actually having the changed state */327 /* now we have the original dsp intact, with a cloned dsp actually having the changed state */
325 328
326 s->voice = realloc(s->voice, (s->nvoice+1)*sizeof(*s->voice));329 v = newvoice(s, clone);
327 s->voice[s->nvoice++] = clone;330 v->state = Vstarting;
331
328 return r;332 return r;
329 }333 }
330 334
331 /* in any other case, just write to the original */335 /* in any other case, just write to the original */
332 s->silent = 0;336 s->voice[0].state = Vstarting;
333 return a->ui->writestr(a->ui, type, data);337 return a->ui->writestr(a->ui, type, data);
334}338}
335 339
fs.h+16−3
@@ -1,7 +1,16 @@
1enum {
2 Vsilent,
3 Vstarting,
4 Vplaying,
5
6 Maxvoice = 16,
7};
8
1typedef struct Aux Aux;9typedef struct Aux Aux;
2typedef struct Auxdsp Auxdsp;10typedef struct Auxdsp Auxdsp;
3typedef struct Fs Fs;11typedef struct Fs Fs;
4typedef struct State State;12typedef struct State State;
13typedef struct Voice Voice;
5 14
6typedef enum {15typedef enum {
7 Xclone,16 Xclone,
@@ -55,15 +64,19 @@ struct Fs { · 47 unmodified lines
55 }dsp;64 }dsp;
56};65};
57 66
67struct Voice {
68 uvlong samples;
69 Auxdsp *dsp;
70 int state;
71};
72
58struct State {73struct State {
59 float *mixer;74 float *mixer;
60 int mixersz;75 int mixersz;
61 76
62 Auxdsp **voice;77 Voice voice[Maxvoice];
63 int nvoice;
64 78
65 int crossvoice; /* copied from metadata on ctl write (gate) */79 int crossvoice; /* copied from metadata on ctl write (gate) */
66 int silent; /* that's about the first voice only */
67};80};
68 81
69void fsinit(void *fs);82void 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