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;