[xiph-cvs] cvs commit: postfish bessel.c bessel.h Makefile compandpanel.c multibar.c multicompand.c multicompand.h postfish-gtkrc version.h

Monty xiphmont at xiph.org
Wed Mar 17 02:32:05 PST 2004



xiphmont    04/03/17 05:32:05

  Modified:    .        Makefile compandpanel.c multibar.c multicompand.c
                        multicompand.h postfish-gtkrc version.h
  Added:       .        bessel.c bessel.h
  Log:
  Tweak overall usability of mutiband compander
  
  Changes not yet tested; debug begins now.

Revision  Changes    Path
1.16      +4 -2      postfish/Makefile

Index: Makefile
===================================================================
RCS file: /usr/local/cvsroot/postfish/Makefile,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- a/Makefile	8 Mar 2004 02:08:23 -0000	1.15
+++ b/Makefile	17 Mar 2004 10:32:04 -0000	1.16
@@ -22,10 +22,12 @@
 
 SRC = main.c mainpanel.c multibar.c readout.c input.c output.c clippanel.c \
         declip.c reconstruct.c multicompand.c windowbutton.c subpanel.c \
-	feedback.c freq.c eq.c eqpanel.c compandpanel.c subband.c lpc.c
+	feedback.c freq.c eq.c eqpanel.c compandpanel.c subband.c lpc.c \
+	bessel.c
 OBJ = main.o mainpanel.o multibar.o readout.o input.o output.o clippanel.o \
         declip.o reconstruct.o multicompand.o windowbutton.o subpanel.o \
-	feedback.o freq.o eq.o eqpanel.o compandpanel.o subband.o lpc.o
+	feedback.o freq.o eq.o eqpanel.o compandpanel.o subband.o lpc.o \
+	bessel.o
 GCF = `pkg-config --cflags gtk+-2.0` -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED
 
 all:	

<p><p>1.7       +730 -556  postfish/compandpanel.c

Index: compandpanel.c
===================================================================
RCS file: /usr/local/cvsroot/postfish/compandpanel.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- a/compandpanel.c	8 Mar 2004 07:20:15 -0000	1.6
+++ b/compandpanel.c	17 Mar 2004 10:32:04 -0000	1.7
@@ -44,111 +44,401 @@
 typedef struct {
   GtkWidget *label;
   GtkWidget *slider;
-  GtkWidget *readoutg;
-  GtkWidget *readoute;
-  GtkWidget *readoutc;
+  GtkWidget *readouto;
+  GtkWidget *readoutu;
   int number;
 } cbar;
 
+typedef struct {
+  Readout *r0;
+  Readout *r1;
+  Readout *r2;
+} multireadout;
+
 static int bank_active=2;
-static cbar bars[multicomp_freqs_max];
+static cbar bars[multicomp_freqs_max+1];
 static int inactive_updatep=1;
 
+static void under_compand_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float val=1./multibar_get_value(MULTIBAR(w),0);
+
+  if(val>=10){
+    sprintf(buffer,"%4.1f:1",val);
+  }else if(val>=1){
+    sprintf(buffer,"%4.2f:1",val);
+  }else if(val>.10001){
+    sprintf(buffer,"1:%4.2f",1./val);
+  }else{
+    sprintf(buffer,"1:%4.1f",1./val);
+  }
+
+  readout_set(r,buffer);
+  
+  c.under_ratio=rint(val*1000.);
+}
+
+static void over_compand_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float val=1./multibar_get_value(MULTIBAR(w),0);
+
+  if(val>=10){
+    sprintf(buffer,"%4.1f:1",val);
+  }else if(val>=1){
+    sprintf(buffer,"%4.2f:1",val);
+  }else if(val>.10001){
+    sprintf(buffer,"1:%4.2f",1./val);
+  }else{
+    sprintf(buffer,"1:%4.1f",1./val);
+  }
+
+  readout_set(r,buffer);
+  
+  c.over_ratio=rint(val*1000.);
+}
+
+static void suppress_compand_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float val=1./multibar_get_value(MULTIBAR(w),0);
+
+  if(val>.10001){
+    sprintf(buffer,"1:%4.2f",1./val);
+  }else{
+    sprintf(buffer,"1:%4.1f",1./val);
+  }
+
+  readout_set(r,buffer);
+  
+  c.suppress_ratio=rint(val*1000.);
+}
+
+static void under_limit_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float val=140.-multibar_get_value(MULTIBAR(w),0);
+
+  sprintf(buffer,"%3ddB",(int)rint(val));
+  readout_set(r,buffer);
+  
+  c.under_limit=rint(val*10.);
+}
+
+static void over_limit_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float val=140.-multibar_get_value(MULTIBAR(w),0);
+
+  sprintf(buffer,"%3ddB",(int)rint(val));
+  readout_set(r,buffer);
+  
+  c.over_limit=rint(val*10.);
+}
+
+static void under_timing_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  multireadout *r=(multireadout *)in;
+  float attack=multibar_get_value(MULTIBAR(w),0);
+  float decay=multibar_get_value(MULTIBAR(w),1);
+
+  if(attack<10){
+    sprintf(buffer,"%4.2fms",attack);
+  }else if(attack<100){
+    sprintf(buffer,"%4.1fms",attack);
+  }else if (attack<1000){
+    sprintf(buffer,"%4.0fms",attack);
+  }else if (attack<10000){
+    sprintf(buffer,"%4.2fs",attack/1000.);
+  }else{
+    sprintf(buffer,"%4.1fs",attack/1000.);
+  }
+
+  readout_set(r->r0,buffer);
+  
+  if(decay<10){
+    sprintf(buffer,"%4.2fms",decay);
+  }else if (decay<100){
+    sprintf(buffer,"%4.1fms",decay);
+  }else if (decay<1000){
+    sprintf(buffer,"%4.0fms",decay);
+  }else if (decay<10000){
+    sprintf(buffer,"%4.2fs",decay/1000.);
+  }else{
+    sprintf(buffer,"%4.1fs",decay/1000.);
+  }
+
+  readout_set(r->r1,buffer);
+  
+  multicompand_under_attack_set(attack);
+  multicompand_under_decay_set(decay);
+  
+}
+
+static void over_timing_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  multireadout *r=(multireadout *)in;
+  float attack=multibar_get_value(MULTIBAR(w),0);
+  float decay=multibar_get_value(MULTIBAR(w),1);
+
+  if(attack<10){
+    sprintf(buffer,"%4.2fms",attack);
+  }else if(attack<100){
+    sprintf(buffer,"%4.1fms",attack);
+  }else if (attack<1000){
+    sprintf(buffer,"%4.0fms",attack);
+  }else if (attack<10000){
+    sprintf(buffer,"%4.2fs",attack/1000.);
+  }else{
+    sprintf(buffer,"%4.1fs",attack/1000.);
+  }
+
+  readout_set(r->r0,buffer);
+  
+  if(decay<10){
+    sprintf(buffer,"%4.2fms",decay);
+  }else if (decay<100){
+    sprintf(buffer,"%4.1fms",decay);
+  }else if (decay<1000){
+    sprintf(buffer,"%4.0fms",decay);
+  }else if (decay<10000){
+    sprintf(buffer,"%4.2fs",decay/1000.);
+  }else{
+    sprintf(buffer,"%4.1fs",decay/1000.);
+  }
+
+  readout_set(r->r1,buffer);
+  
+  multicompand_over_attack_set(attack);
+  multicompand_over_decay_set(decay);
+  
+}
+
+static void suppress_timing_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  multireadout *r=(multireadout *)in;
+  float attack=multibar_get_value(MULTIBAR(w),0);
+  float decay=multibar_get_value(MULTIBAR(w),1);
+  float release=multibar_get_value(MULTIBAR(w),2);
+
+  if(attack<10){
+    sprintf(buffer,"%4.2fms",attack);
+  }else if(attack<100){
+    sprintf(buffer,"%4.1fms",attack);
+  }else if (attack<1000){
+    sprintf(buffer,"%4.0fms",attack);
+  }else if (attack<10000){
+    sprintf(buffer,"%4.2fs",attack/1000.);
+  }else{
+    sprintf(buffer,"%4.1fs",attack/1000.);
+  }
+
+  readout_set(r->r0,buffer);
+  
+  if(decay<10){
+    sprintf(buffer,"%4.2fms",decay);
+  }else if (decay<100){
+    sprintf(buffer,"%4.1fms",decay);
+  }else if (decay<1000){
+    sprintf(buffer,"%4.0fms",decay);
+  }else if (decay<10000){
+    sprintf(buffer,"%4.2fs",decay/1000.);
+  }else{
+    sprintf(buffer,"%4.1fs",decay/1000.);
+  }
+
+  readout_set(r->r1,buffer);
+
+  if(release<10){
+    sprintf(buffer,"%4.2fms",release);
+  }else if (release<100){
+    sprintf(buffer,"%4.1fms",release);
+  }else if (release<1000){
+    sprintf(buffer,"%4.0fms",release);
+  }else if (release<10000){
+    sprintf(buffer,"%4.2fs",release/1000.);
+  }else{
+    sprintf(buffer,"%4.1fs",release/1000.);
+  }
+
+  readout_set(r->r2,buffer);
+  
+  multicompand_suppress_attack_set(attack);
+  multicompand_suppress_decay_set(decay);
+  multicompand_suppress_release_set(release);
+  
+}
+
+static void under_lookahead_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float val=multibar_get_value(MULTIBAR(w),0);
+
+  sprintf(buffer,"%5.1f%%",val);
+  readout_set(r,buffer);
+  
+  c.under_lookahead=rint(val*10.);
+}
+
+static void over_lookahead_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float val=multibar_get_value(MULTIBAR(w),0);
+
+  sprintf(buffer,"%5.1f%%",val);
+  readout_set(r,buffer);
+  
+  c.over_lookahead=rint(val*10.);
+}
+
+
+static int updating_av_slider=0;
+static void average_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  cbar *b=bars+multicomp_freqs_max;
+  float o,u;
+  float oav=0,uav=0;
+  int i;
+
+  u=rint(multibar_get_value(MULTIBAR(b->slider),0));
+  o=rint(multibar_get_value(MULTIBAR(b->slider),1));
+
+  /* compute the current average */
+  for(i=0;i<multicomp_freqs[bank_active];i++){
+    oav+=rint(bc[bank_active].static_o[i]/10.);
+    uav+=rint(bc[bank_active].static_u[i]/10.);
+  }
+  oav=rint(oav/multicomp_freqs[bank_active]);
+  uav=rint(uav/multicomp_freqs[bank_active]);
+
+  u-=uav;
+  o-=oav;
+
+  if(!updating_av_slider){
+    updating_av_slider=1;
+    if(o!=0.){
+      /* update o sliders */
+      for(i=0;i<multicomp_freqs[bank_active];i++){
+	float val=multibar_get_value(MULTIBAR(bars[i].slider),1);
+	multibar_thumb_set(MULTIBAR(bars[i].slider),val+o,1);
+      }
+      /* update u average (might have pushed it) */
+      uav=0;
+      for(i=0;i<multicomp_freqs[bank_active];i++)
+	uav+=rint(bc[bank_active].static_u[i]/10.);
+      uav=rint(uav/multicomp_freqs[bank_active]);
+      multibar_thumb_set(MULTIBAR(bars[multicomp_freqs_max].slider),uav,0);
+    }else{
+      /* update u sliders */
+      for(i=0;i<multicomp_freqs[bank_active];i++){
+	float val=multibar_get_value(MULTIBAR(bars[i].slider),0);
+	multibar_thumb_set(MULTIBAR(bars[i].slider),val+u,0);
+      }
+      /* update o average (might have pushed it) */
+      oav=0;
+      for(i=0;i<multicomp_freqs[bank_active];i++)
+	oav+=rint(bc[bank_active].static_o[i]/10.);
+      oav=rint(oav/multicomp_freqs[bank_active]);
+      multibar_thumb_set(MULTIBAR(bars[multicomp_freqs_max].slider),oav,1);
+    }
+    updating_av_slider=0;
+  }
+}
+
 static void slider_change(GtkWidget *w,gpointer in){
   char buffer[80];
   cbar *b=(cbar *)in;
-  float g,e,c;
+  float o,u;
+  int i;
 
-  g=multibar_get_value(MULTIBAR(b->slider),0);
-  sprintf(buffer,"%3.0f",g);
-  readout_set(READOUT(b->readoutg),buffer);
-  g=rint(g*10.);
-  bc[bank_active].static_g[b->number]=g;
-  
-  e=multibar_get_value(MULTIBAR(b->slider),1);
-  sprintf(buffer,"%+4.0f",e);
-  readout_set(READOUT(b->readoute),buffer);
-  e=rint(e*10.);
-  bc[bank_active].static_e[b->number]=e;
-
-  c=multibar_get_value(MULTIBAR(b->slider),2);
-  sprintf(buffer,"%+4.0f",c);
-  readout_set(READOUT(b->readoutc),buffer);
-  c=rint(c*10.);
-  bc[bank_active].static_c[b->number]=c;
+  u=multibar_get_value(MULTIBAR(b->slider),0);
+  sprintf(buffer,"%+4.0fdB",u);
+  readout_set(READOUT(b->readoutu),buffer);
+  u=rint(u*10.);
+  bc[bank_active].static_u[b->number]=u;
   
+  o=multibar_get_value(MULTIBAR(b->slider),1);
+  sprintf(buffer,"%+4.0fdB",o);
+  readout_set(READOUT(b->readouto),buffer);
+  o=rint(o*10.);
+  bc[bank_active].static_o[b->number]=o;
+
   if(inactive_updatep){
     /* keep the inactive banks also tracking settings, but only where it
        makes sense */
     
     switch(bank_active){
     case 0:
-      bc[1].static_g[b->number*2]=g;
-      bc[1].static_e[b->number*2]=e;
-      bc[1].static_c[b->number*2]=c;
-      bc[2].static_g[b->number*3+1]=g;
-      bc[2].static_e[b->number*3+1]=e;
-      bc[2].static_c[b->number*3+1]=c;
+      bc[1].static_o[b->number*2]=o;
+      bc[1].static_u[b->number*2]=u;
+      bc[2].static_o[b->number*3+1]=o;
+      bc[2].static_u[b->number*3+1]=u;
       break;
     case 1:
       if((b->number&1)==0){
-	bc[0].static_g[b->number>>1]=g;
-	bc[0].static_e[b->number>>1]=e;
-	bc[0].static_c[b->number>>1]=c;
-	bc[2].static_g[b->number/2*3+1]=g;
-	bc[2].static_e[b->number/2*3+1]=e;
-	bc[2].static_c[b->number/2*3+1]=c;
+	bc[0].static_o[b->number>>1]=o;
+	bc[0].static_u[b->number>>1]=u;
+	bc[2].static_o[b->number/2*3+1]=o;
+	bc[2].static_u[b->number/2*3+1]=u;
       }else{
         if(b->number<19){
-	  float val=(bc[2].static_g[b->number/2*3+2]+
-		     bc[2].static_g[b->number/2*3+3])*.5;
-	  bc[2].static_g[b->number/2*3+2]+=(g-val);
-	  bc[2].static_g[b->number/2*3+3]+=(g-val);
+	  float val=(bc[2].static_o[b->number/2*3+2]+
+		     bc[2].static_o[b->number/2*3+3])*.5;
+	  bc[2].static_o[b->number/2*3+2]+=(o-val);
+	  bc[2].static_o[b->number/2*3+3]+=(o-val);
           
-	  val=(bc[2].static_e[b->number/2*3+2]+
-	       bc[2].static_e[b->number/2*3+3])*.5;
-	  bc[2].static_e[b->number/2*3+2]+=(e-val);
-	  bc[2].static_e[b->number/2*3+3]+=(e-val);
+	  val=(bc[2].static_u[b->number/2*3+2]+
+	       bc[2].static_u[b->number/2*3+3])*.5;
+	  bc[2].static_u[b->number/2*3+2]+=(u-val);
+	  bc[2].static_u[b->number/2*3+3]+=(u-val);
           
-	  val=(bc[2].static_c[b->number/2*3+2]+
-	       bc[2].static_c[b->number/2*3+3])*.5;
-	  bc[2].static_c[b->number/2*3+2]+=(c-val);
-	  bc[2].static_c[b->number/2*3+3]+=(c-val);
         }else{
-	  bc[2].static_g[b->number/2*3+2]=g;
-	  bc[2].static_e[b->number/2*3+2]=e;
-	  bc[2].static_c[b->number/2*3+2]=c;
+	  bc[2].static_o[b->number/2*3+2]=o;
+	  bc[2].static_u[b->number/2*3+2]=u;
         }
       }
       break;
     case 2:
       if((b->number%3)==1){
-	bc[0].static_g[b->number/3]=g;
-	bc[0].static_e[b->number/3]=e;
-	bc[0].static_c[b->number/3]=c;
-	bc[1].static_g[b->number/3*2]=g;
-	bc[1].static_e[b->number/3*2]=e;
-	bc[1].static_c[b->number/3*2]=c;
+	bc[0].static_o[b->number/3]=o;
+	bc[0].static_u[b->number/3]=u;
+	bc[1].static_o[b->number/3*2]=o;
+	bc[1].static_u[b->number/3*2]=u;
       }else if(b->number>1){
         if(b->number<29){
-	  bc[1].static_g[(b->number-1)/3*2+1]=
-	    (bc[2].static_g[(b->number-1)/3*3+2]+
-	     bc[2].static_g[(b->number-1)/3*3+3])*.5;
-	  bc[1].static_e[(b->number-1)/3*2+1]=
-	    (bc[2].static_e[(b->number-1)/3*3+2]+
-	     bc[2].static_e[(b->number-1)/3*3+3])*.5;
-	  bc[1].static_c[(b->number-1)/3*2+1]=
-	    (bc[2].static_c[(b->number-1)/3*3+2]+
-	     bc[2].static_c[(b->number-1)/3*3+3])*.5;
+	  bc[1].static_o[(b->number-1)/3*2+1]=
+	    (bc[2].static_o[(b->number-1)/3*3+2]+
+	     bc[2].static_o[(b->number-1)/3*3+3])*.5;
+	  bc[1].static_u[(b->number-1)/3*2+1]=
+	    (bc[2].static_u[(b->number-1)/3*3+2]+
+	     bc[2].static_u[(b->number-1)/3*3+3])*.5;
         }else{
-	  bc[1].static_g[(b->number-1)/3*2+1]=g;
-	  bc[1].static_e[(b->number-1)/3*2+1]=e;
-	bc[1].static_c[(b->number-1)/3*2+1]=c;
+	  bc[1].static_o[(b->number-1)/3*2+1]=o;
+	  bc[1].static_u[(b->number-1)/3*2+1]=u;
         }
       }
       break;
     }
   }
+
+  /* update average slider */
+  if(!updating_av_slider && bars[multicomp_freqs_max].slider){
+    float oav=0,uav=0;
+    updating_av_slider=1;
+    
+    /* compute the current average */
+    for(i=0;i<multicomp_freqs[bank_active];i++){
+      oav+=rint(bc[bank_active].static_o[i]/10.);
+      uav+=rint(bc[bank_active].static_u[i]/10.);
+    }
+    oav=rint(oav/multicomp_freqs[bank_active]);
+    uav=rint(uav/multicomp_freqs[bank_active]);
+    
+    multibar_thumb_set(MULTIBAR(bars[multicomp_freqs_max].slider),uav,0);
+    multibar_thumb_set(MULTIBAR(bars[multicomp_freqs_max].slider),oav,1);
+    updating_av_slider=0;
+  }
 }
 
 static void static_octave(GtkWidget *w,gpointer in){
@@ -165,20 +455,17 @@
                              multicomp_freq_labels[bank_active][i]);
           gtk_widget_show(bars[i].label);
           gtk_widget_show(bars[i].slider);
