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;

Reply via email to