I think this is great. I find our current code odd and I agree this is what one expects.
On Wed, May 11, 2011 at 02:50:36AM +0300, Sviatoslav Chagaev wrote: > I'm sitting at work, listening to music, debugging a web-application > with JavaScript alert()s. Each time an alert window pops up, the > browser plays a sound. For a brief moment, the volume drops twicefold > then goes back to normal. This is annoying and doesn't make sense. > > In real life, if you are surrounded by multiple sound sources, their > sound volumes will not be divided by the total amount of sound sources. > Their sounds will add up until they blur and you can't distinguish > anything anymore. Other operating systems, such as Macrohard Doors, do > mixing by modeling this real world behaviour. > > In this sense, aucat violates the principle of least surprise. > I'm used to how sound interacts in real world and then aucat steps in > and introduces it's own laws of physics. > > To remedy this, aucat has an option -v, which lets you pre-divide the > volume of inputs. This results in loss of dynamic range (quiet sounds > might disappear and the maximum volume that you can set decreases). And > also, if during usage the count of inputs raises above of what I > predicted, the volume starts to jump up and down again. > > Experimentally, I've found that if you do a saturating addition between > inputs, it sounds very much how it might have sounded in real world and > how Macrohard Doors, among others, sounds like when playing > multiple sounds. > > > So, why is what I'm proposing better than what currently exists: > > * Resembles how sound behaves in real world more closely; > * Doesn't violate the principle of least surprise; > * No more annoying volume jumps up and down; > * No need to use the -v option anymore / less stuff to remember / "it > just works"; > * No more choosing between being annoyed by volume jumps or loosing > dynamic range. > > (This doesn't affect the -v option, it remains fully functional.) > > Tested on i386 and amd64 with 16 bits and 24 bits. > > > Index: abuf.h > =================================================================== > RCS file: /OpenBSD/src/usr.bin/aucat/abuf.h,v > retrieving revision 1.23 > diff -u -r1.23 abuf.h > --- abuf.h 21 Oct 2010 18:57:42 -0000 1.23 > +++ abuf.h 10 May 2011 22:58:18 -0000 > @@ -46,7 +46,6 @@ > union { > struct { > int weight; /* dynamic range */ > - int maxweight; /* max dynamic range allowed */ > unsigned vol; /* volume within the dynamic range */ > unsigned done; /* frames ready */ > unsigned xrun; /* underrun policy */ > Index: aparams.h > =================================================================== > RCS file: /OpenBSD/src/usr.bin/aucat/aparams.h,v > retrieving revision 1.11 > diff -u -r1.11 aparams.h > --- aparams.h 5 Nov 2010 16:42:17 -0000 1.11 > +++ aparams.h 10 May 2011 22:58:18 -0000 > @@ -19,6 +19,8 @@ > > #include <sys/param.h> > > +#include <limits.h> > + > #define NCHAN_MAX 16 /* max channel in a stream */ > #define RATE_MIN 4000 /* min sample rate */ > #define RATE_MAX 192000 /* max sample rate */ > @@ -64,6 +66,9 @@ > > typedef short adata_t; > > +#define ADATA_MIN SHRT_MIN > +#define ADATA_MAX SHRT_MAX > + > #define ADATA_MUL(x,y) (((int)(x) * (int)(y)) >> (ADATA_BITS - > 1)) > #define ADATA_MULDIV(x,y,z) ((int)(x) * (int)(y) / (int)(z)) > > @@ -71,6 +76,9 @@ > > typedef int adata_t; > > +#define ADATA_MIN (-0xffffff / 2) > +#define ADATA_MAX (0xffffff / 2) > + > #if defined(__i386__) && defined(__GNUC__) > > static inline int > @@ -119,6 +127,28 @@ > #else > #error "only 16-bit and 24-bit precisions are supported" > #endif > + > +/* saturating addition */ > +static inline adata_t > +adata_sadd(register adata_t x, register adata_t y) > +{ > +#if ADATA_BITS <= 16 > + register int sum; > +#else > + register long long sum; > +#endif > + > + sum = x; > + sum += y; > + > + if (sum < ADATA_MIN) > + sum = ADATA_MIN; > + else if (sum > ADATA_MAX) > + sum = ADATA_MAX; > + > + return (adata_t) sum; > +} > +#define ADATA_SADD(x,y) adata_sadd(x,y) > > #define MIDI_MAXCTL 127 > #define MIDI_TO_ADATA(m) (aparams_ctltovol[m] << (ADATA_BITS - 16)) > Index: aproc.c > =================================================================== > RCS file: /OpenBSD/src/usr.bin/aucat/aproc.c,v > retrieving revision 1.64 > diff -u -r1.64 aproc.c > --- aproc.c 28 Apr 2011 07:20:03 -0000 1.64 > +++ aproc.c 10 May 2011 22:58:19 -0000 > @@ -617,6 +617,7 @@ > unsigned i, j, cc, istart, inext, onext, ostart; > unsigned scount, icount, ocount; > int vol; > + adata_t sample; > > #ifdef DEBUG > if (debug_level >= 4) { > @@ -673,7 +674,8 @@ > idata += istart; > for (i = scount; i > 0; i--) { > for (j = cc; j > 0; j--) { > - *odata += ADATA_MUL(*idata, vol); > + sample = ADATA_MUL(*idata, vol); > + *odata = ADATA_SADD(*odata, sample); > idata++; > odata++; > } > @@ -914,8 +916,6 @@ > struct abuf *i, *obuf = LIST_FIRST(&p->outs); > unsigned odone; > > - mix_setmaster(p); > - > if (!aproc_inuse(p)) { > #ifdef DEBUG > if (debug_level >= 3) { > @@ -962,7 +962,6 @@ > ibuf->r.mix.done = 0; > ibuf->r.mix.vol = ADATA_UNIT; > ibuf->r.mix.weight = ADATA_UNIT; > - ibuf->r.mix.maxweight = ADATA_UNIT; > ibuf->r.mix.xrun = XRUN_IGNORE; > ibuf->r.mix.drop = 0; > } > @@ -1028,57 +1027,6 @@ > p->u.mix.ctl = NULL; > p->u.mix.mon = NULL; > return p; > -} > - > -/* > - * Normalize input levels. > - */ > -void > -mix_setmaster(struct aproc *p) > -{ > - unsigned n; > - struct abuf *i, *j; > - int weight; > - > - /* > - * count the number of inputs. If a set of inputs > - * uses channels that have no intersection, they are > - * counted only once because they don't need to > - * share their volume > - * > - * XXX: this is wrong, this is not optimal if we have two > - * buckets of N and N' clients, in which case we should > - * get 1/N and 1/N' respectively > - */ > - n = 0; > - LIST_FOREACH(i, &p->ins, ient) { > - j = LIST_NEXT(i, ient); > - for (;;) { > - if (j == NULL) { > - n++; > - break; > - } > - if (i->cmin > j->cmax || i->cmax < j->cmin) > - break; > - j = LIST_NEXT(j, ient); > - } > - } > - LIST_FOREACH(i, &p->ins, ient) { > - weight = ADATA_UNIT / n; > - if (weight > i->r.mix.maxweight) > - weight = i->r.mix.maxweight; > - i->r.mix.weight = weight; > -#ifdef DEBUG > - if (debug_level >= 3) { > - abuf_dbg(i); > - dbg_puts(": setmaster: "); > - dbg_puti(i->r.mix.weight); > - dbg_puts("/"); > - dbg_puti(i->r.mix.maxweight); > - dbg_puts("\n"); > - } > -#endif > - } > } > > void > Index: dev.c > =================================================================== > RCS file: /OpenBSD/src/usr.bin/aucat/dev.c,v > retrieving revision 1.64 > diff -u -r1.64 dev.c > --- dev.c 21 Oct 2010 18:57:42 -0000 1.64 > +++ dev.c 10 May 2011 22:58:19 -0000 > @@ -998,8 +998,7 @@ > } > aproc_setin(d->mix, ibuf); > ibuf->r.mix.xrun = xrun; > - ibuf->r.mix.maxweight = vol; > - mix_setmaster(d->mix); > + ibuf->r.mix.weight = vol; > } > if (mode & MODE_REC) { > opar = *sopar;