-	  gtk_widget_show(bars[i].readoutg);
-	  gtk_widget_show(bars[i].readoute);
-	  gtk_widget_show(bars[i].readoutc);
+	  gtk_widget_show(bars[i].readouto);
+	  gtk_widget_show(bars[i].readoutu);
           
           inactive_updatep=0;
           
           {
-	    float g=bc[bank_active].static_g[i]/10.;
-	    float e=bc[bank_active].static_e[i]/10.;
-	    float c=bc[bank_active].static_c[i]/10.;
-
-	    multibar_thumb_set(MULTIBAR(bars[i].slider),g,0);
-	    multibar_thumb_set(MULTIBAR(bars[i].slider),e,1);
-	    multibar_thumb_set(MULTIBAR(bars[i].slider),c,2);
+	    float o=bc[bank_active].static_o[i]/10.;
+	    float u=bc[bank_active].static_u[i]/10.;
+
+	    multibar_thumb_set(MULTIBAR(bars[i].slider),u,0);
+	    multibar_thumb_set(MULTIBAR(bars[i].slider),o,1);
           }
 
           inactive_updatep=1;
@@ -186,9 +473,8 @@
         }else{
           gtk_widget_hide(bars[i].label);
           gtk_widget_hide(bars[i].slider);
-	  gtk_widget_hide(bars[i].readoutg);
-	  gtk_widget_hide(bars[i].readoute);
-	  gtk_widget_hide(bars[i].readoutc);
+	  gtk_widget_hide(bars[i].readouto);
+	  gtk_widget_hide(bars[i].readoutu);
         }
       }
       multicompand_set_bank(bank_active);
@@ -197,211 +483,34 @@
   }
 }
 
-static void static_compressor_change(GtkWidget *w,gpointer in){
-  char buffer[80];
-  Readout *r=READOUT(in);
-  float val=-multibar_get_value(MULTIBAR(w),0);
-
-  if(rint(1./val)>=10.)
-    sprintf(buffer,"%3.1f:1",1./val);
-  else
-    sprintf(buffer,"%3.2f:1",1./val);
-
-  if(val==1.)
-    sprintf(buffer,"   off");
-
-  readout_set(r,buffer);
-  c.static_c_ratio=rint(100./val);
-}
-
-static void static_expander_change(GtkWidget *w,gpointer in){
-  char buffer[80];
-  Readout *r=READOUT(in);
-  float val=-multibar_get_value(MULTIBAR(w),0);
-
-  if(rint(1./val)>=10.)
-    sprintf(buffer,"%3.1f:1",1./val);
-  else
-  sprintf(buffer,"%3.2f:1",1./val);
-
-  if(val==1.)
-    sprintf(buffer,"   off");
-
-  readout_set(r,buffer);
-  c.static_e_ratio=rint(100./val);
-}
-
-static void static_decay_change(GtkWidget *w,gpointer in,sig_atomic_t *v){
-  char buffer[80];
-  Readout *r=READOUT(in);
-  float val=multibar_get_value(MULTIBAR(w),0);
-  float set=val/input_rate*(1024*1024);
-  float ms =-100000./val;
-
-  if(ms<100)
-    sprintf(buffer,".%04d",(int)rint(ms*10.));
-  else if(ms<1000)
-    sprintf(buffer,".%03d",(int)rint(ms));
-  else if(ms<10000)
-    sprintf(buffer,"%4.2f",ms/1000);
-  else
-    sprintf(buffer,"%4.1f",ms/1000);
-
-  if(ms<=10)
-    sprintf(buffer," fast");
-
-  if(ms>99999.)
-    sprintf(buffer," slow");
-
-  readout_set(r,buffer);
-  *v=rint(set);
-}
-
-static void static_c_decay_change(GtkWidget *w,gpointer in){
-  static_decay_change(w,in,&c.static_c_decay);
-}
-static void static_e_decay_change(GtkWidget *w,gpointer in){
-  static_decay_change(w,in,&c.static_e_decay);
-}
-static void static_g_decay_change(GtkWidget *w,gpointer in){
-  static_decay_change(w,in,&c.static_g_decay);
-}
-
-static void static_trim_change(GtkWidget *w,gpointer in,sig_atomic_t *v){
-  char buffer[80];
-  Readout *r=READOUT(in);
-  float val=multibar_get_value(MULTIBAR(w),0);
-
-  sprintf(buffer,"%+3.0fdB",val);
-
-  readout_set(r,buffer);
-  *v=rint(val*10.);
-}
-
-static void static_c_trim_change(GtkWidget *w,gpointer in){
-  static_trim_change(w,in,&c.static_c_trim);
-}
-static void static_e_trim_change(GtkWidget *w,gpointer in){
-  static_trim_change(w,in,&c.static_e_trim);
-}
-static void static_g_trim_change(GtkWidget *w,gpointer in){
-  static_trim_change(w,in,&c.static_g_trim);
-
-}
-
-static void static_trim_apply(GtkWidget *w,gpointer in,int gec){
-  char buffer[80];
-  int i,j;
-  Multibar *m=MULTIBAR(in);
-  float trim=multibar_get_value(m,0);
-
-  /* apply the trim to all sliders */
-  for(j=0;j<multicomp_freqs[bank_active];j++){
-    float sv=multibar_get_value(MULTIBAR(bars[j].slider),gec);
-    multibar_thumb_set(MULTIBAR(bars[j].slider),sv+trim,gec);
-  }
-
-  multibar_thumb_set(m,0,0);
-}
-
-static void static_c_trim_apply(GtkWidget *w,gpointer in){
-  static_trim_apply(w,in,2);
-}
-static void static_e_trim_apply(GtkWidget *w,gpointer in){
-  static_trim_apply(w,in,1);
-}
-static void static_g_trim_apply(GtkWidget *w,gpointer in){
-  static_trim_apply(w,in,0);
-}
-
 static void link_toggled(GtkToggleButton *b,gpointer in){
   int active=gtk_toggle_button_get_active(b);
   c.link_mode=active;
 }
 
-static void static_mode(GtkButton *b,gpointer in){
+static void over_mode(GtkButton *b,gpointer in){
   int mode=(int)in;
-  c.static_mode=mode;
+  c.over_mode=mode;
 }
 
-static void suppressor_mode(GtkButton *b,gpointer in){
+static void under_mode(GtkButton *b,gpointer in){
   int mode=(int)in;
-  c.suppress_mode=mode;
+  c.under_mode=mode;
 }
 
-static void envelope_mode(GtkButton *b,gpointer in){
+static void under_knee(GtkButton *b,gpointer in){
   int mode=(int)in;
-  c.envelope_mode=mode;
-}
-
-static void suppress_decay_change(GtkWidget *w,gpointer in){
-  char buffer[80];
-  Readout *r=READOUT(in);
-  float val=multibar_get_value(MULTIBAR(w),0);
-  float set=val/input_rate*(1024*1024);
-  float ms =-100000./val;
-
-  if(ms<100)
-    sprintf(buffer,".%04d",(int)rint(ms*10.));
-  else if(ms<1000)
-    sprintf(buffer,".%03d",(int)rint(ms));
-  else if(ms<10000)
-    sprintf(buffer,"%4.2f",ms/1000);
-  else
-    sprintf(buffer,"%4.1f",ms/1000);
-
-  if(ms<=10)
-    sprintf(buffer," fast");
-
-  if(ms>99999.)
-    sprintf(buffer," slow");
-
-  readout_set(r,buffer);
-  c.suppress_decay=rint(set);
-}
-
-static void suppress_ratio_change(GtkWidget *w,gpointer in){
-  char buffer[80];
-  Readout *r=READOUT(in);
-  float val=-multibar_get_value(MULTIBAR(w),0);
-
-  if(rint(1./val)>=10.)
-    sprintf(buffer,"%3.1f:1",1./val);
-  else
-    sprintf(buffer,"%3.2f:1",1./val);
-
-  if(val==1.)
-    sprintf(buffer,"   off");
-
-  readout_set(r,buffer);
-  c.suppress_ratio=rint(100./val);
+  c.under_softknee=mode;
 }
 
-static void suppress_depth_change(GtkWidget *w,gpointer in){
-  char buffer[80];
-  Readout *r=READOUT(in);
-  float val=-140.-multibar_get_value(MULTIBAR(w),0);
-  
-  sprintf(buffer,"%3.0fdB",-val);
-
-  if(val==0.)
-    sprintf(buffer,"  off");
-
-  if(val==-140)
-    sprintf(buffer," deep");
-
-  readout_set(r,buffer); 
-  c.suppress_depth=rint(val*10.);
+static void over_knee(GtkButton *b,gpointer in){
+  int mode=(int)in;
+  c.over_softknee=mode;
 }
 
-static void envelope_compander_change(GtkWidget *w,gpointer in){
-  char buffer[80];
-  Readout *r=READOUT(in);
-  float val=multibar_get_value(MULTIBAR(w),0);
-
-  sprintf(buffer,"%+5.1f%%",val);
-  readout_set(r,buffer);
-  c.envelope_c=rint(val*10.);
+static void suppress_mode(GtkButton *b,gpointer in){
+  int mode=(int)in;
+  c.suppress_mode=mode;
 }
 
 void compandpanel_create(postfish_mainpanel *mp,
@@ -413,41 +522,51 @@
   float levels[15]={-140,-130,-120,-110,-100,-90,-80,-70,-60,-50,-40,
                      -30,-20,-10,0};
 
-  float ratio_levels[7]={-1.,-.833,-.667,-.5,-.33,-.2,-.1};
-  char  *ratio_labels[6]={"1.2:1","1.5:1","2:1","3:1","5:1","10:1"};
+  float compand_levels[9]={.1,.25,.5,.667,1,1.5,2,4,10};
+  char  *compand_labels[8]={"4:1","2:1","1:1.5","1:1","1:1.5","1:2","1:4","1:10"};
 
-  float decay_levels[7]={-10000,-1000,-100,-30,-10,-3,-1};
-  char  *decay_labels[6]={".1","1","3","10","30","slow"};
-    
-  float bias_levels[7]={-30,-20,-10,0,10,20,30};
-  char  *bias_labels[6]={"20","10","0","10","20","30"};
+  float compand_levels_low[5]={1,1.2,1.5,2,3};
+  char  *compand_labels_low[4]={"1:1.2","1:1.5","1:2","1:3"};
+
+  float timing_levels[6]={.5,1,10,100,1000,10000};
+  char  *timing_labels[5]={"1ms","10ms","100ms","1s","10s"};
 
-  float depth_levels[7]={-140,-130,-120,-110,-100,-80,-0};
-  char  *depth_labels[6]={"10","20","30","40","60","deep"};
+  float limit_levels[9]={0,40,60,80,100,110,120,130,135};
+  char  *limit_labels[8]={"100dB","80dB","60dB","40dB","30dB","20dB","10dB","5dB"};
+
+  float per_levels[9]={0,12.5,25,37.5,50,62.5,75,87.5,100};
+  char  *per_labels[8]={"","25%","","50%","","75%","","100%"};
 
 
   subpanel_generic *panel=subpanel_create(mp,windowbutton,activebutton,
                                           &compand_active,
                                           &compand_visible,
-					  "_Multiband Compand and Noise Gate"," [m] ");
+					  "_Multiband Compand"," [m] ");
   
   GtkWidget *hbox=gtk_hbox_new(0,0);
   GtkWidget *sliderbox=gtk_vbox_new(0,0);
   GtkWidget *sliderframe=gtk_frame_new(NULL);
   GtkWidget *staticbox=gtk_vbox_new(0,0);
-  GtkWidget *staticframe=gtk_frame_new(NULL);
-  GtkWidget *slidertable=gtk_table_new(multicomp_freqs_max+1,5,0);
+  GtkWidget *slidertable=gtk_table_new(multicomp_freqs_max+2,4,0);
+
+  GtkWidget *overlabel=gtk_label_new("Over threshold compand");
+  GtkWidget *overtable=gtk_table_new(6,4,0);
 
-  GtkWidget *envelopeframe=gtk_frame_new(" Dynamic Range ");
-  GtkWidget *suppressframe=gtk_frame_new(" Reverberation Suppressor ");
+  GtkWidget *underlabel=gtk_label_new("Under threshold compand");
+  GtkWidget *undertable=gtk_table_new(5,4,0);
 
-  GtkWidget *envelopetable=gtk_table_new(3,3,0);
-  GtkWidget *suppresstable=gtk_table_new(4,3,0);
-  GtkWidget *statictable=gtk_table_new(10,4,0);
+  GtkWidget *suppressframe=gtk_frame_new(" Reverb suppression ");
+  GtkWidget *suppresstable=gtk_table_new(4,5,0);
 
   GtkWidget *link_box=gtk_hbox_new(0,0);
   GtkWidget *link_check=gtk_check_button_new_with_mnemonic("_link channel envelopes");
 
+  gtk_widget_set_name(overlabel,"framelabel");
+  gtk_widget_set_name(underlabel,"framelabel");
+  gtk_container_set_border_width(GTK_CONTAINER(suppresstable),4);
+
+  
+    
   {
   
     GtkWidget *octave_box=gtk_hbox_new(0,0);
@@ -458,9 +577,14 @@
     GtkWidget *octave_c=
       gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(octave_b),
                                                   "third-octave");
-    GtkWidget *label2=gtk_label_new("gate");
-    GtkWidget *label3=gtk_label_new("expd");
-    GtkWidget *label4=gtk_label_new("cmpr");
+    GtkWidget *label2=gtk_label_new("under");
+    GtkWidget *label3=gtk_label_new("over");
+
+    gtk_misc_set_alignment(GTK_MISC(label2),.5,1.);
+    gtk_misc_set_alignment(GTK_MISC(label3),.5,1.);
+    gtk_widget_set_name(label2,"scalemarker");
+    gtk_widget_set_name(label3,"scalemarker");
+
 
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(octave_c),1);
 
@@ -471,14 +595,13 @@
     g_signal_connect (G_OBJECT (octave_c), "clicked",
                       G_CALLBACK (static_octave), (gpointer)2);
    
-    gtk_table_attach(GTK_TABLE(slidertable),label2,2,3,0,1,GTK_FILL,0,2,0);
-    gtk_table_attach(GTK_TABLE(slidertable),label3,3,4,0,1,GTK_FILL,0,2,0);
-    gtk_table_attach(GTK_TABLE(slidertable),label4,4,5,0,1,GTK_FILL,0,2,0);
-
+    gtk_table_attach(GTK_TABLE(slidertable),label2,1,2,0,1,GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(slidertable),label3,3,4,0,1,GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+ 
     gtk_box_pack_start(GTK_BOX(octave_box),octave_a,0,1,3);
     gtk_box_pack_start(GTK_BOX(octave_box),octave_b,0,1,3);
     gtk_box_pack_start(GTK_BOX(octave_box),octave_c,0,1,3);
-    gtk_table_attach(GTK_TABLE(slidertable),octave_box,1,2,0,1,
+    gtk_table_attach(GTK_TABLE(slidertable),octave_box,2,3,0,1,
                      GTK_EXPAND,0,0,0);
 
     gtk_box_pack_start(GTK_BOX(sliderbox),sliderframe,0,0,0);
@@ -503,314 +626,347 @@
   gtk_box_pack_start(GTK_BOX(hbox),sliderbox,0,0,0);
   gtk_box_pack_start(GTK_BOX(hbox),staticbox,0,0,0);
 
-  gtk_box_pack_start(GTK_BOX(staticbox),staticframe,0,0,0);
+  gtk_box_pack_start(GTK_BOX(staticbox),overtable,0,0,0);
+  gtk_box_pack_start(GTK_BOX(staticbox),undertable,0,0,10);
+ 
+
   gtk_box_pack_end(GTK_BOX(staticbox),suppressframe,0,0,0);
-  gtk_box_pack_end(GTK_BOX(staticbox),envelopeframe,0,0,0);
-  gtk_container_add(GTK_CONTAINER(staticframe),statictable);
-  gtk_container_add(GTK_CONTAINER(envelopeframe),envelopetable);
   gtk_container_add(GTK_CONTAINER(suppressframe),suppresstable);
 
   gtk_container_set_border_width(GTK_CONTAINER(suppressframe),5);
-  gtk_container_set_border_width(GTK_CONTAINER(envelopeframe),5);
-  gtk_container_set_border_width(GTK_CONTAINER(envelopeframe),5);
-  gtk_frame_set_shadow_type(GTK_FRAME(staticframe),GTK_SHADOW_NONE);
+  gtk_container_set_border_width(GTK_CONTAINER(overtable),5);
+  gtk_container_set_border_width(GTK_CONTAINER(undertable),5);
 
-  /* static compand */
+  /* under compand: mode and knee */
   {
-
-    //GtkWidget *label0=gtk_label_new("mode:");
     GtkWidget *envelopebox=gtk_hbox_new(0,0);
     GtkWidget *rms_button=gtk_radio_button_new_with_label(NULL,"RMS");
     GtkWidget *peak_button=
       gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(rms_button),
                                                   "peak");
+    GtkWidget *knee_button=gtk_check_button_new_with_label("soft knee");
 
-    GtkWidget *labelR=gtk_label_new("Ratio");
-    GtkWidget *labelR1=gtk_label_new("cmpr:");
-    GtkWidget *labelR2=gtk_label_new("expd:");
-    GtkWidget *readoutR1=readout_new("1.50:1");
-    GtkWidget *readoutR2=readout_new("1.50:1");
-    GtkWidget *sliderR1=multibar_slider_new(6,ratio_labels,ratio_levels,1);
-    GtkWidget *sliderR2=multibar_slider_new(6,ratio_labels,ratio_levels,1);
-
-    GtkWidget *labelD=gtk_label_new("Decay");
-    GtkWidget *labelD1=gtk_label_new("cmpr:");
-    GtkWidget *labelD2=gtk_label_new("expd:");
-    GtkWidget *labelD3=gtk_label_new("gate:");
-    GtkWidget *readoutD1=readout_new(" fast");
-    GtkWidget *readoutD2=readout_new(" fast");
-    GtkWidget *readoutD3=readout_new(" fast");
-    GtkWidget *sliderD1=multibar_slider_new(6,decay_labels,decay_levels,1);
-    GtkWidget *sliderD2=multibar_slider_new(6,decay_labels,decay_levels,1);
-    GtkWidget *sliderD3=multibar_slider_new(6,decay_labels,decay_levels,1);
-
-    GtkWidget *labelT=gtk_label_new("Trim");
-    GtkWidget *labelT1=gtk_label_new("cmpr:");
-    GtkWidget *labelT2=gtk_label_new("expd:");
-    GtkWidget *labelT3=gtk_label_new("gate:");
-    GtkWidget *readoutT1=readout_new("-40dB");
-    GtkWidget *readoutT2=readout_new("-40dB");
-    GtkWidget *readoutT3=readout_new("-40dB");
-    GtkWidget *sliderT1=multibar_slider_new(6,bias_labels,bias_levels,1);
-    GtkWidget *sliderT2=multibar_slider_new(6,bias_labels,bias_levels,1);
-    GtkWidget *sliderT3=multibar_slider_new(6,bias_labels,bias_levels,1);
-    GtkWidget *buttonT1=gtk_button_new_with_label("apply");
-    GtkWidget *buttonT2=gtk_button_new_with_label("apply");
-    GtkWidget *buttonT3=gtk_button_new_with_label("apply");
-
+    gtk_box_pack_start(GTK_BOX(envelopebox),underlabel,0,0,5);
     gtk_box_pack_end(GTK_BOX(envelopebox),peak_button,0,0,5);
     gtk_box_pack_end(GTK_BOX(envelopebox),rms_button,0,0,5);
+    gtk_box_pack_end(GTK_BOX(envelopebox),knee_button,0,0,5);
+
+    g_signal_connect (G_OBJECT (knee_button), "clicked",
+		      G_CALLBACK (under_knee), (gpointer)0);
     g_signal_connect (G_OBJECT (rms_button), "clicked",
-		      G_CALLBACK (static_mode), (gpointer)0); //To Hell I Go
+		      G_CALLBACK (under_mode), (gpointer)0);
     g_signal_connect (G_OBJECT (peak_button), "clicked",
-		      G_CALLBACK (static_mode), (gpointer)1);
+		      G_CALLBACK (under_mode), (gpointer)1); //To Hell I Go
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rms_button),1);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(knee_button),1);
+    gtk_table_attach(GTK_TABLE(undertable),envelopebox,0,4,0,1,GTK_FILL,0,0,0);
+  }
 
