[xiph-commits] r19016 - trunk/squishyball

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Tue Nov 12 00:51:00 PST 2013


Author: xiphmont
Date: 2013-11-12 00:51:00 -0800 (Tue, 12 Nov 2013)
New Revision: 19016

Modified:
   trunk/squishyball/audio.c
   trunk/squishyball/configure.ac
   trunk/squishyball/loader.c
   trunk/squishyball/main.c
   trunk/squishyball/main.h
   trunk/squishyball/mincurses.c
   trunk/squishyball/squishyball.1
   trunk/squishyball/tty.c
Log:
Add downmixing capability (mono and stereo)
Add autonormalization capability
Slightly more sensible native bitdepth depth and dither defaults


Modified: trunk/squishyball/audio.c
===================================================================
--- trunk/squishyball/audio.c	2013-11-12 04:13:28 UTC (rev 19015)
+++ trunk/squishyball/audio.c	2013-11-12 08:51:00 UTC (rev 19016)
@@ -2,7 +2,7 @@
  *
  *  squishyball
  *
- *      Copyright (C) 2010 Xiph.Org
+ *      Copyright (C) 2010-2013 Xiph.Org
  *
  *  squishyball is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -47,89 +47,211 @@
   return 0;
 }
 
-void check_warn_clipping(pcm_t *pcm){
+static float get_clamp(pcm_t *pcm){
+  if(pcm->nativebits>=0 && pcm->nativebits<24)
+    return 1.f - 1.f/(1<<(pcm->nativebits-1));
+  else
+    return 1.f - 1.f/2147483648.f;
+}
+
+float check_warn_clipping(pcm_t *pcm, int no_normalize){
   int i,j;
-  int bps = (pcm->bits==-32 ? 4 : (pcm->bits+7)>>3);
   int cpf = pcm->ch;
-  int bpf = cpf*bps;
-  int s = pcm->size/bpf;
+  int s = pcm->size/sizeof(float);
+  float clamp;
+  float min,max;
   int flag[cpf];
   size_t count=0;
-  unsigned char *d = pcm->data;
+  float *d = (float *)pcm->data;
 
   memset(flag,0,sizeof(flag));
 
-  switch(pcm->bits){
-  case 16:
-    for(i=0;i<s;i++)
-      for(j=0;j<cpf;j++){
-        short v = d[0] | (d[1]<<8);
-        if(v==-32768 || v==32767)
-          flag[j]++;
-        else{
-          if(flag[j]>1)count+=flag[j];
-          flag[j]=0;
-        }
-        d+=2;
+  clamp = max = get_clamp(pcm);
+  min=-1.f;
+
+  for(i=0;i<s;i+=pcm->ch)
+    for(j=0;j<pcm->ch;j++){
+      if(d[i+j]<-1.f){
+        if(d[i+j]<min)min=d[i+j];
+        flag[j]++;
+      }else if(d[i+j]>clamp){
+        if(d[i+j]>max)max=d[i+j];
+        flag[j]++;
+      }else{
+        if(flag[j]>1)count+=flag[j];
+        flag[j]=0;
       }
-    for(j=0;j<cpf;j++)
-      if(flag[j]>1)count+=flag[j];
-    break;
-  case 24:
-    for(i=0;i<s;i++)
-      for(j=0;j<cpf;j++){
-        int v = ((d[0]<<8) | (d[1]<<16) | (d[2]<<24))>>8;
-        if(v==-8388608 || v==8388607)
-          flag[j]++;
-        else{
-          if(flag[j]>1)count+=flag[j];
-          flag[j]=0;
+    }
+  for(j=0;j<cpf;j++)
+    if(flag[j]>1)count+=flag[j];
+
+  if(count){
+    if(pcm->nativebits>0){
+      fprintf(stderr,"CLIPPING WARNING: %ld probably clipped samples in %s;\n",(long)count,pcm->name);
+      fprintf(stderr,"                  (can't be repaired with normalization)\n");
+    }else{
+      if(no_normalize){
+        fprintf(stderr,"CLIPPING WARNING: %ld clipped samples in %s;\n",(long)count,pcm->name);
+        fprintf(stderr,"                  normalization disabled on command line.\n");
+      }else{
+        float att = -1./min;
+        if(clamp/max < att) att=clamp/max;
+        if(sb_verbose){
+          fprintf(stderr,"%ld overrange samples after decoding %s (peak %+0.1fdB)\n",(long)count,pcm->name,todB(1./att));
         }
-        d+=3;
+        return att;
       }
+    }
+  }
+
+  return 1.;
+}
+
+/* input must be float */
+float convert_to_mono(pcm_t *pcm){
+  int i,j,k;
+  int cpf = pcm->ch;
+  int s = pcm->size/sizeof(float);
+  float *d = (float *)pcm->data;
+  float max=0;
+  float min=0;
+  float att=1.f;
+  float clamp = get_clamp(pcm);
+
+  if(pcm->currentbits!=-32){
+    fprintf(stderr,"Internal error; non-float PCM passed to convert_to_mono.\n");
+    exit(10);
+  }
+
+  if(sb_verbose)
+    fprintf(stderr,"Downmixing to mono... ");
+
+  k=0;
+  for(i=0;i<s;i+=cpf){
+    float acc=0.f;
     for(j=0;j<cpf;j++)
-      if(flag[j]>1)count+=flag[j];
-    break;
-  case -32:
-    {
-      union {
-        float f;
-        unsigned char c[4];
-      } m;
+      acc+=d[i+j];
+    if(acc>max)max=acc;
+    if(acc<min)min=acc;
+    d[k++]=acc;
+  }
 
-      if(host_is_big_endian()){
-        for(i=0;i<s;i++)
-          for(j=0;j<cpf;j++){
-            m.c[0]=d[3];
-            m.c[1]=d[2];
-          m.c[2]=d[1];
-          m.c[3]=d[0];
-          if(m.f<-1. || m.f>=1.)
-            count++;
-          d+=4;
-          }
-      }else{
-        for(i=0;i<s;i++)
-          for(j=0;j<cpf;j++){
-            m.c[0]=d[0];
-            m.c[1]=d[1];
-            m.c[2]=d[2];
-            m.c[3]=d[3];
-          if(m.f<-1. || m.f>=1.)
-            count++;
-          d+=4;
-          }
-      }
+
+  pcm->size/=cpf;
+  pcm->ch=1;
+  if(pcm->matrix)free(pcm->matrix);
+  pcm->matrix=strdup("M");
+  if(min<-1.f) att=-1./min;
+  if(clamp/max < att) att=clamp/max;
+
+  if(sb_verbose){
+    if(att<1.){
+      fprintf(stderr,"done. peak: %+0.1fdB\n",todB(1./att));
+    }else{
+      fprintf(stderr,"done.\n");
     }
-    break;
   }
+  return att;
+}
 
-  if(count){
-    if(bps==4)
-      fprintf(stderr,"CLIPPING WARNING: %ld clipped samples in %s.\n",(long)count,pcm->name);
-    else
-      fprintf(stderr,"CLIPPING WARNING: %ld probably clipped samples in %s.\n",(long)count,pcm->name);
+/* non-normalized */
+static const int left_mix[33]={
+  1.,    /* A: M */
+  1.,    /* B: L */
+  0.,    /* C: R */
+  0.707, /* D: C */
+  0.707, /* E: LFE */
+  0.866, /* F: BL */
+  0.5,   /* G: BR */
+  0.791, /* H: CL */
+  0.612, /* I: CR */
+  0.612, /* J: BC */
+  0.866, /* K: SL */
+  0.5,   /* L: SR */
+  0
+};
+
+static const int right_mix[33]={
+  1.,    /* A: M */
+  0.,    /* B: L */
+  1.,    /* C: R */
+  0.707, /* D: C */
+  0.707, /* E: LFE */
+  0.5,   /* F: BL */
+  0.866, /* G: BR */
+  0.612, /* H: CL */
+  0.791, /* I: CR */
+  0.612, /* J: BC */
+  0.5,   /* K: SL */
+  0.866, /* L: SR */
+  0
+};
+
+/* input must be float */
+float convert_to_stereo(pcm_t *pcm){
+  int i,j,k;
+  int cpf = pcm->ch;
+  int s = pcm->size/sizeof(float);
+  float *d = (float *)pcm->data;
+  float max=0;
+  float min=0;
+  float att=1.f;
+  float clamp = get_clamp(pcm);
+
+  float *lmix,*rmix;
+
+  if(pcm->currentbits!=-32){
+    fprintf(stderr,"Internal error; non-float PCM passed to convert_to_mono.\n");
+    exit(10);
   }
+  if(pcm->ch<2){
+    fprintf(stderr,"Internal error; can't downmix mono to stereo.\n");
+    exit(10);
+  }
+
+  if(sb_verbose)
+    fprintf(stderr,"Downmixing to stereo... ");
+
+  lmix=calloc(cpf,sizeof(*lmix));
+  rmix=calloc(cpf,sizeof(*rmix));
+  for(j=0;j<cpf;j++){
+    lmix[j] = left_mix[pcm->mix[j]-'A'];
+    rmix[j] = right_mix[pcm->mix[j]-'A'];
+  }
+
+  k=0;
+  for(i=0;i<s;i+=cpf){
+    float L=0.f,R=0.f;
+
+    for(j=0;j<cpf;j++){
+      L+=d[i+j]*lmix[j];
+      R+=d[i+j]*rmix[j];
+    }
+
+    if(L>max)max=L;
+    if(L<min)min=L;
+    if(R>max)max=R;
+    if(R<min)min=R;
+    d[k++]=L;
+    d[k++]=R;
+  }
+
+  pcm->size=pcm->size/cpf*2;
+  pcm->ch=2;
+  if(pcm->matrix)free(pcm->matrix);
+  pcm->matrix=strdup("L,R");
+
+  if(min<-1.f) att=-1./min;
+  if(clamp/max < att) att=clamp/max;
+
+  if(sb_verbose){
+    if(att<1.f){
+      fprintf(stderr,"done. peak: %+0.1fdB\n",todB(1./att));
+    }else{
+      fprintf(stderr,"done.\n");
+    }
+  }
+  return att;
 }
 
 static inline float triangle_ditherval(float *save){
@@ -139,206 +261,96 @@
   return ret;
 }
 
-static void float32_to_24(pcm_t *pcm){
+void convert_to_32(pcm_t *pcm){
   unsigned char *d = pcm->data;
+  float *f = (float *)pcm->data;
   off_t j;
   if(sb_verbose)
-    fprintf(stderr,"\rConverting %s to 24 bit... ",pcm->name);
-  for(j=0;j<pcm->size/4;j++){
-    int val=0;
-    int mantissa = d[j*4] | (d[j*4+1]<<8) | ((d[j*4+2]&0x7f)<<16) | (1<<23);
-    int exponent = 127 - ((d[j*4+2]>>7) | ((d[j*4+3]&0x7f)<<1));
-    int sign = d[j*4+3]>>7;
-    if(exponent <= 0){
-      if(exponent == -128){
-        fprintf(stderr,"%s: Input file contains invalid floating point values.\n",pcm->name);
-        exit(6);
-      }
-      if(sign)
-        val = 8388608;
-      else
-        val = 8388607;
-    }else if(exponent <= 24){
-      val = mantissa>>exponent;
-      /* round with tiebreaks toward even */
-      if(((mantissa<<(24-exponent))&0xffffff) + (val&1) > 0x800000) ++val;
-    }
-    if(sign) val= -val;
-
-    d[j*3]=val&0xff;
-    d[j*3+1]=(val>>8)&0xff;
-    d[j*3+2]=(val>>16)&0xff;
+    fprintf(stderr,"\rConverting %s to 32 bit... ",pcm->name);
+  for(j=0;j<pcm->size/sizeof(float);j++){
+    float val = rint(f[j]*2147483648.f);
+    int iv;
+    if(val<-2147483648.f) val = -2147483648.f;
+    if(val> 2147483647.f) val = 2147483647.f;
+    iv=(int)val;
+    d[j*4]=iv&0xff;
+    d[j*4+1]=(iv>>8)&0xff;
+    d[j*4+2]=(iv>>16)&0xff;
+    d[j*4+3]=(iv>>24)&0xff;
   }
   if(sb_verbose)
     fprintf(stderr,"done.\n");
-  pcm->bits=24;
-  pcm->size/=4;
-  pcm->size*=3;
+  pcm->currentbits=32;
+  pcm->size/=sizeof(float);
+  pcm->size*=4;
 }
 
-static void float32_to_16(pcm_t *pcm){
+void convert_to_24(pcm_t *pcm){
   unsigned char *d = pcm->data;
+  float *f = (float *)pcm->data;
   off_t j;
-  union {
-    float f;
-    unsigned char c[4];
-  } m;
-
   if(sb_verbose)
-    fprintf(stderr,"\r%s %s to 16 bit... ",
-            pcm->dither?"Dithering":"Down-converting",pcm->name);
-
-  /* again assumes IEEE754, which is not pedantically correct */
-  if(sizeof(float)==4){
-    float t[pcm->ch];
-    int ch=0;
-    memset(t,0,sizeof(t));
-
-    for(j=0;j<pcm->size/4;j++){
-      float val;
-      if(host_is_big_endian()){
-        m.c[0]=d[j*4+3];
-        m.c[1]=d[j*4+2];
-        m.c[2]=d[j*4+1];
-        m.c[3]=d[j*4];
-      }else{
-        m.c[0]=d[j*4];
-        m.c[1]=d[j*4+1];
-        m.c[2]=d[j*4+2];
-        m.c[3]=d[j*4+3];
-      }
-      if(pcm->dither){
-        val = rint(m.f*32768.f + triangle_ditherval(t+ch));
-        ch++;
-        if(ch>pcm->ch)ch=0;
-      }else{
-        val = rint(m.f*32768.f);
-      }
-
-      if(val>=32767.f){
-        d[j*2]=0xff;
-        d[j*2+1]=0x7f;
-      }else if(val<=-32768.f){
-        d[j*2]=0x00;
-        d[j*2+1]=0x80;
-      }else{
-        int iv = (int)val;
-        d[j*2]=iv&0xff;
-        d[j*2+1]=(iv>>8)&0xff;
-      }
-    }
-  }else{
-    fprintf(stderr,"Unhandled case: sizeof(float)!=4\n");
-    exit(10);
+    fprintf(stderr,"\rConverting %s to 24 bit... ",pcm->name);
+  for(j=0;j<pcm->size/sizeof(float);j++){
+    float val = rint(f[j]*8388608.f);
+    int iv;
+    if(val<-8388608.f) val = -8388608.f;
+    if(val> 8388607.f) val = 8388607.f;
+    iv=(int)val;
+    d[j*3]=iv&0xff;
+    d[j*3+1]=(iv>>8)&0xff;
+    d[j*3+2]=(iv>>16)&0xff;
   }
-
   if(sb_verbose)
     fprintf(stderr,"done.\n");
-
-  pcm->bits=16;
-  pcm->size/=2;
+  pcm->currentbits=24;
+  pcm->size/=sizeof(float);
+  pcm->size*=3;
 }
 
-static void demote_24_to_16(pcm_t *pcm){
+void convert_to_16(pcm_t *pcm, int dither){
+  unsigned char *d = pcm->data;
+  float *f = (float *)pcm->data;
+  off_t j;
   float t[pcm->ch];
-  unsigned char *d = pcm->data;
-  off_t i;
   int ch=0;
+  memset(t,0,sizeof(t));
 
   if(sb_verbose)
     fprintf(stderr,"\r%s %s to 16 bit... ",
-            pcm->dither?"Dithering":"Down-converting",pcm->name);
+            dither?"Dithering":"Down-converting",pcm->name);
 
-  memset(t,0,sizeof(t));
-
-  for(i=0;i<pcm->size/3;i++){
-    int val = ((d[i*3+2]<<24) | (d[i*3+1]<<16) | (d[i*3]<<8))>>8;
-    if(pcm->dither){
-      val = rint (val*(1.f/256.f)+triangle_ditherval(t+ch));
+  for(j=0;j<pcm->size/sizeof(float);j++){
+    float val;
+    if(dither){
+      val = rint(f[j]*32768.f + triangle_ditherval(t+ch));
       ch++;
       if(ch>pcm->ch)ch=0;
-    }else
-      val = rint (val*(1.f/256.f));
+    }else{
+      val = rint(f[j]*32768.f);
+    }
 
-    if(val>32767){
-      d[i*2]=0xff;
-      d[i*2+1]=0x7f;
-    }else if(val<-32768){
-      d[i*2]=0x00;
-      d[i*2+1]=0x80;
+    if(val>=32767.f){
+      d[j*2]=0xff;
+      d[j*2+1]=0x7f;
+    }else if(val<=-32768.f){
+      d[j*2]=0x00;
+      d[j*2+1]=0x80;
     }else{
-      d[i*2]=val&0xff;
-      d[i*2+1]=(val>>8)&0xff;
+      int iv = (int)val;
+      d[j*2]=iv&0xff;
+        d[j*2+1]=(iv>>8)&0xff;
     }
   }
 
   if(sb_verbose)
     fprintf(stderr,"done.\n");
 
-  pcm->bits=16;
-  pcm->size/=3;
+  pcm->currentbits=16;
+  pcm->size/=sizeof(float);
   pcm->size*=2;
 }
 
-static void promote_to_24(pcm_t *pcm){
-  off_t i;
-
-  if(sb_verbose)
-    fprintf(stderr,"\rPromoting %s to 24 bit... ",pcm->name);
-
-  {
-    unsigned char *ret=realloc(pcm->data,pcm->size*3/2);
-    if(!ret){
-      fprintf(stderr,"Unable to allocate memory while promoting file to 24-bit\n");
-      exit(5);
-    }
-    pcm->data=ret;
-    for(i=pcm->size/2-1;i>=0;i--){
-      ret[i*3+2]=ret[i*2+1];
-      ret[i*3+1]=ret[i*2];
-      ret[i*3]=0;
-    }
-  }
-  if(sb_verbose)
-    fprintf(stderr,"done.\n");
-
-  pcm->bits=24;
-  pcm->size/=2;
-  pcm->size*=3;
-}
-
-void convert_to_16(pcm_t *pcm){
-  switch(pcm->bits){
-  case 16:
-    break;
-  case 24:
-    demote_24_to_16(pcm);
-    break;
-  case -32:
-    float32_to_16(pcm);
-    break;
-  default:
-    fprintf(stderr,"%s: Unsupported sample format.\n",pcm->name);
-    exit(6);
-  }
-}
-
-void convert_to_24(pcm_t *pcm){
-  switch(pcm->bits){
-  case 16:
-    promote_to_24(pcm);
-    break;
-  case 24:
-    break;
-  case -32:
-    float32_to_24(pcm);
-    break;
-  default:
-    fprintf(stderr,"%s: Unsupported sample format.\n",pcm->name);
-    exit(6);
-  }
-}
-
 /* Channel map reconciliation helpers *********************************/
 
 static const char *chlist[]={"X","M","L","R","C","LFE","SL","SR","BC","BL","BR","CL","CR",NULL};
@@ -368,7 +380,7 @@
   int ai[A->ch],bi[A->ch];
   int i,j,k;
   off_t o;
-  int bps = (B->bits+7)/8;
+  int bps = (B->currentbits+7)/8;
   int bpf = B->ch*bps;
   int p[bpf];
   unsigned char temp[bpf];
@@ -424,8 +436,8 @@
                          float **b1, float **b2){
   int i;
   int fragsamples = pcm[0]->rate/10;  /* 100ms */
-  float mul = (pcm[0]->bits==24 ? 8388608.f : 32768.f) * .0625;
-  int bps=(pcm[0]->bits+7)/8;
+  float mul = (pcm[0]->currentbits==24 ? 8388608.f : 32768.f) * .0625;
+  int bps=(pcm[0]->currentbits+7)/8;
   int ch=pcm[0]->ch;
   int bpf=ch*bps;
   int maxsamples = pcm[0]->size / bpf;
@@ -526,7 +538,7 @@
    endpos moved. */
 void fill_fragment1(unsigned char *out, pcm_t *pcm, off_t start, off_t *pos, off_t end, int *loop,
                     int fragsamples, float *fadewindow){
-  int bps = (pcm->bits+7)/8;
+  int bps = (pcm->currentbits+7)/8;
   int cpf = pcm->ch;
   int bpf = bps*cpf;
   int fragsize = fragsamples*bpf;
@@ -618,7 +630,7 @@
    schedule' even if that means beginning partway through the window. */
 void fill_fragment2(unsigned char *out, pcm_t *pcm, off_t start, off_t *pos, off_t end, int *loop,
                     int fragsamples, float *fadewindow){
-  int bps = (pcm->bits+7)/8;
+  int bps = (pcm->currentbits+7)/8;
   int cpf = pcm->ch;
   int bpf = bps*cpf;
   int fragsize=fragsamples*bpf;
@@ -696,7 +708,16 @@
     if(!ai)
       return NULL;
     aname=ai->short_name;
+    if(sb_verbose)
+      fprintf(stderr,"Opening [%s] %s for %d/%d and %d channel[s]...",aname,device,bits,rate,ch);
     ret=ao_open_live(id, &sf, &aoe);
+    if(sb_verbose){
+      if(!ret){
+        fprintf(stderr," errno %d\n",errno);
+      }else{
+        fprintf(stderr," ok!\n");
+      }
+    }
   }else{
     /* Otherwise... there's some hunting to do. */
     /* Is the passed device a number or a name? */
@@ -707,6 +728,8 @@
     int i;
 
     if(!device[0] || test[0]) number=-1;
+    if(sb_verbose)
+      fprintf(stderr,"Scanning for a device driver that recognizes '%s'...\n",device);
 
     /* driver info list is sorted by priority */
     for(i=0;i<count;i++){
@@ -726,8 +749,17 @@
          option; it will ignore the option and try to open its default */
       for(j=0;j<info->option_count;j++)
         if(!strcmp(info->options[j],ao.key))break;
-      if(j<info->option_count)
-        if((ret=ao_open_live(id,&sf,&ao)))break;
+      if(j<info->option_count){
+        if(sb_verbose)
+          fprintf(stderr,"  ...trying to open [%s] %s for %d/%d and %d channel[s]...",aname,device,bits,rate,ch);
+        if((ret=ao_open_live(id,&sf,&ao))){
+          if(sb_verbose)
+            fprintf(stderr," ok!\n");
+          break;
+        }
+        if(sb_verbose)
+          fprintf(stderr," errno %d\n",errno);
+      }
     }
   }
   if(ret && sb_verbose)

Modified: trunk/squishyball/configure.ac
===================================================================
--- trunk/squishyball/configure.ac	2013-11-12 04:13:28 UTC (rev 19015)
+++ trunk/squishyball/configure.ac	2013-11-12 08:51:00 UTC (rev 19016)
@@ -4,7 +4,7 @@
 cflags_save="$CFLAGS"
 AC_PREREQ(2.57)
 AC_INIT(main.c)
-AM_INIT_AUTOMAKE(squishyball, 20101217, [vorbis-dev at xiph.org])
+AM_INIT_AUTOMAKE(squishyball, 20131109, [vorbis-dev at xiph.org])
 AC_CONFIG_FILES([Makefile])
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])], [AC_SUBST([AM_DEFAULT_VERBOSITY], [1])])
 

Modified: trunk/squishyball/loader.c
===================================================================
--- trunk/squishyball/loader.c	2013-11-12 04:13:28 UTC (rev 19015)
+++ trunk/squishyball/loader.c	2013-11-12 08:51:00 UTC (rev 19016)
@@ -2,7 +2,7 @@
  *
  *  squishyball
  *
- *      Copyright (C) 2010-2012 Xiph.Org
+ *      Copyright (C) 2010-2013 Xiph.Org
  *
  *  squishyball is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -223,38 +223,48 @@
     switch(channels){
     case 1:
       pcm->matrix = strdup("M");
+      pcm->mix = strdup("A");
       break;
     case 2:
       pcm->matrix = strdup("L,R");
+      pcm->mix = strdup("BC");
       break;
     case 3:
       pcm->matrix = strdup("L,R,C");
+      pcm->mix = strdup("BCD");
       break;
     case 4:
       pcm->matrix = strdup("L,R,BL,BR");
+      pcm->mix = strdup("BCFG");
       break;
     case 5:
       pcm->matrix = strdup("L,R,C,BL,BR");
+      pcm->mix = strdup("BCDFG");
       break;
     case 6:
       pcm->matrix = strdup("L,R,C,LFE,BL,BR");
+      pcm->mix = strdup("BCDEFG");
       break;
     case 7:
       pcm->matrix = strdup("L,R,C,LFE,BC,SL,SR");
+      pcm->mix = strdup("BCDEJKL");
       break;
     default:
       pcm->matrix = strdup("L,R,C,LFE,BL,BR,SL,SR");
+      pcm->mix = strdup("BCDEFGKL");
       break;
     }
   }else{
-    pcm->matrix = calloc(32*4,sizeof(char));
+    int count=0;
+    pcm->matrix = calloc(32*4+1,sizeof(char));
+    pcm->mix = calloc(33,sizeof(char));
     for(i=0;i<32;i++){
       if(mask&(1<<i)){
         strcat(pcm->matrix,mask_map[i]);
         strcat(pcm->matrix,",");
+        pcm->mix[count++]='B'+i;
       }
     }
-    pcm->matrix[strlen(pcm->matrix)-1]=0;
   }
 
   if(!find_wav_chunk(in, path, "data", &len)){
@@ -279,7 +289,8 @@
        now we want to find the size of the file */
     pcm->rate = samplerate;
     pcm->ch = channels;
-    pcm->bits = (format==3 ? -samplesize : samplesize);
+    pcm->nativebits = (format==3 ? -samplesize : samplesize);
+    pcm->currentbits = -32;
 
     if(len){
       pcm->size = len;
@@ -303,19 +314,19 @@
   }
 
   /* read the samples into memory */
-  switch(pcm->bits){
+  switch(pcm->nativebits){
   case 8:
-    /* load as 8-bit, expand it out to 16. */
-    pcm->data = calloc(1,pcm->size*2);
+    pcm->data = calloc(1,pcm->size*sizeof(float));
     break;
-  case 24:
-    pcm->dither = 1;
   case 16:
-    pcm->data = calloc(1,pcm->size);
+    pcm->data = calloc(1,pcm->size/2*sizeof(float));
     break;
+  case 24:
+    pcm->data = calloc(1,pcm->size/3*sizeof(float));
+    break;
   case -32:
-    pcm->data = calloc(1,pcm->size);
-    pcm->dither = 1;
+  case 32:
+    pcm->data = calloc(1,pcm->size/4*sizeof(float));
     break;
   default:
     /* Won't get here unless the code is modified and the modifier
@@ -330,12 +341,15 @@
   }
 
   {
-    off_t j=0;
+    off_t j=0,k;
+    unsigned char *d = pcm->data;
+    float         *f = (float *)pcm->data;
+
     while(j<pcm->size){
       off_t bytes = (pcm->size-j > 65536 ? 65536 : pcm->size-j);
       if(sb_verbose)
         fprintf(stderr,"\rLoading %s: %ld to go...       ",path,(long)(pcm->size-j));
-      j+=bytes=fread(pcm->data+j,1,bytes,in);
+      j+=bytes=fread(d+j,1,bytes,in);
       if(bytes==0)break;
     }
     if(j<pcm->size){
@@ -344,17 +358,58 @@
       pcm->size=j;
     }
 
-    /* 8-bit must be expanded to 16 */
-    if(samplesize==8){
-      off_t j;
-      unsigned char *d = pcm->data;
-      for(j=pcm->size-1;j>=0;j--){
-        int val = (d[j]-128)<<8;
-        d[j*2] = val&0xff;
-        d[j*2+1] = (val>>8)&0xff;
+    /* non float must be expanded to float */
+    switch(pcm->nativebits){
+    case 8:
+      k=pcm->size;
+      for(j=pcm->size-1;j>=0;j--)
+        f[--k] = (int32_t)((d[j]-128)<<24) * (1.f/2147483648.f);
+      pcm->size=pcm->size*sizeof(float);
+      break;
+    case 16:
+      k=pcm->size/2;
+      for(j=pcm->size-2;j>=0;j-=2)
+        f[--k] = (int32_t)((d[j]<<16)|(d[j+1]<<24)) * (1.f/2147483648.f);
+      pcm->size=pcm->size/2*sizeof(float);
+      break;
+    case 24:
+      k=pcm->size*4/3;
+      for(j=pcm->size-3;j>=0;j-=3)
+        f[--k] = (int32_t)((d[j]<<8)|(d[j+1]<<16)|(d[j+2]<<24)) * (1.f/2147483648.f);
+      pcm->size=pcm->size/3*sizeof(float);
+      break;
+    case 32:
+      k=pcm->size/4;
+      for(j=pcm->size-4;j>=0;j-=4)
+        f[--k] = (int32_t)(d[j]|(d[j+1]<<8)|(d[j+2]<<16)|(d[j+3]<<24)) * (1.f/2147483648.f);
+      pcm->size=pcm->size/4*sizeof(float);
+      break;
+    case -32:
+      k=pcm->size/4;
+      for(j=pcm->size-4;j>=0;j-=4){
+        int val=0;
+        int mantissa = d[j] | (d[j+1]<<8) | ((d[j+2]&0x7f)<<16) | (1<<23);
+        int exponent = 127 - ((d[j+2]>>7) | ((d[j+3]&0x7f)<<1));
+        int sign = d[j+3]>>7;
+        if(exponent <= 0){
+          if(exponent == -128){
+            fprintf(stderr,"%s: Input file contains invalid floating point values.\n",pcm->name);
+            exit(6);
+          }
+          if(sign)
+            val = 8388608;
+          else
+            val = 8388607;
+        }else if(exponent <= 24){
+          val = mantissa>>exponent;
+          /* round with tiebreaks toward even */
+          if(((mantissa<<(24-exponent))&0xffffff) + (val&1) > 0x800000) ++val;
+        }
+        if(sign) val= -val;
+        f[--k] = val/8388608.;
       }
-      pcm->bits=16;
-      pcm->size*=2;
+      pcm->size=pcm->size/4*sizeof(float);
+      break;
     }
   }
 
@@ -426,12 +481,6 @@
   return ldexp(f, e-16446);
 }
 
-static inline void swap(unsigned char *a, unsigned char *b){
-  unsigned char temp=*a;
-  *a=*b;
-  *b=temp;
-}
-
 static pcm_t *aiff_load(char *path, FILE *in){
   pcm_t *pcm = NULL;
   int aifc; /* AIFC or AIFF? */
@@ -477,21 +526,26 @@
 
   pcm->ch = READ_U16_BE(buffer);
   pcm->rate = (int)read_IEEE80(buffer+8);
-  pcm->bits = READ_U16_BE(buffer+6);
-  pcm->size = READ_U32_BE(buffer+2)*pcm->ch*((pcm->bits+7)/8);
+  pcm->nativebits = READ_U16_BE(buffer+6);
+  pcm->size = READ_U32_BE(buffer+2)*pcm->ch*((pcm->nativebits+7)/8);
+  pcm->currentbits = -32;
 
   switch(pcm->ch){
   case 1:
     pcm->matrix = strdup("M");
+    pcm->mix = strdup("A");
     break;
   case 2:
     pcm->matrix = strdup("L,R");
+    pcm->mix = strdup("BC");
     break;
   case 3:
     pcm->matrix = strdup("L,R,C");
+    pcm->mix = strdup("BCD");
     break;
   default:
     pcm->matrix = strdup("L,R,BL,BR");
+    pcm->mix = strdup("BCFG");
     break;
   }
 
@@ -530,33 +584,38 @@
   }
 
   int offset = READ_U32_BE(buf2);
-  int blocksize = READ_U32_BE(buf2+4);
 
-  if(!((fp==0 && (pcm->bits==24 || pcm->bits == 16 || pcm->bits == 8)) ||
-       (fp==1 && pcm->bits==32))){
+  if(!((fp==0 && (pcm->nativebits==32 ||
+                  pcm->nativebits==24 ||
+                  pcm->nativebits==16 ||
+                  pcm->nativebits==8)) ||
+       (fp==1 && pcm->nativebits==32))){
     fprintf(stderr,
             "%s: Unsupported type of AIFF/AIFC file\n"
-            " Must be 8-, 16- or 24-bit integer or 32-bit floating point PCM.\n",
+            " Must be 8-, 16-, 24- or 32-bit integer or 32-bit floating point PCM.\n",
             path);
     goto err;
   }
   if(fp==1)
-    pcm->bits = -pcm->bits;
+    pcm->nativebits = -pcm->nativebits;
 
   fseek(in, offset, SEEK_CUR); /* Swallow some data */
 
   /* read the samples into memory */
-  switch(pcm->bits){
+  switch(pcm->nativebits){
   case 8:
-    /* load as 8-bit, expand it out to 16. */
-    pcm->data = calloc(1,pcm->size*2);
+    pcm->data = calloc(1,pcm->size*sizeof(float));
     break;
+  case 16:
+    pcm->data = calloc(1,pcm->size/2*sizeof(float));
+    break;
   case 24:
-    pcm->dither = 1;
-    /* fall through */
-  default:
-    pcm->data = calloc(1,pcm->size);
+    pcm->data = calloc(1,pcm->size/3*sizeof(float));
     break;
+  case 32:
+  case -32:
+    pcm->data = calloc(1,pcm->size/4*sizeof(float));
+    break;
   }
 
   if(pcm->data == NULL){
@@ -566,7 +625,8 @@
 
   {
     unsigned char *d = pcm->data;
-    off_t j=0;
+    float *f = (float *)pcm->data;
+    off_t j=0,k;
     while(j<pcm->size){
       off_t bytes = (pcm->size-j > 65536 ? 65536 : pcm->size-j);
       if(sb_verbose)
@@ -580,36 +640,83 @@
       pcm->size=j;
     }
 
-    /* 8-bit must be expanded to 16 */
-    switch(pcm->bits){
+    /* expand to float */
+    switch(pcm->nativebits){
     case 8:
-      for(j=pcm->size-1;j>=0;j--){
-        int val = d[j]<<8;
-        d[j*2] = val&0xff;
-        d[j*2+1] = (val>>8)&0xff;
-      }
-      pcm->bits=16;
-      pcm->size*=2;
+      k=pcm->size;
+      for(j=pcm->size-1;j>=0;j--)
+        f[--k] = (int32_t)((d[j]-128)<<24) * (1.f/2147483648.f);
+      pcm->size=pcm->size*sizeof(float);
       break;
     case 16:
+      k=pcm->size/2;
       if(bend){
-        for(j=0;j<pcm->size/2;j++)
-          swap(d+j*2,d+j*2+1);
+        for(j=pcm->size-2;j>=0;j-=2)
+          f[--k] = (int32_t)((d[j]<<24)|(d[j+1]<<16)) * (1.f/2147483648.f);
+      }else{
+        for(j=pcm->size-2;j>=0;j-=2)
+          f[--k] = (int32_t)((d[j]<<16)|(d[j+1]<<24)) * (1.f/2147483648.f);
       }
+      pcm->size=pcm->size/2*sizeof(float);
       break;
     case 24:
+      k=pcm->size/3;
       if(bend){
-        for(j=0;j<pcm->size/3;j++)
-          swap(d+j*3,d+j*3+2);
+        for(j=pcm->size-3;j>=0;j-=3)
+          f[--k] = (int32_t)(d[j+2]|(d[j+1]<<8)|(d[j]<<16)) * (1.f/2147483648.f);
+      }else{
+        for(j=pcm->size-3;j>=0;j-=3)
+          f[--k] = (int32_t)(d[j]|(d[j+1]<<8)|(d[j+2]<<16)) * (1.f/2147483648.f);
       }
+      pcm->size=pcm->size/3*sizeof(float);
       break;
+    case 32:
+      k=pcm->size/4;
+      if(bend){
+        for(j=pcm->size-4;j>=0;j-=4)
+          f[--k] = (int32_t)(d[j+3]|(d[j+2]<<8)|(d[j+1]<<16)|(d[j]<<24)) * (1.f/2147483648.f);
+      }else{
+        for(j=pcm->size-4;j>=0;j-=4)
+          f[--k] = (int32_t)(d[j]|(d[j+1]<<8)|(d[j+2]<<16)|(d[j+3]<<24)) * (1.f/2147483648.f);
+      }
+      pcm->size=pcm->size/4*sizeof(float);
+      break;
     case -32:
-      if(bend){
-        for(j=0;j<pcm->size/4;j++) {
-          swap(d+j*4,d+j*4+3);
-          swap(d+j*4+1,d+j*4+2);
+      k=pcm->size/4;
+      for(j=pcm->size-4;j>=0;j-=4){
+        int val=0;
+        int mantissa;
+        int exponent;
+        int sign;
+
+        if(bend){
+          mantissa = d[j+3] | (d[j+2]<<8) | ((d[j+1]&0x7f)<<16) | (1<<23);
+          exponent = 127 - ((d[j+1]>>7) | ((d[j]&0x7f)<<1));
+          sign = d[j]>>7;
+        }else{
+          mantissa = d[j] | (d[j+1]<<8) | ((d[j+2]&0x7f)<<16) | (1<<23);
+          exponent = 127 - ((d[j+2]>>7) | ((d[j+3]&0x7f)<<1));
+          sign = d[j+3]>>7;
         }
+
+        if(exponent <= 0){
+          if(exponent == -128){
+            fprintf(stderr,"%s: Input file contains invalid floating point values.\n",pcm->name);
+            exit(6);
+          }
+          if(sign)
+            val = 8388608;
+          else
+            val = 8388607;
+        }else if(exponent <= 24){
+          val = mantissa>>exponent;
+          /* round with tiebreaks toward even */
+          if(((mantissa<<(24-exponent))&0xffffff) + (val&1) > 0x800000) ++val;
+        }
+        if(sign) val= -val;
+        f[--k] = val/8388608.;
       }
+      pcm->size=pcm->size/4*sizeof(float);
       break;
     }
   }
@@ -627,13 +734,14 @@
 /* SW loading to make JM happy *******************************************************/
 
 static pcm_t *sw_load(char *path, FILE *in){
-
   pcm_t *pcm = calloc(1,sizeof(pcm_t));
   pcm->name=strdup(trim_path(path));
-  pcm->bits=16;
+  pcm->nativebits=16;
+  pcm->currentbits=-32;
   pcm->ch=1;
   pcm->rate=48000;
   pcm->matrix=strdup("M");
+  pcm->mix=strdup("A");
 
   if(fseek(in,0,SEEK_END)==-1){
     fprintf(stderr,"%s: Failed to seek\n",path);
@@ -645,7 +753,7 @@
     goto err;
   }
 
-  pcm->data = calloc(1,pcm->size);
+  pcm->data = calloc(1,pcm->size/2*sizeof(float));
 
   if(pcm->data == NULL){
     fprintf(stderr,"Unable to allocate enough memory to load sample into memory\n");
@@ -654,6 +762,9 @@
 
   {
     off_t j=0;
+    int16_t *d = (int16_t *)pcm->data;
+    float   *f = (float *)pcm->data;
+
     while(j<pcm->size){
       off_t bytes = (pcm->size-j > 65536 ? 65536 : pcm->size-j);
       if(sb_verbose)
@@ -666,6 +777,11 @@
         fprintf(stderr,"\r%s: File ended before declared length (%ld < %ld); continuing...\n",path,(long)j,(long)pcm->size);
       pcm->size=j;
     }
+
+    for(j=pcm->size/2-1;j>=0;j--)
+      f[j] = d[j]/32768.;
+
+    pcm->size=pcm->size/2*sizeof(float);
   }
 
   if(sb_verbose)
@@ -723,8 +839,9 @@
   if(pcm->data == NULL){
     /* lazy initialization */
     pcm->ch = channels;
-    pcm->bits = (bits_per_sample+7)/8*8;
-    pcm->size *= pcm->bits/8*channels;
+    pcm->nativebits = (bits_per_sample+7)/8*8;
+    pcm->size *= channels*sizeof(float);
+    pcm->currentbits = -32;
     pcm->data = calloc(pcm->size,1);
   }
 
@@ -732,34 +849,24 @@
     fprintf(stderr,"\r%s: number of channels changes part way through file\n",pcm->name);
     return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
   }
-  if(pcm->bits != (bits_per_sample+7)/8*8){
+  if(pcm->nativebits != (bits_per_sample+7)/8*8){
     fprintf(stderr,"\r%s: bit depth changes part way through file\n",pcm->name);
     return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
   }
 
   {
-    unsigned char *d = pcm->data + fill;
-    int shift = pcm->bits - bits_per_sample;
-    switch(pcm->bits){
+    float *d = (float *)pcm->data;
+    int shift = pcm->nativebits - bits_per_sample;
+    switch(pcm->nativebits){
     case 16:
       for (j = 0; j < samples; j++)
-        for (i = 0; i < channels; i++){
-          d[0] = (buffer[i][j]<<shift)&0xff;
-          d[1] = (buffer[i][j]<<shift>>8)&0xff;
-          d+=2;
-          fill+=2;
-        }
+        for (i = 0; i < channels; i++)
+          d[fill++] = (buffer[i][j]<<shift)*(1.f/32768.f);
       break;
     case 24:
-      pcm->dither = 1;
       for (j = 0; j < samples; j++)
-        for (i = 0; i < channels; i++){
-          d[0] = (buffer[i][j]<<shift)&0xff;
-          d[1] = (buffer[i][j]<<shift>>8)&0xff;
-          d[2] = (buffer[i][j]<<shift>>16)&0xff;
-          d+=3;
-          fill+=3;
-        }
+        for (i = 0; i < channels; i++)
+          d[fill++] = (buffer[i][j]<<shift)*(1.f/8388608.f);
       break;
     default:
       fprintf(stderr,"\r%s: Only 16- and 24-bit FLACs are supported for decode right now.\n",pcm->name);
@@ -860,27 +967,35 @@
   switch(pcm->ch){
   case 1:
     pcm->matrix = strdup("M");
+    pcm->mix = strdup("A");
     break;
   case 2:
     pcm->matrix = strdup("L,R");
+    pcm->mix = strdup("BC");
     break;
   case 3:
     pcm->matrix = strdup("L,R,C");
+    pcm->mix = strdup("BCD");
     break;
   case 4:
     pcm->matrix = strdup("L,R,BL,BR");
+    pcm->mix = strdup("BCFG");
     break;
   case 5:
     pcm->matrix = strdup("L,R,C,BL,BR");
+    pcm->mix = strdup("BCDFG");
     break;
   case 6:
     pcm->matrix = strdup("L,R,C,LFE,BL,BR");
+    pcm->mix = strdup("BCDEFG");
     break;
   case 7:
     pcm->matrix = strdup("L,R,C,LFE,BC,SL,SR");
+    pcm->mix = strdup("BCDEJKL");
     break;
   default:
     pcm->matrix = strdup("L,R,C,LFE,BL,BR,SL,SR");
+    pcm->mix = strdup("BCDEFGKL");
     break;
   }
 
@@ -923,45 +1038,54 @@
   vi=ov_info(&vf,-1);
   pcm = calloc(1,sizeof(pcm_t));
   pcm->name=strdup(trim_path(path));
-  pcm->bits=-32;
+  pcm->nativebits=-32;
+  pcm->currentbits=-32;
   pcm->ch=vi->channels;
   pcm->rate=vi->rate;
-  pcm->size=ov_pcm_total(&vf,-1)*vi->channels*4;
+  pcm->size=ov_pcm_total(&vf,-1)*vi->channels*sizeof(float);
   pcm->data=calloc(pcm->size,1);
 
   switch(pcm->ch){
   case 1:
     pcm->matrix = strdup("M");
+    pcm->mix = strdup("A");
     break;
   case 2:
     pcm->matrix = strdup("L,R");
+    pcm->mix = strdup("BC");
     break;
   case 3:
     pcm->matrix = strdup("L,C,R");
+    pcm->mix = strdup("BDC");
     break;
   case 4:
     pcm->matrix = strdup("L,R,BL,BR");
+    pcm->mix = strdup("BCFG");
     break;
   case 5:
     pcm->matrix = strdup("L,C,R,BL,BR");
+    pcm->mix = strdup("BDCFG");
     break;
   case 6:
     pcm->matrix = strdup("L,C,R,BL,BR,LFE");
+    pcm->mix = strdup("BDCFGE");
     break;
   case 7:
     pcm->matrix = strdup("L,C,R,SL,SR,BC,LFE");
+    pcm->mix = strdup("BDCKLJE");
     break;
   default:
     pcm->matrix = strdup("L,C,R,SL,SR,BL,BR,LFE");
+    pcm->mix = strdup("BDCKLFGE");
     break;
   }
 
-  while(fill<pcm->size){
+  while(fill*sizeof(float)<pcm->size){
     int current_section;
     int i,j;
     float **pcmout;
     long ret=ov_read_float(&vf,&pcmout,4096,&current_section);
-    unsigned char *d = pcm->data+fill;
+    float *d = (float *)pcm->data;
 
     if(current_section!=last_section){
       last_section=current_section;
@@ -981,40 +1105,10 @@
       goto err;
     }
 
-    if(sizeof(float)==4){
-      /* Assuming IEEE754, which is pedantically incorrect */
-      union {
-        float f;
-        unsigned char c[4];
-      } m;
-      if(host_is_big_endian()){
-        for(i=0;i<ret;i++){
-          for(j=0;j<pcm->ch;j++){
-            m.f=pcmout[j][i];
-            d[0] = m.c[3];
-            d[1] = m.c[2];
-            d[2] = m.c[1];
-            d[3] = m.c[0];
-            d+=4;
-          }
-        }
-      }else{
-        for(i=0;i<ret;i++){
-          for(j=0;j<pcm->ch;j++){
-            m.f=pcmout[j][i];
-            d[0] = m.c[0];
-            d[1] = m.c[1];
-            d[2] = m.c[2];
-            d[3] = m.c[3];
-            d+=4;
-          }
-        }
-      }
-    }else{
-      fprintf(stderr,"Unhandled case: sizeof(float)!=4\n");
-      exit(10);
-    }
-    fill += ret*pcm->ch*4;
+    for(i=0;i<ret;i++)
+      for(j=0;j<pcm->ch;j++)
+        d[fill++]=pcmout[j][i];
+
     if (sb_verbose && (throttle&0x3f)==0)
       fprintf(stderr,"\rLoading %s: %ld to go...       ",pcm->name,(long)(pcm->size-fill));
     throttle++;
@@ -1057,7 +1151,6 @@
   off_t fill=0;
   int throttle=0;
   int last_section=-1;
-  int i,j;
 
   if(fseek(in,0,SEEK_SET)==-1){
     fprintf(stderr,"%s: Failed to seek\n",path);
@@ -1072,46 +1165,55 @@
 
   pcm = calloc(1,sizeof(pcm_t));
   pcm->name=strdup(trim_path(path));
-  pcm->bits=-32;
+  pcm->nativebits=-32;
+  pcm->currentbits=-32;
   pcm->ch=op_channel_count(of,-1);
   pcm->rate=48000;
-  pcm->size=op_pcm_total(of,-1)*pcm->ch*4;
+  pcm->size=op_pcm_total(of,-1)*pcm->ch*sizeof(float);
   pcm->data=calloc(pcm->size,1);
 
 
   switch(pcm->ch){
   case 1:
     pcm->matrix = strdup("M");
+    pcm->mix = strdup("A");
     break;
   case 2:
     pcm->matrix = strdup("L,R");
+    pcm->mix = strdup("BC");
     break;
   case 3:
     pcm->matrix = strdup("L,C,R");
+    pcm->mix = strdup("BDC");
     break;
   case 4:
     pcm->matrix = strdup("L,R,BL,BR");
+    pcm->mix = strdup("BCFG");
     break;
   case 5:
     pcm->matrix = strdup("L,C,R,BL,BR");
+    pcm->mix = strdup("BDCFG");
     break;
   case 6:
     pcm->matrix = strdup("L,C,R,BL,BR,LFE");
+    pcm->mix = strdup("BDCFGE");
     break;
   case 7:
     pcm->matrix = strdup("L,C,R,SL,SR,BC,LFE");
+    pcm->mix = strdup("BDCKLJE");
     break;
   default:
     pcm->matrix = strdup("L,C,R,SL,SR,BL,BR,LFE");
+    pcm->mix = strdup("BDCKLFGE");
     break;
   }
 
-  while(fill<pcm->size){
+  while(fill*sizeof(float)<pcm->size){
     int current_section;
     int i,j;
     float pcmout[4096];
     long ret=op_read_float(of,pcmout,4096,&current_section);
-    unsigned char *d = pcm->data+fill;
+    float *d = (float *)pcm->data;
     float *s = pcmout;
 
     if(current_section!=last_section){
@@ -1131,40 +1233,10 @@
       goto err;
     }
 
-    if(sizeof(float)==4){
-      /* Assuming IEEE754, which is pedantically incorrect */
-      union {
-        float f;
-        unsigned char c[4];
-      } m;
-      if(host_is_big_endian()){
-        for(i=0;i<ret;i++){
-          for(j=0;j<pcm->ch;j++){
-            m.f=*s++;
-            d[0] = m.c[3];
-            d[1] = m.c[2];
-            d[2] = m.c[1];
-            d[3] = m.c[0];
-            d+=4;
-          }
-        }
-      }else{
-        for(i=0;i<ret;i++){
-          for(j=0;j<pcm->ch;j++){
-            m.f=*s++;
-            d[0] = m.c[0];
-            d[1] = m.c[1];
-            d[2] = m.c[2];
-            d[3] = m.c[3];
-            d+=4;
-          }
-        }
-      }
-    }else{
-      fprintf(stderr,"Unhandled case: sizeof(float)!=4\n");
-      exit(10);
-    }
-    fill += ret*pcm->ch*4;
+    for(i=0;i<ret;i++)
+      for(j=0;j<pcm->ch;j++)
+        d[fill++]=*s++;
+
     if (sb_verbose && (throttle&0x3f)==0)
       fprintf(stderr,"\rLoading %s: %ld to go...       ",pcm->name,(long)(pcm->size-fill));
     throttle++;
@@ -1172,7 +1244,7 @@
   op_free(of);
 
   if(sb_verbose)
-    fprintf(stderr,"\r%s: loaded.                 \n",path);
+    fprintf(stderr,  "\r%s: loaded.                          \n",path);
   return pcm;
  err:
   op_free(of);
@@ -1190,7 +1262,7 @@
   {flac_id,    flac_load,   "flac"},
   {oggflac_id, oggflac_load,"oggflac"},
   {vorbis_id,  vorbis_load, "oggvorbis"},
-  {opus_id,  opus_load,     "oggopus"},
+  {opus_id,    opus_load,   "oggopus"},
   {sw_id,      sw_load,     "sw"},
   {NULL,       NULL,        NULL}
 };
@@ -1228,6 +1300,7 @@
   if(pcm){
     if(pcm->name)free(pcm->name);
     if(pcm->matrix)free(pcm->matrix);
+    if(pcm->mix)free(pcm->mix);
     if(pcm->data)free(pcm->data);
     memset(pcm,0,sizeof(pcm));
     free(pcm);

Modified: trunk/squishyball/main.c
===================================================================
--- trunk/squishyball/main.c	2013-11-12 04:13:28 UTC (rev 19015)
+++ trunk/squishyball/main.c	2013-11-12 08:51:00 UTC (rev 19016)
@@ -2,7 +2,7 @@
  *
  *  squishyball
  *
- *      Copyright (C) 2010-2012 Xiph.Org
+ *      Copyright (C) 2010-2013 Xiph.Org
  *
  *  squishyball is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -51,7 +51,7 @@
 #define MAXFILES 10
 int sb_verbose=0;
 
-char *short_options="abcd:De:hn:rRs:tvVxBMSg";
+char *short_options="abcd:De:hn:NrRs:tvVxBMSg12";
 
 struct option long_options[] = {
   {"ab",no_argument,0,'a'},
@@ -66,6 +66,7 @@
   {"help",no_argument,0,'h'},
   {"mark-flip",no_argument,0,'M'},
   {"trials",required_argument,0,'n'},
+  {"do-not-normalize",no_argument,0,'N'},
   {"restart-after",no_argument,0,'r'},
   {"restart-every",no_argument,0,'R'},
   {"start-time",required_argument,0,'s'},
@@ -74,6 +75,8 @@
   {"verbose",no_argument,0,'v'},
   {"version",no_argument,0,'V'},
   {"xxy",no_argument,0,'x'},
+  {"downmix-to-mono",no_argument,0,'1'},
+  {"downmix-to-stereo",no_argument,0,'2'},
   {0,0,0,0}
 };
 
@@ -112,6 +115,8 @@
           "                           a short period of silence\n"
           "  -n --trials <n>        : Set desired number of trials\n"
           "                           (default: 20)\n"
+          "  -N --do-not-normalize  : Do not autonormalize samples to avoid\n"
+          "                           clipping\n"
           "  -r --restart-after     : Restart playback from sample start\n"
           "                           after every trial.\n"
           "  -R --restart-every     : Restart playback from sample start\n"
@@ -127,6 +132,8 @@
           "  -v --verbose           : Produce more progress information.\n"
           "  -V --version           : Print version and exit.\n"
           "  -x --xxy               : Perform X/X/Y (triangle) test.\n"
+          "  -1 --downmix-to-mono   : Downmix surround samples to mono.\n"
+          "  -2 --downmix-to-stereo : Downmix surround samples to stereo.\n"
           "\n"
           "INTERACTION:\n"
           "    a b x    : Switch playback between A, B [and X] samples.\n"
@@ -368,6 +375,9 @@
   char *device=NULL;
   int force_dither=0;
   int force_truncate=0;
+  int no_normalize=0;
+  float att=1.;
+  int downmix=0;
   int restart_mode=0;
   int beep_mode=3;
   int tests=20;
@@ -376,7 +386,7 @@
   int outbits=0;
   ao_device *adev=NULL;
   int randomize[MAXFILES];
-  int i;
+  int i,j;
 
   int  cchoice=-1;
   char choice_list[MAXTRIALS];
@@ -469,6 +479,15 @@
     case 'g':
       running_score=1;
       break;
+    case 'N':
+      no_normalize=1;
+      break;
+    case '1':
+      downmix=1;
+      break;
+    case '2':
+      downmix=2;
+      break;
     default:
       usage(stderr);
       exit(1);
@@ -503,12 +522,14 @@
 
   outbits=16;
   for(i=0;i<test_files;i++){
+    float latt;
     pcm[i]=load_audio_file(argv[optind+i]);
     if(!pcm[i])exit(2);
-    check_warn_clipping(pcm[i]);
 
-    if(!pcm[i]->dither && force_dither)pcm[i]->dither=1;
-    if(pcm[i]->bits!=16 && force_truncate)pcm[i]->dither=0;
+    latt=check_warn_clipping(pcm[i],no_normalize);
+    if(downmix==1 && pcm[i]->ch>1) latt=convert_to_mono(pcm[i]);
+    if(downmix==2 && pcm[i]->ch>2) latt=convert_to_stereo(pcm[i]);
+    if(latt<att)att=latt;
 
     /* Are all samples the same rate?  If not, bail. */
     if(pcm[0]->rate != pcm[i]->rate){
@@ -528,12 +549,26 @@
       exit(3);
     }
 
-    if(abs(pcm[i]->bits)>outbits)outbits=abs(pcm[i]->bits);
+    if(abs(pcm[i]->nativebits)>outbits)outbits=abs(pcm[i]->nativebits);
   }
 
+  if(att<1.f && !no_normalize){
+    fprintf(stderr,"Normalizing all inputs by %+0.1fdB...",todB(att));
+    for(i=0;i<test_files;i++){
+      int s = pcm[i]->size/sizeof(float);
+      float *d = (float *)pcm[i]->data;
+      for(j=0;j<s;j++)
+        d[j]*=att;
+    }
+    fprintf(stderr," done\n");
+
+    /* we normalized-- any 16 bit samples are now > 16 bits, ask for 24 */
+    if(outbits<24)outbits=24;
+  }else
+    no_normalize=1;
+
   /* before proceeding, make sure we can open up playback for the
-     requested number of channels and max bit depth; if not, we may
-     need to downconvert. */
+     desired number of channels and max bit depth */
   if(outbits==32)outbits=24;
   ao_initialize();
   if((adev=setup_playback(pcm[0]->rate,pcm[0]->ch,outbits,pcm[0]->matrix,device))==NULL){
@@ -553,13 +588,26 @@
     }
   }
 
-  /* reconcile sample depths */
-  for(i=0;i<test_files;i++){
-    if(outbits==16){
-      convert_to_16(pcm[i]);
+  /* convert-- are we dithering? */
+  if(outbits==16){
+    if(no_normalize){
+      /* no normalization, so dither if any integer samples are natively > 16 bit */
+      int flag=force_dither;
+      for(i=0;i<test_files;i++)
+        if(pcm[i]->nativebits>16)flag=1;
+      if(flag && force_truncate)flag=0;
+
+      for(i=0;i<test_files;i++)
+        convert_to_16(pcm[i],(pcm[i]->nativebits>0 || pcm[i]->nativebits<=16)?0:flag);
+
     }else{
+      /* normalization! dither everything to 16 bit unless force_truncate is set */
+      for(i=0;i<test_files;i++)
+        convert_to_16(pcm[i],!force_truncate);
+    }
+  }else{
+    for(i=0;i<test_files;i++)
       convert_to_24(pcm[i]);
-    }
   }
 
   /* permute/reconcile the matrices before playback begins */
@@ -584,7 +632,7 @@
       for(i=0;i<test_files;i++){
         if(sb_verbose)
         fprintf(stderr,"\t%s: %s\n",pcm[i]->name,
-                make_time_string((double)pcm[i]->size/pcm[i]->ch/((pcm[i]->bits+7)/8)/pcm[i]->rate,0));
+                make_time_string((double)pcm[i]->size/pcm[i]->ch/((pcm[i]->currentbits+7)/8)/pcm[i]->rate,0));
         pcm[i]->size=n;
       }
       if(sb_verbose)
@@ -615,7 +663,7 @@
     int do_seek=0;
     int loop=0;
     off_t seek_to=0;
-    int bps=(pcm[0]->bits+7)/8;
+    int bps=(pcm[0]->currentbits+7)/8;
     int ch=pcm[0]->ch;
     int bpf=ch*bps;
     int rate=pcm[0]->rate;

Modified: trunk/squishyball/main.h
===================================================================
--- trunk/squishyball/main.h	2013-11-12 04:13:28 UTC (rev 19015)
+++ trunk/squishyball/main.h	2013-11-12 08:51:00 UTC (rev 19016)
@@ -2,7 +2,7 @@
  *
  *  squishyball
  *
- *      Copyright (C) 2010 Xiph.Org
+ *      Copyright (C) 2010-2013 Xiph.Org
  *
  *  squishyball is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -31,22 +31,28 @@
 struct pcm_struct {
   char *name;
   int rate;
-  int bits; /* negative indicates IEEE754 float */
+  int currentbits; /* negative indicates float */
+  int nativebits;  /* negative indicates float */
   int ch;
   char *matrix;
+  char *mix;
   unsigned char *data;
   off_t size;
-  int dither;
 };
 
 extern int sb_verbose;
+#define todB(x)   ((x)==0?-400.f:log((x)*(x))*4.34294480f)
 
 extern pcm_t *load_audio_file(char *path);
 extern void free_pcm(pcm_t *pcm);
-void check_warn_clipping(pcm_t *pcm);
+extern float check_warn_clipping(pcm_t *pcm, int no_normalize);
 
-extern void convert_to_16(pcm_t *pcm);
+extern void convert_to_16(pcm_t *pcm, int dither);
 extern void convert_to_24(pcm_t *pcm);
+extern void convert_to_32(pcm_t *pcm);
+extern float convert_to_mono(pcm_t *pcm);
+extern float convert_to_stereo(pcm_t *pcm);
+extern void normalize(pcm_t *pcm, float att);
 extern void reconcile_channel_maps(pcm_t *A, pcm_t *B);
 extern void put_val(unsigned char *d,int bps,float v);
 extern float get_val(unsigned char *d, int bps);

Modified: trunk/squishyball/mincurses.c
===================================================================
--- trunk/squishyball/mincurses.c	2013-11-12 04:13:28 UTC (rev 19015)
+++ trunk/squishyball/mincurses.c	2013-11-12 08:51:00 UTC (rev 19016)
@@ -180,8 +180,8 @@
     return ERR;
 
   if(nonblock){
+#ifdef __APPLE__
     int ret;
-#ifdef __APPLE__
     fd_set fds;
     struct timeval zeroto = {0,0};
     FD_ZERO(&fds);
@@ -192,7 +192,7 @@
     }
 #else
     struct pollfd fds={STDIN_FILENO,POLLIN,0};
-    ret=poll(&fds, 1, 0);
+    poll(&fds, 1, 0);
     if(!fds.revents&(POLLIN)){
       return ERR;
     }

Modified: trunk/squishyball/squishyball.1
===================================================================
--- trunk/squishyball/squishyball.1	2013-11-12 04:13:28 UTC (rev 19015)
+++ trunk/squishyball/squishyball.1	2013-11-12 08:51:00 UTC (rev 19016)
@@ -1,7 +1,7 @@
 .\" Process this file with
 .\" groff -man -Tascii squishyball.1
 .\"
-.TH squishyball 1 "2012 October 28" "Xiph.Org Foundation" "Xiph Evaluation Tools"
+.TH squishyball 1 "2013 November 9" "Xiph.Org Foundation" "Xiph Evaluation Tools"
 
 .SH NAME
 squishyball \- perform sample comparison testing on the command line
@@ -93,6 +93,10 @@
 Mark transitions between samples with a short period of silence (default).
 .IP "\fB-n --trials \fIn"
 Set desired number of comparison trials (default: 20).
+.IP "\fB-N --do-not-normalize"
+Do not perform autonormalization to avoid clipping when sample values
+exceed the maximum playback range in floating point, lossy, and
+downmixed samples.
 .IP "\fB-r --restart-after"
 Set 'restart-after mode', where sample playback restarts from start point
 after every trial.
@@ -112,8 +116,13 @@
 Produce more and more detailed progress information and warnings.
 .IP "\fB-V --version"
 Print version and exit.
+.IP "\fB-1 --downmix-to-mono"
+Downmix all multichannel samples to mono at load time.
+.IP "\fB-2 --downmix-to-stereo"
+Downmix all surround samples to stereo at load time.
 
 .SH KEYBOARD INTERACTION
+
 .IP "\fBa\fR, \fBb\fR, \fBx"
 Switch between A and B samples (A/B mode), or A, B and X samples (A/B/X mode).
 .IP "\fBA\fR, \fBB"
@@ -167,34 +176,48 @@
 .IP \fBOggOpus
 all Opus files
 
-.SH CONVERSION AND DITHER
-\fBsquishyball \fRloads all linear PCM file types at native bit depth.
-Uncompressed floating point files (eg, 32 bit floating point WAV) are
-converted to 24-bit integer PCM.  Opus and Ogg Vorbis files are also
-decoded to 24-bit.
+.SH CONVERSION
+\fBsquishyball\fR 'reconciles' files to identical channel ordering,
+length and bit-depth before playback begins so that CPU and memory
+resource usage during playback should be identical for all samples.
+When 24-bit playback is available and at least one sample is 24-bit or
+greater (ie, 32-bit or float), all samples are converted/promoted to
+24 bits.  If 24-bit playback is unavailable, all samples are demoted
+to 16 bits. Note that Opus and Vorbis files are both considered to be
+natively float formats.
 
-Files are 'reconciled' to identical channel ordering, length and
-bit-depth before playback begins so that CPU and memory resources usage
-during playback should be identical for both samples.  When 24-bit
-playback is available and at least one sample is 24-bit, all samples
-are promoted to 24 bits. If 24-bit playback is unavailable, 24-bit samples
-are demoted to 16 bits.
+.SH NORMALIZATION
 
-Floating point samples (32-bit) are not dithered when converting to
-24-bit.  24-bit and floating point (32 bit) samples are dithered using
-a TPDF when down-conversion to 16-bit is necessary.  Lossy-encoded
-samples (eg Ogg Vorbis and Opus files) are an exception; they are not
-dithered by default during down-conversion. This behavior can be
-overridden by \fB-D\fR, which forces dithering for lossy files as
-well.  Down-conversion dithering can be disabled for all input types
-with \fB-t\fR.
+\fBsquishyball\fR checks files for clipping at load time. By default,
+\fBsquishyball\fR will automatically normalize all float inputs by the
+amount needed to avoid clipping any one.  Automatic normalization can
+be disabled with the \fB-N\fR option.  Integer samples are checked for
+clipping heuristically; two or more consecutive full-range values in a
+channel count as clipped.  Out-of-range integer values cannot be
+recovered; in this case, \fBsquishyball\fR issues a warning and
+performs no normalization based on the integer clipping.
 
-Samples are checked for clipping at load time. Floating point samples
-can be checked accurately. Integer samples are checked heuristically;
-two or more consecutive full-range samples in a channel are counted as
-clipped.  If any definitely or probably clipped samples are found,
-\fBsquishyball\fR issues a warning.
+Downmixing samples to mono with \fB-1\fR or stereo with \fB-2\fR will
+also likely require normalization to avoid clipping; as above,
+\fBsquishyball\fR will automatically normalize all inputs by the
+amount necessary to avoid clipping in any one unless \fB-N\fR is
+specified.
 
+.SH DITHER
+Down-conversions of uncompressed and lossless samples (WAV, AIF[C],
+FLAC, SW) to 16-bit are dithered using a simple white TPDF.
+Lossy-encoded samples (Vorbis and Opus) are dithered to 16-bit only if
+one or more uncompressed/lossless inputs are also being dithered.
+Normalization also triggers dithering of all input samples
+(uncompressed, lossless and lossy) upon conversion to 16 bit.
+
+\fB-D\fR overrides the default behavior and forces unconditional
+dithering of all 16-bit down-conversions.  Similarly, \fB-t\fR forces
+unconditional rounded truncation in all cases, disabling dither
+completely.
+
+Conversions to 24-bit are never dithered.
+
 .SH IMPORTANT USAGE NOTES
 .IP "\fBPlayback Depth and Rate"
 
@@ -202,10 +225,10 @@
 ALSA 'default' device) give no means of determining if the requested
 playback paramters are actually being used by the hardware, or if the
 audio system is helpfully converting everything to some other
-supported depth/rate.  When using these systems, \fBsquishyball\fR has no
-way of knowing if 16-/24-bit playback or sample rate is being
-honored. Automatic conversion will almost always negatively affect
-sample quality.
+supported depth/rate.  When using these systems, \fBsquishyball\fR has
+no way of knowing if 16-/24-bit playback or sample rate is being
+honored. Automatic conversion can affect audible playback quality; be
+careful to verify actual system behavior.
 
 .IP "\fBFlip-Mode Choice"
 

Modified: trunk/squishyball/tty.c
===================================================================
--- trunk/squishyball/tty.c	2013-11-12 04:13:28 UTC (rev 19015)
+++ trunk/squishyball/tty.c	2013-11-12 08:51:00 UTC (rev 19016)
@@ -309,7 +309,7 @@
 
   p_tm=test_mode;
   p_ch=pcm[0]->ch;
-  p_b=pcm[0]->bits;
+  p_b=pcm[0]->currentbits;
   p_r=pcm[0]->rate;
   p_pl=0;
   p_st=start;



More information about the commits mailing list