-    g_signal_connect (G_OBJECT (buttonT1), "clicked",
-		      G_CALLBACK (static_c_trim_apply), sliderT1);
-    g_signal_connect (G_OBJECT (buttonT2), "clicked",
-		      G_CALLBACK (static_e_trim_apply), sliderT2);
-    g_signal_connect (G_OBJECT (buttonT3), "clicked",
-		      G_CALLBACK (static_g_trim_apply), sliderT3);
-
-    gtk_container_set_border_width(GTK_CONTAINER(statictable),4);
-
-    multibar_callback(MULTIBAR(sliderR1),static_compressor_change,readoutR1);
-    multibar_thumb_set(MULTIBAR(sliderR1),-1.,0);
-    multibar_callback(MULTIBAR(sliderR2),static_expander_change,readoutR2);
-    multibar_thumb_set(MULTIBAR(sliderR2),-1.,0);
-
-    multibar_callback(MULTIBAR(sliderD1),static_c_decay_change,readoutD1);
-    multibar_thumb_set(MULTIBAR(sliderD1),-100000.,0);
-    multibar_callback(MULTIBAR(sliderD2),static_e_decay_change,readoutD2);
-    multibar_thumb_set(MULTIBAR(sliderD2),-100.,0);
-    multibar_callback(MULTIBAR(sliderD3),static_g_decay_change,readoutD3);
-    multibar_thumb_set(MULTIBAR(sliderD3),-10.,0);
-
-    multibar_callback(MULTIBAR(sliderT1),static_c_trim_change,readoutT1);
-    multibar_thumb_increment(MULTIBAR(sliderT1),1.,10.);
-    multibar_callback(MULTIBAR(sliderT2),static_e_trim_change,readoutT2);
-    multibar_thumb_increment(MULTIBAR(sliderT2),1.,10.);
-    multibar_callback(MULTIBAR(sliderT3),static_g_trim_change,readoutT3);
-    multibar_thumb_increment(MULTIBAR(sliderT3),1.,10.);
-
-    //gtk_misc_set_alignment(GTK_MISC(labelR),0,.5);
-    //gtk_misc_set_alignment(GTK_MISC(labelD),0,.5);
-    //gtk_misc_set_alignment(GTK_MISC(labelT),0,.5);
-    gtk_widget_set_name(labelR,"framelabel");
-    gtk_widget_set_name(labelD,"framelabel");
-    gtk_widget_set_name(labelT,"framelabel");
-
-    gtk_misc_set_alignment(GTK_MISC(labelR1),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(labelR2),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(labelD1),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(labelD2),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(labelD3),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(labelT1),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(labelT2),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(labelT3),1,.5);
-
-    //gtk_table_attach(GTK_TABLE(statictable),label0,0,1,0,1,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),envelopebox,1,4,0,1,GTK_FILL,0,0,0);
-
-
-    gtk_table_attach(GTK_TABLE(statictable),labelR,0,4,0,1,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),labelR1,0,1,1,2,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),labelR2,0,1,2,3,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),sliderR1,1,3,1,2,
-		     GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(statictable),sliderR2,1,3,2,3,
-		     GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(statictable),readoutR1,3,4,1,2,GTK_FILL,0,0,2);
-    gtk_table_attach(GTK_TABLE(statictable),readoutR2,3,4,2,3,GTK_FILL,0,0,2);
-
-
-    gtk_table_attach(GTK_TABLE(statictable),labelD,0,4,3,4,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),labelD1,0,1,4,5,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),labelD2,0,1,5,6,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),labelD3,0,1,6,7,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),sliderD1,1,3,4,5,
-		     GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(statictable),sliderD2,1,3,5,6,
-		     GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(statictable),sliderD3,1,3,6,7,
-		     GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(statictable),readoutD1,3,4,4,5,GTK_FILL,0,0,2);
-    gtk_table_attach(GTK_TABLE(statictable),readoutD2,3,4,5,6,GTK_FILL,0,0,2);
-    gtk_table_attach(GTK_TABLE(statictable),readoutD3,3,4,6,7,GTK_FILL,0,0,2);
-
-    gtk_table_attach(GTK_TABLE(statictable),labelT,0,4,7,8,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),labelT1,0,1,8,9,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),labelT2,0,1,9,10,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),labelT3,0,1,10,11,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),sliderT1,1,2,8,9,
-		     GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(statictable),sliderT2,1,2,9,10,
-		     GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(statictable),sliderT3,1,2,10,11,
-		     GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(statictable),buttonT1,3,4,8,9,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),buttonT2,3,4,9,10,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),buttonT3,3,4,10,11,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(statictable),readoutT1,2,3,8,9,GTK_FILL,0,0,2);
-    gtk_table_attach(GTK_TABLE(statictable),readoutT2,2,3,9,10,GTK_FILL,0,0,2);
-    gtk_table_attach(GTK_TABLE(statictable),readoutT3,2,3,10,11,GTK_FILL,0,0,2);
-
-    gtk_table_set_row_spacing(GTK_TABLE(statictable),2,5);
-    gtk_table_set_row_spacing(GTK_TABLE(statictable),6,5);
-    gtk_table_set_row_spacing(GTK_TABLE(statictable),10,10);
-
-    gtk_container_set_border_width(GTK_CONTAINER(statictable),4);
- }
-
-  /* envelope compand */
-  {
-    float levels[11]={-50,-40,-30,-20,-10,0,10,20,30,40,50};
-    char *labels[10]={"40%","30%","20%","10%","0%","10%","20%",
-		      "30%","40%","50%"};
-
-    //GtkWidget *label0=gtk_label_new("mode:");
-    GtkWidget *label1=gtk_label_new("compress");
-    GtkWidget *label2=gtk_label_new("expand");
-    GtkWidget *envelope_compress_readout=readout_new(" 0.0%");
-    GtkWidget *envelope_compress_slider=multibar_slider_new(10,labels,levels,1);
+  /* under compand: ratio */
+  {
 
+    GtkWidget *label=gtk_label_new("compand ratio:");
+    GtkWidget *readout=readout_new("1.55:1");
+    GtkWidget *slider=multibar_slider_new(8,compand_labels,compand_levels,1);
+   
+    multibar_callback(MULTIBAR(slider),under_compand_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),1.,0);
+    multibar_thumb_increment(MULTIBAR(slider),.01,.1);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
+
+    gtk_table_set_row_spacing(GTK_TABLE(undertable),0,4);
+    gtk_table_attach(GTK_TABLE(undertable),label,0,1,1,2,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(undertable),slider,1,3,1,2,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(undertable),readout,3,4,1,2,GTK_FILL,0,0,0);
+
+  }
+
+  /* under compand: limit */
+  {
+
+    GtkWidget *label=gtk_label_new("compand limit:");
+    GtkWidget *readout=readout_new("140dB");
+    GtkWidget *slider=multibar_slider_new(8,limit_labels,limit_levels,1);
+   
+    multibar_callback(MULTIBAR(slider),under_limit_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),0.,0);
+    multibar_thumb_increment(MULTIBAR(slider),1.,10.);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1,.5);
+    
+    gtk_table_set_row_spacing(GTK_TABLE(undertable),1,4);
+    gtk_table_attach(GTK_TABLE(undertable),label,0,1,2,3,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(undertable),slider,1,3,2,3,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(undertable),readout,3,4,2,3,GTK_FILL,0,0,0);
+  }
+
+  /* under compand: timing */
+  {
+
+    GtkWidget *label=gtk_label_new("attack/decay:");
+    GtkWidget *readout1=readout_new(" 100ms");
+    GtkWidget *readout2=readout_new(" 100ms");
+    GtkWidget *slider=multibar_slider_new(5,timing_labels,timing_levels,2);
+    multireadout *r=calloc(1,sizeof(*r));
+
+    r->r0=(Readout *)readout1;
+    r->r1=(Readout *)readout2;
+   
+    multibar_callback(MULTIBAR(slider),under_timing_change,r);
+    multibar_thumb_set(MULTIBAR(slider),1,0);
+    multibar_thumb_set(MULTIBAR(slider),100,1);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1,.5);
+
+    gtk_table_set_row_spacing(GTK_TABLE(undertable),2,4);
+    gtk_table_attach(GTK_TABLE(undertable),label,0,1,4,5,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(undertable),slider,1,2,4,5,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(undertable),readout1,2,3,4,5,GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(undertable),readout2,3,4,4,5,GTK_FILL,0,0,0);
+
+  }
+
+  /* under compand: lookahead */
+  {
+
+    GtkWidget *label=gtk_label_new("lookahead:");
+    GtkWidget *readout=readout_new("100%");
+    GtkWidget *slider=multibar_slider_new(8,per_labels,per_levels,1);
+    GtkWidget *labelbox=gtk_hbox_new(0,0);
+    
+    multibar_callback(MULTIBAR(slider),under_lookahead_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),100.,0);
+    multibar_thumb_increment(MULTIBAR(slider),1.,10.);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1,.5);
+    
+    gtk_table_set_row_spacing(GTK_TABLE(undertable),3,4);
+    gtk_table_attach(GTK_TABLE(undertable),label,0,1,3,4,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(undertable),slider,1,3,3,4,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(undertable),readout,3,4,3,4,GTK_FILL,0,0,0);
+  }
+
+  /* over compand: mode and knee */
+  {
     GtkWidget *envelopebox=gtk_hbox_new(0,0);
     GtkWidget *rms_button=gtk_radio_button_new_with_label(NULL,"RMS");
     GtkWidget *peak_button=
       gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(rms_button),
                                                   "peak");
+    GtkWidget *knee_button=gtk_check_button_new_with_label("soft knee");
 
-    //gtk_box_pack_start(GTK_BOX(envelopebox),label0,0,0,1);
+    gtk_box_pack_start(GTK_BOX(envelopebox),overlabel,0,0,5);
     gtk_box_pack_end(GTK_BOX(envelopebox),peak_button,0,0,5);
     gtk_box_pack_end(GTK_BOX(envelopebox),rms_button,0,0,5);
+    gtk_box_pack_end(GTK_BOX(envelopebox),knee_button,0,0,5);
 
+    g_signal_connect (G_OBJECT (knee_button), "clicked",
+		      G_CALLBACK (over_knee), (gpointer)0);
     g_signal_connect (G_OBJECT (rms_button), "clicked",
-		      G_CALLBACK (envelope_mode), (gpointer)0); //To Hell I Go
+		      G_CALLBACK (over_mode), (gpointer)0);
     g_signal_connect (G_OBJECT (peak_button), "clicked",
-		      G_CALLBACK (envelope_mode), (gpointer)1);
+		      G_CALLBACK (over_mode), (gpointer)1); //To Hell I Go
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rms_button),1);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(knee_button),1);
+    gtk_table_attach(GTK_TABLE(overtable),envelopebox,0,4,0,1,GTK_FILL,0,0,0);
+  }
 
+  /* over compand: ratio */
+  {
+
+    GtkWidget *label=gtk_label_new("compand ratio:");
+    GtkWidget *readout=readout_new("1.55:1");
+    GtkWidget *slider=multibar_slider_new(8,compand_labels,compand_levels,1);
    
-    gtk_container_set_border_width(GTK_CONTAINER(envelopetable),4);
-    multibar_callback(MULTIBAR(envelope_compress_slider),
-		      envelope_compander_change,
-		      envelope_compress_readout);
-    multibar_thumb_increment(MULTIBAR(envelope_compress_slider),.1,1.);
-
-    gtk_widget_set_name(label1,"smallmarker");
-    gtk_widget_set_name(label2,"smallmarker");
-    gtk_misc_set_alignment(GTK_MISC(label1),0,.5);
-    gtk_misc_set_alignment(GTK_MISC(label2),1,.5);
-
-    gtk_table_attach(GTK_TABLE(envelopetable),label1,0,1,2,3,GTK_FILL,0,2,0);
-    gtk_table_attach(GTK_TABLE(envelopetable),label2,1,2,2,3,GTK_FILL,0,2,0);
-    gtk_table_attach(GTK_TABLE(envelopetable),envelopebox,
-		     0,3,0,1,GTK_FILL,0,0,0);
-
-    gtk_table_attach(GTK_TABLE(envelopetable),envelope_compress_slider,
-		     0,2,1,2,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(envelopetable),envelope_compress_readout,
-		     2,3,1,2,GTK_FILL,0,0,0);
+    multibar_callback(MULTIBAR(slider),over_compand_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),1.,0);
+    multibar_thumb_increment(MULTIBAR(slider),.01,.1);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
+
+    gtk_table_set_row_spacing(GTK_TABLE(overtable),0,4);
+    gtk_table_attach(GTK_TABLE(overtable),label,0,1,1,2,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),slider,1,3,1,2,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),readout,3,4,1,2,GTK_FILL,0,0,0);
 
   }
 
-  /* suppressor */
-  {
-
-    GtkWidget *suppress_decay_slider=multibar_slider_new(6,decay_labels,decay_levels,1);
-    GtkWidget *suppress_ratio_slider=multibar_slider_new(6,ratio_labels,ratio_levels,1);
-    GtkWidget *suppress_depth_slider=multibar_slider_new(6,depth_labels,depth_levels,1);
-    GtkWidget *suppress_decay_readout=readout_new("fast");
-    GtkWidget *suppress_ratio_readout=readout_new("1.50:1");
-    GtkWidget *suppress_depth_readout=readout_new("gate");
-
-    //GtkWidget *label1=gtk_label_new("mode:");
-    GtkWidget *label3=gtk_label_new("decay:");
-    GtkWidget *label4=gtk_label_new("ratio:");
-    GtkWidget *label5=gtk_label_new("depth:");
+  /* over compand: limit */
+  {
+
+    GtkWidget *label=gtk_label_new("compand limit:");
+    GtkWidget *readout=readout_new("140dB");
+    GtkWidget *slider=multibar_slider_new(8,limit_labels,limit_levels,1);
+   
+    multibar_callback(MULTIBAR(slider),over_limit_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),0.,0);
+    multibar_thumb_increment(MULTIBAR(slider),1.,10.);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1,.5);
     
+    gtk_table_set_row_spacing(GTK_TABLE(overtable),1,4);
+    gtk_table_attach(GTK_TABLE(overtable),label,0,1,2,3,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),slider,1,3,2,3,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),readout,3,4,2,3,GTK_FILL,0,0,0);
+  }
+
+  /* over compand: timing */
+  {
+
+    GtkWidget *label=gtk_label_new("attack/decay:");
+    GtkWidget *readout1=readout_new(" 100ms");
+    GtkWidget *readout2=readout_new(" 100ms");
+    GtkWidget *slider=multibar_slider_new(5,timing_labels,timing_levels,2);
+   
+    multireadout *r=calloc(1,sizeof(*r));
+    r->r0=(Readout *)readout1;
+    r->r1=(Readout *)readout2;
+
+    multibar_callback(MULTIBAR(slider),over_timing_change,r);
+    multibar_thumb_set(MULTIBAR(slider),1,0);
+    multibar_thumb_set(MULTIBAR(slider),100,1);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1,.5);
+
+    gtk_table_set_row_spacing(GTK_TABLE(overtable),2,4);
+    gtk_table_attach(GTK_TABLE(overtable),label,0,1,5,6,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),slider,1,2,5,6,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),readout1,2,3,5,6,GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(overtable),readout2,3,4,5,6,GTK_FILL,0,0,0);
+
+  }
+
+  /* over compand: lookahead */
+  {
+
+    GtkWidget *label=gtk_label_new("lookahead:");
+    GtkWidget *readout=readout_new("100%");
+    GtkWidget *slider=multibar_slider_new(8,per_labels,per_levels,1);
+    GtkWidget *labelbox=gtk_hbox_new(0,0);
+   
+    multibar_callback(MULTIBAR(slider),over_lookahead_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),100.,0);
+    multibar_thumb_increment(MULTIBAR(slider),1.,10.);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1,.5);
+    
+    gtk_table_set_row_spacing(GTK_TABLE(overtable),3,4);
+    gtk_table_attach(GTK_TABLE(overtable),label,0,1,3,4,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),slider,1,3,3,4,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),readout,3,4,3,4,GTK_FILL,0,0,0);
+  }
+  
+  /* base compand: ratio */
+  {
+
+    GtkWidget *label=gtk_label_new("global ratio:");
+    GtkWidget *readout=readout_new("1.55:1");
+    GtkWidget *slider=multibar_slider_new(8,compand_labels,compand_levels,1);
+   
+    //multibar_callback(MULTIBAR(slider),over_compand_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),1.,0);
+    multibar_thumb_increment(MULTIBAR(slider),.01,.1);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
+
+    gtk_table_set_row_spacing(GTK_TABLE(overtable),4,4);
+
+    gtk_table_attach(GTK_TABLE(overtable),label,0,1,4,5,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),slider,1,3,4,5,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(overtable),readout,3,4,4,5,GTK_FILL,0,0,0);
+
+  }
+
+  /* suppress: mode  */
+  {
     GtkWidget *envelopebox=gtk_hbox_new(0,0);
-    GtkWidget *suppress_rms_button=gtk_radio_button_new_with_label(NULL,"RMS");
-    GtkWidget *suppress_peak_button=
-      gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(suppress_rms_button),
+    GtkWidget *rms_button=gtk_radio_button_new_with_label(NULL,"RMS");
+    GtkWidget *peak_button=
+      gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(rms_button),
                                                   "peak");
-    gtk_box_pack_end(GTK_BOX(envelopebox),suppress_peak_button,0,0,5);
-    gtk_box_pack_end(GTK_BOX(envelopebox),suppress_rms_button,0,0,5);
 
+    gtk_box_pack_end(GTK_BOX(envelopebox),peak_button,0,0,5);
+    gtk_box_pack_end(GTK_BOX(envelopebox),rms_button,0,0,5);
 
-    g_signal_connect (G_OBJECT (suppress_rms_button), "clicked",
-		      G_CALLBACK (suppressor_mode), (gpointer)0); //To Hell I Go
-    g_signal_connect (G_OBJECT (suppress_peak_button), "clicked",
-		      G_CALLBACK (suppressor_mode), (gpointer)1);
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(suppress_rms_button),1);
-
-
-    gtk_container_set_border_width(GTK_CONTAINER(suppresstable),4);
-
-    //gtk_misc_set_alignment(GTK_MISC(label1),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(label3),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(label4),1,.5);
-    gtk_misc_set_alignment(GTK_MISC(label5),1,.5);
-
-
-    //gtk_table_attach(GTK_TABLE(suppresstable),label1,0,1,0,1,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(suppresstable),label3,0,1,1,2,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(suppresstable),label4,0,1,2,3,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(suppresstable),label5,0,1,3,4,GTK_FILL,0,0,0);
-
-    gtk_table_attach(GTK_TABLE(suppresstable),envelopebox,1,3,0,1,GTK_FILL,0,0,0);
-
-    multibar_callback(MULTIBAR(suppress_decay_slider),
-		      suppress_decay_change,
-		      suppress_decay_readout);
-    multibar_thumb_set(MULTIBAR(suppress_decay_slider),-1.,0);
-
-    multibar_callback(MULTIBAR(suppress_ratio_slider),
-		      suppress_ratio_change,
-		      suppress_ratio_readout);
-    multibar_thumb_set(MULTIBAR(suppress_ratio_slider),-1.,0);
-
-    multibar_callback(MULTIBAR(suppress_depth_slider),
-		      suppress_depth_change,
-		      suppress_depth_readout);
-    multibar_thumb_set(MULTIBAR(suppress_depth_slider),0.,0);
-
-    gtk_table_attach(GTK_TABLE(suppresstable),
-		     suppress_decay_slider,1,2,1,2,GTK_FILL|GTK_EXPAND,
-		     GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(suppresstable),
-		     suppress_ratio_slider,1,2,2,3,GTK_FILL|GTK_EXPAND,
-		     GTK_FILL|GTK_EXPAND,2,0);
-    gtk_table_attach(GTK_TABLE(suppresstable),
-		     suppress_depth_slider,1,2,3,4,GTK_FILL|GTK_EXPAND,
-		     GTK_FILL|GTK_EXPAND,2,0);
-
-    gtk_table_attach(GTK_TABLE(suppresstable),
-		     suppress_decay_readout,2,3,1,2,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(suppresstable),
-		     suppress_ratio_readout,2,3,2,3,GTK_FILL,0,0,0);
-    gtk_table_attach(GTK_TABLE(suppresstable),
-		     suppress_depth_readout,2,3,3,4,GTK_FILL,0,0,0);
+    g_signal_connect (G_OBJECT (rms_button), "clicked",
+		      G_CALLBACK (suppress_mode), (gpointer)0);
+    g_signal_connect (G_OBJECT (peak_button), "clicked",
+		      G_CALLBACK (suppress_mode), (gpointer)1); //To Hell I Go
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rms_button),1);
+    gtk_table_attach(GTK_TABLE(suppresstable),envelopebox,1,5,0,1,GTK_FILL,0,0,0);
+  }
+
+  /* suppress: ratio */
+  {
+
+    GtkWidget *label=gtk_label_new("ratio:");
+    GtkWidget *readout=readout_new("1.55:1");
+    GtkWidget *slider=multibar_slider_new(4,compand_labels_low,compand_levels_low,1);
+    
+    multibar_callback(MULTIBAR(slider),suppress_compand_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),1.,0);
+    multibar_thumb_increment(MULTIBAR(slider),.01,.1);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
+
+    gtk_table_set_row_spacing(GTK_TABLE(suppresstable),0,4);
+    gtk_table_attach(GTK_TABLE(suppresstable),label,0,1,1,2,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(suppresstable),slider,1,4,1,2,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(suppresstable),readout,4,5,1,2,GTK_FILL,0,0,0);
 
   }
 
+  /* suppress: timing */
+  {
+
+    GtkWidget *label=gtk_label_new("timing:");
+    GtkWidget *label2=gtk_label_new("attack / decay / release");
+    GtkWidget *readout1=readout_new(" 100ms");
+    GtkWidget *readout2=readout_new(" 100ms");
+    GtkWidget *readout3=readout_new(" 100ms");
+    GtkWidget *slider=multibar_slider_new(5,timing_labels,timing_levels,3);
+
+    multireadout *r=calloc(1,sizeof(*r));
+    r->r0=(Readout *)readout1;
+    r->r1=(Readout *)readout2;
+    r->r2=(Readout *)readout3;
+
+    gtk_widget_set_name(label2,"scalemarker");
+   
+    multibar_callback(MULTIBAR(slider),suppress_timing_change,r);
+    multibar_thumb_set(MULTIBAR(slider),1,0);
+    multibar_thumb_set(MULTIBAR(slider),100,1);
+    multibar_thumb_set(MULTIBAR(slider),1000,2);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1,.5);
+
+    gtk_table_set_row_spacing(GTK_TABLE(suppresstable),1,4);
+    gtk_table_attach(GTK_TABLE(suppresstable),label,0,1,2,3,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(suppresstable),slider,1,2,2,3,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(suppresstable),readout1,2,3,2,3,GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(suppresstable),readout2,3,4,2,3,GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(suppresstable),readout3,4,5,2,3,GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(suppresstable),label2,1,2,3,4,GTK_FILL,0,0,0);
+
+  }
+
+  /* threshold controls */
+
   for(i=0;i<multicomp_freqs_max;i++){
     GtkWidget *label=gtk_label_new(NULL);
-    gtk_widget_set_name(label,"smallmarker");
+    gtk_widget_set_name(label,"scalemarker");
     
-    bars[i].readoutg=readout_new("  +0");
-    bars[i].readoute=readout_new("  +0");
-    bars[i].readoutc=readout_new("  +0");
-    bars[i].slider=multibar_new(14,labels,levels,3,
+    bars[i].readoutu=readout_new("  +0");
+    bars[i].readouto=readout_new("  +0");
+    bars[i].slider=multibar_new(14,labels,levels,2,
                                 LO_DECAY|HI_DECAY|LO_ATTACK);
     bars[i].number=i;
     bars[i].label=label;
 
     multibar_callback(MULTIBAR(bars[i].slider),slider_change,bars+i);
     multibar_thumb_set(MULTIBAR(bars[i].slider),-140.,0);
-    multibar_thumb_set(MULTIBAR(bars[i].slider),-140.,1);
-    multibar_thumb_set(MULTIBAR(bars[i].slider),0.,2);
+    multibar_thumb_set(MULTIBAR(bars[i].slider),0.,1);
     multibar_thumb_bounds(MULTIBAR(bars[i].slider),-140,0);
     multibar_thumb_increment(MULTIBAR(bars[i].slider),1.,10.);
     
@@ -818,16 +974,34 @@
       
     gtk_table_attach(GTK_TABLE(slidertable),label,0,1,i+1,i+2,
                      GTK_FILL,0,10,0);
-    gtk_table_attach(GTK_TABLE(slidertable),bars[i].readoutg,2,3,i+1,i+2,
+    gtk_table_attach(GTK_TABLE(slidertable),bars[i].readoutu,1,2,i+1,i+2,
+		     0,0,0,0);
+    gtk_table_attach(GTK_TABLE(slidertable),bars[i].slider,2,3,i+1,i+2,
+		     GTK_FILL|GTK_EXPAND,GTK_EXPAND,0,0);
+    gtk_table_attach(GTK_TABLE(slidertable),bars[i].readouto,3,4,i+1,i+2,
                      0,0,0,0);
-    gtk_table_attach(GTK_TABLE(slidertable),bars[i].readoute,3,4,i+1,i+2,
-		       0,0,0,0);
-      gtk_table_attach(GTK_TABLE(slidertable),bars[i].readoutc,4,5,i+1,i+2,
-		       0,0,0,0);
-      gtk_table_attach(GTK_TABLE(slidertable),bars[i].slider,1,2,i+1,i+2,
-		       GTK_FILL|GTK_EXPAND,GTK_EXPAND,0,0);
   }
 
+  /* "average" slider */
+  {
+    GtkWidget *label=gtk_label_new("average");
+    
+    bars[multicomp_freqs_max].slider=multibar_slider_new(14,labels,levels,2);
+
+    multibar_callback(MULTIBAR(bars[multicomp_freqs_max].slider),average_change,bars+multicomp_freqs_max);
+
+    multibar_thumb_set(MULTIBAR(bars[multicomp_freqs_max].slider),-140.,0);
+    multibar_thumb_set(MULTIBAR(bars[multicomp_freqs_max].slider),0.,1);
+    multibar_thumb_bounds(MULTIBAR(bars[multicomp_freqs_max].slider),-140,0);
+    
+    gtk_table_attach(GTK_TABLE(slidertable),label,0,2,multicomp_freqs_max+1,multicomp_freqs_max+2,
+		     GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(slidertable),bars[i].slider,2,3,multicomp_freqs_max+1,multicomp_freqs_max+2,
+		     GTK_FILL|GTK_EXPAND,GTK_EXPAND,0,0);
+    gtk_table_set_row_spacing(GTK_TABLE(slidertable),multicomp_freqs_max,10);
+  }
+
+  
   subpanel_show_all_but_toplevel(panel);
 
   /* Now unmap the sliders we don't want */

<p><p>1.25      +152 -111  postfish/multibar.c

Index: multibar.c
===================================================================
RCS file: /usr/local/cvsroot/postfish/multibar.c,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -r1.24 -r1.25
--- a/multibar.c	8 Mar 2004 02:08:23 -0000	1.24
+++ b/multibar.c	17 Mar 2004 10:32:04 -0000	1.25
@@ -68,10 +68,7 @@
   if(m->thumbs<1){
     xpad=2;
   }else{
-    if(m->thumbs>1)
-      xpad=((height+1)/2)+(height/2-3)*3/2+1;
-    else
-      xpad=height/2+1;
+    xpad=((height+1)/2)+(height/2-3)*3/2+1;
   }
   m->xpad=xpad;
 
@@ -362,10 +359,15 @@
     
     if(m->levels[i]>0.)gc=1;
     
-    gdk_draw_line (m->backing,
-		   widget->style->text_gc[gc],
-		   x, upad, x, y+upad);
-    
+    if(m->readout)
+      gdk_draw_line (m->backing,
+		     widget->style->text_gc[gc],
+		     x, upad, x, y+upad);
+    else
+      gdk_draw_line (m->backing,
+		     widget->style->text_gc[gc],
+		     x, y/4+upad, x, y+upad);
+
     if(i>0){
       pango_layout_get_pixel_size(m->layout[i-1],&px,&py);
       x-=px+2;
@@ -383,7 +385,7 @@
   {
     int width=widget->allocation.width;
     int height=widget->allocation.height;
-    int apad=(m->thumbs<2?-height/2+2:3-xpad);
+    int apad=3-xpad;
     GdkGC *gc=parent->style->bg_gc[0];
     GdkGC *light_gc=parent->style->light_gc[0];
     GdkGC *dark_gc=parent->style->dark_gc[0];
@@ -455,12 +457,14 @@
     int height=widget->allocation.height;
     GdkGC *black_gc=widget->style->black_gc;
     int y=height/2-3;
+    int y0=height/3-1;
     int y1=height-3;
-    int y0=y-(y1-y-1);
+    int yM=(y1+y0)/2-y0;
 
     GdkColor yellow={0,0xff00,0xd000,0};
-
-    if(m->thumbs>1){
+    
+    if(m->thumbs==1){
+      /* single thumb: wide center */
       GdkGC *gc=widget->style->bg_gc[m->thumbstate[0]];
       GdkGC *light_gc=widget->style->light_gc[m->thumbstate[0]];
       GdkGC *dark_gc=widget->style->dark_gc[m->thumbstate[0]];
@@ -470,123 +474,163 @@
       int x1=x+(y1-y-2);
       int x2=x0-y*3/2;
       int x3=x1+y*3/2;
-
-      GdkPoint tp[6]={ {x-1,y},{x2+3,y},{x2,y+3},
-		       {x2,y1+1},{x0-1,y1+1},{x0-1,y1-2}};
-
-      gdk_draw_polygon(m->backing,gc,TRUE,tp,6);
-
-      gdk_draw_line(m->backing,light_gc,x-2,y,x2+3,y);
-      gdk_draw_line(m->backing,light_gc,x2+3,y,x2,y+3);
-      gdk_draw_line(m->backing,light_gc,x2,y+3,x2,y1);
-
-      gdk_draw_line(m->backing,dark_gc,x2+1,y1,x0-2,y1);
-      gdk_draw_line(m->backing,dark_gc,x0-2,y1,x0-2,y1-2);
-      gdk_draw_line(m->backing,dark_gc,x0-2,y1-2,x-3,y+1);
-
-      gdk_draw_line(m->backing,black_gc,x2,y1+1,x0-1,y1+1);
-      gdk_draw_line(m->backing,black_gc,x0-1,y1+1,x0-1,y1-2);
-      gdk_draw_line(m->backing,black_gc,x0,y1-3,x-2,y+1);
-
-      gdk_gc_set_rgb_fg_color(m->boxcolor,&yellow);
-      gdk_draw_line(m->backing,m->boxcolor,x,0,x,height-lpad);
-
+      
+      GdkPoint tp[7]={ {x,y0},{x-yM,y0+yM},{x2,y0+yM},{x2,y1+1},{x3,y1+1},{x3,y0+yM},{x+yM,y0+yM} };
+      
+      gdk_draw_polygon(m->backing,gc,TRUE,tp,7);
+      
+      gdk_draw_line(m->backing,dark_gc,x2+1,y1,x3-1,y1);
+      gdk_draw_line(m->backing,dark_gc,x3-1,y1,x3-1,y0+yM+1);
+      
       if(m->thumbfocus==0){
-	GdkPoint tp[6]={ {x-3,y+1},{x2+3,y+1},{x2+1,y+3},
-			 {x2+1,y1+1},{x0-1,y1+1},{x0-1,y1-2}};
-
         if(x&1)
           gdk_gc_set_stipple(black_gc,stipple);
         else
           gdk_gc_set_stipple(black_gc,stippleB);
         gdk_gc_set_fill(black_gc,GDK_STIPPLED);
-	gdk_draw_polygon(m->backing,black_gc,TRUE,tp,6);
+	gdk_draw_polygon(m->backing,black_gc,TRUE,tp,7);
         gdk_gc_set_fill(black_gc,GDK_SOLID);
       }
-
+      
+      gdk_draw_line(m->backing,light_gc,x3-1,y0+yM,x+yM,y0+yM);
+      gdk_draw_line(m->backing,light_gc,x+yM,y0+yM,x,y0);
+      gdk_draw_line(m->backing,light_gc,x,y0,x-yM,y0+yM);
+      gdk_draw_line(m->backing,light_gc,x-yM,y0+yM,x2,y0+yM);
+      gdk_draw_line(m->backing,light_gc,x2,y0+yM,x2,y1);
+      
+      gdk_draw_line(m->backing,black_gc,x2,y1+1,x3,y1+1);
+      gdk_draw_line(m->backing,black_gc,x3,y1+1,x3,y0+yM);
+      
+      gdk_gc_set_rgb_fg_color(m->boxcolor,&yellow);
+      gdk_draw_line(m->backing,m->boxcolor,x,y1-1,x,0);
+      
     }
-
+  
     if(m->thumbs>1){
-      int num=(m->thumbs>2?2:1);
-      GdkGC *gc=widget->style->bg_gc[m->thumbstate[num]];
-      GdkGC *light_gc=widget->style->light_gc[m->thumbstate[num]];
-      GdkGC *dark_gc=widget->style->dark_gc[m->thumbstate[num]];
       
-      int x=m->thumbpixel[num]+xpad;
-      int x0=x-(y1-y-2);
-      int x1=x+(y1-y-2);
-      int x2=x0-y*3/2;
-      int x3=x1+y*3/2;
-
-      GdkPoint tp[6]={ {x+1,y},{x3-3,y},{x3,y+3},
-		       {x3,y1+1},{x1+1,y1+1},{x1+1,y1-2}};
-
-      gdk_draw_polygon(m->backing,gc,TRUE,tp,6);
-
-      gdk_draw_line(m->backing,light_gc,x+1,y,x3-3,y);
-      gdk_draw_line(m->backing,light_gc,x3-3,y,x3-1,y+2);
-      gdk_draw_line(m->backing,light_gc,x1+1,y1-2,x1+1,y1);
-
-      gdk_draw_line(m->backing,dark_gc,x+3,y+1,x1+1,y1-3);
-      gdk_draw_point(m->backing,dark_gc,x1+1,y1-2);
-      gdk_draw_line(m->backing,dark_gc,x1+2,y1,x3-1,y1);
-      gdk_draw_line(m->backing,dark_gc,x3-1,y1,x3-1,y+3);
-
-      gdk_draw_line(m->backing,black_gc,x+2,y+1,x1,y1-3);
-      gdk_draw_line(m->backing,black_gc,x1+1,y1+1,x3,y1+1);
-      gdk_draw_line(m->backing,black_gc,x3,y1+1,x3,y+4);
+      /* two thumbs: left */
+      {
+	GdkGC *gc=widget->style->bg_gc[m->thumbstate[0]];
+	GdkGC *light_gc=widget->style->light_gc[m->thumbstate[0]];
+	GdkGC *dark_gc=widget->style->dark_gc[m->thumbstate[0]];
+	
+	int x=m->thumbpixel[0]+xpad;
+	int x0=x-(y1-y-2);
+	int x1=x+(y1-y-2);
+	int x2=x0-y*3/2;
+	int x3=x1+y*3/2;
+	
+	GdkPoint tp[5]={ {x,y0},{x-yM,y0+yM},{x2,y0+yM},{x2,y1+1},
+			 {x,y1+1}};
+	
+	gdk_draw_polygon(m->backing,gc,TRUE,tp,5);
 
-      gdk_gc_set_rgb_fg_color(m->boxcolor,&yellow);
-      gdk_draw_line(m->backing,m->boxcolor,x,0,x,height-lpad);
+	gdk_draw_line(m->backing,dark_gc,x2+1,y1,x-1,y1);
+	gdk_draw_line(m->backing,dark_gc,x-1,y1,x-1,y0+1);
 
-      if(m->thumbfocus==num){
-	GdkPoint tp[6]={ {x+3,y+1},{x3-2,y+1},{x3,y+3},
-			 {x3,y1+1},{x1+1,y1+1},{x1+1,y1-2}};
+	if(m->thumbfocus==0){
+	  if(x&1)
+	    gdk_gc_set_stipple(black_gc,stipple);
+	  else
+	    gdk_gc_set_stipple(black_gc,stippleB);
+	  gdk_gc_set_fill(black_gc,GDK_STIPPLED);
+	  gdk_draw_polygon(m->backing,black_gc,TRUE,tp,5);
+	  gdk_gc_set_fill(black_gc,GDK_SOLID);
+	}
+	
+	gdk_draw_line(m->backing,light_gc,x,y0,x-yM,y0+yM);
+	gdk_draw_line(m->backing,light_gc,x-yM-1,y0+yM,x2,y0+yM);
+	gdk_draw_line(m->backing,light_gc,x2,y0+yM,x2,y1+1);
 
-	if(x&1)
-	  gdk_gc_set_stipple(black_gc,stipple);
-	else
-	  gdk_gc_set_stipple(black_gc,stippleB);
-	gdk_gc_set_fill(black_gc,GDK_STIPPLED);
-	gdk_draw_polygon(m->backing,black_gc,TRUE,tp,6);
-	gdk_gc_set_fill(black_gc,GDK_SOLID);
+	gdk_draw_line(m->backing,black_gc,x2,y1+1,x,y1+1);
+	gdk_draw_line(m->backing,black_gc,x,y1+1,x,y0);
+	
+	gdk_gc_set_rgb_fg_color(m->boxcolor,&yellow);
+	gdk_draw_line(m->backing,m->boxcolor,x,y1-1,x,0);
+	
       }
-    }
-
-    if(m->thumbs==1 || m->thumbs==3){
-      int num=(m->thumbs>1?1:0);
-
-      GdkGC *gc=widget->style->bg_gc[m->thumbstate[num]];
-      GdkGC *light_gc=widget->style->light_gc[m->thumbstate[num]];
-      GdkGC *dark_gc=widget->style->dark_gc[m->thumbstate[num]];
-      
-      int x=m->thumbpixel[num]+xpad;
-      int x0=x-(y1-y-2);
-      int x1=x+(y1-y-2);
+      /* two thumbs: right */
+      {
+	int num=(m->thumbs>2?2:1);
+	GdkGC *gc=widget->style->bg_gc[m->thumbstate[num]];
+	GdkGC *light_gc=widget->style->light_gc[m->thumbstate[num]];
+	GdkGC *dark_gc=widget->style->dark_gc[m->thumbstate[num]];
+	
+	int x=m->thumbpixel[num]+xpad;
+	int x0=x-(y1-y-2);
+	int x1=x+(y1-y-2);
+	int x2=x0-y*3/2;
+	int x3=x1+y*3/2;
+	
+	GdkPoint tp[5]={ {x,y0},{x+yM,y0+yM},{x3,y0+yM},{x3,y1+1},
+			 {x,y1+1}};
 
-      GdkPoint tp[5]={ {x,y},{x0,y1-2},{x0,y1+1},{x1,y1+1},{x1,y1-2} };
+	gdk_draw_polygon(m->backing,gc,TRUE,tp,5);
 
-      gdk_draw_polygon(m->backing,gc,TRUE,tp,5);
+	gdk_draw_line(m->backing,dark_gc,x+1,y1,x3-1,y1);
+	gdk_draw_line(m->backing,dark_gc,x3-1,y1,x3-1,y0+yM+1);
 
-      gdk_draw_line(m->backing,light_gc,x,y,x1,y1-2);
-      gdk_draw_line(m->backing,light_gc,x,y,x0,y1-2);
-      gdk_draw_line(m->backing,light_gc,x0,y1-2,x0,y1+1);
+	if(m->thumbfocus==num){
+	  if(x&1)
+	    gdk_gc_set_stipple(black_gc,stipple);
+	  else
+	    gdk_gc_set_stipple(black_gc,stippleB);
+	  gdk_gc_set_fill(black_gc,GDK_STIPPLED);
+	  gdk_draw_polygon(m->backing,black_gc,TRUE,tp,5);
+	  gdk_gc_set_fill(black_gc,GDK_SOLID);
+	}
 
-      gdk_draw_line(m->backing,dark_gc,x0+1,y1,x1-1,y1);
-      gdk_draw_line(m->backing,dark_gc,x1-1,y1,x1-1,y1-2);
+	gdk_draw_line(m->backing,light_gc,x3-1,y0+yM,x+yM,y0+yM);
+	gdk_draw_line(m->backing,light_gc,x+yM,y0+yM,x,y0);
+	gdk_draw_line(m->backing,light_gc,x,y0,x,y1);
+	
+	gdk_draw_line(m->backing,black_gc,x,y1+1,x3,y1+1);
+	gdk_draw_line(m->backing,black_gc,x3,y1+1,x3,y0+yM);
+	
+	gdk_gc_set_rgb_fg_color(m->boxcolor,&yellow);
+	gdk_draw_line(m->backing,m->boxcolor,x,y1-1,x,0);
+	
+      }
 
-      gdk_draw_line(m->backing,black_gc,x0,y1+1,x1,y1+1);
-      gdk_draw_line(m->backing,black_gc,x1,y1+1,x1,y1-1);
+      if(m->thumbs==3){
+	/* three thumbs: minimal center */
+	GdkGC *gc=widget->style->bg_gc[m->thumbstate[1]];
+	GdkGC *light_gc=widget->style->light_gc[m->thumbstate[1]];
+	GdkGC *dark_gc=widget->style->dark_gc[m->thumbstate[1]];
+      
+	int x=m->thumbpixel[1]+xpad;
+	int x0=x-(y1-y0-3);
+	int x1=x+(y1-y0-3);
+	
+	GdkPoint tp[5]={ {x,y0},{x0,y1-3},{x0,y1+1},{x1,y1+1},{x1,y1-3} };
+	
+	gdk_draw_polygon(m->backing,gc,TRUE,tp,5);
+	
+	
+	gdk_draw_line(m->backing,dark_gc,x0+1,y1,x1-1,y1);
+	gdk_draw_line(m->backing,dark_gc,x1-1,y1,x1-1,y1-3);
+	
+	if(m->thumbfocus==1){
+	  for(i=0;i<(height-1)/2;i++)
+	    for(j=0;j<=i*2;j++)
+	      if(!(j&1))
+		gdk_draw_point(m->backing,black_gc,x-i+j,y0+i+2);
+	  for(j=0;j<=(i-1)*2;j++)
+	    if((j&1))
+	      gdk_draw_point(m->backing,black_gc,x-(i-1)+j,y0+i+2);
+	}
 
-      gdk_gc_set_rgb_fg_color(m->boxcolor,&yellow);
-      gdk_draw_line(m->backing,m->boxcolor,x,y+(y1-y)/2,x,0);
+	gdk_draw_line(m->backing,light_gc,x,y0,x1,y1-3);
+	gdk_draw_line(m->backing,light_gc,x,y0,x0,y1-3);
+	gdk_draw_line(m->backing,light_gc,x0,y1-3,x0,y1+1);
 
-      if(m->thumbfocus==num){
-	for(i=0;i<(height-1)/2;i++)
-	  for(j=0;j<=i*2;j++)
-	    if(!(j&1))
-	      gdk_draw_point(m->backing,black_gc,x-i+j,y+i+2);
-      } 
+	gdk_draw_line(m->backing,black_gc,x0,y1+1,x1,y1+1);
+	gdk_draw_line(m->backing,black_gc,x1,y1+1,x1,y1-2);
+	
+	gdk_gc_set_rgb_fg_color(m->boxcolor,&yellow);
+	gdk_draw_line(m->backing,m->boxcolor,x,y1-1,x,0);
+	
+      }
     }
   }
 }
@@ -635,10 +679,7 @@
     xpad=2;
   }else{
     maxy+=3;
-    if(m->thumbs>1)
-      xpad=((maxy+1)/2)+(maxy/2-3)*3/2+1;
-    else
-      xpad=maxy/2+1+2;
+    xpad=((maxy+1)/2)+(maxy/2-3)*3/2+1;
   }
 
   requisition->width = (maxx*1.5+2)*m->labels+xpad*2;

<p><p>1.5       +438 -469  postfish/multicompand.c

Index: multicompand.c
===================================================================
RCS file: /usr/local/cvsroot/postfish/multicompand.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- a/multicompand.c	8 Mar 2004 11:07:06 -0000	1.4
+++ b/multicompand.c	17 Mar 2004 10:32:04 -0000	1.5
@@ -26,6 +26,7 @@
 #include "multicompand.h"
 #include <fftw3.h>
 #include "subband.h"
+#include "bessel.h"
 
 static int offset=0;
 static void _analysis(char *base,int i,float *v,int n,int bark,int dB){
@@ -80,37 +81,35 @@
 extern int input_rate;
 extern int input_ch;
 
-/* An array of two tap Bessel filters for bandlimiting our followers
-   and performing continuous RMS estimation.  Why Bessel filters?  Two
-   reasons: They exhibit very little ringing in the time domain and
-   about a half-order of magnitude less roundoff noise from gain
-   (compared to other IIR families) */
-
-static float iirg_list[8]={
-  4.875178475e+06,  // 5
-  1.008216556e+06,  // 11
-  2.524889925e+05,  // 22
-  6.3053949794e+04, // 44
-  1.5872282930e+04, // 88
-  4.086422543e+03,  // 175
-  1.049342702e+03,  // 350
-  2.764099277e+02   // 700
-};
-
-static float iirc_list[8][2]={
-  {1.9984308946, -.9984317150},  // 5
-  {1.9965490514, -.9965530188},  // 11
-  {1.9931020727, -.9931179150},  // 22
-  {1.9861887623, -.98625220002}, // 44
-  {1.9724411246, -.97269313627}, // 88
-  {1.9455669653, -.9465458165},  // 175
-  {1.8921217885, -.8959336985},  // 350
-  {1.7881166933, -0.8025879536}  // 700
-};
-
-static float iir_corner_list[8]={5,11,22,44,88,175,350,700};
-static float iir_rmsoffset_list[8]={1048,524,262,131,65,32,16,8};
-static float iir_peakoffset_list[8]={5400,2700,1350,675,338,169,84,42};
+/* lookup table this soon */
+static float soft_knee(float x){
+  return atanf(-x*.2)*x/3.14159-x*.5-(5./M_PI);
+}
+
+static float hard_knee(float x){
+  return (x>0?-x:0.);
+}
+
+typedef struct {
+  double c0;
+  double c1;
+  double g;
+  float alpha; 
+  //int impulseahead;  //  5764 == 1Hz @ 44.1
+  //int stepahead;     // 14850 == 1Hz @ 44.1
+} iir_filter;
+
+static inline long impulse_ahead(float alpha){
+  return rint(.1307f/alpha);
+}
+
+static inline long step_ahead(float alpha){
+  return rint(.3367f/alpha);
+}
+
+static inline float step_freq(long ahead){
+  return input_rate*.3367/ahead;
+}
 
 #if NASTY_IEEE_FLOAT32_HACK_IS_FASTER_THAN_LOG
 
@@ -123,34 +122,38 @@
 #endif
 
 typedef struct {
-  /* RMS follower state */
-  float rms_smooth_y[2];
-  float rms_smooth_x[2];
-
-  float static_c_decay_chase;
-  float static_e_decay_chase;
-  float static_g_decay_chase;
-  float suppress_decay_chase;
-
-  float env1_x[2];
-  float env1_y[2];
-  float env2_x[2];
-  float env2_y[2];
-
-  int dummymarker;
-} envelope_state;
+  double x[2];
+  double y[2];
+  int state;
+} iir_state;
 
 typedef struct {
   subband_state ss;
   subband_window sw[multicomp_banks];
+  
+  iir_filter over_attack[multicomp_banks][multicomp_freqs_max];
+  iir_filter over_decay[multicomp_banks][multicomp_freqs_max];
+
+  iir_filter under_attack[multicomp_banks][multicomp_freqs_max];
+  iir_filter under_decay[multicomp_banks][multicomp_freqs_max];
 
-  int rmschoice[multicomp_banks][multicomp_freqs_max];
-  int attack[multicomp_banks][multicomp_freqs_max];
-  int decay[multicomp_banks][multicomp_freqs_max];
+  iir_filter suppress_attack[multicomp_banks][multicomp_freqs_max];
+  iir_filter suppress_decay[multicomp_banks][multicomp_freqs_max];
+  iir_filter suppress_release[multicomp_banks][multicomp_freqs_max];
+
+  iir_state *over_iir[multicomp_freqs_max];
+  iir_state *under_iir[multicomp_freqs_max];
+  iir_state *suppress_iirA[multicomp_freqs_max];
+  iir_state *suppress_iirB[multicomp_freqs_max];
 
-  envelope_state **e;
   sig_atomic_t pending_bank;
   sig_atomic_t active_bank;
+
+  int previous_over_mode; // RMS or peak?  The iir follower domains
+  // are different, and upon transition, must be converted.
+  int previous_under_mode;    // as above
+  int previous_suppress_mode; // as above
+
 } multicompand_state;
 
 sig_atomic_t compand_visible;
@@ -170,15 +173,14 @@
   int h,i,j;
   
   subband_reset(&ms.ss);
-
-  for(i=0;i<input_ch;i++)
-    for(j=0;j<multicomp_freqs_max;j++){
-      memset(&ms.e[j][i],0,sizeof(**ms.e));
-      ms.e[j][i].static_c_decay_chase=0.;
-      ms.e[j][i].static_e_decay_chase=0.;
-      ms.e[j][i].static_g_decay_chase=0.;
+  
+  for(i=0;i<multicomp_freqs_max;i++)
+    for(j=0;j<input_ch;j++){
+      memset(&ms.over_iir[i][j],0,sizeof(iir_state));
+      memset(&ms.under_iir[i][j],0,sizeof(iir_state));
+      memset(&ms.suppress_iirA[i][j],0,sizeof(iir_state));
+      memset(&ms.suppress_iirB[i][j],0,sizeof(iir_state));
     }
-
 }
 
 int multicompand_load(void){
@@ -188,140 +190,182 @@
 
   subband_load(&ms.ss,multicomp_freqs_max,qblocksize);
 
-  for(h=0;h<multicomp_banks;h++){
+  for(h=0;h<multicomp_banks;h++)
     subband_load_freqs(&ms.ss,&ms.sw[h],multicomp_freq_list[h],
                        multicomp_freqs[h]);
     
-    /* select the RMS follower filters to use according to 
-       sample rate and band frequency */
-    for(i=0;i<multicomp_freqs[h];i++){
-      /* select a filter with a corner frequency of about 1/8 the
-	 band center. Don't go lower than 11 or higher than the 44 */
-      /* also... make sure 'workahead' is within input size restrictions */
-      double bf=multicomp_freq_list[h][i];
-      
-      for(j=7;j>=0;j--)
-	if(iir_peakoffset_list[j]<=input_size*2-qblocksize*3){
-	  ms.rmschoice[h][i]=j;
-	  if(iir_corner_list[j]*input_rate/44100 < bf*.25 &&
-	    iir_corner_list[j]*input_rate/44100 < 100)
-	    break;
-	}
-
-      for(j=7;j>=0;j--)
-	if(iir_peakoffset_list[j]<=input_size*2-qblocksize*3){
-	  ms.attack[h][i]=j;
-	  if(iir_corner_list[j]*input_rate/44100 < bf*.5)
-	    break;
-	}
-      ms.decay[h][i]=j;
-      for(j=7;j>=0;j--)
-	if(iir_peakoffset_list[j]<=input_size*2-qblocksize*3){
-	  ms.decay[h][i]=j;
-	  if(iir_corner_list[j]*input_rate/44100 < 30 &&
-	     j<=ms.attack[h][i])
-	    break;
-	}
-    }
-  }
-  
-  ms.e=calloc(multicomp_freqs_max,sizeof(*(ms.e)));
-  
   for(i=0;i<multicomp_freqs_max;i++){
-    ms.e[i]=calloc(input_ch,sizeof(**(ms.e)));
-    
-    for(j=0;j<input_ch;j++){
-      ms.e[i][j].static_c_decay_chase=0.;
-      ms.e[i][j].static_e_decay_chase=0.;
-      ms.e[i][j].static_g_decay_chase=0.;
-    }
+    ms.over_iir[i]=calloc(input_ch,sizeof(iir_state));
+    ms.under_iir[i]=calloc(input_ch,sizeof(iir_state));
+    ms.suppress_iirA[i]=calloc(input_ch,sizeof(iir_state));
+    ms.suppress_iirB[i]=calloc(input_ch,sizeof(iir_state));
   }
-  
+
   ms.active_bank=0;
 
   return 0;
 }
 
-/* The Bessel filter followers are inlined and unrolled here as later
-   to avoid some really shockingly bad code generation by gcc on x86 */
-static void rms_chase(float *rms, int i, int linkp, int filternum,
-		      envelope_state *e, float **xxi){
-  int k,l;
-  float iirc0,iirc1;
-  float iirg,x,y;
-  float iirx0,iirx1;
-  float iiry0,iiry1;
-
-  if(linkp==0 || i==0){
-    float val;
-    int ahead=iir_rmsoffset_list[filternum];
-    iirg=iirg_list[filternum];
-    iirc0=iirc_list[filternum][0];
-    iirc1=iirc_list[filternum][1];
-    if(linkp)memset(rms,0,sizeof(*rms)*input_size);
+int multicompand_filterbank_set(float msec,iir_filter filterbank[multicomp_banks][multicomp_freqs_max]){
+  int i,j;
+  for(j=0;j<multicomp_banks;j++){
+    float *freqs=multicomp_freq_list[j];
+    int bands=multicomp_freqs[j];
+    iir_filter *filters=filterbank[j];
     
-    for(l=i;l<(linkp?input_ch:i+1);l++){
-      float *xx=xxi[l];
-      iirx0=e[l].rms_smooth_x[0];
-      iirx1=e[l].rms_smooth_x[1];
-      iiry0=e[l].rms_smooth_y[0];
-      iiry1=e[l].rms_smooth_y[1];
+    for(i=0;i<bands;i++){
+      float alpha;
+      float corner_freq=500./msec;
+
+      /* limit the filter corner frequency to prevent fast attacks
+         from destroying low frequencies */
+      if(corner_freq>freqs[i]/2)corner_freq=freqs[i]/2;
       
-      for(k=0;k<input_size;k+=2){
-	y=x=(xx[k+ahead]*xx[k+ahead])/iirg;
-	y+=iirx0;
-	y+=iirx0;
-	y+=iirx1;
-	y+=iiry0*iirc0;
-	y+=iiry1*iirc1;
-	
-	iirx1=x;
-	rms[k]=iiry1=y;
-	
-	y=x=(xx[k+ahead+1]*xx[k+ahead+1])/iirg;
-	y+=iirx1;
-	y+=iirx1;
-	y+=iirx0;
-	y+=iiry1*iirc0;
-	y+=iiry0*iirc1;
-	
-	iirx0=x;
-	rms[k+1]=iiry0=y;
-      }
+      /* make sure the chosen frequency doesn't require a lookahead
+         greater than what's available */
+      if(step_freq(input_size*2-ms.ss.qblocksize*3)*.95<freqs[i])
+	corner_freq=step_freq(input_size*2-ms.ss.qblocksize*3);
+		     
+      alpha=corner_freq/input_rate*2.;
+      filters[i].g=mkbessel_2(alpha,&filters[i].c0,&filters[i].c1);
+      filters[i].alpha=alpha;
+    }
+  }
+}
+
 
-      e[l].rms_smooth_x[0]=iirx0;
-      e[l].rms_smooth_x[1]=iirx1;
-      e[l].rms_smooth_y[0]=iiry0;
-      e[l].rms_smooth_y[1]=iiry1;
+int multicompand_over_attack_set(float msec){
+  multicompand_filterbank_set(msec,ms.over_attack);
+}
+
+int multicompand_over_decay_set(float msec){
+  multicompand_filterbank_set(msec,ms.over_decay);  
+}
+
+int multicompand_under_attack_set(float msec){
+  multicompand_filterbank_set(msec,ms.under_attack);
+}
+
+int multicompand_under_decay_set(float msec){
+  multicompand_filterbank_set(msec,ms.under_decay);
+}
+
+int multicompand_suppress_attack_set(float msec){
+  multicompand_filterbank_set(msec,ms.suppress_attack);
+}
+
+int multicompand_suppress_decay_set(float msec){
+  multicompand_filterbank_set(msec,ms.suppress_decay);
+}
+
+int multicompand_suppress_release_set(float msec){
+  multicompand_filterbank_set(msec,ms.suppress_release);
+}
+
+/* assymetrical attack/decay filter; the z-space fixup is designed for
+   the case where decay is the same or slower than attack */
+/* The Bessel filter followers are inlined here as later to avoid some
+   really shockingly bad code generation by gcc on x86 */
+static void compute_iir(float *y, float *x, int n,
+			iir_state *is, iir_filter *attack, iir_filter *decay){
+  double a_c0=attack->c0;
+  double d_c0=decay->c0;
+  double a_c1=attack->c1;
+  double d_c1=decay->c1;
+  double a_g=attack->g;
+  double d_g=decay->g;
+  
+  double x0=is->x[0];
+  double x1=is->x[1];
+  double y0=is->y[0];
+  double y1=is->y[1];
+  int state=is->state;
+    
+  int i=0;
+
+  if(x[0]>y0)state=0; 
       
-    }
+  while(i<n){
     
-    {
-      float scale=(linkp?1./input_ch:1);
-      for(k=0;k<input_size;k++){
-	val=fabs(rms[k]*scale);
-	rms[k]=sqrt(val);
+    if(state==0){
+      /* attack case */
+      while(i<n){
+	y[i] = (x[i]+x0*2.+x1)/a_g + y0*a_c0+y1*a_c1;
+    
+	if(y[i]<y0){
+	  /* decay fixup; needed because we're in discontinuous time.  In a
+	     physical time-assymmetric Bessel follower, the decay case would
+	     take over at the instant the attack filter peaks.  In z-space,
+	     the attack filter has already begun to decay, and the decay
+	     filter otherwise takes over with an already substantial
+	     negative slope in the filter state, or if it takes over in the
+	     preceeding time quanta, a substantial positive slope.  Fix this
+	     up to take over at zero y slope. */
+	  y1=y0;
+	  state=1; 
+	  break;
+	}
+	x1=x0;x0=x[i];
+	y1=y0;y0=y[i];
+	i++;
       }
-    } 
+    }
 
-    for(k=0;k<input_size;k++)
-      rms[k]=todB_p(rms+k);
+    if(state==1){
+      /* decay case */
+      while(1){
+	y[i] = (x[i]+x0*2+x1)/d_g + y0*d_c0+y1*d_c1;
+
+	x1=x0;x0=x[i];
+	y1=y0;y0=y[i];
+	i++;
+
+	if(i>=n)break;
+	if(x[i]>y0){
+	  state=0;
+	  break;
+	}
+      }
+    }
+  }
+  
+  is->x[0]=x0;
+  is->x[1]=x1;
+  is->y[0]=y0;
+  is->y[1]=y1;
+  is->state=state;
+  
+}
+
+static void prepare_rms(float *rms, float **xx, int n, int ch,int ahead){
+  if(ch){
+    int i,j;
+
+    memset(rms,0,sizeof(*rms)*n);
     
+    for(j=0;j<ch;j++){
+      float *x=xx[j]+ahead;
+      for(i=0;i<n;i++)
+	rms[i]+=x[i]*x[i];
+    }
+    if(ch>1)
+      for(i=0;i<n;i++)
+	rms[i]/=ch;
   }
 }
 
-static void peak_chase(float *peak, int i, int linkp, int filternum,
-		       float **xxi){
-  int l;
-  if(linkp==0 || i==0){
-    memset(peak,0,sizeof(*peak)*input_size);
-    for(l=i;l<(linkp?input_ch:i+1);l++){
+static void prepare_peak(float *peak, float **xx, int n, int ch,int ahead){
+  if(ch){
+    int j,k,l;
+
+    memset(peak,0,sizeof(*peak)*n);
+
+    for(l=0;l<ch;l++){
       
-      float *x=xxi[l];
+      float *x=xx[l];
       int ii,jj;
       int loc=0;
       float val=fabs(x[0]);
-      int ahead=iir_peakoffset_list[filternum];
       
       /* find highest point in next [ahead] */
       for(ii=1;ii<ahead;ii++)
@@ -331,7 +375,7 @@
         }
       if(val>peak[0])peak[0]=val;
       
-      for(ii=1;ii<input_size;ii++){
+      for(ii=1;ii<n;ii++){
         if(fabs(x[ii+ahead])>val){
           val=fabs(x[ii+ahead]);
           loc=ii+ahead;
@@ -341,118 +385,170 @@
           val=0;
           for(jj=ii+ahead-1;jj>=ii;jj--){
             if(fabs(x[jj])>val)val=fabs(x[jj]);
-	    if(jj<input_size && val>peak[jj])peak[jj]=val;
+	    if(jj<n && val>peak[jj])peak[jj]=val;
           }
           val=fabs(x[ii+ahead-1]);
           loc=ii+ahead;
         }
         if(val>peak[ii])peak[ii]=val; 
       }
-      
-      for(ii=0;ii<input_size;ii++)
-	peak[ii]=todB_p(peak+ii);
     }
+    
+    for(j=0;j<n;j++)
+      peak[j]=todB_p(peak+j);
   }
 }
 
-static void static_compand(float *peak, float *rms, float *gain, 
-			   float gate,float expand, float compress,
-			   envelope_state *e){
-  int ii;
-  float current_c=e->static_c_decay_chase;
-  float current_e=e->static_e_decay_chase;
-  float current_g=e->static_g_decay_chase;
+static void run_filter_only(float *dB,float *work,float **x,int n,int mode,
+			    iir_state *iir,iir_filter *attack,iir_filter *decay){
+  int i;
 
-  float decay_c=c.static_c_decay/(float)(1024*1024);
-  float decay_e=c.static_e_decay/(float)(1024*1024);
-  float decay_g=c.static_g_decay/(float)(1024*1024);
+  compute_iir(dB, work, n, &iir[i], attack, decay);
 
-  float *envelope=rms;
-  
-  float ratio_c=100./c.static_c_ratio;
-  float ratio_e=c.static_e_ratio/100.;
-  
-  if(c.static_mode)envelope=peak;
-  
-  for(ii=0;ii<input_size;ii++){
-    float adj=0.,local_adj;
-    float current=envelope[ii];
-    
-    /* compressor */
-    local_adj=0.;
-    if(current>compress){
-      float current_over=current-compress;
-      float compress_over=current_over*ratio_c;
-      local_adj=compress_over-current_over;
-    }
-    /* compressor decay is related to how quickly we release
-       attenuation */
-    current_c-=decay_c;
-    if(local_adj<current_c)current_c=local_adj;
-    adj+=current_c;
-
-    /* expander */
-    local_adj=0.;
-    if(current<expand){
-      float current_under=expand-current;
-      float expand_under=current_under*ratio_e;
-      local_adj= current_under-expand_under;
-    }
-    /* expander decay is related to how quickly we engage
-       attenuation */
-    current_e+=decay_e;
-    if(local_adj>current_e)current_e=local_adj;
-    adj+=current_e;
-
-    /* gate */
-    local_adj=0.;
-    if(current<gate){
-      float current_under=gate-current;
-      float gate_under=current_under*10.;
-      local_adj= current_under-gate_under;
-    }
-    /* gate decay is related to how quickly we engage
-       attenuation */
-    current_g+=decay_g;
-    if(local_adj>current_g)current_g=local_adj;
-    adj+=current_g;
+  if(mode==0)
+    for(i=0;i<n;i++)
+      dB[i]=todB_p(dB+i)*.5f;
+}
 
-    
-    if(adj<-150.)adj=-150;
-    gain[ii]=adj;
+static void run_filter(float *dB,float *work,float **x,int n, int ch, int i,
+		       float lookahead,int link,int mode,
+		       iir_state *iir,iir_filter *attack,iir_filter *decay){
+  if(link){
+    if(i==0){
+      if(mode)
+	prepare_peak(work, x, n, ch, step_ahead(attack->alpha)*lookahead);
+
+      else
+	prepare_rms(work, x, n, ch, impulse_ahead(attack->alpha)*lookahead);
+
+    }
+  }else{
+    if(mode)
+      prepare_peak(work, x+i, n, 1, step_ahead(attack->alpha)*lookahead);
+    else
+      prepare_rms(work, x+i, n, 1, impulse_ahead(attack->alpha)*lookahead);
   }
-  
-  e->static_c_decay_chase=current_c;
-  e->static_e_decay_chase=current_e;
-  e->static_g_decay_chase=current_g;
-  
+
+  run_filter_only(dB,work,x,n,mode,iir,attack,decay);
 }
 
-static void range_compand(float *peak, float *rms, float *gain){
-  int ii;
-  float *envelope; 
-  float ratio=1.+c.envelope_c/1000.;
+static void over_compand(float ***x){
+  int i,j,k;
+  float work[input_size];
+  float dB[input_size];
+  float lookahead=c.over_lookahead/1000.;
+  int link=c.link_mode;
+  int mode=c.over_mode;
+
+  float (*knee)(float)=(c.over_softknee?soft_knee:hard_knee);
   
-  switch(c.envelope_mode){
-  case 0:
-    envelope=rms;
-    break;
-  default:
-    envelope=peak;
-    break;
+  float corner_multiplier=(1.-1./(c.over_ratio/1000.));
+  float base_multiplier=(1.-1./(c.base_ratio/1000.));
+
+  if(ms.previous_over_mode!=mode){
+    /* the followers for RMS are in linear^2 mode, the followers for
+       peak are dB.  If the mode has changed, we need to convert from
+       one to the other to avoid pops */
+    if(mode==0){
+      /* from dB to linear^2 */
+      for(i=0;i<multicomp_freqs_max;i++){
+	for(j=0;j<input_ch;j++){
+	  ms.over_iir[i][j].x[0]=fromdB(ms.over_iir[i][j].x[0]*2.);
+	  ms.over_iir[i][j].x[1]=fromdB(ms.over_iir[i][j].x[1]*2.);
+	  ms.over_iir[i][j].y[0]=fromdB(ms.over_iir[i][j].y[0]*2.);
+	  ms.over_iir[i][j].y[1]=fromdB(ms.over_iir[i][j].y[1]*2.);
+	}
+      }
+    }else{
+      /* from linear^2 to dB */
+      for(i=0;i<multicomp_freqs_max;i++){
+	for(j=0;j<input_ch;j++){
+	  ms.over_iir[i][j].x[0]=todB(ms.over_iir[i][j].x[0])*.5;
+	  ms.over_iir[i][j].x[1]=todB(ms.over_iir[i][j].x[1])*.5;
+	  ms.over_iir[i][j].y[0]=todB(ms.over_iir[i][j].y[0])*.5;
+	  ms.over_iir[i][j].y[1]=todB(ms.over_iir[i][j].y[1])*.5;
+	}
+      }
+    }
   }
+
+  ms.previous_over_mode=mode;
+
+  for(i=0;i<multicomp_freqs[ms.active_bank];i++){
+    float zerocorner=bc[ms.active_bank].static_o[i]/10.;
+    float limitcorner=zerocorner + fabs((c.over_limit*10.)/corner_multiplier);
+
+    for(j=0;j<input_ch;j++){
+      float *lx=x[i][j];
+      run_filter(dB,work,x[i],input_size,input_ch,j,lookahead,link,mode,
+		 ms.over_iir[i],&ms.over_attack[ms.active_bank][i],&ms.over_decay[ms.active_bank][i]);
+
+      if(compand_active)
+	for(k=0;k<input_size;k++){
+	  float adj=(knee(dB[i]-zerocorner)-knee(dB[i]-limitcorner))*corner_multiplier;
+	  adj-=  (dB[i]+adj)*base_multiplier;
+	  lx[k]*=fromdB(adj);
+	}
+    }
+  }
+}
+
+static void under_compand(float ***x){
+  int i,j,k;
+  float work[input_size];
+  float dB[input_size];
+  float lookahead=c.under_lookahead/1000.;
+  int link=c.link_mode;
+  int mode=c.under_mode;
+
+  float (*knee)(float)=(c.under_softknee?soft_knee:hard_knee);
   
-  for(ii=0;ii<input_size;ii++){
-    float adj=0.;
-    float current=envelope[ii];
-    float target=current*ratio;
-    
-    gain[ii]+=target-current;
+  float corner_multiplier=(1.-1./(c.under_ratio/1000.));
+
+  if(ms.previous_under_mode!=mode){
+    /* the followers for RMS are in linear^2 mode, the followers for
+       peak are dB.  If the mode has changed, we need to convert from
+       one to the other to avoid pops */
+    if(mode==0){
+      /* from dB to linear^2 */
+      for(i=0;i<multicomp_freqs_max;i++){
+	for(j=0;j<input_ch;j++){
+	  ms.under_iir[i][j].x[0]=fromdB(ms.under_iir[i][j].x[0]*2.);
+	  ms.under_iir[i][j].x[1]=fromdB(ms.under_iir[i][j].x[1]*2.);
+	  ms.under_iir[i][j].y[0]=fromdB(ms.under_iir[i][j].y[0]*2.);
+	  ms.under_iir[i][j].y[1]=fromdB(ms.under_iir[i][j].y[1]*2.);
+	}
+      }
+    }else{
+      /* from linear^2 to dB */
+      for(i=0;i<multicomp_freqs_max;i++){
+	for(j=0;j<input_ch;j++){
+	  ms.under_iir[i][j].x[0]=todB(ms.under_iir[i][j].x[0])*.5;
+	  ms.under_iir[i][j].x[1]=todB(ms.under_iir[i][j].x[1])*.5;
+	  ms.under_iir[i][j].y[0]=todB(ms.under_iir[i][j].y[0])*.5;
+	  ms.under_iir[i][j].y[1]=todB(ms.under_iir[i][j].y[1])*.5;
+	}
+      }
+    }
+  }
+
+  ms.previous_under_mode=mode;
+
+  for(i=0;i<multicomp_freqs[ms.active_bank];i++){
+    float zerocorner=bc[ms.active_bank].static_o[i]/10.;
+    float limitcorner=zerocorner- fabs((c.under_limit*10.)/corner_multiplier);
+
+    for(j=0;j<input_ch;j++){
+      float *lx=x[i][j];
+      run_filter(dB,work,x[i],input_size,input_ch,j,lookahead,link,mode,
+		 ms.under_iir[i],&ms.under_attack[ms.active_bank][i],&ms.under_decay[ms.active_bank][i]);
+      if(compand_active)
+	for(k=0;k<input_size;k++)
+	  lx[k]*=fromdB((knee(zerocorner-dB[i])-knee(limitcorner-dB[i]))*corner_multiplier);
+    }
   }
 }
 
-static void suppress(float *peak, float *rms, float *gain,
-		     envelope_state *e){
   /* (since this one is kinda unique) The Suppressor....
 
      Reverberation in a measurably live environment displays
@@ -472,231 +568,114 @@
      Thus, the suppressor can be used to 'dry out' a very 'wet'
      reverberative track. */
 
-  int ii;
-  float *envelope; 
-  float ratio=c.suppress_ratio/100.;
-  float decay=c.suppress_decay/(float)(1024*1024);
-  float depth=-c.suppress_depth/10.;
-  float chase=e->suppress_decay_chase;
-  float undepth=depth/ratio;
-
-  switch(c.suppress_mode){
-  case 0:
-    envelope=rms;
-    break;
-  default:
-    envelope=peak;
-    break;
-  }
-
-  for(ii=0;ii<input_size;ii++){
-    float current=envelope[ii];
-
-    chase+=decay;
-    if(current>chase){
-      chase=current;
-    }else{
-      /* yes, need to expand */
-      float difference = chase - current;
-      float expanded = difference * ratio;
+static void suppress(float ***x){
+  int i,j,k;
+  float work[input_size];
+  float dB_fast[input_size];
+  float dB_slow[input_size];
+  int link=c.link_mode;
+  int mode=c.suppress_mode;
 
-      if(expanded>depth){
-	chase=current+undepth;
-	gain[ii]-=depth-undepth;
-      }else{
-	gain[ii]-=expanded-difference;
+  float multiplier=(1.-1./(c.suppress_ratio/1000.));
 
+  if(ms.previous_suppress_mode!=mode){
+    /* the followers for RMS are in linear^2 mode, the followers for
+       peak are dB.  If the mode has changed, we need to convert from
+       one to the other to avoid pops */
+    if(mode==0){
+      /* from dB to linear^2 */
+      for(i=0;i<multicomp_freqs_max;i++){
+	for(j=0;j<input_ch;j++){
+	  ms.suppress_iirA[i][j].x[0]=fromdB(ms.suppress_iirA[i][j].x[0]*2.);
+	  ms.suppress_iirA[i][j].x[1]=fromdB(ms.suppress_iirA[i][j].x[1]*2.);
+	  ms.suppress_iirA[i][j].y[0]=fromdB(ms.suppress_iirA[i][j].y[0]*2.);
+	  ms.suppress_iirA[i][j].y[1]=fromdB(ms.suppress_iirA[i][j].y[1]*2.);
+	  ms.suppress_iirB[i][j].x[0]=fromdB(ms.suppress_iirB[i][j].x[0]*2.);
+	  ms.suppress_iirB[i][j].x[1]=fromdB(ms.suppress_iirB[i][j].x[1]*2.);
+	  ms.suppress_iirB[i][j].y[0]=fromdB(ms.suppress_iirB[i][j].y[0]*2.);
+	  ms.suppress_iirB[i][j].y[1]=fromdB(ms.suppress_iirB[i][j].y[1]*2.);
+	}
+      }
+    }else{
+      /* from linear^2 to dB */
+      for(i=0;i<multicomp_freqs_max;i++){
+	for(j=0;j<input_ch;j++){
+	  ms.suppress_iirA[i][j].x[0]=todB(ms.suppress_iirA[i][j].x[0])*.5;
+	  ms.suppress_iirA[i][j].x[1]=todB(ms.suppress_iirA[i][j].x[1])*.5;
+	  ms.suppress_iirA[i][j].y[0]=todB(ms.suppress_iirA[i][j].y[0])*.5;
+	  ms.suppress_iirA[i][j].y[1]=todB(ms.suppress_iirA[i][j].y[1])*.5;
+	  ms.suppress_iirB[i][j].x[0]=todB(ms.suppress_iirB[i][j].x[0])*.5;
+	  ms.suppress_iirB[i][j].x[1]=todB(ms.suppress_iirB[i][j].x[1])*.5;
+	  ms.suppress_iirB[i][j].y[0]=todB(ms.suppress_iirB[i][j].y[0])*.5;
+	  ms.suppress_iirB[i][j].y[1]=todB(ms.suppress_iirB[i][j].y[1])*.5;
+	}
       }
     }
   }
 
-  e->suppress_decay_chase=chase;
-}
-
-static void final_followers(float *gain,envelope_state *e,int attack,int decay){
-  float iirx0,iirx1;
-  float iiry0,iiry1;
-
-  float iirg_a;
-  float iirg_d;
-  float iirc0_a,iirc1_a;
-  float iirc0_d,iirc1_d;
-
-  float x,y;
-  int k;
-
-  iirg_a=iirg_list[attack];
-  iirc0_a=iirc_list[attack][0];
-  iirc1_a=iirc_list[attack][1];
-  iirg_d=iirg_list[decay];
-  iirc0_d=iirc_list[decay][0];
-  iirc1_d=iirc_list[decay][1];
-
-  iirx0=e->env1_x[0];
-  iirx1=e->env1_x[1];
-  iiry0=e->env1_y[0];
-  iiry1=e->env1_y[1];
+  ms.previous_suppress_mode=mode;
 
-  /* assymetrical filter; in general, fast attack, slow decay */
-  for(k=0;k<input_size;k+=2){
-    if(iiry0>iiry1){
-      /* decay */
-      y = (gain[k]+iirx0+iirx0+iirx1)/iirg_d + iiry0*iirc0_d+iiry1*iirc1_d;
-      iirx1=gain[k];
-      gain[k]=iiry1=y;
-    }else{
-      /* attack */
-      y = (gain[k]+iirx0+iirx0+iirx1)/iirg_a + iiry0*iirc0_a+iiry1*iirc1_a;
-      iirx1=gain[k];
-      gain[k]=iiry1=y;
-    }
-
-    if(iiry0>iiry1){
-      /* attack */
-      y = (gain[k+1]+iirx1+iirx1+iirx0)/iirg_a + iiry1*iirc0_a+iiry0*iirc1_a;
-      iirx0=gain[k+1];
-      gain[k+1]=iiry0=y;
-    }else{
-      /* decay */
-      y = (gain[k+1]+iirx1+iirx1+iirx0)/iirg_d + iiry1*iirc0_d+iiry0*iirc1_d;
-      iirx0=gain[k+1];
-      gain[k+1]=iiry0=y;
-    }
-  }
-  
-  e->env1_x[0]=iirx0;
-  e->env1_x[1]=iirx1;
-  e->env1_y[0]=iiry0;
-  e->env1_y[1]=iiry1;
-
-  iirx0=e->env2_x[0];
-  iirx1=e->env2_x[1];
-  iiry0=e->env2_y[0];
-  iiry1=e->env2_y[1];
-
-  for(k=0;k<input_size;k+=2){
-    if(iiry0>iiry1){
-      /* decay */
-      y = (gain[k]+iirx0+iirx0+iirx1)/iirg_d + iiry0*iirc0_d+iiry1*iirc1_d;
-      iirx1=gain[k];
-      gain[k]=iiry1=y;
-    }else{
-      /* attack */
-      y = (gain[k]+iirx0+iirx0+iirx1)/iirg_a + iiry0*iirc0_a+iiry1*iirc1_a;
-      iirx1=gain[k];
-      gain[k]=iiry1=y;
-    }
+  for(i=0;i<multicomp_freqs[ms.active_bank];i++){
+    for(j=0;j<input_ch;j++){
+      float *lx=x[i][j];
 
-    if(iiry0>iiry1){
-      /* attack */
-      y = (gain[k+1]+iirx1+iirx1+iirx0)/iirg_a + iiry1*iirc0_a+iiry0*iirc1_a;
-      iirx0=gain[k+1];
-      gain[k+1]=iiry0=y;
-    }else{
-      /* decay */
-      y = (gain[k+1]+iirx1+iirx1+iirx0)/iirg_d + iiry1*iirc0_d+iiry0*iirc1_d;
-      iirx0=gain[k+1];
-      gain[k+1]=iiry0=y;
+      run_filter(dB_fast,work,x[i],input_size,input_ch,j,1.,link,mode,
+		 ms.suppress_iirA[i],&ms.suppress_attack[ms.active_bank][i],&ms.suppress_decay[ms.active_bank][i]);
+      run_filter_only(dB_slow,work,x[i],input_size,mode,
+		      ms.suppress_iirB[i],&ms.suppress_attack[ms.active_bank][i],&ms.suppress_release[ms.active_bank][i]);
+      
+      if(compand_active && multiplier!=0.)
+	for(k=0;k<input_size;k++){
+	  float adj=(dB_fast[i]-dB_slow[i])*multiplier;  
+	  lx[k]*=fromdB(adj);
+	}
     }
   }
-  
-  e->env2_x[0]=iirx0;
-  e->env2_x[1]=iirx1;
-  e->env2_y[0]=iiry0;
-  e->env2_y[1]=iiry1;
-
 }
 
 static void multicompand_work(float **peakfeed,float **rmsfeed){
-  int i,j,k,l;
-  int link=c.link_mode;
-  float rms[input_size];
-  float peak[input_size];
-  float gain[input_size];
-  int bands=multicomp_freqs[ms.active_bank];
-
-  /* we chase both peak and RMS forward at all times. All portions of
-     the compander can use the raw values of one or the other at any
-     time */
+  int i,j,k;
 
-  for(j=0;j<bands;j++){
-    for(i=0;i<input_ch;i++){
-      
-      //_analysis_append("band",j,ms.ss.lap[j][i],input_size,offset);
-
-
-      /* RMS chase */
-      rms_chase(rms,i,link,ms.rmschoice[ms.active_bank][j],
-		ms.e[j],ms.ss.lap[j]);
+  under_compand(ms.ss.lap);
+  over_compand(ms.ss.lap);
 
-      /* peak shelving */
-      peak_chase(peak,i,link,ms.attack[ms.active_bank][j],
-		 ms.ss.lap[j]);
-
-      if(compand_active)
-	/* run the static compander */
-	static_compand(peak,rms,gain,
-		       (bc[ms.active_bank].static_g[j]+c.static_g_trim)/10.,
-		       (bc[ms.active_bank].static_e[j]+c.static_e_trim)/10.,
-		       (bc[ms.active_bank].static_c[j]+c.static_c_trim)/10.,
-		       &ms.e[j][i]);
-      else
-	memset(gain,0,sizeof(gain));
-
-      /* feedback displays the results of the static compander */
-      {
-	float *x=ms.ss.lap[j][i];
-	float rmsmax=-150,peakmax=-150;
+  /* feedback displays the results of the static compander */
+  if(c.link_mode==0){
+    for(i=0;i<multicomp_freqs[ms.active_bank];i++){
+      for(j=0;j<input_ch;j++){
+	float *x=ms.ss.lap[i][j];
+	float rms=0,peak=0;
         
         for(k=0;k<input_size;k++){
-	  int r=rms[k]+gain[k];
-	  x[k]*=fromdB(gain[k]);
-	  
-	  if(r>rmsmax)rmsmax=r;
-	  if(fabs(x[k])>peakmax)peakmax=fabs(x[k]);
+	  rms+=x[k]*x[k];
+	  if(fabs(x[k])>peak)peak=fabs(x[k]);
         }	    
         
-	peakfeed[j][i]=todB(peakmax);
-	rmsfeed[j][i]=rmsmax;
+	peakfeed[i][j]=todB(peak);
+	rmsfeed[i][j]=todB(rms/input_size)*.5;
       }
-
-      if(compand_active){
-	float *x=ms.ss.lap[j][i];
-
-	/* run the range compander */
-	range_compand(peak,rms,gain);
-	
-	/* run the suppressor */
-	suppress(peak,rms,gain,ms.e[j]);
-	
-	//_analysis_append("peak",j,gain,input_size,offset);
-
-	/* Run the Bessel followers */
-	final_followers(gain,&ms.e[j][i],
-			ms.attack[ms.active_bank][j],
-			ms.decay[ms.active_bank][j]);
+    }
+  }else{
+    for(i=0;i<multicomp_freqs[ms.active_bank];i++){
+      float rms=0,peak=0;
+      for(j=0;j<input_ch;j++){
+	float *x=ms.ss.lap[i][j];
         
-	//_analysis_append("gain",j,gain,input_size,offset);
+	for(k=0;k<input_size;k++){
+	  rms+=x[k]*x[k];
+	  if(fabs(x[k])>peak)peak=fabs(x[k]);
+	}	
+      }    
         
-
-	/* application to data */
-	for(k=0;k<input_size;k++)
-	  x[k]*=fromdB(gain[k]);
-
-      }else{
-	/* else reset the bessel followers */
-	ms.e[j][i].env1_x[0]=0.;
-	ms.e[j][i].env1_x[1]=0.;
-	ms.e[j][i].env1_y[0]=0.;
-	ms.e[j][i].env1_y[1]=0.;
-	ms.e[j][i].env2_x[0]=0.;
-	ms.e[j][i].env2_x[1]=0.;
-	ms.e[j][i].env2_y[0]=0.;
-	ms.e[j][i].env2_y[1]=0.;
+      for(j=0;j<input_ch;j++){
+	peakfeed[i][j]=todB(peak);
+	rmsfeed[i][j]=todB(rms/input_size/input_ch)*.5;
       }
     }
   }
 
+  suppress(ms.ss.lap);
+  
   offset+=input_size;
 }
 
@@ -712,17 +691,7 @@
   int bypass=!(compand_visible||compand_active);
   
   if(pending!=ms.active_bank || (bypass && !bypass_state)){
-    /* reset unused envelope followers to a known state for if/when
-       they're brought online */
-    
-    for(j=(bypass?0:multicomp_freqs[pending]);j<multicomp_freqs_max;j++){
-      for(i=0;i<input_ch;i++){
-	memset(&ms.e[j][i],0,sizeof(**ms.e));
-	ms.e[j][i].static_c_decay_chase=0.;
-	ms.e[j][i].static_e_decay_chase=0.;
-	ms.e[j][i].static_g_decay_chase=0.;
-      }
-    }
+
   }
    
   ms.active_bank=ms.pending_bank;

<p><p>1.3       +32 -18    postfish/multicompand.h

Index: multicompand.h
===================================================================
RCS file: /usr/local/cvsroot/postfish/multicompand.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- a/multicompand.h	8 Mar 2004 02:08:23 -0000	1.2
+++ b/multicompand.h	17 Mar 2004 10:32:04 -0000	1.3
@@ -46,31 +46,39 @@
 };
 
 typedef struct {
-  sig_atomic_t static_g[multicomp_freqs_max];
-  sig_atomic_t static_e[multicomp_freqs_max];
-  sig_atomic_t static_c[multicomp_freqs_max];
+  sig_atomic_t static_o[multicomp_freqs_max];
+  sig_atomic_t static_u[multicomp_freqs_max];
 } banked_compand_settings;
 
 typedef struct {
-  sig_atomic_t link_mode;
-
-  sig_atomic_t static_mode;
-  sig_atomic_t static_c_trim;
-  sig_atomic_t static_e_trim;
-  sig_atomic_t static_g_trim;
-  sig_atomic_t static_c_decay;
-  sig_atomic_t static_e_decay;
-  sig_atomic_t static_g_decay;
-  sig_atomic_t static_c_ratio;
-  sig_atomic_t static_e_ratio;
 
-  sig_atomic_t envelope_mode;
-  sig_atomic_t envelope_c;
+  sig_atomic_t over_mode;
+  sig_atomic_t over_softknee;
+  sig_atomic_t over_ratio;
+  sig_atomic_t over_limit;
+  sig_atomic_t base_ratio;
+  sig_atomic_t over_attack;
+  sig_atomic_t over_decay;
+  sig_atomic_t over_lookahead;
+  sig_atomic_t over_trim;
+
+  sig_atomic_t under_mode;
+  sig_atomic_t under_softknee;
+  sig_atomic_t under_ratio;
+  sig_atomic_t under_limit;
+  sig_atomic_t under_attack;
+  sig_atomic_t under_decay;
+  sig_atomic_t under_lookahead;
+  sig_atomic_t under_trim;
 
   sig_atomic_t suppress_mode;
   sig_atomic_t suppress_ratio;
+
+  sig_atomic_t suppress_attack;
   sig_atomic_t suppress_decay;
-  sig_atomic_t suppress_depth;
+  sig_atomic_t suppress_release;
+
+  sig_atomic_t link_mode;
 
 } other_compand_settings;
 
@@ -80,4 +88,10 @@
 extern void multicompand_set_bank(int bank);
 extern int pull_multicompand_feedback(float **peak,float **rms,int *bands);
 
-
+extern int multicompand_over_attack_set(float msec);
+extern int multicompand_over_decay_set(float msec);
+extern int multicompand_under_attack_set(float msec);
+extern int multicompand_under_decay_set(float msec);
+extern int multicompand_suppress_attack_set(float msec);
+extern int multicompand_suppress_decay_set(float msec);
+extern int multicompand_suppress_release_set(float msec);

<p><p>1.10      +8 -2      postfish/postfish-gtkrc

Index: postfish-gtkrc
===================================================================
RCS file: /usr/local/cvsroot/postfish/postfish-gtkrc,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- a/postfish-gtkrc	8 Mar 2004 02:08:23 -0000	1.9
+++ b/postfish-gtkrc	17 Mar 2004 10:32:04 -0000	1.10
@@ -20,7 +20,12 @@
 }
 
 style "small-marker" {
-	font_name = "sans 8"
+        fg[NORMAL]="#905050" 
+	font_name = "sans 6"
+}
+
+style "scale-marker" {
+	font_name = "sans 7"
 }
 
 style "frame-label" {
@@ -59,7 +64,7 @@
 
         text[NORMAL]="#c0c0d0" 
         text[ACTIVE]="#ffb0b0" 
-        font_name = "sans 6"    
+        font_name = "sans 7"    
 }
 
 style "multislide" {
@@ -138,6 +143,7 @@
 }
 
 widget "*.GtkLabel" style "panel-text"
+widget "*.scalemarker" style "scale-marker"
 widget "*.smallmarker" style "small-marker"
 
 widget "*.color0" style "left"

<p><p>1.43      +2 -2      postfish/version.h

Index: version.h
===================================================================
RCS file: /usr/local/cvsroot/postfish/version.h,v
retrieving revision 1.42
retrieving revision 1.43
diff -u -r1.42 -r1.43
--- a/version.h	8 Mar 2004 11:07:06 -0000	1.42
+++ b/version.h	17 Mar 2004 10:32:04 -0000	1.43
@@ -1,2 +1,2 @@
-#define VERSION "$Id: version.h,v 1.42 2004/03/08 11:07:06 xiphmont Exp $ "
-/* DO NOT EDIT: Automated versioning hack [Mon Mar  8 06:01:38 EST 2004] */
+#define VERSION "$Id: version.h,v 1.43 2004/03/17 10:32:04 xiphmont Exp $ "
+/* DO NOT EDIT: Automated versioning hack [Wed Mar 17 05:30:15 EST 2004] */

<p><p>1.1                  postfish/bessel.c

Index: bessel.c
===================================================================
/*
 *
 *  postfish
 *    
 *      Copyright (C) 2002-2004 Monty
 *
 *  Postfish is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  Postfish is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with Postfish; see the file COPYING.  If not, write to the
 *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * 
 */

/* code derived directly from mkfilter by the late A.J. Fisher,
   University of York <fisher at minster.york.ac.uk> September 1992; this
   is only the minimum code needed to build an arbitrary 2nd order
   Bessel filter */

#include "postfish.h"

#define TWOPI	    (2.0 * M_PIl)
#define EPS	    1e-10
#define MAXORDER    2
#define MAXPZ	    4

typedef struct { 
  double re, im;
} complex;

typedef struct { 
  complex poles[MAXPZ], zeros[MAXPZ];
  int numpoles, numzeros;
} pzrep;

tatic complex cdiv(complex z1, complex z2){ 
  double mag = (z2.re * z2.re) + (z2.im * z2.im);
  complex ret={((z1.re * z2.re) + (z1.im * z2.im)) / mag,
               ((z1.im * z2.re) - (z1.re * z2.im)) / mag};
  return ret;
}

tatic complex cmul(complex z1, complex z2){ 
  complex ret={z1.re*z2.re - z1.im*z2.im,
               z1.re*z2.im + z1.im*z2.re};
  return ret;
}
static complex cadd(complex z1, complex z2){ 
  z1.re+=z2.re;
  z1.im+=z2.im;
  return z1;
}

tatic complex csub(complex z1, complex z2){ 
  z1.re-=z2.re;
  z1.im-=z2.im;
  return z1;
}

tatic complex eval(complex coeffs[], int npz, complex z){ 
  complex sum = (complex){0.0,0.0};
  int i;
  for (i = npz; i >= 0; i--) sum = cadd(cmul(sum, z), coeffs[i]);
  return sum;
}

tatic complex evaluate(complex topco[], int nz, complex botco[], int np, complex z){ 
  return cdiv(eval(topco, nz, z),eval(botco, np, z));
}

tatic complex blt(complex pz){ 
  complex two={2.,0.};
  return cdiv(cadd(two, pz), csub(two, pz));
}

tatic void multin(complex w, int npz, complex coeffs[]){ 
  int i;
  complex nw = (complex){-w.re , -w.im};
  for (i = npz; i >= 1; i--) 
    coeffs[i] = cadd(cmul(nw, coeffs[i]), coeffs[i-1]);
  coeffs[0] = cmul(nw, coeffs[0]);
}
 
static void expand(complex pz[], int npz, complex coeffs[]){ 
  /* compute product of poles or zeros as a polynomial of z */
  int i;
  coeffs[0] = (complex){1.0,0.0};
  for (i=0; i < npz; i++) coeffs[i+1] = (complex){0.0,0.0};
  for (i=0; i < npz; i++) multin(pz[i], npz, coeffs);
  /* check computed coeffs of z^k are all real */
  for (i=0; i < npz+1; i++){ 
    if (fabs(coeffs[i].im) > EPS){ 
      fprintf(stderr, "mkfilter: coeff of z^%d is not real; poles/zeros are not complex conjugates\n", i);
      exit(1);
    }
  }
}

double mkbessel_2(double raw_alpha,double *ycoeff0,double *ycoeff1){ 
  int i;
  pzrep splane, zplane;
  complex topcoeffs[MAXPZ+1], botcoeffs[MAXPZ+1];
  double warped_alpha;
  complex dc_gain;

  memset(&splane,0,sizeof(splane));
  memset(&zplane,0,sizeof(zplane));

  splane.poles[splane.numpoles++] = (complex){ -1.10160133059e+00, 6.36009824757e-01};
  splane.poles[splane.numpoles++] = (complex){ -1.10160133059e+00, -6.36009824757e-01};
  
  warped_alpha = tan(M_PIl * raw_alpha) / M_PIl;
  for (i = 0; i < splane.numpoles; i++){
    splane.poles[i].re *= TWOPI * warped_alpha;
    splane.poles[i].im *= TWOPI * warped_alpha;
  }
  
  zplane.numpoles = splane.numpoles;
  zplane.numzeros = splane.numzeros;
  for (i=0; i < zplane.numpoles; i++) 
    zplane.poles[i] = blt(splane.poles[i]);
  for (i=0; i < zplane.numzeros; i++) 
    zplane.zeros[i] = blt(splane.zeros[i]);
  while (zplane.numzeros < zplane.numpoles) 
    zplane.zeros[zplane.numzeros++] = (complex){-1.0,0.};
  
  expand(zplane.zeros, zplane.numzeros, topcoeffs);
  expand(zplane.poles, zplane.numpoles, botcoeffs);
  dc_gain = evaluate(topcoeffs, zplane.numzeros, botcoeffs, zplane.numpoles, (complex){1.0,0.0});

  *ycoeff0 = -(botcoeffs[0].re / botcoeffs[zplane.numpoles].re);
  *ycoeff1 = -(botcoeffs[1].re / botcoeffs[zplane.numpoles].re);

  return hypot(dc_gain.re,dc_gain.im);
}

<p><p><p><p><p>1.1                  postfish/bessel.h

Index: bessel.h
===================================================================
/*
 *
 *  postfish
 *    
 *      Copyright (C) 2002-2004 Monty
 *
 *  Postfish is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  Postfish is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with Postfish; see the file COPYING.  If not, write to the
 *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * 
 */

extern double mkbessel_2(double raw_alpha,double *ycoeff0,double *ycoeff1);

<p><p>--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list