[xiph-cvs] r6506 - trunk/postfish

xiphmont at xiph.org xiphmont at xiph.org
Tue Apr 13 21:50:39 PDT 2004



Author: xiphmont
Date: 2004-04-14 00:50:38 -0400 (Wed, 14 Apr 2004)
New Revision: 6506

Added:
   trunk/postfish/limit.c
   trunk/postfish/limit.h
   trunk/postfish/limitpanel.c
   trunk/postfish/limitpanel.h
   trunk/postfish/rexperiment.c
   trunk/postfish/singlecomp.c
   trunk/postfish/singlecomp.h
   trunk/postfish/singlepanel.c
   trunk/postfish/singlepanel.h
   trunk/postfish/suppress.c
   trunk/postfish/suppress.h
   trunk/postfish/suppresspanel.c
   trunk/postfish/suppresspanel.h
Modified:
   trunk/postfish/Makefile
   trunk/postfish/bessel.c
   trunk/postfish/bessel.h
   trunk/postfish/compandpanel.c
   trunk/postfish/compandpanel.h
   trunk/postfish/declip.c
   trunk/postfish/eq.c
   trunk/postfish/eq.h
   trunk/postfish/freq.c
   trunk/postfish/freq.h
   trunk/postfish/input.c
   trunk/postfish/main.c
   trunk/postfish/mainpanel.c
   trunk/postfish/mainpanel.h
   trunk/postfish/multibar.c
   trunk/postfish/multicompand.c
   trunk/postfish/multicompand.h
   trunk/postfish/output.c
   trunk/postfish/postfish-gtkrc
   trunk/postfish/postfish.h
   trunk/postfish/readout.c
   trunk/postfish/readout.h
   trunk/postfish/reconstruct.c
   trunk/postfish/subband.c
   trunk/postfish/subband.h
   trunk/postfish/subpanel.c
   trunk/postfish/version.h
   trunk/postfish/windowbutton.c
Log:
Must... get... changes... under... source... control...

Subversion is not scary, and there are no monsters under my bed.

Monty

<p><p>Modified: trunk/postfish/Makefile
===================================================================
--- trunk/postfish/Makefile	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/Makefile	2004-04-14 04:50:38 UTC (rev 6506)
@@ -2,7 +2,8 @@
 # Fuck the horse it rode in on
 # and Fuck its little dog Libtool too
 
-CC=gcc
+ADD_DEF= -DUGLY_IEEE754_FLOAT32_HACK=1
+CC=gcc 
 LD=gcc
 INSTALL=install
 PREFIX=/usr/local
@@ -10,34 +11,26 @@
 ETCDIR=/etc
 MANDIR=$PREFIX/man
 
-# is this a platform that uses IEEE 754/854 32 bit floats?  The
-# following is good for a speedup on most of these systems, otherwise
-# comment it out.  Using this define on a system where a 'float' is
-# *not* an IEEE 32 bit float will destroy, destroy, destroy the audio.
-
-IEEE=-DNASTY_IEEE_FLOAT32_HACK_IS_FASTER_THAN_LOG=1
-
-
-
-
 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 \
-	bessel.c
+	bessel.c suppresspanel.c suppress.c singlecomp.c singlepanel.c \
+	limit.c limitpanel.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 \
-	bessel.o
+	bessel.o suppresspanel.o suppress.o singlecomp.o singlepanel.o \
+	limit.o limitpanel.o
 GCF = `pkg-config --cflags gtk+-2.0` -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED
 
 all:	
-	$(MAKE) target CFLAGS="-W -O3 -ffast-math $(GCF) $(IEEE)"
+	$(MAKE) target CFLAGS="-O3 -ffast-math -fomit-frame-pointer $(GCF) $(ADD_DEF)"
 
 debug:
-	$(MAKE) target CFLAGS="-g -W -D__NO_MATH_INLINES $(GCF) $(IEEE)"
+	$(MAKE) target CFLAGS="-g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES $(GCF) $(ADD_DEF)"
 
 profile:
-	$(MAKE) target CFLAGS="-W -pg -g -O3 -ffast-math $(GCF) $(IEEE)" LIBS="-lgprof-helper"
+	$(MAKE) target CFLAGS="-pg -g -O3 -ffast-math $(GCF) $(ADD_DEF)" LIBS="-lgprof-helper"
 
 clean:
         rm -f $(OBJ) *.d gmon.out

Modified: trunk/postfish/bessel.c
===================================================================
--- trunk/postfish/bessel.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/bessel.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -23,20 +23,52 @@
 
 /* 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 */
+   is only the minimum code needed to build an arbitrary Bessel filter */
 
 #include "postfish.h"
+#include "bessel.h"
 
-#define TWOPI	    (2.0 * M_PIl)
-#define EPS	    1e-10
-#define MAXORDER    2
-#define MAXPZ	    4
-
 typedef struct { 
   double re, im;
 } complex;
 
+static complex bessel_poles[] = {
+  { -1.00000000000e+00, 0.00000000000e+00}, 
+  { -1.10160133059e+00, 6.36009824757e-01},
+  { -1.32267579991e+00, 0.00000000000e+00}, 
+  { -1.04740916101e+00, 9.99264436281e-01},
+  { -1.37006783055e+00, 4.10249717494e-01}, 
+  { -9.95208764350e-01, 1.25710573945e+00},
+  { -1.50231627145e+00, 0.00000000000e+00}, 
+  { -1.38087732586e+00, 7.17909587627e-01},
+  { -9.57676548563e-01, 1.47112432073e+00}, 
+  { -1.57149040362e+00, 3.20896374221e-01},
+  { -1.38185809760e+00, 9.71471890712e-01}, 
+  { -9.30656522947e-01, 1.66186326894e+00},
+  { -1.68436817927e+00, 0.00000000000e+00}, 
+  { -1.61203876622e+00, 5.89244506931e-01},
+  { -1.37890321680e+00, 1.19156677780e+00}, 
+  { -9.09867780623e-01, 1.83645135304e+00},
+  { -1.75740840040e+00, 2.72867575103e-01}, 
+  { -1.63693941813e+00, 8.22795625139e-01},
+  { -1.37384121764e+00, 1.38835657588e+00}, 
+  { -8.92869718847e-01, 1.99832584364e+00},
+  { -1.85660050123e+00, 0.00000000000e+00}, 
+  { -1.80717053496e+00, 5.12383730575e-01},
+  { -1.65239648458e+00, 1.03138956698e+00}, 
+  { -1.36758830979e+00, 1.56773371224e+00},
+  { -8.78399276161e-01, 2.14980052431e+00},
+  { -1.92761969145e+00, 2.41623471082e-01},
+  { -1.84219624443e+00, 7.27257597722e-01}, 
+  { -1.66181024140e+00, 1.22110021857e+00},
+  { -1.36069227838e+00, 1.73350574267e+00}, 
+  { -8.65756901707e-01, 2.29260483098e+00},
+};
+
+#define TWOPI	    (2.0 * M_PIl)
+#define EPS	    1e-10
+#define MAXPZ	    8
+
 typedef struct { 
   complex poles[MAXPZ], zeros[MAXPZ];
   int numpoles, numzeros;
@@ -66,6 +98,11 @@
   return z1;
 }
 
+static complex cconj(complex z){ 
+  z.im = -z.im;
+  return z;
+}
+
 static complex eval(complex coeffs[], int npz, complex z){ 
   complex sum = (complex){0.0,0.0};
   int i;
@@ -105,8 +142,8 @@
   }
 }
 
-double mkbessel_2(double raw_alpha,double *ycoeff0,double *ycoeff1){ 
-  int i;
+double mkbessel(double raw_alpha,int order,double *ycoeff){ 
+  int i,p= (order*order)/4; 
   pzrep splane, zplane;
   complex topcoeffs[MAXPZ+1], botcoeffs[MAXPZ+1];
   double warped_alpha;
@@ -115,8 +152,11 @@
   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};
+  if (order & 1) splane.poles[splane.numpoles++] = bessel_poles[p++];
+  for (i = 0; i < order/2; i++){ 
+    splane.poles[splane.numpoles++] = bessel_poles[p];
+    splane.poles[splane.numpoles++] = cconj(bessel_poles[p++]);
+  }
   
   warped_alpha = tan(M_PIl * raw_alpha) / M_PIl;
   for (i = 0; i < splane.numpoles; i++){
@@ -137,11 +177,296 @@
   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);
+  for(i=0;i<order;i++)
+    ycoeff[order-i-1] = -(botcoeffs[i].re / botcoeffs[zplane.numpoles].re);
 
   return hypot(dc_gain.re,dc_gain.im);
 }
 
+/* assymetrical attack/decay filter computation */
+/* this one is designed for fast attack, slow decay */
+void compute_iir_fast_attack2(float *x, int n, iir_state *is, 
+			     iir_filter *attack, iir_filter *decay){
+  double a_c0=attack->c[0],d_c0=decay->c[0];
+  double a_c1=attack->c[1],d_c1=decay->c[1];
+  double a_g=attack->g, d_g=decay->g;
+  
+  double x0=is->x[0],x1=is->x[1];
+  double y0=is->y[0],y1=is->y[1];
+  int state=is->state;
+  int i=0;
 
+  if(x[0]>y0)state=0; 
+      
+  while(i<n){
+    
+    if(state==0){
+      /* attack case */
+      while(i<n){
+	double ya= (x[i]+x0*2.+x1)/a_g + y0*a_c0+y1*a_c1;
+    
+	if(ya<y0){
+	  state=1; 
+	  break;
+	}
+	x1=x0;x0=x[i];
+	y1=y0;x[i]=y0=ya;
+	i++;
+      }
+    }
 
+    if(state==1){
+      /* decay case */
+      if(y1<y0){
+	/* decay fixup needed because we're in discontinuous time */
+	  y1=y0;
+      }
+
+      while(1){
+	double yd = (x[i]+x0*2.+x1)/d_g + y0*d_c0+y1*d_c1;
+
+	x1=x0;x0=x[i];
+	y1=y0;x[i]=y0=yd;
+	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;
+  
+}
+
+/* allow decay to proceed in freefall */
+void compute_iir_freefall1(float *x, int n, iir_state *is, 
+			   iir_filter *decay){
+  double d_c0=decay->c[0];
+  
+  double x0=is->x[0];
+  double y0=is->y[0];
+  int i=0;
+  
+  while(i<n){
+    double yd;
+
+     yd = y0*d_c0;
+
+    if(x[i]>yd)yd=x[i];
+
+    x0=x[i];
+    x[i]=y0=yd;
+    i++;
+  }
+  
+  is->x[0]=x0;
+  is->y[0]=y0;
+  
+}
+
+void compute_iir_freefall2(float *x, int n, iir_state *is, 
+			  iir_filter *decay){
+  double d_c0=decay->c[0];
+  double d_c1=decay->c[1];
+  
+  double x0=is->x[0];
+  double x1=is->x[1];
+  double y0=is->y[0];
+  double y1=is->y[1];
+  int i=0;
+
+  while(i<n){
+    double yd;
+    if(y1<y0)y1=y0; // slope fixup
+
+     yd = y0*d_c0+y1*d_c1;
+
+
+    if(x[i]>yd)yd=x[i];
+
+    x1=x0;x0=x[i];
+    y1=y0;x[i]=y0=yd;
+    i++;
+  }
+  
+  is->x[0]=x0;
+  is->x[1]=x1;
+  is->y[0]=y0;
+  is->y[1]=y1;
+  
+}
+
+void compute_iir_freefall3(float *x, int n, iir_state *is, 
+			  iir_filter *decay){
+  double d_c0=decay->c[0];
+  double d_c1=decay->c[1];
+  double d_c2=decay->c[2];
+  
+  double x0=is->x[0],y0=is->y[0];
+  double x1=is->x[1],y1=is->y[1];
+  double x2=is->x[2],y2=is->y[2];
+  int i=0;
+
+  while(i<n){
+    double yd;
+    if(y1<y0)y1=y0; // slope fixup
+    if(y2<y1)y2=y1; // slope fixup
+
+
+     yd = y0*d_c0+y1*d_c1+y2*d_c2;
+
+
+    if(x[i]>yd)yd=x[i];
+
+    x2=x1;x1=x0;x0=x[i];
+    y2=y1;y1=y0;x[i]=y0=yd;
+    i++;
+  }
+  
+  is->x[0]=x0;is->y[0]=y0;
+  is->x[1]=x1;is->y[1]=y1;
+  is->x[2]=x2;is->y[2]=y2;
+  
+}
+
+void compute_iir_freefall4(float *x, int n, iir_state *is, 
+			  iir_filter *decay){
+  double d_c0=decay->c[0];
+  double d_c1=decay->c[1];
+  double d_c2=decay->c[2];
+  double d_c3=decay->c[3];
+  
+  double x0=is->x[0],y0=is->y[0];
+  double x1=is->x[1],y1=is->y[1];
+  double x2=is->x[2],y2=is->y[2];
+  double x3=is->x[3],y3=is->y[3];
+  int i=0;
+
+  while(i<n){
+    double yd;
+    if(y1<y0)y1=y0; // slope fixup
+    if(y2<y1)y2=y1; // slope fixup
+    if(y3<y2)y3=y2; // slope fixup
+
+
+     yd = y0*d_c0+y1*d_c1+y2*d_c2+y3*d_c3;
+
+    if(x[i]>yd)yd=x[i];
+
+    x3=x2;x2=x1;x1=x0;x0=x[i];
+    y3=y2;y2=y1;y1=y0;x[i]=y0=yd;
+    i++;
+  }
+  
+  is->x[0]=x0;is->y[0]=y0;
+  is->x[1]=x1;is->y[1]=y1;
+  is->x[2]=x2;is->y[2]=y2;
+  is->x[3]=x3;is->y[3]=y3;
+  
+}
+
+/* symmetric filter computation */
+
+void compute_iir_symmetric2(float *x, int n, iir_state *is, 
+			   iir_filter *filter){
+  double c0=filter->c[0];
+  double c1=filter->c[1];
+  double g=filter->g;
+  
+  double x0=is->x[0];
+  double x1=is->x[1];
+  double y0=is->y[0];
+  double y1=is->y[1];
+    
+  int i=0;
+      
+  while(i<n){
+    double yd= (x[i]+x0*2.+x1)/g + y0*c0+y1*c1;
+    x1=x0;x0=x[i];
+    y1=y0;x[i]=y0=yd;
+    i++;
+  }
+  
+  is->x[0]=x0;
+  is->x[1]=x1;
+  is->y[0]=y0;
+  is->y[1]=y1;
+  
+}
+
+void compute_iir_symmetric3(float *x, int n, iir_state *is, 
+			   iir_filter *filter){
+  double c0=filter->c[0];
+  double c1=filter->c[1];
+  double c2=filter->c[2];
+  double g=filter->g;
+  
+  double x0=is->x[0],y0=is->y[0];
+  double x1=is->x[1],y1=is->y[1];
+  double x2=is->x[2],y2=is->y[2];
+    
+  int i=0;
+      
+  while(i<n){
+    double yd= (x[i]+(x0+x1)*3.+x2)/g + y0*c0+y1*c1+y2*c2;
+    x2=x1;x1=x0;x0=x[i];
+    y2=y1;y1=y0;x[i]=y0=yd;
+    i++;
+  }
+  
+  is->x[0]=x0;is->y[0]=y0;
+  is->x[1]=x1;is->y[1]=y1;
+  is->x[2]=x2;is->y[2]=y2;
+  
+}
+
+void compute_iir_symmetric4(float *x, int n, iir_state *is, 
+			   iir_filter *filter){
+  double c0=filter->c[0];
+  double c1=filter->c[1];
+  double c2=filter->c[2];
+  double c3=filter->c[3];
+  double g=filter->g;
+  
+  double x0=is->x[0],y0=is->y[0];
+  double x1=is->x[1],y1=is->y[1];
+  double x2=is->x[2],y2=is->y[2];
+  double x3=is->x[3],y3=is->y[3];
+    
+  int i=0;
+      
+  while(i<n){
+    double yd= (x[i]+(x0+x2)*4.+x1*6.+x3)/g + 
+      y0*c0+y1*c1+y2*c2+y3*c3;
+    x3=x2;x2=x1;x1=x0;x0=x[i];
+    y3=y2;y2=y1;y1=y0;x[i]=y0=yd;
+    i++;
+  }
+  
+  is->x[0]=x0;is->y[0]=y0;
+  is->x[1]=x1;is->y[1]=y1;
+  is->x[2]=x2;is->y[2]=y2;
+  is->x[3]=x3;is->y[3]=y3;
+  
+}
+
+
+/* filter decision wrapper */
+void compute_iir2(float *x, int n, iir_state *is, 
+		 iir_filter *attack, iir_filter *decay){
+
+  if (attack->alpha > decay->alpha){
+    /* fast attack, slow decay */
+    compute_iir_fast_attack2(x, n, is, attack, decay);
+  }else{
+    compute_iir_symmetric2(x, n, is, attack);
+  }
+}
+
+

Modified: trunk/postfish/bessel.h
===================================================================
--- trunk/postfish/bessel.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/bessel.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -21,4 +21,71 @@
  * 
  */
 
-extern double mkbessel_2(double raw_alpha,double *ycoeff0,double *ycoeff1);
+#include "postfish.h"
+extern int input_rate;
+
+#define MAXORDER    4
+
+typedef struct {
+  double c[MAXORDER];
+  double g;
+  int   order;
+  float alpha; 
+  float Hz; 
+  float ms; 
+} iir_filter;
+
+static inline long impulse_ahead2(float alpha){
+  return rint(.13f/alpha);
+}
+static inline long impulse_ahead3(float alpha){
+  return rint(.22f/alpha);
+}
+static inline long impulse_ahead4(float alpha){
+  return rint(.32f/alpha);
+}
+
+static inline long step_ahead(float alpha){
+  return rint(.6f/alpha);
+}
+
+static inline float step_freq(long ahead){
+  return input_rate*.6f/ahead;
+}
+
+static inline float impulse_freq2(long ahead){
+  return input_rate*.13f/ahead;
+}
+static inline float impulse_freq3(long ahead){
+  return input_rate*.22f/ahead;
+}
+static inline float impulse_freq4(long ahead){
+  return input_rate*.32f/ahead;
+}
+
+typedef struct {
+  double x[MAXORDER];
+  double y[MAXORDER];
+  int state;
+} iir_state;
+
+extern double mkbessel(double raw_alpha,int order,double *ycoeff);
+extern void compute_iir_fast_attack2(float *x, int n, iir_state *is, 
+				     iir_filter *attack, iir_filter *decay);
+extern void compute_iir_symmetric2(float *x, int n, iir_state *is, 
+				  iir_filter *filter);
+extern void compute_iir_symmetric3(float *x, int n, iir_state *is, 
+				  iir_filter *filter);
+extern void compute_iir_symmetric4(float *x, int n, iir_state *is, 
+				  iir_filter *filter);
+extern void compute_iir2(float *x, int n, iir_state *is, 
+			iir_filter *attack, iir_filter *decay);
+extern void compute_iir_freefall1(float *x, int n, iir_state *is, 
+				 iir_filter *decay);
+extern void compute_iir_freefall2(float *x, int n, iir_state *is, 
+				 iir_filter *decay);
+extern void compute_iir_freefall3(float *x, int n, iir_state *is, 
+				 iir_filter *decay);
+extern void compute_iir_freefall4(float *x, int n, iir_state *is, 
+				 iir_filter *decay);
+

Modified: trunk/postfish/compandpanel.c
===================================================================
--- trunk/postfish/compandpanel.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/compandpanel.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -59,12 +59,13 @@
 static cbar bars[multicomp_freqs_max+1];
 static int inactive_updatep=1;
 
-static void under_compand_change(GtkWidget *w,gpointer in){
+static void compand_change(GtkWidget *w,Readout *r,sig_atomic_t *var){
   char buffer[80];
-  Readout *r=(Readout *)in;
   float val=1./multibar_get_value(MULTIBAR(w),0);
 
-  if(val>=10){
+  if(val==1.){
+    sprintf(buffer,"   off");
+  }else if(val>=10){
     sprintf(buffer,"%4.1f:1",val);
   }else if(val>=1){
     sprintf(buffer,"%4.2f:1",val);
@@ -76,198 +77,74 @@
 
   readout_set(r,buffer);
   
-  c.under_ratio=rint(val*1000.);
+  *var=rint(val*1000.);
 }
+static void under_compand_change(GtkWidget *w,gpointer in){
+  compand_change(w,(Readout *)in,&c.under_ratio);
+}
 
 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);
+  compand_change(w,(Readout *)in,&c.over_ratio);
+}
 
-  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 base_compand_change(GtkWidget *w,gpointer in){
+  compand_change(w,(Readout *)in,&c.base_ratio);
 }
 
-static void suppress_compand_change(GtkWidget *w,gpointer in){
+static void timing_display(GtkWidget *w,Readout *r,float v){
   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);
+  if(v<10){
+    sprintf(buffer,"%4.2fms",v);
+  }else if(v<100){
+    sprintf(buffer,"%4.1fms",v);
+  }else if (v<1000){
+    sprintf(buffer,"%4.0fms",v);
+  }else if (v<10000){
+    sprintf(buffer,"%4.2fs",v/1000.);
   }else{
-    sprintf(buffer,"1:%4.1f",1./val);
+    sprintf(buffer,"%4.1fs",v/1000.);
   }
 
   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);
-  
+  timing_display(w,r->r0,attack);
+  timing_display(w,r->r1,decay);
+ 
   multicompand_under_attack_set(attack);
-  multicompand_under_decay_set(decay);
-  
+  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);
+  timing_display(w,r->r0,attack);
+  timing_display(w,r->r1,decay);
   
-  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];
+static void base_timing_change(GtkWidget *w,gpointer in){
   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);
+  timing_display(w,r->r0,attack);
+  timing_display(w,r->r1,decay);
   
-  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_base_attack_set(attack);
+  multicompand_base_decay_set(decay);
   
-  multicompand_suppress_attack_set(attack);
-  multicompand_suppress_decay_set(decay);
-  multicompand_suppress_release_set(release);
-  
 }
 
 static void under_lookahead_change(GtkWidget *w,gpointer in){
@@ -275,7 +152,7 @@
   Readout *r=(Readout *)in;
   float val=multibar_get_value(MULTIBAR(w),0);
 
-  sprintf(buffer,"%5.1f%%",val);
+  sprintf(buffer,"%3.0f%%",val);
   readout_set(r,buffer);
   
   c.under_lookahead=rint(val*10.);
@@ -286,16 +163,14 @@
   Readout *r=(Readout *)in;
   float val=multibar_get_value(MULTIBAR(w),0);
 
-  sprintf(buffer,"%5.1f%%",val);
+  sprintf(buffer,"%3.0f%%",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;
@@ -306,8 +181,8 @@
 
   /* 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(bc[bank_active].static_o[i]);
+    uav+=rint(bc[bank_active].static_u[i]);
   }
   oav=rint(oav/multicomp_freqs[bank_active]);
   uav=rint(uav/multicomp_freqs[bank_active]);
@@ -326,7 +201,7 @@
       /* 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(bc[bank_active].static_u[i]);
       uav=rint(uav/multicomp_freqs[bank_active]);
       multibar_thumb_set(MULTIBAR(bars[multicomp_freqs_max].slider),uav,0);
     }else{
@@ -338,7 +213,7 @@
       /* 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(bc[bank_active].static_o[i]);
       oav=rint(oav/multicomp_freqs[bank_active]);
       multibar_thumb_set(MULTIBAR(bars[multicomp_freqs_max].slider),oav,1);
     }
@@ -349,31 +224,135 @@
 static void slider_change(GtkWidget *w,gpointer in){
   char buffer[80];
   cbar *b=(cbar *)in;
-  float o,u;
+  int o,u;
   int i;
 
   u=multibar_get_value(MULTIBAR(b->slider),0);
-  sprintf(buffer,"%+4.0fdB",u);
+  sprintf(buffer,"%+4ddB",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);
+  sprintf(buffer,"%+4ddB",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 */
+    /* keep the inactive banks also tracking settings */
     
     switch(bank_active){
     case 0:
+      if(b->number==0){
+	bc[2].static_o[0]+=o-bc[2].static_o[1];
+	bc[2].static_u[0]+=u-bc[2].static_u[1];
+      }
+      if (b->number<9){
+	int adj;
+
+	/* convolutions for roundoff behavior */
+	if(b->number>0){
+	  adj=(bc[1].static_o[b->number*2-1]*2 -
+	       bc[1].static_o[b->number*2-2]-bc[1].static_o[b->number*2])/2;
+	  bc[1].static_o[b->number*2-1]=
+	    (bc[1].static_o[b->number*2-2]+o)/2+adj;
+
+	  adj=(bc[1].static_u[b->number*2-1]*2 -
+	       bc[1].static_u[b->number*2-2]-bc[1].static_u[b->number*2])/2;
+	  bc[1].static_u[b->number*2-1]=
+	    (bc[1].static_u[b->number*2-2]+u)/2+adj;
+
+	  adj=(bc[2].static_o[b->number*3-1]*3 -
+	       bc[2].static_o[b->number*3-2] - 
+	       bc[2].static_o[b->number*3-2] - 
+	       bc[2].static_o[b->number*3+1])/3;
+	  bc[2].static_o[b->number*3-1]=
+	    (bc[2].static_o[b->number*3-2]+
+	     bc[2].static_o[b->number*3-2]+
+	     o)/3+adj;
+
+	  adj=(bc[2].static_o[b->number*3]*3 -
+	       bc[2].static_o[b->number*3-2] - 
+	       bc[2].static_o[b->number*3+1] - 
+	       bc[2].static_o[b->number*3+1])/3;
+	  bc[2].static_o[b->number*3]=
+	    (bc[2].static_o[b->number*3-2]+o+o)/3+adj;
+
+	  adj=(bc[2].static_u[b->number*3-1]*3 -
+	       bc[2].static_u[b->number*3-2] - 
+	       bc[2].static_u[b->number*3-2] - 
+	       bc[2].static_u[b->number*3+1])/3;
+	  bc[2].static_u[b->number*3-1]=
+	    (bc[2].static_u[b->number*3-2]+
+	     bc[2].static_u[b->number*3-2]+
+	     u)/3+adj;
+
+	  adj=(bc[2].static_u[b->number*3]*3 -
+	       bc[2].static_u[b->number*3-2] - 
+	       bc[2].static_u[b->number*3+1] - 
+	       bc[2].static_u[b->number*3+1])/3;
+	  bc[2].static_u[b->number*3]=
+	    (bc[2].static_u[b->number*3-2]+u+u)/3+adj;
+
+	}
+
+	if(b->number<9){
+	  adj=(bc[1].static_o[b->number*2+1]*2-
+	       bc[1].static_o[b->number*2+2]-bc[1].static_o[b->number*2])/2;
+	  bc[1].static_o[b->number*2+1]=
+	    (bc[1].static_o[b->number*2+2]+o)/2+adj;
+	  
+	  adj=(bc[1].static_u[b->number*2+1]*2-
+	       bc[1].static_u[b->number*2+2]-bc[1].static_u[b->number*2])/2;
+	  bc[1].static_u[b->number*2+1]=
+	    (bc[1].static_u[b->number*2+2]+u)/2+adj;
+
+	  adj=(bc[2].static_o[b->number*3+3]*3 -
+	       bc[2].static_o[b->number*3+4] - 
+	       bc[2].static_o[b->number*3+4] - 
+	       bc[2].static_o[b->number*3+1])/3;
+	  bc[2].static_o[b->number*3+3]=
+	    (bc[2].static_o[b->number*3+4]+
+	     bc[2].static_o[b->number*3+4]+
+	     o)/3+adj;
+
+	  adj=(bc[2].static_o[b->number*3+2]*3 -
+	       bc[2].static_o[b->number*3+4] - 
+	       bc[2].static_o[b->number*3+1] - 
+	       bc[2].static_o[b->number*3+1])/3;
+	  bc[2].static_o[b->number*3+2]=
+	    (bc[2].static_o[b->number*3+4]+o+o)/3+adj;
+
+	  adj=(bc[2].static_u[b->number*3+3]*3 -
+	       bc[2].static_u[b->number*3+4] - 
+	       bc[2].static_u[b->number*3+4] - 
+	       bc[2].static_u[b->number*3+1])/3;
+	  bc[2].static_u[b->number*3+3]=
+	    (bc[2].static_u[b->number*3+4]+
+	     bc[2].static_u[b->number*3+4]+
+	     u)/3+adj;
+
+	  adj=(bc[2].static_u[b->number*3+2]*3 -
+	       bc[2].static_u[b->number*3+4] - 
+	       bc[2].static_u[b->number*3+1] - 
+	       bc[2].static_u[b->number*3+1])/3;
+	  bc[2].static_u[b->number*3+2]=
+	    (bc[2].static_u[b->number*3+4]+u+u)/3+adj;
+
+	}
+      }
+
+      if(b->number==9){
+	bc[1].static_o[19]+=o-bc[1].static_o[18];
+	bc[1].static_u[19]+=u-bc[1].static_u[18];
+	bc[2].static_o[29]+=o-bc[2].static_o[28];
+	bc[2].static_u[29]+=u-bc[2].static_u[28];
+      }
+
       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){
@@ -383,13 +362,13 @@
         bc[2].static_u[b->number/2*3+1]=u;
       }else{
         if(b->number<19){
-	  float val=(bc[2].static_o[b->number/2*3+2]+
-		     bc[2].static_o[b->number/2*3+3])*.5;
+	  int val=(bc[2].static_o[b->number/2*3+2]+
+		     bc[2].static_o[b->number/2*3+3])/2;
           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_u[b->number/2*3+2]+
-	       bc[2].static_u[b->number/2*3+3])*.5;
+	       bc[2].static_u[b->number/2*3+3])/2;
           bc[2].static_u[b->number/2*3+2]+=(u-val);
           bc[2].static_u[b->number/2*3+3]+=(u-val);
           
@@ -398,6 +377,11 @@
           bc[2].static_u[b->number/2*3+2]=u;
         }
       }
+      if(b->number==0){
+	bc[2].static_o[0]+=o-bc[2].static_o[1];
+	bc[2].static_u[0]+=u-bc[2].static_u[1];
+      }
+
       break;
     case 2:
       if((b->number%3)==1){
@@ -409,10 +393,10 @@
         if(b->number<29){
           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[2].static_o[(b->number-1)/3*3+3])/2;
           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;
+	     bc[2].static_u[(b->number-1)/3*3+3])/2;
         }else{
           bc[1].static_o[(b->number-1)/3*2+1]=o;
           bc[1].static_u[(b->number-1)/3*2+1]=u;
@@ -429,8 +413,8 @@
     
     /* 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(bc[bank_active].static_o[i]);
+      uav+=rint(bc[bank_active].static_u[i]);
     }
     oav=rint(oav/multicomp_freqs[bank_active]);
     uav=rint(uav/multicomp_freqs[bank_active]);
@@ -461,8 +445,8 @@
           inactive_updatep=0;
           
           {
-	    float o=bc[bank_active].static_o[i]/10.;
-	    float u=bc[bank_active].static_u[i]/10.;
+	    float o=bc[bank_active].static_o[i];
+	    float u=bc[bank_active].static_u[i];
 
             multibar_thumb_set(MULTIBAR(bars[i].slider),u,0);
             multibar_thumb_set(MULTIBAR(bars[i].slider),o,1);
@@ -483,11 +467,6 @@
   }
 }
 
-static void link_toggled(GtkToggleButton *b,gpointer in){
-  int active=gtk_toggle_button_get_active(b);
-  c.link_mode=active;
-}
-
 static void over_mode(GtkButton *b,gpointer in){
   int mode=(int)in;
   c.over_mode=mode;
@@ -498,42 +477,36 @@
   c.under_mode=mode;
 }
 
-static void under_knee(GtkButton *b,gpointer in){
+static void base_mode(GtkButton *b,gpointer in){
   int mode=(int)in;
+  c.base_mode=mode;
+}
+
+static void under_knee(GtkToggleButton *b,gpointer in){
+  int mode=gtk_toggle_button_get_active(b);
   c.under_softknee=mode;
 }
 
-static void over_knee(GtkButton *b,gpointer in){
-  int mode=(int)in;
+static void over_knee(GtkToggleButton *b,gpointer in){
+  int mode=gtk_toggle_button_get_active(b);
   c.over_softknee=mode;
 }
 
-static void suppress_mode(GtkButton *b,gpointer in){
-  int mode=(int)in;
-  c.suppress_mode=mode;
-}
-
 void compandpanel_create(postfish_mainpanel *mp,
                          GtkWidget *windowbutton,
                          GtkWidget *activebutton){
-  int i,j;
+  int i;
   char *labels[14]={"130","120","110","100","90","80","70",
                     "60","50","40","30","20","10","0"};
   float levels[15]={-140,-130,-120,-110,-100,-90,-80,-70,-60,-50,-40,
                      -30,-20,-10,0};
 
-  float compand_levels[9]={.1,.25,.5,.667,1,1.5,2,4,10};
+  float compand_levels[9]={.1,.25,.5,.6667,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 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 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%"};
 
@@ -549,23 +522,23 @@
   GtkWidget *staticbox=gtk_vbox_new(0,0);
   GtkWidget *slidertable=gtk_table_new(multicomp_freqs_max+2,4,0);
 
-  GtkWidget *overlabel=gtk_label_new("Over threshold compand");
+  GtkWidget *overlabel=gtk_label_new("Over threshold compand ");
   GtkWidget *overtable=gtk_table_new(6,4,0);
 
-  GtkWidget *underlabel=gtk_label_new("Under threshold compand");
+  GtkWidget *underlabel=gtk_label_new("Under threshold compand ");
   GtkWidget *undertable=gtk_table_new(5,4,0);
 
-  GtkWidget *suppressframe=gtk_frame_new(" Reverb suppression ");
-  GtkWidget *suppresstable=gtk_table_new(4,5,0);
+  GtkWidget *baselabel=gtk_label_new("Global compand ");
+  GtkWidget *basetable=gtk_table_new(3,4,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);
+  gtk_widget_set_name(baselabel,"framelabel");
 
-  
+  gtk_misc_set_alignment(GTK_MISC(overlabel),0,.5);
+  gtk_misc_set_alignment(GTK_MISC(underlabel),0,.5);
+  gtk_misc_set_alignment(GTK_MISC(baselabel),0,.5);
+
     
   {
   
@@ -586,7 +559,7 @@
     gtk_widget_set_name(label3,"scalemarker");
 
 
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(octave_c),1);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(octave_b),1);
 
     g_signal_connect (G_OBJECT (octave_a), "clicked",
                       G_CALLBACK (static_octave), (gpointer)0);
@@ -613,30 +586,28 @@
 
   }
 
-  if(input_ch>1)
-    gtk_box_pack_end(GTK_BOX(link_box),link_check,0,0,0);  
-
-  gtk_box_pack_end(GTK_BOX(staticbox),link_box,0,0,0);
-  gtk_container_set_border_width(GTK_CONTAINER(link_box),5);
-  g_signal_connect (G_OBJECT (link_check), "toggled",
-		    G_CALLBACK (link_toggled), (gpointer)0);
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(link_check),0);
-
   gtk_box_pack_start(GTK_BOX(panel->subpanel_box),hbox,0,0,0);
   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),overtable,0,0,0);
-  gtk_box_pack_start(GTK_BOX(staticbox),undertable,0,0,10);
- 
+  {
+    GtkWidget *hs1=gtk_hseparator_new();
+    GtkWidget *hs2=gtk_hseparator_new();
+    GtkWidget *hs3=gtk_hseparator_new();
 
-  gtk_box_pack_end(GTK_BOX(staticbox),suppressframe,0,0,0);
-  gtk_container_add(GTK_CONTAINER(suppressframe),suppresstable);
+    gtk_box_pack_start(GTK_BOX(staticbox),overtable,0,0,10);
+    gtk_box_pack_start(GTK_BOX(staticbox),hs1,0,0,0);
+    gtk_box_pack_start(GTK_BOX(staticbox),undertable,0,0,10);
+    gtk_box_pack_start(GTK_BOX(staticbox),hs2,0,0,0);
+    gtk_box_pack_start(GTK_BOX(staticbox),basetable,0,0,10);
+    gtk_box_pack_start(GTK_BOX(staticbox),hs3,0,0,0);
 
-  gtk_container_set_border_width(GTK_CONTAINER(suppressframe),5);
-  gtk_container_set_border_width(GTK_CONTAINER(overtable),5);
-  gtk_container_set_border_width(GTK_CONTAINER(undertable),5);
+    gtk_container_set_border_width(GTK_CONTAINER(overtable),5);
+    gtk_container_set_border_width(GTK_CONTAINER(undertable),5);
+    gtk_container_set_border_width(GTK_CONTAINER(basetable),5);
 
+  }
+
   /* under compand: mode and knee */
   {
     GtkWidget *envelopebox=gtk_hbox_new(0,0);
@@ -646,7 +617,7 @@
                                                   "peak");
     GtkWidget *knee_button=gtk_check_button_new_with_label("soft knee");
 
-    gtk_box_pack_start(GTK_BOX(envelopebox),underlabel,0,0,5);
+    gtk_box_pack_start(GTK_BOX(envelopebox),underlabel,0,0,0);
     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);
@@ -671,7 +642,6 @@
    
     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);
 
@@ -682,25 +652,6 @@
 
   }
 
-  /* 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 */
   {
 
@@ -733,7 +684,6 @@
     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);
@@ -756,7 +706,7 @@
                                                   "peak");
     GtkWidget *knee_button=gtk_check_button_new_with_label("soft knee");
 
-    gtk_box_pack_start(GTK_BOX(envelopebox),overlabel,0,0,5);
+    gtk_box_pack_start(GTK_BOX(envelopebox),overlabel,0,0,0);
     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);
@@ -781,7 +731,6 @@
    
     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);
 
@@ -792,25 +741,6 @@
 
   }
 
-  /* 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 */
   {
 
@@ -843,7 +773,6 @@
     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);
@@ -856,29 +785,9 @@
     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  */
+  /* base compand: mode */
   {
     GtkWidget *envelopebox=gtk_hbox_new(0,0);
     GtkWidget *rms_button=gtk_radio_button_new_with_label(NULL,"RMS");
@@ -886,68 +795,60 @@
       gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(rms_button),
                                                   "peak");
 
+    gtk_box_pack_start(GTK_BOX(envelopebox),baselabel,0,0,0);
     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 (rms_button), "clicked",
-		      G_CALLBACK (suppress_mode), (gpointer)0);
+		      G_CALLBACK (base_mode), (gpointer)0);
     g_signal_connect (G_OBJECT (peak_button), "clicked",
-		      G_CALLBACK (suppress_mode), (gpointer)1); //To Hell I Go
+		      G_CALLBACK (base_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);
+    gtk_table_attach(GTK_TABLE(basetable),envelopebox,0,4,0,1,GTK_FILL,0,0,0);
   }
 
-  /* suppress: ratio */
+  /* base compand: ratio */
   {
 
-    GtkWidget *label=gtk_label_new("ratio:");
+    GtkWidget *label=gtk_label_new("compand 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);
+    GtkWidget *slider=multibar_slider_new(8,compand_labels,compand_levels,1);
+   
+    multibar_callback(MULTIBAR(slider),base_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);
+    gtk_table_set_row_spacing(GTK_TABLE(basetable),0,4);
+    gtk_table_attach(GTK_TABLE(basetable),label,0,1,1,2,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(basetable),slider,1,3,1,2,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(basetable),readout,3,4,1,2,GTK_FILL,0,0,0);
 
   }
 
-  /* suppress: timing */
+  /* base compand: timing */
   {
 
-    GtkWidget *label=gtk_label_new("timing:");
-    GtkWidget *label2=gtk_label_new("attack / decay / release");
+    GtkWidget *label=gtk_label_new("attack/decay:");
     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);
-
+    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;
-    r->r2=(Readout *)readout3;
-
-    gtk_widget_set_name(label2,"scalemarker");
    
-    multibar_callback(MULTIBAR(slider),suppress_timing_change,r);
+    multibar_callback(MULTIBAR(slider),base_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);
+    gtk_table_set_row_spacing(GTK_TABLE(basetable),2,4);
+    gtk_table_attach(GTK_TABLE(basetable),label,0,1,4,5,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(basetable),slider,1,2,4,5,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(basetable),readout1,2,3,4,5,GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(basetable),readout2,3,4,4,5,GTK_FILL,0,0,0);
 
   }
 
@@ -959,8 +860,7 @@
     
     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].slider=multibar_new(14,labels,levels,2,HI_DECAY|LO_DECAY|LO_ATTACK);
     bars[i].number=i;
     bars[i].label=label;
 
@@ -1005,7 +905,7 @@
   subpanel_show_all_but_toplevel(panel);
 
   /* Now unmap the sliders we don't want */
-  static_octave(NULL,(gpointer)2);
+  static_octave(NULL,(gpointer)1);
 
 }
 
@@ -1031,7 +931,7 @@
 }
 
 void compandpanel_reset(void){
-  int i,j;
+  int i;
   for(i=0;i<multicomp_freqs_max;i++)
     multibar_reset(MULTIBAR(bars[i].slider));
 }

Modified: trunk/postfish/compandpanel.h
===================================================================
--- trunk/postfish/compandpanel.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/compandpanel.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -21,12 +21,16 @@
  * 
  */
 
+extern void suppresspanel_create(postfish_mainpanel *mp,
+				 GtkWidget *windowbutton,
+				 GtkWidget *activebutton);
+
 extern void compandpanel_create(postfish_mainpanel *mp,
                                 GtkWidget *windowbutton,
                                 GtkWidget *activebutton);
 
 extern void compandpanel_feedback(int displayit);
 extern void compandpanel_reset(void);
-extern void compandpanel_set_play(int playp);
 
 
+

Modified: trunk/postfish/declip.c
===================================================================
--- trunk/postfish/declip.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/declip.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -80,7 +80,7 @@
 }
 
 static void push_declip_feedback(int *clip,float *peak,int total){
-  int i,n=input_ch;
+  int n=input_ch;
   declip_feedback *f=(declip_feedback *)
     feedback_new(&feedpool,new_declip_feedback);
   memcpy(f->clipcount,clip,n*sizeof(*clip));
@@ -91,7 +91,6 @@
 
 int pull_declip_feedback(int *clip,float *peak,int *total){
   declip_feedback *f=(declip_feedback *)feedback_pull(&feedpool);
-  int i,j;
 
   if(!f)return 0;
 
@@ -147,12 +146,14 @@
   pthread_mutex_lock(&master_mutex);
   iterations=it;
   pthread_mutex_unlock(&master_mutex);
+  return 0;
 }
 
 int declip_setconvergence(float c){
   pthread_mutex_lock(&master_mutex);
   convergence=c;
   pthread_mutex_unlock(&master_mutex);
+  return 0;
 }
 
 /* called only in playback thread */
@@ -164,7 +165,7 @@
 }
 
 static void sliding_bark_average(float *f,int n,float width){
-  int i=0,j;
+  int i=0;
   float acc=0.,del=0.;
   float sec[hipad+1];
 
@@ -172,7 +173,6 @@
 
   for(i=0;i<n/2;i++){
 
-    float bark=toBark(44100.*i/n);
     int hi=widthlookup[i]>>16;
     int lo=widthlookup[i]&(0xffff);
     float del=hypot(f[(i<<1)+1],f[i<<1])/(lo-hi);
@@ -317,7 +317,6 @@
   switch(fillstate){
   case 0: /* prime the lapping and cache */
     for(i=0;i<input_ch;i++){
-      int j;
       float *temp=in->data[i];
       total=0;
       memset(work+blocksize/2,0,sizeof(*work)*blocksize/2);

Modified: trunk/postfish/eq.c
===================================================================
--- trunk/postfish/eq.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/eq.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -61,7 +61,7 @@
 static float *curve_cache=0;
  
 static void workfunc(freq_state *f,float **data,float **peak, float **rms){
-  int h,i,j,k;
+  int h,i,j;
   float sq_mags[f->qblocksize*2+1];
   
   if(curve_dirty || !curve_cache){

Modified: trunk/postfish/eq.h
===================================================================
--- trunk/postfish/eq.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/eq.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -25,14 +25,14 @@
 
 #define eq_freqs 30
 
-static float eq_freq_list[eq_freqs+1]={
+static const float eq_freq_list[eq_freqs+1]={
   25,31.5,40,50,63,80,
   100,125,160,200,250,315,
   400,500,630,800,1000,1250,1600,
   2000,2500,3150,4000,5000,6300,
   8000,10000,12500,16000,20000,9e10};
 
-static char *eq_freq_labels[eq_freqs]={
+static char * const eq_freq_labels[eq_freqs]={
   "25","31.5","40","50","63","80",
   "100","125","160","200","250","315",
   "400","500","630","800","1k","1.2k","1.6k",
@@ -40,7 +40,6 @@
   "8k","10k","12.5k","16k","20k"
 };
 
-
 extern int pull_eq_feedback(float **peak,float **rms);
 extern int eq_load(void);
 extern int eq_reset();

Modified: trunk/postfish/freq.c
===================================================================
--- trunk/postfish/freq.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/freq.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -41,7 +41,6 @@
 } freq_feedback;
 
 static feedback_generic *new_freq_feedback(void){
-  int i;
   freq_feedback *ret=malloc(sizeof(*ret));
   ret->peak=calloc(input_ch,sizeof(*ret->peak));
   ret->rms=calloc(input_ch,sizeof(*ret->rms));
@@ -76,14 +75,13 @@
 }
 
 /* called only by initial setup */
-int freq_load(freq_state *f,float *frequencies, int bands){
+int freq_load(freq_state *f,const float *frequencies, int bands){
   int i,j;
   int blocksize=input_size*2;
   memset(f,0,sizeof(*f));
 
   f->qblocksize=input_size;
   f->bands=bands;
-  f->frequencies=frequencies;
 
   f->fillstate=0;
   f->cache_samples=0;
@@ -332,7 +330,7 @@
                         void (*func)(freq_state *f,float **data,
                                      float **peak, float **rms),
                         int bypass){
-  int i,j,k;
+  int i;
 
   float feedback_peak[input_ch][f->bands];
   float feedback_rms[input_ch][f->bands];

Modified: trunk/postfish/freq.h
===================================================================
--- trunk/postfish/freq.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/freq.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -34,7 +34,6 @@
   
   int qblocksize;
   int bands;
-  float *frequencies;
 
   float **ho_window;
   float  *ho_area;
@@ -51,7 +50,7 @@
 
 extern void freq_transform_work(float *work,freq_state *f);
 extern int pull_freq_feedback(freq_state *ff,float **peak,float **rms);
-extern int freq_load(freq_state *f,float *frequencies, int bands);
+extern int freq_load(freq_state *f,const float *frequencies, int bands);
 extern int freq_reset(freq_state *f);
 extern time_linkage *freq_read(time_linkage *in, freq_state *f,
                                void (*func)(freq_state *f,

Modified: trunk/postfish/input.c
===================================================================
--- trunk/postfish/input.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/input.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -401,7 +401,7 @@
 }
 
 static void push_input_feedback(float *peak,float *rms, off_t cursor){
-  int i,n=input_ch+2;
+  int n=input_ch+2;
   input_feedback *f=(input_feedback *)
     feedback_new(&feedpool,new_input_feedback);
   f->cursor=cursor;
@@ -412,7 +412,7 @@
 
 int pull_input_feedback(float *peak,float *rms,off_t *cursor){
   input_feedback *f=(input_feedback *)feedback_pull(&feedpool);
-  int i,j,n=input_ch+2;
+  int n=input_ch+2;
   if(!f)return 0;
   if(rms)memcpy(rms,f->rms,sizeof(*rms)*n);
   if(peak)memcpy(peak,f->peak,sizeof(*peak)*n);
@@ -496,7 +496,6 @@
   k=0;
   for(i=0;i<out.samples;i++){
     float mean=0.;
-    float div=0.;
     float divrms=0.;
 
     for(j=0;j<out.channels;j++){

Added: trunk/postfish/limit.c
===================================================================
--- trunk/postfish/limit.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/limit.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,244 @@
+/*
+ *
+ *  postfish
+ *    
+ *      Copyright (C) 2002-2004 Monty and Xiph.Org
+ *
+ *  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.
+ *
+ * 
+ */
+
+#include "postfish.h"
+#include "feedback.h"
+#include "bessel.h"
+#include "limit.h"
+
+extern int input_size;
+extern int input_rate;
+extern int input_ch;
+
+sig_atomic_t limit_active;
+sig_atomic_t limit_visible;
+
+typedef struct{
+  time_linkage out;
+  feedback_generic_pool feedpool;
+
+  iir_state *iir;
+  iir_filter decay;
+
+} limit_state;
+
+limit_settings limitset;
+limit_state limitstate;
+
+static void _analysis(char *base,int i,float *v,int n,int dB,int offset){
+  int j;
+  FILE *of;
+  char buffer[80];
+
+  sprintf(buffer,"%s_%d.m",base,i);
+  of=fopen(buffer,"a");
+  
+  if(!of)perror("failed to open data dump file");
+  
+  for(j=0;j<n;j++){
+    fprintf(of,"%f ",(float)j+offset);
+    if(dB)
+      fprintf(of,"%f\n",todB(v[j]));
+    else
+      fprintf(of,"%f\n",(v[j]));
+  }
+  fprintf(of,"\n");
+  fclose(of);
+}
+
+static int offset=0;
+
+/* feedback! */
+typedef struct limit_feedback{
+  feedback_generic parent_class;
+  float *peak;
+  float *att;
+} limit_feedback;
+
+static feedback_generic *new_limit_feedback(void){
+  limit_feedback *ret=calloc(1,sizeof(*ret));
+  return (feedback_generic *)ret;
+}
+
+
+int pull_limit_feedback(float *peak,float *att){
+  limit_feedback *f=(limit_feedback *)feedback_pull(&limitstate.feedpool);
+  
+  if(!f)return 0;
+  
+  if(peak)
+    memcpy(peak,f->peak,sizeof(*peak)*input_ch);
+  if(att)
+    memcpy(att,f->att,sizeof(*att)*input_ch);
+  feedback_old(&limitstate.feedpool,(feedback_generic *)f);
+  return 1;
+}
+
+/* called only by initial setup */
+int limit_load(void){
+  int i;
+  memset(&limitstate,0,sizeof(limitstate));
+
+  limitstate.iir=calloc(input_ch,sizeof(*limitstate.iir));
+  limitstate.out.size=input_size;
+  limitstate.out.channels=input_ch;
+  limitstate.out.rate=input_rate;
+  limitstate.out.data=malloc(input_ch*sizeof(*limitstate.out.data));
+  for(i=0;i<input_ch;i++)
+    limitstate.out.data[i]=malloc(input_size*sizeof(**limitstate.out.data));
+
+  return(0);
+}
+
+static void filter_set(float msec,
+                       iir_filter *filter){
+  float alpha;
+  float corner_freq= 500./msec;
+  
+  alpha=corner_freq/input_rate;
+  filter->g=mkbessel(alpha,2,filter->c);
+  filter->alpha=alpha;
+  filter->Hz=alpha*input_rate;
+  filter->ms=msec;
+}
+
+/* called only in playback thread */
+int limit_reset(void ){
+  /* reset cached pipe state */
+  while(pull_limit_feedback(NULL,NULL));
+  memset(limitstate.iir,0,input_ch*sizeof(&limitstate.iir));
+  return 0;
+}
+
+static inline float limit_knee(float x,float d){
+  return (sqrtf(x*x+d)-x)*-.5f;
+}
+
+
+time_linkage *limit_read(time_linkage *in){
+  float peakfeed[input_ch];
+  float attfeed[input_ch];
+
+  int active=limit_active;
+  int visible=limit_visible;
+  int bypass=!(active || visible);
+  int i,k;
+
+  float thresh=limitset.thresh/10.-.01;
+  float depth=limitset.depth;
+  float localpeak;
+  float localatt;
+
+  float decayms=limitset.decay*.1;
+  if(decayms!=limitstate.decay.ms)filter_set(decayms,&limitstate.decay);
+
+  if(in->samples==0){
+    limitstate.out.samples=0;
+    return &limitstate.out;
+  }
+
+  depth=depth*.2;
+  depth*=depth;
+
+  for(i=0;i<input_ch;i++){
+    localpeak=0.;
+    localatt=0.;
+    
+    if(!bypass){
+      float *inx=in->data[i];
+      float *x=limitstate.out.data[i];
+      
+      if(active){
+
+	/* 'knee' the actual samples, compute attenuation depth */
+	for(k=0;k<in->samples;k++){
+	  float dB=todB(inx[k]);
+	  float knee=limit_knee(dB-thresh,depth)+thresh;
+	  float att=dB-knee;
+
+	  if(att>localatt)localatt=att;
+
+	  x[k]=att;
+	}
+	
+
+	compute_iir_freefall2(x,input_size,limitstate.iir+i,&limitstate.decay);
+
+	
+	for(k=0;k<in->samples;k++)
+	  x[k]=inx[k]*fromdB(-x[k]);
+     
+	
+      }
+    }
+    
+    if(!active){
+      float *temp=in->data[i];
+      in->data[i]=limitstate.out.data[i];
+      limitstate.out.data[i]=temp;
+    }
+
+    if(!bypass){
+      float *x=limitstate.out.data[i];
+      /* get peak feedback */
+      for(k=0;k<in->samples;k++)
+	if(fabs(x[k])>localpeak)localpeak=fabs(x[k]);
+
+    }
+
+    peakfeed[i]=todB(localpeak);
+    attfeed[i]=localatt;
+    
+  }
+
+  limitstate.out.samples=in->samples;
+  
+  /* finish up the state feedabck */
+  {
+    limit_feedback *ff=
+      (limit_feedback *)feedback_new(&limitstate.feedpool,new_limit_feedback);
+    
+    if(!ff->peak)
+      ff->peak=malloc(input_ch*sizeof(*ff->peak));
+    
+    if(!ff->att)
+      ff->att=malloc(input_ch*sizeof(*ff->att));
+    
+    memcpy(ff->peak,peakfeed,sizeof(peakfeed));
+    memcpy(ff->att,attfeed,sizeof(attfeed));
+
+    feedback_push(&limitstate.feedpool,(feedback_generic *)ff);
+  }
+   
+  {
+    int tozero=limitstate.out.size-limitstate.out.samples;
+    if(tozero)
+      for(i=0;i<limitstate.out.channels;i++)
+        memset(limitstate.out.data[i]+limitstate.out.samples,0,sizeof(**limitstate.out.data)*tozero);
+  }
+
+  offset+=input_size;
+
+  return &limitstate.out;
+}
+

Added: trunk/postfish/limit.h
===================================================================
--- trunk/postfish/limit.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/limit.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,33 @@
+/*
+ *
+ *  postfish
+ *    
+ *      Copyright (C) 2002-2004 Monty and Xiph.Org
+ *
+ *  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.
+ *
+ * 
+ */
+
+typedef struct{
+  sig_atomic_t thresh;
+  sig_atomic_t depth;
+  sig_atomic_t decay;
+} limit_settings;
+
+extern int pull_limit_feedback(float *peak,float *att);
+extern int limit_load(void);
+extern int limit_reset(void);
+extern time_linkage *limit_read(time_linkage *in);

Added: trunk/postfish/limitpanel.c
===================================================================
--- trunk/postfish/limitpanel.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/limitpanel.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,190 @@
+/*
+ *
+ *  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.
+ *
+ * 
+ */
+
+#include "postfish.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "readout.h"
+#include "multibar.h"
+#include "mainpanel.h"
+#include "subpanel.h"
+#include "feedback.h"
+#include "limit.h"
+#include "limitpanel.h"
+
+extern sig_atomic_t limit_active;
+extern sig_atomic_t limit_visible;
+extern int input_ch;
+extern int input_size;
+extern int input_rate;
+
+extern limit_settings limitset;
+
+static GtkWidget *t_slider;
+static GtkWidget *a_slider;
+
+static void limit_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float val=multibar_get_value(MULTIBAR(w),0);
+
+  sprintf(buffer,"%+5.1fdB",val);
+  readout_set(r,buffer);
+  
+  limitset.thresh=rint(val*10.);
+}
+
+static void depth_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float val=multibar_get_value(MULTIBAR(w),0);
+
+  sprintf(buffer,"%+5.1fdB",val);
+  readout_set(r,buffer);
+  
+  limitset.depth=rint(val*10.);
+}
+
+static void decay_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  Readout *r=(Readout *)in;
+  float v=multibar_get_value(MULTIBAR(w),0);
+
+
+  if(v<10){
+    sprintf(buffer," %4.2fms",v);
+  }else if(v<100){
+    sprintf(buffer," %4.1fms",v);
+  }else if (v<1000){
+    sprintf(buffer," %4.0fms",v);
+  }else if (v<10000){
+    sprintf(buffer," %4.2fs",v/1000.);
+  }else{
+    sprintf(buffer," %4.1fs",v/1000.);
+  }
+
+  readout_set(READOUT(r),buffer);
+
+  limitset.decay=rint(v*10.);
+}
+
+void limitpanel_create(postfish_mainpanel *mp,
+		       GtkWidget *windowbutton,
+		       GtkWidget *activebutton){
+
+
+  char *labels[8]={"-80","-60","-40","-20","-10","-6","-3","+0"};
+  float levels[9]={-80,-60,-50,-40,-30,-10,-6,-3,0};
+
+  char *labels2[4]={"-20","-10","-3","0"};
+  float levels2[5]={-30,-20,-10,-3,0};
+  
+  char *rlabels[3]={"6","   20","40"};
+  float rlevels[4]={0,3,10,20};
+
+  float timing_levels[6]={.1,1,10,100,1000,10000};
+  char  *timing_labels[5]={"1ms","10ms","100ms","1s","10s"};
+
+  subpanel_generic *panel=subpanel_create(mp,windowbutton,activebutton,
+					  &limit_active,
+					  &limit_visible,
+					  "Hard _Limiter"," [l] ");
+  
+  GtkWidget *slidertable=gtk_table_new(4,4,0);
+
+  GtkWidget *label1=gtk_label_new("hard limit");
+  GtkWidget *label2=gtk_label_new("knee depth");
+  GtkWidget *label3=gtk_label_new("decay speed");
+  GtkWidget *label4=gtk_label_new("attenuation");
+
+  GtkWidget *readout1=readout_new("+XXXXdB");
+  GtkWidget *readout2=readout_new("+XXXXdB");
+  GtkWidget *readout3=readout_new("+XXXXms");
+
+  GtkWidget *slider2=multibar_slider_new(4,labels2,levels2,1);
+  GtkWidget *slider3=multibar_slider_new(5,timing_labels,timing_levels,1);
+
+  t_slider=multibar_new(8,labels,levels,1,HI_DECAY);
+  a_slider=multibar_new(3,rlabels,rlevels,0,0);
+
+  gtk_misc_set_alignment(GTK_MISC(label1),1,.5);
+  gtk_misc_set_alignment(GTK_MISC(label2),1,.5);
+  gtk_misc_set_alignment(GTK_MISC(label3),1,.5);
+  gtk_widget_set_name(label4,"scalemarker");
+
+  gtk_table_attach(GTK_TABLE(slidertable),label1,0,1,1,2,GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+  gtk_table_attach(GTK_TABLE(slidertable),label2,0,1,2,3,GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+  gtk_table_attach(GTK_TABLE(slidertable),label3,0,1,3,4,GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+  gtk_table_attach(GTK_TABLE(slidertable),label4,3,4,0,1,GTK_FILL,GTK_FILL|GTK_EXPAND,0,0);
+ 
+  gtk_table_attach(GTK_TABLE(slidertable),t_slider,1,2,1,2,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,0,0);
+  gtk_table_attach(GTK_TABLE(slidertable),a_slider,3,4,1,2,GTK_FILL,GTK_FILL|GTK_EXPAND,0,0);
+  gtk_table_attach(GTK_TABLE(slidertable),slider2,1,2,2,3,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,0,4);
+  gtk_table_attach(GTK_TABLE(slidertable),slider3,1,2,3,4,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,0,0);
+
+  gtk_table_attach(GTK_TABLE(slidertable),readout1,2,3,1,2,GTK_FILL,GTK_FILL|GTK_EXPAND,0,0);
+  gtk_table_attach(GTK_TABLE(slidertable),readout2,2,3,2,3,GTK_FILL,GTK_FILL|GTK_EXPAND,0,4);
+  gtk_table_attach(GTK_TABLE(slidertable),readout3,2,3,3,4,GTK_FILL,GTK_FILL|GTK_EXPAND,0,0);
+
+  gtk_container_add(GTK_CONTAINER(panel->subpanel_box),slidertable);
+
+  multibar_callback(MULTIBAR(t_slider),limit_change,readout1);
+  multibar_callback(MULTIBAR(slider2),depth_change,readout2);
+  multibar_callback(MULTIBAR(slider3),decay_change,readout3);
+
+  multibar_thumb_set(MULTIBAR(t_slider),0.,0);
+  multibar_thumb_set(MULTIBAR(slider2),0.,0);
+  multibar_thumb_set(MULTIBAR(slider3),10.,0);
+  
+  subpanel_show_all_but_toplevel(panel);
+
+}
+
+static float *peakfeed=0;
+static float *attfeed=0;
+static float *zerofeed=0;
+
+void limitpanel_feedback(int displayit){
+  if(!peakfeed){
+    int i;
+    peakfeed=malloc(sizeof(*peakfeed)*input_ch);
+    attfeed=malloc(sizeof(*attfeed)*input_ch);
+    zerofeed=malloc(sizeof(*zerofeed)*input_ch);
+    for(i=0;i<input_ch;i++)zerofeed[i]=-150.;
+  }
+  
+  if(pull_limit_feedback(peakfeed,attfeed)==1){
+    multibar_set(MULTIBAR(t_slider),zerofeed,peakfeed,
+		 input_ch,(displayit && limit_visible));
+    multibar_set(MULTIBAR(a_slider),zerofeed,attfeed,
+		 input_ch,(displayit && limit_visible));
+  }
+}
+
+void limitpanel_reset(void){
+  multibar_reset(MULTIBAR(t_slider));
+  multibar_reset(MULTIBAR(a_slider));
+}
+
+
+

Added: trunk/postfish/limitpanel.h
===================================================================
--- trunk/postfish/limitpanel.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/limitpanel.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,30 @@
+/*
+ *
+ *  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 void limitpanel_create(postfish_mainpanel *mp,
+			       GtkWidget *windowbutton,
+			       GtkWidget *activebutton);
+extern void limitpanel_feedback(int displayit);
+extern void limitpanel_reset(void);
+
+

Modified: trunk/postfish/main.c
===================================================================
--- trunk/postfish/main.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/main.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -31,6 +31,12 @@
 #include <fftw3.h>
 #include "input.h"
 #include "output.h"
+#include "declip.h"
+#include "eq.h"
+#include "suppress.h"
+#include "multicompand.h"
+#include "singlecomp.h"
+#include "limit.h"
 
 pthread_mutex_t master_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 
@@ -45,7 +51,10 @@
   /* set up filter chains */
   if(declip_load())exit(1);
   if(eq_load())exit(1);
+  if(suppress_load())exit(1);
   if(multicompand_load())exit(1);
+  if(singlecomp_load())exit(1);
+  if(limit_load())exit(1);
 
   /* look at stdout... do we have a file or device? */
   if(!isatty(STDOUT_FILENO)){

Modified: trunk/postfish/mainpanel.c
===================================================================
--- trunk/postfish/mainpanel.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/mainpanel.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -34,6 +34,13 @@
 #include "mainpanel.h"
 #include "windowbutton.h"
 
+static void meterhold_reset(postfish_mainpanel *p){
+  p->inpeak=-200;
+  p->outpeak=-200;
+  
+  readout_set(READOUT(p->inreadout),"------");
+  readout_set(READOUT(p->outreadout),"------");
+}
 
 static void action_zero(GtkWidget *widget,postfish_mainpanel *p){
   const char *time=gtk_entry_get_text(GTK_ENTRY(p->entry_a));
@@ -47,6 +54,9 @@
   clippanel_reset();
   eqpanel_reset();
   compandpanel_reset();
+  singlepanel_reset();
+  limitpanel_reset();
+  meterhold_reset(p);
 }
 
 static void action_end(GtkWidget *widget,postfish_mainpanel *p){
@@ -62,6 +72,9 @@
   clippanel_reset();
   eqpanel_reset();
   compandpanel_reset();
+  singlepanel_reset();
+  limitpanel_reset();
+  meterhold_reset(p);
 }
 
 static void action_bb(GtkWidget *widget,postfish_mainpanel *p){
@@ -92,11 +105,67 @@
   readout_set(READOUT(p->cue),(char *)time);
 }
 
+/* gotta have the Fucking Fish */
+static int reanimate_fish(postfish_mainpanel *p){
+  if(playback_active || (p->fishframe>0 && p->fishframe<12)){
+    /* continue spinning */
+    p->fishframe++;
+    if(p->fishframe>=12)p->fishframe=0;
+
+    gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
+			      p->ff[p->fishframe],
+			      p->fb[p->fishframe]);
+
+    if(p->fishframe==0 && !playback_active){
+      /* reschedule to blink */
+      p->fishframe_timer=
+	gtk_timeout_add(rand()%1000*30,(GtkFunction)reanimate_fish,p);
+      return FALSE;
+    }
+  }else{
+    p->fishframe++;
+    if(p->fishframe<=1)p->fishframe=12;
+    if(p->fishframe>=19)p->fishframe=0;
+
+    gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
+			      p->ff[p->fishframe],
+			      p->fb[p->fishframe]);
+
+
+    if(p->fishframe==12){
+      /* reschedule to animate */
+      p->fishframe_timer=
+	gtk_timeout_add(10,(GtkFunction)reanimate_fish,p);
+      return FALSE;
+    }
+    if(p->fishframe==0){
+      /* reschedule to blink */
+      p->fishframe_timer=
+	gtk_timeout_add(rand()%1000*30,(GtkFunction)reanimate_fish,p);
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+static void animate_fish(postfish_mainpanel *p){
+  if(p->fishframe_init){
+    gtk_timeout_remove(p->fishframe_timer);
+    p->fishframe_timer=
+      gtk_timeout_add(80,(GtkFunction)reanimate_fish,p);
+  }else{
+    p->fishframe_init=1;
+    p->fishframe_timer=
+      gtk_timeout_add(rand()%1000*30,(GtkFunction)reanimate_fish,p);
+  }
+}
+
 static void action_play(GtkWidget *widget,postfish_mainpanel *p){
   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))){
     if(!playback_active){
       pthread_t playback_thread_id;
       playback_active=1;
+      meterhold_reset(p);
       animate_fish(p);
       pthread_create(&playback_thread_id,NULL,&playback_thread,NULL);
     }
@@ -109,13 +178,9 @@
   const char *time=gtk_entry_get_text(GTK_ENTRY(p->entry_a));
   off_t cursor=input_time_to_cursor(time);
   
-  if(cursor==0){
-    time=readout_get(READOUT(p->cue));
-    gtk_entry_set_text(GTK_ENTRY(p->entry_a),time);
-  }else{
-    input_seek(cursor);
-    readout_set(READOUT(p->cue),(char *)time);
-  }
+  input_seek(cursor);
+  readout_set(READOUT(p->cue),(char *)time);
+
 }
 
 static void action_entryb(GtkWidget *widget,postfish_mainpanel *p){
@@ -125,12 +190,6 @@
     off_t cursora,cursorb=input_time_to_cursor(time);
     time=gtk_entry_get_text(GTK_ENTRY(p->entry_a));
     cursora=input_time_to_cursor(time);
-  
-    if(cursorb==0){
-      time=readout_get(READOUT(p->cue));
-      cursorb=input_time_to_cursor(time);
-      gtk_entry_set_text(GTK_ENTRY(p->entry_b),time);
-    }
     
     if(cursora<cursorb){
       input_Acursor_set(cursora);
@@ -148,6 +207,26 @@
     
 }
 
+static void action_seta(GtkWidget *widget,postfish_mainpanel *p){
+  const char *time=gtk_entry_get_text(GTK_ENTRY(p->entry_a));
+  
+  time=readout_get(READOUT(p->cue));
+  gtk_entry_set_text(GTK_ENTRY(p->entry_a),time);
+
+}
+
+static void action_setb(GtkWidget *widget,postfish_mainpanel *p){
+  const char *time=gtk_entry_get_text(GTK_ENTRY(p->entry_b));
+  off_t cursora,cursorb=input_time_to_cursor(time);
+  time=gtk_entry_get_text(GTK_ENTRY(p->entry_a));
+  cursora=input_time_to_cursor(time);
+  
+  time=readout_get(READOUT(p->cue));
+  cursorb=input_time_to_cursor(time);
+  gtk_entry_set_text(GTK_ENTRY(p->entry_b),time);
+    
+}
+
 static void action_reseta(GtkWidget *widget,postfish_mainpanel *p){
   char time[14];
   input_cursor_to_time(0,time);
@@ -159,64 +238,9 @@
   char time[14];
   input_cursor_to_time(0,time);
   gtk_entry_set_text(GTK_ENTRY(p->entry_b),time);
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->cue_set[1]),0);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->cue_act[1]),0);
 }
 
-/* gotta have the Fucking Fish */
-int reanimate_fish(postfish_mainpanel *p){
-  if(playback_active || (p->fishframe>0 && p->fishframe<12)){
-    /* continue spinning */
-    p->fishframe++;
-    if(p->fishframe>=12)p->fishframe=0;
-
-    gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
-			      p->ff[p->fishframe],
-			      p->fb[p->fishframe]);
-
-    if(p->fishframe==0 && !playback_active){
-      /* reschedule to blink */
-      p->fishframe_timer=
-	gtk_timeout_add(rand()%1000*30,(GtkFunction)reanimate_fish,p);
-      return FALSE;
-    }
-  }else{
-    p->fishframe++;
-    if(p->fishframe<=1)p->fishframe=12;
-    if(p->fishframe>=19)p->fishframe=0;
-
-    gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
-			      p->ff[p->fishframe],
-			      p->fb[p->fishframe]);
-
-
-    if(p->fishframe==12){
-      /* reschedule to animate */
-      p->fishframe_timer=
-	gtk_timeout_add(10,(GtkFunction)reanimate_fish,p);
-      return FALSE;
-    }
-    if(p->fishframe==0){
-      /* reschedule to blink */
-      p->fishframe_timer=
-	gtk_timeout_add(rand()%1000*30,(GtkFunction)reanimate_fish,p);
-      return FALSE;
-    }
-  }
-  return TRUE;
-}
-
-int animate_fish(postfish_mainpanel *p){
-  if(p->fishframe_init){
-    gtk_timeout_remove(p->fishframe_timer);
-    p->fishframe_timer=
-      gtk_timeout_add(80,(GtkFunction)reanimate_fish,p);
-  }else{
-    p->fishframe_init=1;
-    p->fishframe_timer=
-      gtk_timeout_add(rand()%1000*30,(GtkFunction)reanimate_fish,p);
-  }
-}
-
 static void shutdown(void){
   gtk_main_quit ();
 }
@@ -333,105 +357,125 @@
 
   /* do not capture Alt accellerators */
   if(event->state&GDK_MOD1_MASK) return FALSE;
-  if(event->state&GDK_CONTROL_MASK) return FALSE;
 
+  if(event->state&GDK_CONTROL_MASK){
 
-  switch(event->keyval){
-  case GDK_less:
-  case GDK_comma:
-  case GDK_period:
-  case GDK_greater:
-    
-    /* GTK has one unfortunate bit of hardwiring; if a key is held down
-       and autorepeats, the default pushbutton widget-unactivate timeout
-       is 250 ms, far slower than autorepeat.  We must defeat this to
-       have autorepeat accellerators function at full speed. */
-    
-    {
-      GdkEventKey copy=*event;
-      copy.type=GDK_KEY_RELEASE;
-      gtk_main_do_event((GdkEvent *)(&copy));
-    }
-    while (gtk_events_pending ())  gtk_main_iteration ();
+    switch(event->keyval){
+    case GDK_a:
+    case GDK_A:
+      gtk_widget_activate(p->cue_reset[0]);
+      break;
+    case GDK_b:
+    case GDK_B:
+      gtk_widget_activate(p->cue_reset[1]);
+      break;
+
+    default:
+    return FALSE;
   }
 
 
-  switch(event->keyval){
-  case GDK_t:
-    /* trigger master dB */
-    gtk_widget_activate(p->masterdB_a);
+  }else{
+    /* non-control keypresses */
+    switch(event->keyval){
+    case GDK_less:
+    case GDK_comma:
+    case GDK_period:
+    case GDK_greater:
+      
+      /* GTK has one unfortunate bit of hardwiring; if a key is held down
+	 and autorepeats, the default pushbutton widget-unactivate timeout
+	 is 250 ms, far slower than autorepeat.  We must defeat this to
+	 have autorepeat accellerators function at full speed. */
+      
+      {
+	GdkEventKey copy=*event;
+	copy.type=GDK_KEY_RELEASE;
+	gtk_main_do_event((GdkEvent *)(&copy));
+      }
+      while (gtk_events_pending ())  gtk_main_iteration ();
+    }
+    
+    
+    switch(event->keyval){
+    case GDK_t:
+      /* trigger master dB */
+      gtk_widget_activate(p->masterdB_a);
+      break;
+    case GDK_minus:
+      multibar_thumb_set(MULTIBAR(p->masterdB_s),
+			 multibar_get_value(MULTIBAR(p->masterdB_s),0)-.1,0);
+      break;
+    case GDK_underscore:
+      multibar_thumb_set(MULTIBAR(p->masterdB_s),
+			 multibar_get_value(MULTIBAR(p->masterdB_s),0)-1.,0);
+      break;
+    case GDK_equal:
+      multibar_thumb_set(MULTIBAR(p->masterdB_s),
+			 multibar_get_value(MULTIBAR(p->masterdB_s),0)+.1,0);
     break;
-  case GDK_minus:
-    multibar_thumb_set(MULTIBAR(p->masterdB_s),
-		       multibar_get_value(MULTIBAR(p->masterdB_s),0)-.1,0);
-    break;
-  case GDK_underscore:
-    multibar_thumb_set(MULTIBAR(p->masterdB_s),
-		       multibar_get_value(MULTIBAR(p->masterdB_s),0)-1.,0);
-    break;
-  case GDK_equal:
-    multibar_thumb_set(MULTIBAR(p->masterdB_s),
-		       multibar_get_value(MULTIBAR(p->masterdB_s),0)+.1,0);
-    break;
-  case GDK_plus:
-    multibar_thumb_set(MULTIBAR(p->masterdB_s),
-		       multibar_get_value(MULTIBAR(p->masterdB_s),0)+1.,0);
-    break;
-  case GDK_d:
-    gtk_widget_activate(p->buttonactive[0]);
-    break;
-  case GDK_c:
-    gtk_widget_activate(p->buttonactive[1]);
-    break;
-  case GDK_m:
-    gtk_widget_activate(p->buttonactive[2]);
-    break;
-  case GDK_s:
-    gtk_widget_activate(p->buttonactive[3]);
-    break;
-  case GDK_e:
-    gtk_widget_activate(p->buttonactive[4]);
-    break;
-  case GDK_l:
-    gtk_widget_activate(p->buttonactive[5]);
-    break;
-  case GDK_a:
-    gtk_widget_activate(p->cue_set[0]);
-    break;
-  case GDK_A:
-    gtk_widget_activate(p->cue_reset[0]);
-    break;
-  case GDK_b:
-    gtk_widget_activate(p->cue_set[1]);
-    break;
-  case GDK_B:
-    gtk_widget_activate(p->cue_reset[1]);
-    break;
-  case GDK_BackSpace:
-    gtk_widget_activate(p->deckactive[0]);
-    break;
-  case GDK_less:
-    gtk_widget_activate(p->deckactive[1]);
-    break;
-  case GDK_comma:
-    gtk_widget_activate(p->deckactive[2]);
-    break;
-  case GDK_space:
-    gtk_widget_activate(p->deckactive[3]);
-    break;
-  case GDK_period:
-    gtk_widget_activate(p->deckactive[4]);
-    break;
-  case GDK_greater:
-    gtk_widget_activate(p->deckactive[5]);
-    break;
-  case GDK_End:
-    gtk_widget_activate(p->deckactive[6]);
-    break;
-  default:
-    return FALSE;
+    case GDK_plus:
+      multibar_thumb_set(MULTIBAR(p->masterdB_s),
+			 multibar_get_value(MULTIBAR(p->masterdB_s),0)+1.,0);
+      break;
+    case GDK_d:
+      gtk_widget_activate(p->buttonactive[0]);
+      break;
+    case GDK_c:
+      gtk_widget_activate(p->buttonactive[1]);
+      break;
+    case GDK_m:
+      gtk_widget_activate(p->buttonactive[2]);
+      break;
+    case GDK_s:
+      gtk_widget_activate(p->buttonactive[3]);
+      break;
+    case GDK_v:
+      gtk_widget_activate(p->buttonactive[4]);
+      break;
+    case GDK_e:
+      gtk_widget_activate(p->buttonactive[5]);
+      break;
+    case GDK_l:
+      gtk_widget_activate(p->buttonactive[6]);
+      break;
+    case GDK_a:
+      gtk_widget_activate(p->cue_act[0]);
+      break;
+    case GDK_A:
+      gtk_widget_activate(p->cue_set[0]);
+      break;
+    case GDK_b:
+      gtk_widget_activate(p->cue_act[1]);
+      break;
+    case GDK_B:
+      gtk_widget_activate(p->cue_set[1]);
+      break;
+    case GDK_BackSpace:
+      gtk_widget_activate(p->deckactive[0]);
+      break;
+    case GDK_less:
+      gtk_widget_activate(p->deckactive[1]);
+      break;
+    case GDK_comma:
+      gtk_widget_activate(p->deckactive[2]);
+      break;
+    case GDK_space:
+      gtk_widget_activate(p->deckactive[3]);
+      break;
+    case GDK_period:
+      gtk_widget_activate(p->deckactive[4]);
+      break;
+    case GDK_greater:
+      gtk_widget_activate(p->deckactive[5]);
+      break;
+    case GDK_End:
+      gtk_widget_activate(p->deckactive[6]);
+      break;
+    default:
+      return FALSE;
+    }
   }
-
   return TRUE;
 }
 
@@ -578,12 +622,24 @@
     GtkWidget *in=gtk_label_new("in:");
     GtkWidget *out=gtk_label_new("out:");
     GtkWidget *show=gtk_label_new("show:");
+    GtkWidget *inbox=gtk_hbox_new(0,0);
+    GtkWidget *outbox=gtk_hbox_new(0,0);
 
     panel->inbar=multibar_new(12,labels,levels, 0,
                               LO_ATTACK|LO_DECAY|HI_DECAY|PEAK_FOLLOW );
     panel->outbar=multibar_new(12,labels,levels, 0,
                                LO_ATTACK|LO_DECAY|HI_DECAY|PEAK_FOLLOW );
+    panel->inreadout=readout_new("------");
+    panel->outreadout=readout_new("------");
 
+    gtk_widget_set_name(panel->inreadout,"smallreadout");
+    gtk_widget_set_name(panel->outreadout,"smallreadout");
+    
+    gtk_box_pack_start(GTK_BOX(inbox),panel->inbar,1,1,0);
+    gtk_box_pack_end(GTK_BOX(inbox),panel->inreadout,0,1,0);
+    gtk_box_pack_start(GTK_BOX(outbox),panel->outbar,1,1,0);
+    gtk_box_pack_end(GTK_BOX(outbox),panel->outreadout,0,1,0);
+
     gtk_container_set_border_width(GTK_CONTAINER (ttable), 3);
     gtk_table_set_col_spacings(GTK_TABLE(ttable),5);
     gtk_misc_set_alignment(GTK_MISC(show),1,.5);
@@ -609,8 +665,8 @@
     gtk_table_attach(GTK_TABLE(ttable),show,0,1,0,1,GTK_FILL|GTK_SHRINK,GTK_FILL|GTK_SHRINK,0,0);
     gtk_table_attach(GTK_TABLE(ttable),in,0,1,1,2,GTK_FILL|GTK_SHRINK,GTK_FILL|GTK_SHRINK,0,0);
     gtk_table_attach(GTK_TABLE(ttable),out,0,1,2,3,GTK_FILL|GTK_SHRINK,GTK_FILL|GTK_SHRINK,0,0);
-    gtk_table_attach(GTK_TABLE(ttable),panel->inbar,1,3,1,2,GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,0);
-    gtk_table_attach(GTK_TABLE(ttable),panel->outbar,1,3,2,3,GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,0);
+    gtk_table_attach(GTK_TABLE(ttable),inbox,1,3,1,2,GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,0);
+    gtk_table_attach(GTK_TABLE(ttable),outbox,1,3,2,3,GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,0);
 
     gtk_table_set_row_spacing(GTK_TABLE(ttable),1,1);
     gtk_table_set_row_spacing(GTK_TABLE(ttable),2,2);
@@ -651,7 +707,6 @@
     /* master action bar */
     {
       GtkWidget *bar_table=gtk_table_new(1,8,1);
-      char buffer[20];
       for(i=0;i<7;i++){
         GtkWidget *box=gtk_vbox_new(0,3);
         GtkWidget *label=gtk_label_new(text_bar[i]);
@@ -717,10 +772,12 @@
       panel->cue=readout_new("    :  :00.00");
 
 
-      panel->cue_set[0]=gtk_button_new_with_label("[a]");
-      panel->cue_set[1]=gtk_toggle_button_new_with_label("[b]");
-      panel->cue_reset[0]=gtk_button_new_with_label("[A]");
-      panel->cue_reset[1]=gtk_button_new_with_label("[B]");
+      panel->cue_act[0]=gtk_button_new_with_label("[a]");
+      panel->cue_act[1]=gtk_toggle_button_new_with_label("[b]");
+      panel->cue_set[0]=gtk_button_new_with_label("[A]");
+      panel->cue_set[1]=gtk_button_new_with_label("[B]");
+      panel->cue_reset[0]=gtk_button_new_with_label("^A");
+      panel->cue_reset[1]=gtk_button_new_with_label("^B");
 
 
       gtk_entry_set_width_chars(GTK_ENTRY(panel->entry_a),13);
@@ -738,10 +795,16 @@
       g_signal_connect_after(G_OBJECT (panel->entry_b), "grab_focus",
                         G_CALLBACK (timeevent_unselect), NULL);
 
+      g_signal_connect (G_OBJECT (panel->cue_act[0]), "clicked",
+			G_CALLBACK (action_entrya), panel);
+      g_signal_connect (G_OBJECT (panel->cue_act[1]), "clicked",
+			G_CALLBACK (action_entryb), panel);
+
       g_signal_connect (G_OBJECT (panel->cue_set[0]), "clicked",
-			G_CALLBACK (action_entrya), panel);
+			G_CALLBACK (action_seta), panel);
       g_signal_connect (G_OBJECT (panel->cue_set[1]), "clicked",
-			G_CALLBACK (action_entryb), panel);
+			G_CALLBACK (action_setb), panel);
+
       g_signal_connect (G_OBJECT (panel->cue_reset[0]), "clicked",
                         G_CALLBACK (action_reseta), panel);
       g_signal_connect (G_OBJECT (panel->cue_reset[1]), "clicked",
@@ -761,14 +824,16 @@
 
       gtk_box_pack_start(GTK_BOX(cuebox),framea,1,1,3);
 
+      gtk_box_pack_start(GTK_BOX(cuebox),panel->cue_act[0],0,0,0);
+      gtk_box_pack_start(GTK_BOX(cuebox),panel->entry_a,0,0,0);
       gtk_box_pack_start(GTK_BOX(cuebox),panel->cue_set[0],0,0,0);
-      gtk_box_pack_start(GTK_BOX(cuebox),panel->entry_a,0,0,0);
       gtk_box_pack_start(GTK_BOX(cuebox),panel->cue_reset[0],0,0,0);
 
       gtk_box_pack_start(GTK_BOX(cuebox),frameb,1,1,3);
 
+      gtk_box_pack_start(GTK_BOX(cuebox),panel->cue_act[1],0,0,0);
+      gtk_box_pack_start(GTK_BOX(cuebox),panel->entry_b,0,0,0);
       gtk_box_pack_start(GTK_BOX(cuebox),panel->cue_set[1],0,0,0);
-      gtk_box_pack_start(GTK_BOX(cuebox),panel->entry_b,0,0,0);
       gtk_box_pack_start(GTK_BOX(cuebox),panel->cue_reset[1],0,0,0);
 
     }
@@ -815,12 +880,13 @@
     gtk_table_attach_defaults(GTK_TABLE(panel->wintable),temp,1,2,0,1);
   }
 
-  mainpanel_panelentry(panel,"_Declip ","[d]",0,clippanel_create);
+  mainpanel_panelentry(panel,"_Declipper ","[d]",0,clippanel_create);
   mainpanel_panelentry(panel,"_Crosstalk ","[c]",1,0);
   mainpanel_panelentry(panel,"_Multicomp ","[m]",2,compandpanel_create);
-  mainpanel_panelentry(panel,"_Singlecomp ","[s]",3,0);
-  mainpanel_panelentry(panel,"_Equalizer ","[e]",4,eqpanel_create);
-  mainpanel_panelentry(panel,"_Limiter ","[l]",5,0);
+  mainpanel_panelentry(panel,"_Singlecomp ","[s]",3,singlepanel_create);
+  mainpanel_panelentry(panel,"De_verber ","[v]",4,suppresspanel_create);
+  mainpanel_panelentry(panel,"_Equalizer ","[e]",5,eqpanel_create);
+  mainpanel_panelentry(panel,"_Limiter ","[l]",6,limitpanel_create);
 
   g_signal_connect (G_OBJECT (panel->toplevel), "delete_event",
                     G_CALLBACK (shutdown), NULL);
@@ -834,7 +900,7 @@
 
 }
 
-static gboolean feedback_process(postfish_mainpanel *panel){
+static void feedback_process(postfish_mainpanel *panel){
 
   /* first order of business: release the play button if playback is
      no longer in progress */
@@ -863,6 +929,13 @@
         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->channelshow[i]))){
           peak[i]=todB(peak[i]);
           rms[i]=todB(rms[i]);
+
+	  if(peak[i]>panel->outpeak){
+	    panel->outpeak=ceil(peak[i]);
+	    sprintf(buffer,"%+4.0fdB",panel->outpeak);
+	    readout_set(READOUT(panel->outreadout),buffer);
+	  }
+
         }else{
           peak[i]=-400;
           rms[i]=-400;
@@ -877,6 +950,13 @@
           if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->channelshow[i]))){
             peak[i]=todB(peak[i]);
             rms[i]=todB(rms[i]);
+
+	    if(peak[i]>panel->inpeak){
+	      panel->inpeak=ceil(peak[i]);
+	      sprintf(buffer,"%+4.0fdB",panel->inpeak);
+	      readout_set(READOUT(panel->inreadout),buffer);
+	    }
+
           }else{
             peak[i]=-400;
             rms[i]=-400;
@@ -891,6 +971,8 @@
       clippanel_feedback(current_p);
       eqpanel_feedback(current_p);
       compandpanel_feedback(current_p);
+      singlepanel_feedback(current_p);
+      limitpanel_feedback(current_p);
       
     }
   }
@@ -900,7 +982,6 @@
                                    GIOCondition condition,
                                    gpointer data){
   postfish_mainpanel *panel=data;
-  int i;
   char buf[1];
   read(eventpipe[0],buf,1);
 
@@ -967,7 +1048,6 @@
   /* set up watching the event pipe */
   {
     GIOChannel *channel = g_io_channel_unix_new (eventpipe[0]);
-    GSource *source;
     guint id;
 
     g_io_channel_set_encoding (channel, NULL, NULL);

Modified: trunk/postfish/mainpanel.h
===================================================================
--- trunk/postfish/mainpanel.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/mainpanel.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -29,6 +29,8 @@
 #include "clippanel.h"
 #include "eqpanel.h"
 #include "compandpanel.h"
+#include "singlepanel.h"
+#include "limitpanel.h"
 
 struct postfish_mainpanel{
   GtkWidget *topframe;
@@ -60,6 +62,7 @@
 
   GtkWidget *buttonactive[7];
 
+  GtkWidget *cue_act[2];
   GtkWidget *cue_set[2];
   GtkWidget *cue_reset[2];
 
@@ -67,7 +70,12 @@
 
   GtkWidget *inbar;
   GtkWidget *outbar;
+  GtkWidget *inreadout;
+  GtkWidget *outreadout;
 
+  float inpeak;
+  float outpeak;
+
   GtkWidget *channelshow[10]; /* support only up to 8 + mid/side */
 
   GtkWidget *cue;

Modified: trunk/postfish/multibar.c
===================================================================
--- trunk/postfish/multibar.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/multibar.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -21,9 +21,9 @@
  * 
  */
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
+#include <string.h>
 #include <gdk/gdkkeysyms.h>
 #include "multibar.h"
 
@@ -516,9 +516,7 @@
         
         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}};
@@ -557,9 +555,7 @@
         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},
@@ -836,7 +832,7 @@
   int i;
   
   if (m->backing)
-    gdk_drawable_unref(m->backing);
+    g_object_unref(m->backing);
   
   m->backing = gdk_pixmap_new(widget->window,
                               widget->allocation.width,
@@ -968,7 +964,7 @@
   return TRUE;
 }
 
-static gint button_press   (GtkWidget        *widget,
+static gboolean button_press   (GtkWidget        *widget,
                             GdkEventButton   *event){
   Multibar *m=MULTIBAR(widget);
   if(m->thumbstate[0]){
@@ -985,13 +981,15 @@
     m->thumbx=m->thumbpixel[2]-event->x;
   }
   draw_and_expose(widget);
+  return TRUE;
 }
 
-static gint button_release   (GtkWidget        *widget,
+static gboolean button_release   (GtkWidget        *widget,
                             GdkEventButton   *event){
   Multibar *m=MULTIBAR(widget);
   m->thumbgrab=-1;
   draw_and_expose(widget);
+  return TRUE;
 }
 
 static gboolean unfocus(GtkWidget        *widget,
@@ -1000,6 +998,7 @@
   m->prev_thumbfocus=m->thumbfocus;
   m->thumbfocus=-1;
   draw_and_expose(widget);
+  return TRUE;
 }
 
 static gboolean refocus(GtkWidget        *widget,
@@ -1008,6 +1007,7 @@
   transition_thumbfocus=m->thumbfocus=m->prev_thumbfocus;
   m->thumbgrab=-1;
   draw_and_expose(widget);
+  return TRUE;
 }
 
 gboolean key_press(GtkWidget *w,GdkEventKey *event){
@@ -1175,7 +1175,6 @@
 
 GtkWidget* multibar_slider_new (int n, char **labels, float *levels, 
                                 int thumbs){
-  int i;
   GtkWidget *ret= multibar_new(n,labels,levels,thumbs,0);
   Multibar *m=MULTIBAR(ret);
   m->readout=0;

Modified: trunk/postfish/multicompand.c
===================================================================
--- trunk/postfish/multicompand.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/multicompand.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -28,106 +28,16 @@
 #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){
-  int j;
-  FILE *of;
-  char buffer[80];
-
-  sprintf(buffer,"%s_%d.m",base,i);
-  of=fopen(buffer,"w");
-  
-  if(!of)perror("failed to open data dump file");
-  
-  for(j=0;j<n;j++){
-    if(bark){
-      float b=toBark((4000.f*j/n)+.25);
-      fprintf(of,"%f ",b);
-    }else
-      fprintf(of,"%f ",(float)j);
-    
-    if(dB){
-      float val=todB(hypot(v[j],v[j+1]));
-      if(val<-140)val=-140;
-      fprintf(of,"%f\n",val);
-      j++;
-     
-    }else{
-      fprintf(of,"%f\n",v[j]);
-    }
-  }
-  fclose(of);
-}
-
-static void _analysis_append(char *base,int basemod,float *v,int n,int off){
-  int j;
-  FILE *of;
-  char buffer[80];
-
-  sprintf(buffer,"%s_%d.m",base,basemod);
-  of=fopen(buffer,"a");
-  
-  if(!of)perror("failed to open data dump file");
-  
-  for(j=0;j<n;j++){
-    fprintf(of,"%f ",(float)j+off);    
-    fprintf(of,"%f\n",v[j]);
-  }
-  fprintf(of,"\n");
-  fclose(of);
-}
-
 extern int input_size;
 extern int input_rate;
 extern int input_ch;
 
-/* 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;
+  int loc;
+  float val;
+} peak_state;
 
-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
-
-#define todB_p(x) (((*(int32_t*)(x)) & 0x7fffffff) * 7.1771144e-7f - 764.27118f)
-
-#else
-
-#define todB_p(x) todB(*(x))
-
-#endif
-
 typedef struct {
-  double x[2];
-  double y[2];
-  int state;
-} iir_state;
-
-typedef struct {
   subband_state ss;
   subband_window sw[multicomp_banks];
   
@@ -137,23 +47,20 @@
   iir_filter under_attack[multicomp_banks][multicomp_freqs_max];
   iir_filter under_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_filter base_attack[multicomp_banks][multicomp_freqs_max];
+  iir_filter base_decay[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];
+  iir_state *base_iir[multicomp_freqs_max];
 
+  peak_state *over_peak[multicomp_freqs_max];
+  peak_state *under_peak[multicomp_freqs_max];
+  peak_state *base_peak[multicomp_freqs_max];
+  
   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;
@@ -161,30 +68,31 @@
 
 banked_compand_settings bc[multicomp_banks];
 other_compand_settings c;
-multicompand_state ms;
 
+static multicompand_state ms;
+
 int pull_multicompand_feedback(float **peak,float **rms,int *bands){
   return pull_subband_feedback(&ms.ss,peak,rms,bands);
 }
 
-static sig_atomic_t pending_bank=0;
-static sig_atomic_t reading=0;
 void multicompand_reset(){
-  int h,i,j;
+  int i,j;
   
   subband_reset(&ms.ss);
   
   for(i=0;i<multicomp_freqs_max;i++)
     for(j=0;j<input_ch;j++){
+      memset(&ms.over_peak[i][j],0,sizeof(peak_state));
+      memset(&ms.under_peak[i][j],0,sizeof(peak_state));
+      memset(&ms.base_peak[i][j],0,sizeof(peak_state));
       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));
+      memset(&ms.base_iir[i][j],0,sizeof(iir_state));
     }
 }
 
 int multicompand_load(void){
-  int h,i,j;
+  int h,i;
   int qblocksize=input_size/8;
   memset(&ms,0,sizeof(ms));
 
@@ -195,10 +103,12 @@
                        multicomp_freqs[h]);
     
   for(i=0;i<multicomp_freqs_max;i++){
+    ms.over_peak[i]=calloc(input_ch,sizeof(peak_state));
+    ms.under_peak[i]=calloc(input_ch,sizeof(peak_state));
+    ms.base_peak[i]=calloc(input_ch,sizeof(peak_state));
     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.base_iir[i]=calloc(input_ch,sizeof(iir_state));
   }
 
   ms.active_bank=0;
@@ -206,499 +116,295 @@
   return 0;
 }
 
-int multicompand_filterbank_set(float msec,iir_filter filterbank[multicomp_banks][multicomp_freqs_max]){
+static void multicompand_filter_set(float msec,
+				   iir_filter *filter,
+				   int attackp){
+  float alpha;
+  float corner_freq= 500./msec;
+
+  /* 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)*1.01>corner_freq && attackp)
+    corner_freq=step_freq(input_size*2-ms.ss.qblocksize*3);
+
+  alpha=corner_freq/input_rate;
+  filter->g=mkbessel(alpha,2,filter->c);
+  filter->alpha=alpha;
+  filter->Hz=alpha*input_rate;
+}
+
+
+static int multicompand_filterbank_set(float msec,
+				       iir_filter 
+				       filterbank[multicomp_banks]
+				       [multicomp_freqs_max],
+				       int attackp){
   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(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;
-      
-      /* 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;
-    }
+    for(i=0;i<bands;i++)
+      multicompand_filter_set(msec,filters+i,attackp);
   }
+  return 0;
 }
 
-
 int multicompand_over_attack_set(float msec){
-  multicompand_filterbank_set(msec,ms.over_attack);
+  return multicompand_filterbank_set(msec,ms.over_attack,1);
 }
 
 int multicompand_over_decay_set(float msec){
-  multicompand_filterbank_set(msec,ms.over_decay);  
+  return multicompand_filterbank_set(msec,ms.over_decay,0);  
 }
 
 int multicompand_under_attack_set(float msec){
-  multicompand_filterbank_set(msec,ms.under_attack);
+  return multicompand_filterbank_set(msec,ms.under_attack,1);
 }
 
 int multicompand_under_decay_set(float msec){
-  multicompand_filterbank_set(msec,ms.under_decay);
+  return multicompand_filterbank_set(msec,ms.under_decay,0);
 }
 
-int multicompand_suppress_attack_set(float msec){
-  multicompand_filterbank_set(msec,ms.suppress_attack);
+int multicompand_base_attack_set(float msec){
+  return multicompand_filterbank_set(msec,ms.base_attack,1);
 }
 
-int multicompand_suppress_decay_set(float msec){
-  multicompand_filterbank_set(msec,ms.suppress_decay);
+int multicompand_base_decay_set(float msec){
+  return multicompand_filterbank_set(msec,ms.base_decay,0);
 }
 
-int multicompand_suppress_release_set(float msec){
-  multicompand_filterbank_set(msec,ms.suppress_release);
+static void prepare_rms(float *rms, float *xx, int n, int ahead){
+  int i;
+  float *x=xx+ahead;
+  for(i=0;i<n;i++)
+    rms[i]+=x[i]*x[i];
 }
 
-/* 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;
+static void prepare_peak(float *peak, float *x, int n, int ahead,int hold,
+			 peak_state *ps){
+  int ii,jj;
+  int loc=ps->loc;
+  float val=ps->val;
 
-  if(x[0]>y0)state=0; 
-      
-  while(i<n){
-    
-    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++;
+  /* Although we have two input_size blocks of zeroes after a
+     reset, we may still need to look ahead explicitly after a
+     reset if the lookahead is exceptionally long */
+  if(loc==0 && val==0){
+    for(ii=0;ii<ahead;ii++) 
+      if(fabs(x[ii])>val){
+	val=fabs(x[ii]);
+	loc=ii+hold;
       }
-    }
-
-    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;
+  if(val>peak[0])peak[0]=val;
   
-}
-
-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 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=xx[l];
-      int ii,jj;
-      int loc=0;
-      float val=fabs(x[0]);
-      
-      /* find highest point in next [ahead] */
-      for(ii=1;ii<ahead;ii++)
-	if(fabs(x[ii])>val){
-	  val=fabs(x[ii]);
-	  loc=ii;
-	}
-      if(val>peak[0])peak[0]=val;
-      
-      for(ii=1;ii<n;ii++){
-	if(fabs(x[ii+ahead])>val){
-	  val=fabs(x[ii+ahead]);
-	  loc=ii+ahead;
-	}	  
-	if(ii>=loc){
-	  /* backfill */
-	  val=0;
-	  for(jj=ii+ahead-1;jj>=ii;jj--){
-	    if(fabs(x[jj])>val)val=fabs(x[jj]);
-	    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=1;ii<n;ii++){
+    if(fabs(x[ii+ahead])>val){
+      val=fabs(x[ii+ahead]);
+      loc=ii+ahead+hold;
+    }	  
+    if(ii>=loc){
+      /* backfill */
+      val=0;
+      for(jj=ii+ahead-1;jj>=ii;jj--){
+	if(fabs(x[jj])>val)val=fabs(x[jj]);
+	if(jj<n && val>peak[jj])peak[jj]=val;
       }
+      val=fabs(x[ii+ahead-1]);
+      loc=ii+ahead+hold;
     }
-    
-    for(j=0;j<n;j++)
-      peak[j]=todB_p(peak+j);
+    if(val>peak[ii])peak[ii]=val; 
   }
+
+  ps->loc=loc-input_size;
+  ps->val=val;
+  
 }
 
-static void run_filter_only(float *dB,float *work,float **x,int n,int mode,
+static void run_filter_only(float *dB,int n,int mode,
                             iir_state *iir,iir_filter *attack,iir_filter *decay){
   int i;
-
-  compute_iir(dB, work, n, &iir[i], attack, decay);
-
+  compute_iir2(dB, n, iir, attack, decay);
+  
   if(mode==0)
     for(i=0;i<n;i++)
-      dB[i]=todB_p(dB+i)*.5f;
+      dB[i]=todB_a(dB+i)*.5f;
+  else
+    for(i=0;i<n;i++)
+      dB[i]=todB_a(dB+i);
+
 }
 
-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);
+static void run_filter(float *dB,float *x,int n,
+		       float lookahead,int mode,
+		       iir_state *iir,iir_filter *attack,iir_filter *decay,
+		       peak_state *ps){
 
-      else
-	prepare_rms(work, x, n, ch, impulse_ahead(attack->alpha)*lookahead);
+  memset(dB,0,sizeof(*dB)*n);
+  
+  if(mode)
+    prepare_peak(dB, x, n,
+		 step_ahead(attack->alpha)*lookahead, 
+		 step_ahead(attack->alpha)*(1.-lookahead),
+		 ps);
+  else
+    prepare_rms(dB, x, n, impulse_ahead2(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);
-  }
+  
+  run_filter_only(dB,n,mode,iir,attack,decay);
+}
 
-  run_filter_only(dB,work,x,n,mode,iir,attack,decay);
+static float soft_knee(float x){
+  return (sqrtf(x*x+30.f)+x)*-.5f;
 }
 
-static void over_compand(float ***x){
-  int i,j,k;
-  float work[input_size];
-  float dB[input_size];
+static float hard_knee(float x){
+  return (x>0.f?-x:0.f);
+}
+
+static void over_compand(float *lx,float zerocorner,
+			 iir_filter *attack, iir_filter *decay,
+			 iir_state *iir, peak_state *ps,
+			 float *peakfeed, float *rmsfeed,float *adj,int active){
+  int k;
+  float overdB[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);
-  
   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.);
-	}
-      }
+  run_filter(overdB,lx,input_size,lookahead,mode,iir,attack, decay,ps);
+  
+  if(active){
+    if(c.over_softknee){
+      for(k=0;k<input_size;k++)
+	adj[k]+=soft_knee(overdB[k]-zerocorner)*corner_multiplier;
     }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;
-	}
+      for(k=0;k<input_size;k++)
+	adj[k]+=hard_knee(overdB[k]-zerocorner)*corner_multiplier;
+    }
+  }
+  
+  {
+    /* determine rms and peak for feedback */
+    float max=-1.;
+    int maxpos=-1;
+    float rms=0.;
+    
+    for(k=0;k<input_size;k++){
+      float val=lx[k]*lx[k];
+      if(val>max){
+	max=val;
+	maxpos=k;
       }
+      rms+=val;
     }
+    *peakfeed=todB(max)*.5+adj[maxpos];
+    *rmsfeed=todB(rms/input_size)*.5+adj[maxpos];
   }
+}
 
-  ms.previous_over_mode=mode;
+static void base_compand(float *x,
+			 iir_filter *attack, iir_filter *decay,
+			 iir_state *iir, peak_state *ps,
+			 float *adj,int active){
+  int k;
+  float basedB[input_size];
+  int mode=c.base_mode;
+  float base_multiplier=(1.-1./(c.base_ratio/1000.));
 
-  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);
-	}
-    }
-  }
+  run_filter(basedB,x,input_size,1.,mode,
+	     iir,attack,decay,ps);
+  
+  if(active)
+    for(k=0;k<input_size;k++)
+      adj[k]-=(basedB[k]+adj[k])*base_multiplier;
+  
 }
 
-static void under_compand(float ***x){
-  int i,j,k;
-  float work[input_size];
-  float dB[input_size];
+static void under_compand(float *x,float zerocorner,
+			 iir_filter *attack, iir_filter *decay,
+			 iir_state *iir, peak_state *ps,
+			 float *adj,int active){
+  int k;
+  float underdB[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);
-  
   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.);
-	}
-      }
+  run_filter(underdB,x,input_size,lookahead,mode,
+	     iir,attack,decay,ps);
+  
+  if(active){
+    if(c.under_softknee){
+      for(k=0;k<input_size;k++)
+	adj[k]-=soft_knee(zerocorner-underdB[k])*corner_multiplier;
     }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;
-	}
-      }
+      for(k=0;k<input_size;k++)
+	adj[k]-=hard_knee(zerocorner-underdB[k])*corner_multiplier;
     }
   }
-
-  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);
-    }
-  }
 }
 
-  /* (since this one is kinda unique) The Suppressor....
-
-     Reverberation in a measurably live environment displays
-     log amplitude decay with time (linear decay when plotted on a dB
-     scale).
-
-     In its simplest form, the suppressor follows actual {RMS|peak}
-     amplitude attacks but chooses a slower-than-actual decay, then
-     expands according to the dB distance between the slow and actual
-     decay.
-
-     The 'depth' setting is used to limit the expanded distance
-     between actual and slow decay; it's also used to drag the slow
-     decay down with the actual decay once the expansion has hit the
-     depth limit.
-
-     Thus, the suppressor can be used to 'dry out' a very 'wet'
-     reverberative track. */
-
-static void suppress(float ***x){
+static void multicompand_work(float **peakfeed,float **rmsfeed){
   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;
+  float adj[input_size];
+  float *x;
+  int active=compand_active;
 
-  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;
-	}
-      }
-    }
-  }
-
-  ms.previous_suppress_mode=mode;
-
   for(i=0;i<multicomp_freqs[ms.active_bank];i++){
+
     for(j=0;j<input_ch;j++){
-      float *lx=x[i][j];
+      memset(adj,0,sizeof(adj));
 
-      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);
-	}
-    }
-  }
-}
-
-static void multicompand_work(float **peakfeed,float **rmsfeed){
-  int i,j,k;
-
-  under_compand(ms.ss.lap);
-  over_compand(ms.ss.lap);
-
-  /* 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;
+      if(active || compand_visible){
+	under_compand(ms.ss.lap[i][j],  
+		      bc[ms.active_bank].static_u[i],
+		      &ms.under_attack[ms.active_bank][i],
+		      &ms.under_decay[ms.active_bank][i],
+		      &ms.under_iir[i][j],
+		      &ms.under_peak[i][j],
+		      adj,active);
         
-	for(k=0;k<input_size;k++){
-	  rms+=x[k]*x[k];
-	  if(fabs(x[k])>peak)peak=fabs(x[k]);
-	}	    
+	over_compand(ms.ss.lap[i][j],  
+		     bc[ms.active_bank].static_o[i],
+		     &ms.over_attack[ms.active_bank][i],
+		     &ms.over_decay[ms.active_bank][i],
+		     &ms.over_iir[i][j],
+		     &ms.over_peak[i][j],
+		     &peakfeed[i][j],
+		     &rmsfeed[i][j],
+		     adj,active);
         
-	peakfeed[i][j]=todB(peak);
-	rmsfeed[i][j]=todB(rms/input_size)*.5;
+	base_compand(ms.ss.lap[i][j],  
+		     &ms.base_attack[ms.active_bank][i],
+		     &ms.base_decay[ms.active_bank][i],
+		     &ms.base_iir[i][j],
+		     &ms.base_peak[i][j],
+		     adj,active);
       }
-    }
-  }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];
-	
-	for(k=0;k<input_size;k++){
-	  rms+=x[k]*x[k];
-	  if(fabs(x[k])>peak)peak=fabs(x[k]);
-	}	
-      }    
-	
-      for(j=0;j<input_ch;j++){
-	peakfeed[i][j]=todB(peak);
-	rmsfeed[i][j]=todB(rms/input_size/input_ch)*.5;
+
+      
+      if(active){
+	x=ms.ss.lap[i][j];
+	for(k=0;k<input_size;k++)
+	  x[k]*=fromdB_a(adj[k]);
       }
     }
   }
-
-  suppress(ms.ss.lap);
-  
-  offset+=input_size;
 }
 
 void multicompand_set_bank(int bank){
   ms.pending_bank=bank;
 }
 
-static int bypass_state;
-static int active_state;
 time_linkage *multicompand_read(time_linkage *in){
-  int i,j;
-  int pending=ms.pending_bank;
   int bypass=!(compand_visible||compand_active);
   
-  if(pending!=ms.active_bank || (bypass && !bypass_state)){
-
-  }
-   
   ms.active_bank=ms.pending_bank;
-  bypass_state=bypass;
 
   return subband_read(in,&ms.ss,&ms.sw[ms.active_bank],
                       multicompand_work,bypass);
 }
 
-

Modified: trunk/postfish/multicompand.h
===================================================================
--- trunk/postfish/multicompand.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/multicompand.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -26,8 +26,8 @@
 #define multicomp_freqs_max 30
 #define multicomp_banks 3
 
-static int multicomp_freqs[multicomp_banks]={10,20,30};
-static float multicomp_freq_list[multicomp_banks][multicomp_freqs_max+1]={
+static const int multicomp_freqs[multicomp_banks]={10,20,30};
+static const float multicomp_freq_list[multicomp_banks][multicomp_freqs_max+1]={
   {31.5,63,125,250,500,1000,2000,4000,8000,16000,9e10},
   {31.5,44,63,88,125,175,250,350,500,700,1000,1400,
      2000,2800,4000,5600,8000,11000,16000,22000},
@@ -36,7 +36,7 @@
    8000,10000,12500,16000,20000,9e10}
 };
 
-static char *multicomp_freq_labels[multicomp_banks][multicomp_freqs_max]={
+static char * const multicomp_freq_labels[multicomp_banks][multicomp_freqs_max]={
   {"31.5","63","125","250","500","1k","2k","4k","8k","16k"},
   {"31.5","44","63","88","125","175","250","350","500","700","1k","1.4k",
    "2k","2.8k","4k","5.6k","8k","11k","16k","22k"},
@@ -55,31 +55,24 @@
   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 base_mode;
+  sig_atomic_t base_attack;
+  sig_atomic_t base_decay;
+  sig_atomic_t base_ratio;
+
   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_release;
-
-  sig_atomic_t link_mode;
-
 } other_compand_settings;
 
 extern void multicompand_reset();
@@ -92,6 +85,5 @@
 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);
+extern int multicompand_base_attack_set(float msec);
+extern int multicompand_base_decay_set(float msec);

Modified: trunk/postfish/output.c
===================================================================
--- trunk/postfish/output.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/output.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -31,6 +31,9 @@
 #include "declip.h"
 #include "eq.h"
 #include "multicompand.h"
+#include "singlecomp.h"
+#include "suppress.h"
+#include "limit.h"
 
 extern int input_size;
 sig_atomic_t playback_active=0;
@@ -56,6 +59,9 @@
   declip_reset();  /* clear any persistent lapping state */
   eq_reset();      /* clear any persistent lapping state */
   multicompand_reset(); /* clear any persistent lapping state */
+  singlecomp_reset(); /* clear any persistent lapping state */
+  suppress_reset(); /* clear any persistent lapping state */
+  limit_reset(); /* clear any persistent lapping state */
   output_reset(); /* clear any persistent lapping state */
 }
 
@@ -75,7 +81,7 @@
 }
 
 static void push_output_feedback(float *peak,float *rms){
-  int i,n=input_ch+2;
+  int n=input_ch+2;
   output_feedback *f=(output_feedback *)
     feedback_new(&feedpool,new_output_feedback);
   
@@ -86,7 +92,7 @@
 
 int pull_output_feedback(float *peak,float *rms){
   output_feedback *f=(output_feedback *)feedback_pull(&feedpool);
-  int i,j,n=input_ch+2;
+  int n=input_ch+2;
   if(!f)return 0;
   if(rms)memcpy(rms,f->rms,sizeof(*rms)*n);
   if(peak)memcpy(peak,f->peak,sizeof(*peak)*n);
@@ -207,7 +213,6 @@
   time_linkage *link;
   int result;
   off_t count=0;
-  long last=-1;
 
   int ch=-1;
   long rate=-1;
@@ -231,6 +236,10 @@
     result|=link->samples;
     link=multicompand_read(link);
     result|=link->samples;
+    link=singlecomp_read(link);
+    result|=link->samples;
+    link=suppress_read(link);
+    result|=link->samples;
     link=eq_read(link);
     result|=link->samples;
     
@@ -238,8 +247,7 @@
     /************/
     
     
-    
-    /* temporary; this would be frequency domain in the finished postfish */
+    /* master att */
     if(link->samples>0){
       float scale=fromdB(master_att/10.);
       for(i=0;i<link->samples;i++)
@@ -248,6 +256,9 @@
     }    
 
 
+    /* the limiter is single-block zero additional latency */
+    link=limit_read(link);
+
     /************/
 
     if(link->samples>0){
@@ -280,7 +291,6 @@
 
       for(k=0,i=0;i<link->samples;i++){
         float mean=0.;
-	float div=0.;
         float divrms=0.;
         
         for(j=0;j<link->channels;j++){

Modified: trunk/postfish/postfish-gtkrc
===================================================================
--- trunk/postfish/postfish-gtkrc	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/postfish-gtkrc	2004-04-14 04:50:38 UTC (rev 6506)
@@ -78,7 +78,7 @@
 
         text[NORMAL]="#707070" 
         text[ACTIVE]="#905050" 
-        font_name = "sans 6"    
+        font_name = "sans 7"    
 }
 
 style "clipbar" {
@@ -98,6 +98,15 @@
         font_name = "Fixed, Nimbus Mono L, Courier, Monospace 10"	
 }
 
+style "small-readout" {
+	base[NORMAL]="#ffffff" 
+	base[ACTIVE]="#ffffff" 
+	bg[NORMAL]="#ffffff" 
+	bg[ACTIVE]="#ffffff" 
+	text[NORMAL]="#606060"
+	font_name = "Fixed, Nimbus Mono L, Courier, Monospace 8"	
+}
+
 style "darkpanel" {
         bg[NORMAL]="#b0b0b0" 
 }
@@ -160,6 +169,7 @@
 widget "*.framelabel" style "frame-label"
 
 widget "*.Readout*" style "readout"
+widget "*.smallreadout" style "small-readout"
 widget "*.GtkEntry" style "readout"
 widget "*.GtkHScale" style "slider"
 widget "*.GtkToggleButton*" style "button-poppy"

Modified: trunk/postfish/postfish.h
===================================================================
--- trunk/postfish/postfish.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/postfish.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -50,8 +50,37 @@
 #include <signal.h>
 #include <fcntl.h>
 
-#define todB(x)   ((x)==0?-400.f:log((x)*(x))*4.34294480f)
-#define fromdB(x) (exp((x)*.11512925f))  
+static inline float todB(float x){
+  return logf((x)*(x)+1e-30f)*4.34294480f;
+}
+
+static inline float fromdB(float x){
+  return expf((x)*.11512925f);
+}
+
+#ifdef UGLY_IEEE754_FLOAT32_HACK
+
+static inline float todB_a(const float *x){
+  return (float)((*(int32_t *)x)&0x7fffffff) * 7.1771144e-7f -764.27118f;
+}
+
+static inline float fromdB_a(float x){
+  int y=1.3933e+06f*(x+764.27118f);
+  return *(float *)&y;
+}
+
+#else
+
+static inline float todB_a(const float *x){
+  return todB(*x);
+}
+
+static inline float fromdB_a(float x){
+  return fromdB(x);
+}
+
+#endif
+
 #define toOC(n)     (log(n)*1.442695f-5.965784f)
 #define fromOC(o)   (exp(((o)+5.965784f)*.693147f))
 #define toBark(n)   (13.1f*atan(.00074f*(n))+2.24f*atan((n)*(n)*1.85e-8f)+1e-4f*(n))

Modified: trunk/postfish/readout.c
===================================================================
--- trunk/postfish/readout.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/readout.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -103,7 +103,7 @@
   Readout *r=READOUT(widget);
   
   if (r->backing)
-    gdk_drawable_unref(r->backing);
+    g_object_unref(r->backing);
   
   r->backing = gdk_pixmap_new(widget->window,
                               widget->allocation.width,

Modified: trunk/postfish/readout.h
===================================================================
--- trunk/postfish/readout.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/readout.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -30,6 +30,7 @@
 #include <glib-object.h>
 #include <gtk/gtkcontainer.h>
 #include <gtk/gtkdrawingarea.h>
+#include <gdk/gdkdrawable.h>
 
 G_BEGIN_DECLS
 

Modified: trunk/postfish/reconstruct.c
===================================================================
--- trunk/postfish/reconstruct.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/reconstruct.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -30,6 +30,7 @@
 
 #include <string.h>
 #include <fftw3.h>
+#include <math.h>
 #include "reconstruct.h"
 
 /* fftw3 requires this kind of static setup */
@@ -39,7 +40,7 @@
 static fftwf_plan fftwf_sb;
 static float *q;
 static float *s;
-static blocksize=0;
+static int blocksize=0;
 
 void reconstruct_reinit(int n){
   if(blocksize!=n){

Added: trunk/postfish/rexperiment.c
===================================================================
--- trunk/postfish/rexperiment.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/rexperiment.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,295 @@
+/*
+ *
+ *  postfish
+ *    
+ *      Copyright (C) 2002-2004 Monty and Xiph.Org
+ *
+ *  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.
+ *
+ * 
+ */
+
+/* arbitrary reconstruction filter.  Postfish uses this for declipping.
+
+   Many thanks to Johnathan Richard Shewchuk and his excellent paper
+   'An Introduction to the Conjugate Gradient Method Without the
+   Agonizing Pain' for the additional understanding needed to make the
+   n^3 -> n^2 log n jump possible. Google for it, you'll find it. */
+
+#include <string.h>
+#include "smallft.h"
+
+static void drft_forward_transpose(drft_lookup *fft, double *x){
+  int i;
+  
+  for(i=1;i<fft->n-1;i++)x[i]*=.5;
+  drft_backward(fft,x);
+}
+
+static void drft_backward_transpose(drft_lookup *fft, double *x){
+  int i;
+  
+  drft_forward(fft,x);
+  for(i=1;i<fft->n-1;i++)x[i]*=2.;
+}
+
+#include "postfish.h"
+#include "test.h"
+
+static void sliding_bark_average(double *f,double *w, int n){
+  int lo=0,hi=0,i;
+  double acc=0.;
+
+  {
+    double bark=toBark(0);
+    int newhi=rint(fromBark(bark+.5)*n/44100.)+5;
+    acc+=fabs(f[0]);
+    for(hi=1;hi<newhi;hi++)acc+=hypot(f[(hi<<1)-1],f[hi<<1]);
+    w[0]=acc/hi;
+  }
+
+  for(i=1;i<n/2;i++){
+    double bark=toBark(44100.*i/n);
+    int newlo=rint(fromBark(bark-.5)*n/44100.)-5;
+    int newhi=rint(fromBark(bark+.5)*n/44100.)+5;
+    if(newhi>n/2)newhi=n/2;
+
+    for(;hi<newhi;hi++)
+      acc+=hypot(f[(hi<<1)-1],f[hi<<1]);
+    for(;lo<newlo;lo++)
+      if(lo==0)
+        acc-=fabs(f[0]);
+      else
+        acc-=hypot(f[(lo<<1)-1],f[lo<<1]);
+
+    w[(i<<1)-1]=w[i<<1]=acc/(hi-lo);
+  }
+  w[n-1]=w[n-2];
+}
+
+void _analysis(char *base,int i,double *v,int n,int bark,int dB){
+  int j;
+  FILE *of;
+  char buffer[80];
+
+  sprintf(buffer,"%s_%d.m",base,i);
+  of=fopen(buffer,"w");
+  
+  if(!of)perror("failed to open data dump file");
+  
+  for(j=0;j<n;j++){
+    if(bark){
+      float b=toBark((4000.f*j/n)+.25);
+      fprintf(of,"%f ",b);
+    }else
+      fprintf(of,"%f ",(double)j);
+    
+    if(dB){
+      if(j==0||j==n-1)
+        fprintf(of,"%f\n",todB(v[j]));
+      else{
+        fprintf(of,"%f\n",todB(hypot(v[j],v[j+1])));
+        j++;
+      }
+    }else{
+      fprintf(of,"%f\n",v[j]);
+    }
+  }
+  fclose(of);
+}
+
+static double inner_product(double *a, double *b, int n){
+  int i;
+  double acc=0.;
+  for(i=0;i<n;i++)acc+=a[i]*b[i];
+  return acc;
+}
+
+static void compute_AtAx(drft_lookup *fft,
+			 double *x,double *w,int *flag,int mask,int n,
+			 double *out){
+  int i;
+
+  if(mask){
+    for(i=0;i<n;i++)
+      if(!flag[i])
+	out[i]=0;
+      else
+	out[i]=x[i];
+  }else
+    for(i=0;i<n;i++)
+      if(flag[i])
+	out[i]=0;
+      else
+	out[i]=x[i];
+  
+  drft_forward(fft,out);
+  for(i=0;i<n;i++)out[i]*=w[i];
+  drft_backward(fft,out);
+
+  for(i=0;i<n;i++)
+    if(!flag[i])out[i]=0;
+  
+}
+
+static void compute_Atb_minus_AtAx(drft_lookup *fft,
+				   double *x,double *w,double *Atb,int *flag,
+				   int n,double *out){
+  int i;
+  compute_AtAx(fft,x,w,flag,1,n,out);
+  for(i=0;i<n;i++)out[i]=Atb[i]-out[i];
+}
+
+void reconstruct(drft_lookup *fft,
+		 double *x, double *w, int *flag, double e,int max,int n){
+  int i,j;
+  double Atb[n];
+  double r[n];
+  double d[n];
+  double q[n];
+  double phi_new,phi_old,phi_0;
+  double alpha,beta;
+
+  /* compute initial Atb */
+  compute_AtAx(fft,x,w,flag,0,n,Atb);
+  for(j=0;j<n;j++)Atb[j]= -Atb[j];
+
+  compute_Atb_minus_AtAx(fft,x,w,Atb,flag,n,r);
+  memcpy(d,r,sizeof(d));
+  phi_0=phi_new=inner_product(r,r,n);
+
+  for(i=0;i<max && phi_new>e*e*phi_0;i++){
+    compute_AtAx(fft,d,w,flag,1,n,q);
+    alpha=phi_new/inner_product(d,q,n);
+    for(j=0;j<n;j++)x[j]+=alpha*d[j];
+
+    _analysis("x",i,x,512,0,0);
+    {
+      double freq[n];
+      memcpy(freq,x,sizeof(freq));
+      drft_forward(fft,freq);
+      _analysis("f",i,freq,512,1,1);
+    }
+
+    if((i & 0x3f)==0x3f)
+      compute_Atb_minus_AtAx(fft,x,w,Atb,flag,n,r);
+    else
+      for(j=0;j<n;j++)r[j]-=alpha*q[j];
+    
+    phi_old=phi_new;
+    phi_new=inner_product(r,r,n);
+    beta=phi_new/phi_old;
+    for(j=0;j<n;j++) d[j]=r[j]+beta*d[j];
+  }
+}
+
+void precondition(drft_lookup *fft,double *x, 
+		  double *w, int *flag, int n,double *out){
+  int i;
+  memcpy(out,x,sizeof(*x)*n);
+  for(i=0;i<n;i++)
+    if(!flag[i])out[i]=0;
+  drft_backward_transpose(fft,out);
+  for(i=0;i<n;i++)out[i]/=w[i]*n;  
+  drft_backward(fft,out);
+  for(i=0;i<n;i++)out[i]/=n;  
+  for(i=0;i<n;i++)
+    if(!flag[i])out[i]=0;
+}
+
+void reconstruct2(drft_lookup *fft,
+		 double *x, double *w, int *flag, double e,int max,int n){
+  int i,j;
+  double Atb[n];
+  double r[n];
+  double s[n];
+  double d[n];
+  double q[n];
+  double phi_new,phi_old,phi_0;
+  double alpha,beta;
+
+  /* compute initial Atb */
+  compute_AtAx(fft,x,w,flag,0,n,Atb);
+  for(j=0;j<n;j++)Atb[j]= -Atb[j];
+
+  compute_Atb_minus_AtAx(fft,x,w,Atb,flag,n,r);
+  precondition(fft,r,w,flag,n,d);
+  phi_0=phi_new=inner_product(r,d,n);
+
+  for(i=0;i<max && phi_new>e*e*phi_0;i++){
+    compute_AtAx(fft,d,w,flag,1,n,q);
+    alpha=phi_new/inner_product(d,q,n);
+    for(j=0;j<n;j++)x[j]+=alpha*d[j];
+
+    _analysis("x1",i,x,512,0,0);
+    {
+      double freq[n];
+      memcpy(freq,x,sizeof(freq));
+      drft_forward(fft,freq);
+      _analysis("f1",i,freq,512,1,1);
+    }
+
+    if((i & 0x3f)==0x3f)
+      compute_Atb_minus_AtAx(fft,x,w,Atb,flag,n,r);
+    else
+      for(j=0;j<n;j++)r[j]-=alpha*q[j];
+    
+    precondition(fft,r,w,flag,n,s);
+    phi_old=phi_new;
+    phi_new=inner_product(r,s,n);
+    beta=phi_new/phi_old;
+    for(j=0;j<n;j++) d[j]=s[j]+beta*d[j];
+  }
+}
+
+int main(){
+  int i,j,k;
+  drft_lookup fft;
+  int blocksize=512;
+  double window[512];
+  double work[512];
+  double freq[blocksize];
+  double w[blocksize];
+  int flag[512];
+
+  drft_init(&fft,512);
+  for(i=0;i<blocksize;i++) window[i]=sin( (double)i/blocksize*M_PIl);
+  for(i=0;i<blocksize;i++) window[i]*=window[i];
+  for(i=0;i<blocksize;i++) window[i]=sin(window[i]*M_PIl*.5);
+
+  memcpy(work,testdata[0],sizeof(work));
+
+  for(i=0;i<blocksize;i++){
+    flag[i]=0;
+    if(work[i]>=.2 || work[i]<=-.2)flag[i]=1;
+  }
+
+  for(i=0;i<512;i++)work[i]*=window[i];
+
+  for(i=0;i<512;i++)_analysis("pcm",i,work,512,0,0);
+  memcpy(freq,work,sizeof(work));
+  drft_forward(&fft,freq);
+  for(i=0;i<512;i++)_analysis("freq",i,freq,512,1,1);
+
+  sliding_bark_average(freq,w,blocksize);
+  _analysis("w",0,w,512,1,1);
+
+  for(i=0;i<512;i++)w[i]= 1./(w[i]*w[i]);
+
+  reconstruct2(&fft,work,w,flag,0,blocksize,blocksize);
+
+}
+
+

Added: trunk/postfish/singlecomp.c
===================================================================
--- trunk/postfish/singlecomp.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/singlecomp.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,521 @@
+/*
+ *
+ *  postfish
+ *    
+ *      Copyright (C) 2002-2004 Monty and Xiph.Org
+ *
+ *  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.
+ *
+ * 
+ */
+
+#include "postfish.h"
+#include "feedback.h"
+#include "bessel.h"
+#include "singlecomp.h"
+
+extern int input_size;
+extern int input_rate;
+extern int input_ch;
+
+sig_atomic_t singlecomp_active;
+sig_atomic_t singlecomp_visible;
+
+typedef struct {
+  int loc;
+  float val;
+} peak_state;
+
+typedef struct{
+  time_linkage out;
+  feedback_generic_pool feedpool;
+
+  iir_state *o_iir;
+  iir_state *u_iir;
+  iir_state *b_iir;
+
+  peak_state *o_peak;
+  peak_state *u_peak;
+  peak_state *b_peak;
+
+  iir_filter o_attack;
+  iir_filter o_decay;
+  iir_filter u_attack;
+  iir_filter u_decay;
+  iir_filter b_attack;
+  iir_filter b_decay;
+
+  int fillstate;
+  float **cache;
+  int cache_samples;
+
+} singlecomp_state;
+
+singlecomp_settings scset;
+singlecomp_state scs;
+
+static void _analysis(char *base,int i,float *v,int n,int dB,int offset){
+  int j;
+  FILE *of;
+  char buffer[80];
+
+  sprintf(buffer,"%s_%d.m",base,i);
+  of=fopen(buffer,"a");
+  
+  if(!of)perror("failed to open data dump file");
+  
+  for(j=0;j<n;j++){
+    fprintf(of,"%f ",(float)j+offset);
+    if(dB)
+      fprintf(of,"%f\n",todB(v[j]));
+    else
+      fprintf(of,"%f\n",(v[j]));
+  }
+  fprintf(of,"\n");
+  fclose(of);
+}
+
+static int offset=0;
+
+
+/* feedback! */
+typedef struct singlecomp_feedback{
+  feedback_generic parent_class;
+  float *peak;
+  float *rms;
+} singlecomp_feedback;
+
+static feedback_generic *new_singlecomp_feedback(void){
+  singlecomp_feedback *ret=calloc(1,sizeof(*ret));
+  return (feedback_generic *)ret;
+}
+
+int pull_singlecomp_feedback(float *peak,float *rms){
+  singlecomp_feedback *f=(singlecomp_feedback *)feedback_pull(&scs.feedpool);
+  
+  if(!f)return 0;
+  
+  if(peak)
+    memcpy(peak,f->peak,sizeof(*peak)*input_ch);
+  if(rms)
+    memcpy(rms,f->rms,sizeof(*rms)*input_ch);
+  feedback_old(&scs.feedpool,(feedback_generic *)f);
+  return 1;
+}
+
+/* called only by initial setup */
+int singlecomp_load(void){
+  int i;
+  memset(&scs,0,sizeof(scs));
+
+  scs.o_iir=calloc(input_ch,sizeof(*scs.o_iir));
+  scs.b_iir=calloc(input_ch,sizeof(*scs.b_iir));
+  scs.u_iir=calloc(input_ch,sizeof(*scs.u_iir));
+
+  scs.o_peak=calloc(input_ch,sizeof(*scs.o_peak));
+  scs.b_peak=calloc(input_ch,sizeof(*scs.b_peak));
+  scs.u_peak=calloc(input_ch,sizeof(*scs.u_peak));
+
+  scs.out.size=input_size;
+  scs.out.channels=input_ch;
+  scs.out.rate=input_rate;
+  scs.out.data=malloc(input_ch*sizeof(*scs.out.data));
+  for(i=0;i<input_ch;i++)
+    scs.out.data[i]=malloc(input_size*sizeof(**scs.out.data));
+
+  scs.fillstate=0;
+  scs.cache=malloc(input_ch*sizeof(*scs.cache));
+  for(i=0;i<input_ch;i++)
+    scs.cache[i]=malloc(input_size*sizeof(**scs.cache));
+
+  return(0);
+}
+
+static void filter_set(float msec,
+                       iir_filter *filter,
+                       int attackp){
+  float alpha;
+  float corner_freq= 500./msec;
+  
+  /* make sure the chosen frequency doesn't require a lookahead
+     greater than what's available */
+  if(step_freq(input_size)*1.01>corner_freq && attackp)
+    corner_freq=step_freq(input_size);
+  
+  alpha=corner_freq/input_rate;
+  filter->g=mkbessel(alpha,2,filter->c);
+  filter->alpha=alpha;
+  filter->Hz=alpha*input_rate;
+  filter->ms=msec;
+}
+
+/* called only in playback thread */
+int singlecomp_reset(void ){
+  /* reset cached pipe state */
+  scs.fillstate=0;
+  while(pull_singlecomp_feedback(NULL,NULL));
+
+  memset(scs.o_peak,0,input_ch*sizeof(&scs.o_peak));
+  memset(scs.u_peak,0,input_ch*sizeof(&scs.u_peak));
+  memset(scs.b_peak,0,input_ch*sizeof(&scs.b_peak));
+  memset(scs.o_iir,0,input_ch*sizeof(&scs.o_iir));
+  memset(scs.u_iir,0,input_ch*sizeof(&scs.u_iir));
+  memset(scs.b_iir,0,input_ch*sizeof(&scs.b_iir));
+  return 0;
+}
+
+static void prepare_peak(float *peak, float *x, int n, int ahead,int hold,
+                         peak_state *ps){
+  int ii,jj;
+  int loc=ps->loc;
+  float val=ps->val;
+
+  /* Although we have two input_size blocks of zeroes after a
+     reset, we may still need to look ahead explicitly after a
+     reset if the lookahead is exceptionally long */
+
+  if(loc==0 && val==0){
+    for(ii=0;ii<ahead;ii++) 
+      if(fabs(x[ii])>val){
+        val=fabs(x[ii]);
+        loc=ii+hold;
+      }
+  }
+  
+  if(val>peak[0])peak[0]=val;
+  
+  for(ii=1;ii<n;ii++){
+    if(fabs(x[ii+ahead])>val){
+      val=fabs(x[ii+ahead]);
+      loc=ii+ahead+hold;
+    }     
+    if(ii>=loc){
+      /* backfill */
+      val=0;
+      for(jj=ii+ahead-1;jj>=ii;jj--){
+        if(fabs(x[jj])>val)val=fabs(x[jj]);
+        if(jj<n && val>peak[jj])peak[jj]=val;
+      }
+      val=fabs(x[ii+ahead-1]);
+      loc=ii+ahead+hold;
+    }
+    if(val>peak[ii])peak[ii]=val; 
+  }
+
+  ps->loc=loc-input_size;
+  ps->val=val;
+}
+
+static void run_filter(float *cache, float *in, float *work,
+                       int ahead,int hold,int mode,
+                       iir_state *iir,iir_filter *attack,iir_filter *decay,
+                       peak_state *ps){
+  int k;
+  float *work2=work+input_size;
+
+  if(mode){
+    /* peak mode */
+    memcpy(work,cache,sizeof(*work)*input_size);
+    memcpy(work2,in,sizeof(*work)*input_size);
+
+    prepare_peak(work, work, input_size, ahead, hold, ps);
+
+  }else{
+    /* rms mode */
+    float *cachea=cache+ahead;
+    float *worka=work+input_size-ahead;
+    
+    for(k=0;k<input_size-ahead;k++)
+      work[k]=cachea[k]*cachea[k];
+    
+    for(k=0;k<ahead;k++)
+      worka[k]=in[k]*in[k];    
+  }
+  
+  compute_iir2(work, input_size, iir, attack, decay);
+  
+  if(mode==0)
+    for(k=0;k<input_size;k++)
+      work[k]=todB_a(work+k)*.5f;
+  else
+    for(k=0;k<input_size;k++)
+      work[k]=todB_a(work+k);
+}
+
+static float soft_knee(float x){
+  return (sqrtf(x*x+30.f)+x)*-.5f;
+}
+
+static float hard_knee(float x){
+  return (x>0.f?-x:0.f);
+}
+
+static void over_compand(float *A,float *B,float *adj,
+			 float zerocorner,float multiplier,
+			 float lookahead,int mode,int softknee,
+                         iir_filter *attack, iir_filter *decay,
+                         iir_state *iir, peak_state *ps,
+			 int active){
+  
+  int k;
+  float work[input_size*2];
+  int ahead=(mode?step_ahead(attack->alpha):impulse_ahead2(attack->alpha));
+  int hold=(1.-lookahead)*ahead;
+  ahead-=hold;
+
+  run_filter(A,B,work,ahead,hold,mode,iir,attack,decay,ps);
+  
+  if(active){
+    if(softknee){
+      for(k=0;k<input_size;k++)
+        adj[k]+=soft_knee(work[k]-zerocorner)*multiplier;
+    }else{
+      for(k=0;k<input_size;k++)
+        adj[k]+=hard_knee(work[k]-zerocorner)*multiplier;
+    }
+  }
+}
+
+static void under_compand(float *A,float *B,float *adj,
+			  float zerocorner,float multiplier,
+			  float lookahead,int mode,int softknee,
+			  iir_filter *attack, iir_filter *decay,
+			  iir_state *iir, peak_state *ps,
+			  int active){
+  int k;
+  float work[input_size*2];
+  int ahead=(mode?step_ahead(attack->alpha):impulse_ahead2(attack->alpha));
+  int hold=(1.-lookahead)*ahead;
+  ahead-=hold;
+  
+  run_filter(A,B,work,ahead,hold,mode,iir,attack,decay,ps);
+
+  if(active){
+    if(softknee){
+      for(k=0;k<input_size;k++)
+        adj[k]= -soft_knee(zerocorner-work[k])*multiplier;
+    }else{
+      for(k=0;k<input_size;k++)
+        adj[k]= -hard_knee(zerocorner-work[k])*multiplier;
+    }
+  }else
+    memset(adj,0,sizeof(*adj)*input_size);
+
+}
+
+static void base_compand(float *A,float *B,float *adj,
+			 float multiplier,int mode,
+			 iir_filter *attack, iir_filter *decay,
+			 iir_state *iir, peak_state *ps,
+			 int active){
+  
+  int k;
+  float work[input_size*2];
+
+  int ahead=(mode?step_ahead(attack->alpha):impulse_ahead2(attack->alpha));
+
+  run_filter(A,B,work,ahead,0,mode,iir,attack,decay,ps);
+
+  if(active)
+    for(k=0;k<input_size;k++)
+      adj[k]-=(work[k]+adj[k])*multiplier;
+
+}
+
+time_linkage *singlecomp_read(time_linkage *in){
+  float peakfeed[input_ch];
+  float rmsfeed[input_ch];
+
+  int active=singlecomp_active;
+  int i;
+
+  float o_attackms=scset.o_attack*.1;
+  float o_decayms=scset.o_decay*.1;
+  float u_attackms=scset.u_attack*.1;
+  float u_decayms=scset.u_decay*.1;
+  float b_attackms=scset.b_attack*.1;
+  float b_decayms=scset.b_decay*.1;
+
+  if(o_attackms!=scs.o_attack.ms)filter_set(o_attackms,&scs.o_attack,1);
+  if(o_decayms!=scs.o_decay.ms)filter_set(o_decayms,&scs.o_decay,0);
+  if(u_attackms!=scs.u_attack.ms)filter_set(u_attackms,&scs.u_attack,1);
+  if(u_decayms!=scs.u_decay.ms)filter_set(u_decayms,&scs.u_decay,0);
+  if(b_attackms!=scs.b_attack.ms)filter_set(b_attackms,&scs.b_attack,1);
+  if(b_decayms!=scs.b_decay.ms)filter_set(b_decayms,&scs.b_decay,0);
+  
+  switch(scs.fillstate){
+  case 0: /* prime the cache */
+    if(in->samples==0){
+      scs.out.samples=0;
+      return &scs.out;
+    }
+    for(i=0;i<input_ch;i++){
+      float *temp=in->data[i];
+      float adj[input_size]; // under will set it
+
+      memset(scs.o_iir+i,0,sizeof(*scs.o_iir));
+      memset(scs.u_iir+i,0,sizeof(*scs.u_iir));
+      memset(scs.b_iir+i,0,sizeof(*scs.b_iir));
+      memset(scs.cache[i],0,sizeof(**scs.cache)*input_size);
+
+      under_compand(scs.cache[i],in->data[i],adj,
+		    (float)(scset.u_thresh),
+		    1.-1./(scset.u_ratio/1000.),
+		    scset.u_lookahead/1000.,
+		    scset.u_mode,
+		    scset.u_softknee,
+		    &scs.u_attack,&scs.u_decay,
+		    scs.u_iir+i,scs.u_peak+i,
+		    active);
+      
+      over_compand(scs.cache[i],in->data[i],adj,
+		   (float)(scset.o_thresh),
+		   1.-1./(scset.o_ratio/1000.),
+		   scset.o_lookahead/1000.,
+		   scset.o_mode,
+		   scset.o_softknee,
+		   &scs.o_attack,&scs.o_decay,
+		   scs.o_iir+i,scs.o_peak+i,
+		   active);
+
+      base_compand(scs.cache[i],in->data[i],adj,
+		   1.-1./(scset.b_ratio/1000.),
+		   scset.b_mode,
+		   &scs.b_attack,&scs.b_decay,
+		   scs.b_iir+i,scs.b_peak+i,
+		   active);
+
+
+      in->data[i]=scs.cache[i];
+      scs.cache[i]=temp;
+    }
+    scs.cache_samples=in->samples;
+    scs.fillstate=1;
+    scs.out.samples=0;
+    if(in->samples==in->size)goto tidy_up;
+    
+    for(i=0;i<input_ch;i++)
+      memset(in->data[i],0,sizeof(**in->data)*in->size);
+    in->samples=0;
+    /* fall through */
+  case 1: /* nominal processing */
+
+    for(i=0;i<input_ch;i++){
+      
+      float adj[input_size]; // under will set it
+      
+      under_compand(scs.cache[i],in->data[i],adj,
+		    (float)(scset.u_thresh),
+		    1.-1./(scset.u_ratio/1000.),
+		    scset.u_lookahead/1000.,
+		    scset.u_mode,
+		    scset.u_softknee,
+		    &scs.u_attack,&scs.u_decay,
+		    scs.u_iir+i,scs.u_peak+i,
+		    active);
+      
+      over_compand(scs.cache[i],in->data[i],adj,
+		   (float)(scset.o_thresh),
+		   1.-1./(scset.o_ratio/1000.),
+		   scset.o_lookahead/1000.,
+		   scset.o_mode,
+		   scset.o_softknee,
+		   &scs.o_attack,&scs.o_decay,
+		   scs.o_iir+i,scs.o_peak+i,
+		   active);
+
+      /* feedback before base */
+      {
+	int k;
+	float rms=0.;
+	float peak=0.;
+        float *x=scs.cache[i];
+
+	for(k=0;k<input_size;k++){
+	  float mul=fromdB_a(adj[k]);
+	  float val=x[k]*mul;
+	  
+	  val*=val;
+	  rms+= val;
+	  if(peak<val)peak=val;
+
+	}
+
+	peakfeed[i]=todB_a(&peak)*.5;
+	rms/=input_size;
+	rmsfeed[i]=todB_a(&rms)*.5;
+      }
+      
+      base_compand(scs.cache[i],in->data[i],adj,
+		   1.-1./(scset.b_ratio/1000.),
+		   scset.b_mode,
+		   &scs.b_attack,&scs.b_decay,
+		   scs.b_iir+i,scs.b_peak+i,
+		   active);
+
+      if(active){
+	int k;
+        float *x=scs.cache[i];
+        float *out=scs.out.data[i];
+
+        for(k=0;k<input_size;k++)
+          out[k]=x[k]*fromdB_a(adj[k]);
+      }else
+	memcpy(scs.out.data[i],scs.cache[i],input_size*sizeof(*scs.cache[i]));
+
+      {
+	float *temp=scs.cache[i];
+	scs.cache[i]=in->data[i];
+	in->data[i]=temp;
+      }
+    }
+    scs.out.samples=scs.cache_samples;
+    scs.cache_samples=in->samples;
+    if(scs.out.samples<scs.out.size)scs.fillstate=2;
+    break;
+  case 2: /* we've pushed out EOF already */
+    scs.out.samples=0;
+  }
+  
+  /* finish up the state feedabck */
+  {
+    singlecomp_feedback *ff=
+      (singlecomp_feedback *)feedback_new(&scs.feedpool,new_singlecomp_feedback);
+    
+    if(!ff->peak)
+      ff->peak=malloc(input_ch*sizeof(*ff->peak));
+    
+    if(!ff->rms)
+      ff->rms=malloc(input_ch*sizeof(*ff->rms));
+
+    memcpy(ff->peak,peakfeed,sizeof(peakfeed));
+    memcpy(ff->rms,rmsfeed,sizeof(rmsfeed));
+
+    feedback_push(&scs.feedpool,(feedback_generic *)ff);
+  }
+   
+ tidy_up:
+  {
+    int tozero=scs.out.size-scs.out.samples;
+    if(tozero)
+      for(i=0;i<scs.out.channels;i++)
+        memset(scs.out.data[i]+scs.out.samples,0,sizeof(**scs.out.data)*tozero);
+  }
+
+  offset+=input_size;
+
+  return &scs.out;
+}
+

Added: trunk/postfish/singlecomp.h
===================================================================
--- trunk/postfish/singlecomp.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/singlecomp.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,54 @@
+/*
+ *
+ *  postfish
+ *    
+ *      Copyright (C) 2002-2004 Monty and Xiph.Org
+ *
+ *  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.
+ *
+ * 
+ */
+
+#include "postfish.h"
+
+typedef struct {
+  sig_atomic_t o_attack;
+  sig_atomic_t o_decay;
+  sig_atomic_t u_attack;
+  sig_atomic_t u_decay;
+  sig_atomic_t b_attack;
+  sig_atomic_t b_decay;
+
+  sig_atomic_t o_thresh;
+  sig_atomic_t o_ratio;
+  sig_atomic_t o_lookahead;
+  sig_atomic_t o_mode;
+  sig_atomic_t o_softknee;
+
+  sig_atomic_t u_thresh;
+  sig_atomic_t u_ratio;
+  sig_atomic_t u_lookahead;
+  sig_atomic_t u_mode;
+  sig_atomic_t u_softknee;
+
+  sig_atomic_t b_ratio;
+  sig_atomic_t b_mode;
+
+} singlecomp_settings;
+
+extern int pull_singlecomp_feedback(float *peak,float *rms);
+extern int singlecomp_load(void);
+extern int singlecomp_reset(void);
+extern time_linkage *singlecomp_read(time_linkage *in);

Added: trunk/postfish/singlepanel.c
===================================================================
--- trunk/postfish/singlepanel.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/singlepanel.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,582 @@
+/*
+ *
+ *  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.
+ *
+ * 
+ */
+
+#include "postfish.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "readout.h"
+#include "multibar.h"
+#include "mainpanel.h"
+#include "subpanel.h"
+#include "feedback.h"
+#include "singlecomp.h"
+#include "singlepanel.h"
+
+extern sig_atomic_t singlecomp_active;
+extern sig_atomic_t singlecomp_visible;
+extern int input_ch;
+extern int input_size;
+extern int input_rate;
+
+extern singlecomp_settings scset;
+
+typedef struct {
+  GtkWidget *r0;
+  GtkWidget *r1;
+} multireadout;
+
+GtkWidget *t_label;
+GtkWidget *t_slider;
+multireadout t_readout;
+
+
+static void compand_change(GtkWidget *w,Readout *r,sig_atomic_t *var){
+  char buffer[80];
+  float val=1./multibar_get_value(MULTIBAR(w),0);
+
+  if(val==1.){
+    sprintf(buffer,"   off");
+  }else 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);
+  
+  *var=rint(val*1000.);
+}
+static void under_compand_change(GtkWidget *w,gpointer in){
+  compand_change(w,(Readout *)in,&scset.u_ratio);
+}
+
+static void over_compand_change(GtkWidget *w,gpointer in){
+  compand_change(w,(Readout *)in,&scset.o_ratio);
+}
+
+static void base_compand_change(GtkWidget *w,gpointer in){
+  compand_change(w,(Readout *)in,&scset.b_ratio);
+}
+
+static void timing_display(GtkWidget *w,GtkWidget *r,float v){
+  char buffer[80];
+
+  if(v<10){
+    sprintf(buffer,"%4.2fms",v);
+  }else if(v<100){
+    sprintf(buffer,"%4.1fms",v);
+  }else if (v<1000){
+    sprintf(buffer,"%4.0fms",v);
+  }else if (v<10000){
+    sprintf(buffer,"%4.2fs",v/1000.);
+  }else{
+    sprintf(buffer,"%4.1fs",v/1000.);
+  }
+
+  readout_set(READOUT(r),buffer);
+}
+
+static void under_timing_change(GtkWidget *w,gpointer in){
+  multireadout *r=(multireadout *)in;
+  float attack=multibar_get_value(MULTIBAR(w),0);
+  float decay=multibar_get_value(MULTIBAR(w),1);
+
+  timing_display(w,r->r0,attack);
+  timing_display(w,r->r1,decay);
+
+  scset.u_attack=rint(attack*10.);
+  scset.u_decay=rint(decay*10.);
+}
+
+static void over_timing_change(GtkWidget *w,gpointer in){
+  multireadout *r=(multireadout *)in;
+  float attack=multibar_get_value(MULTIBAR(w),0);
+  float decay=multibar_get_value(MULTIBAR(w),1);
+
+  timing_display(w,r->r0,attack);
+  timing_display(w,r->r1,decay);
+
+  scset.o_attack=rint(attack*10.);
+  scset.o_decay=rint(decay*10.);
+}
+
+static void base_timing_change(GtkWidget *w,gpointer in){
+  multireadout *r=(multireadout *)in;
+  float attack=multibar_get_value(MULTIBAR(w),0);
+  float decay=multibar_get_value(MULTIBAR(w),1);
+
+  timing_display(w,r->r0,attack);
+  timing_display(w,r->r1,decay);
+
+  scset.b_attack=rint(attack*10.);
+  scset.b_decay=rint(decay*10.);
+}
+
+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,"%3.0f%%",val);
+  readout_set(r,buffer);
+  
+  scset.u_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,"%3.0f%%",val);
+  readout_set(r,buffer);
+  
+  scset.o_lookahead=rint(val*10.);
+}
+
+static void slider_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  multireadout *r=(multireadout *)in;
+  int o,u;
+
+  u=multibar_get_value(MULTIBAR(w),0);
+  sprintf(buffer,"%+4ddB",u);
+  readout_set(READOUT(r->r0),buffer);
+  scset.u_thresh=u;
+  
+  o=multibar_get_value(MULTIBAR(w),1);
+  sprintf(buffer,"%+4ddB",o);
+  readout_set(READOUT(r->r1),buffer);
+  scset.o_thresh=o;
+}
+
+static void over_mode(GtkButton *b,gpointer in){
+  int mode=(int)in;
+  scset.o_mode=mode;
+}
+
+static void under_mode(GtkButton *b,gpointer in){
+  int mode=(int)in;
+  scset.u_mode=mode;
+}
+
+static void base_mode(GtkButton *b,gpointer in){
+  int mode=(int)in;
+  scset.b_mode=mode;
+}
+
+static void under_knee(GtkToggleButton *b,gpointer in){
+  int mode=gtk_toggle_button_get_active(b);
+  scset.u_softknee=mode;
+}
+
+static void over_knee(GtkToggleButton *b,gpointer in){
+  int mode=gtk_toggle_button_get_active(b);
+  scset.o_softknee=mode;
+}
+
+void singlepanel_create(postfish_mainpanel *mp,
+			GtkWidget *windowbutton,
+			GtkWidget *activebutton){
+
+  char *labels[14]={"130","120","110","100","90","80","70",
+		    "60","50","40","30","20","10","0"};
+  float levels[15]={-140,-130,-120,-110,-100,-90,-80,-70,-60,-50,-40,
+		     -30,-20,-10,0};
+
+  float compand_levels[9]={.1,.25,.5,.6667,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 timing_levels[6]={.5,1,10,100,1000,10000};
+  char  *timing_labels[5]={"1ms","10ms","100ms","1s","10s"};
+
+  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,
+					  &singlecomp_active,
+					  &singlecomp_visible,
+					  "_Singleband Compand"," [s] ");
+  
+  GtkWidget *sliderframe=gtk_frame_new(NULL);
+  GtkWidget *allbox=gtk_vbox_new(0,0);
+  GtkWidget *slidertable=gtk_table_new(2,3,0);
+
+  GtkWidget *overlabel=gtk_label_new("Over threshold compand ");
+  GtkWidget *overtable=gtk_table_new(6,4,0);
+
+  GtkWidget *underlabel=gtk_label_new("Under threshold compand ");
+  GtkWidget *undertable=gtk_table_new(5,4,0);
+
+  GtkWidget *baselabel=gtk_label_new("Global compand ");
+  GtkWidget *basetable=gtk_table_new(3,4,0);
+
+  gtk_widget_set_name(overlabel,"framelabel");
+  gtk_widget_set_name(underlabel,"framelabel");
+  gtk_widget_set_name(baselabel,"framelabel");
+
+  gtk_misc_set_alignment(GTK_MISC(overlabel),0,.5);
+  gtk_misc_set_alignment(GTK_MISC(underlabel),0,.5);
+  gtk_misc_set_alignment(GTK_MISC(baselabel),0,.5);
+
+    
+  {
+    GtkWidget *label1=gtk_label_new("compand threshold");
+    GtkWidget *label2=gtk_label_new("under");
+    GtkWidget *label3=gtk_label_new("over");
+
+    gtk_misc_set_alignment(GTK_MISC(label1),.5,1.);
+    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_table_attach(GTK_TABLE(slidertable),label2,0,1,0,1,GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(slidertable),label1,1,2,0,1,GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(slidertable),label3,2,3,0,1,GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+ 
+    gtk_container_add(GTK_CONTAINER(sliderframe),slidertable);
+
+    //gtk_frame_set_shadow_type(GTK_FRAME(sliderframe),GTK_SHADOW_NONE);
+    
+    gtk_container_set_border_width(GTK_CONTAINER(sliderframe),4);
+    gtk_container_set_border_width(GTK_CONTAINER(slidertable),10);
+
+  }
+
+  gtk_box_pack_start(GTK_BOX(panel->subpanel_box),allbox,0,0,0);
+  gtk_box_pack_start(GTK_BOX(allbox),sliderframe,0,0,0);
+
+
+  {
+    GtkWidget *hs1=gtk_hseparator_new();
+    GtkWidget *hs2=gtk_hseparator_new();
+
+    //gtk_box_pack_start(GTK_BOX(allbox),hs3,0,0,0);
+    gtk_box_pack_start(GTK_BOX(allbox),overtable,0,0,10);
+    gtk_box_pack_start(GTK_BOX(allbox),hs1,0,0,0);
+    gtk_box_pack_start(GTK_BOX(allbox),undertable,0,0,10);
+    gtk_box_pack_start(GTK_BOX(allbox),hs2,0,0,0);
+    gtk_box_pack_start(GTK_BOX(allbox),basetable,0,0,10);
+
+    gtk_container_set_border_width(GTK_CONTAINER(overtable),5);
+    gtk_container_set_border_width(GTK_CONTAINER(undertable),5);
+    gtk_container_set_border_width(GTK_CONTAINER(basetable),5);
+
+  }
+
+  /* under 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),underlabel,0,0,0);
+    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 (under_mode), (gpointer)0);
+    g_signal_connect (G_OBJECT (peak_button), "clicked",
+		      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);
+  }
+
+  /* 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);
+
+    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: 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=readout1;
+    r->r1=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);
+    
+    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),overlabel,0,0,0);
+    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 (over_mode), (gpointer)0);
+    g_signal_connect (G_OBJECT (peak_button), "clicked",
+		      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);
+   
+    multibar_callback(MULTIBAR(slider),over_compand_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),1.,0);
+
+    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);
+
+  }
+
+  /* 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=readout1;
+    r->r1=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);
+   
+    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: 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");
+
+    gtk_box_pack_start(GTK_BOX(envelopebox),baselabel,0,0,0);
+    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 (rms_button), "clicked",
+		      G_CALLBACK (base_mode), (gpointer)0);
+    g_signal_connect (G_OBJECT (peak_button), "clicked",
+		      G_CALLBACK (base_mode), (gpointer)1); //To Hell I Go
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rms_button),1);
+    gtk_table_attach(GTK_TABLE(basetable),envelopebox,0,4,0,1,GTK_FILL,0,0,0);
+  }
+
+  /* base 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),base_compand_change,readout);
+    multibar_thumb_set(MULTIBAR(slider),1.,0);
+
+    gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
+
+    gtk_table_set_row_spacing(GTK_TABLE(basetable),0,4);
+    gtk_table_attach(GTK_TABLE(basetable),label,0,1,1,2,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(basetable),slider,1,3,1,2,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(basetable),readout,3,4,1,2,GTK_FILL,0,0,0);
+
+  }
+
+  /* base 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=readout1;
+    r->r1=readout2;
+   
+    multibar_callback(MULTIBAR(slider),base_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(basetable),2,4);
+    gtk_table_attach(GTK_TABLE(basetable),label,0,1,4,5,GTK_FILL,0,2,0);
+    gtk_table_attach(GTK_TABLE(basetable),slider,1,2,4,5,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,0);
+    gtk_table_attach(GTK_TABLE(basetable),readout1,2,3,4,5,GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(basetable),readout2,3,4,4,5,GTK_FILL,0,0,0);
+
+  }
+
+  /* threshold controls */
+
+  {
+    t_readout.r0=readout_new("  +0");
+    t_readout.r1=readout_new("  +0");
+    t_slider=multibar_new(14,labels,levels,2,HI_DECAY|LO_DECAY|LO_ATTACK);
+
+    multibar_callback(MULTIBAR(t_slider),slider_change,&t_readout);
+    multibar_thumb_set(MULTIBAR(t_slider),-140.,0);
+    multibar_thumb_set(MULTIBAR(t_slider),0.,1);
+    multibar_thumb_bounds(MULTIBAR(t_slider),-140,0);
+    multibar_thumb_increment(MULTIBAR(t_slider),1.,10.);
+    
+    
+    gtk_table_attach(GTK_TABLE(slidertable),t_readout.r0,0,1,1,2,
+		     0,0,0,0);
+    gtk_table_attach(GTK_TABLE(slidertable),t_slider,1,2,1,2,
+		     GTK_FILL|GTK_EXPAND,GTK_EXPAND,0,0);
+    gtk_table_attach(GTK_TABLE(slidertable),t_readout.r1,2,3,1,2,
+		     0,0,0,0);
+  }
+  
+  subpanel_show_all_but_toplevel(panel);
+
+}
+
+static float *peakfeed=0;
+static float *rmsfeed=0;
+
+void singlepanel_feedback(int displayit){
+  if(!peakfeed){
+    peakfeed=malloc(sizeof(*peakfeed)*input_ch);
+    rmsfeed=malloc(sizeof(*rmsfeed)*input_ch);
+  }
+  
+  if(pull_singlecomp_feedback(peakfeed,rmsfeed)==1)
+    multibar_set(MULTIBAR(t_slider),rmsfeed,peakfeed,
+		 input_ch,(displayit && singlecomp_visible));
+}
+
+void singlepanel_reset(void){
+  multibar_reset(MULTIBAR(t_slider));
+}
+
+
+

Added: trunk/postfish/singlepanel.h
===================================================================
--- trunk/postfish/singlepanel.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/singlepanel.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,30 @@
+/*
+ *
+ *  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 void singlepanel_create(postfish_mainpanel *mp,
+			       GtkWidget *windowbutton,
+			       GtkWidget *activebutton);
+extern void singlepanel_feedback(int displayit);
+extern void singlepanel_reset(void);
+
+

Modified: trunk/postfish/subband.c
===================================================================
--- trunk/postfish/subband.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/subband.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -41,9 +41,7 @@
 } subband_feedback;
 
 static feedback_generic *new_subband_feedback(void){
-  int i;
   subband_feedback *ret=calloc(1,sizeof(*ret));
-
   return (feedback_generic *)ret;
 }
 
@@ -51,7 +49,7 @@
 
 int pull_subband_feedback(subband_state *ff,float **peak,float **rms,int *b){
   subband_feedback *f=(subband_feedback *)feedback_pull(&ff->feedpool);
-  int i,j;
+  int i;
   
   if(!f)return 0;
   
@@ -130,12 +128,11 @@
 
 /* called only by initial setup */
 int subband_load_freqs(subband_state *f,subband_window *w,
-		       float *freq_list,int bands){
+		       const float *freq_list,int bands){
   int i,j;
 
   memset(w,0,sizeof(*w));
 
-  w->freq_list=freq_list;
   w->freq_bands=bands;
 
   /* supersample the spectrum */
@@ -233,7 +230,7 @@
    and padded FFTs with 75% overlap. */
 
 static void subband_work(subband_state *f,time_linkage *in,subband_window *w){
-  int i,j,k,l,m,off;
+  int i,j,k,l,off;
   float *workoff=f->fftwf_forward_in+f->qblocksize;
 
   for(i=0;i<input_ch;i++){
@@ -303,7 +300,6 @@
 
 static void bypass_work(subband_state *f,time_linkage *in){
   int i,j;
-  float *workoff=f->fftwf_forward_in+f->qblocksize;
   float scale=f->qblocksize*4.;
 
   for(i=0;i<input_ch;i++){
@@ -358,13 +354,14 @@
     for(j=bands-1;j>=0;j--){
       
       /* add bands back together for output */
-      if(out)
+      if(out){
         if(j==bands-1){
           memcpy(out[i],f->lap[j][i],input_size*(sizeof **out));
         }else{
           for(k=0;k<input_size;k++)
             out[i][k]+=f->lap[j][i][k];
         }
+      }
       
       /* shift bands for next lap */
       /* optimization target: ringbuffer me! */
@@ -420,8 +417,8 @@
     }else{
       /* extrapolation mechanism; avoid harsh transients at edges */
       for(i=0;i<input_ch;i++){
-      preextrapolate_helper(in->data[i],input_size,
-			    f->cache[i],input_size);
+	preextrapolate_helper(in->data[i],input_size,
+			      f->cache[i],input_size);
       
       if(in->samples<in->size)
         postextrapolate_helper(f->cache[i],input_size,
@@ -526,7 +523,6 @@
 
   /* finish up the state feedabck */
   if(!bypass){
-    int j;
     subband_feedback *ff=
       (subband_feedback *)feedback_new(&f->feedpool,new_subband_feedback);
 

Modified: trunk/postfish/subband.h
===================================================================
--- trunk/postfish/subband.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/subband.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -49,7 +49,6 @@
 
 typedef struct {
 
-  float   *freq_list;
   int      freq_bands;
   float  **ho_window;
   float   *ho_area;
@@ -60,7 +59,7 @@
 
 extern int subband_load(subband_state *f,int bands, int qb);
 extern int subband_load_freqs(subband_state *f,subband_window *w,
-			      float *freq_list,int bands);
+			      const float *freq_list,int bands);
 
 extern time_linkage *subband_read(time_linkage *in, subband_state *f,
                                   subband_window *w,

Modified: trunk/postfish/subpanel.c
===================================================================
--- trunk/postfish/subpanel.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/subpanel.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -116,7 +116,6 @@
                                   char *prompt,char *shortcut){
 
   subpanel_generic *panel=calloc(1,sizeof(*panel));
-  GdkWindow *root=gdk_get_default_root_window();
 
   GtkWidget *toplabelbox=gtk_event_box_new();
   GtkWidget *toplabelframe=gtk_frame_new(NULL);

Added: trunk/postfish/suppress.c
===================================================================
--- trunk/postfish/suppress.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/suppress.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,204 @@
+/*
+ *
+ *  postfish
+ *    
+ *      Copyright (C) 2002-2004 Monty and Xiph.Org
+ *
+ *  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.
+ *
+ * 
+ */
+
+#include "postfish.h"
+#include "feedback.h"
+#include <fftw3.h>
+#include "subband.h"
+#include "bessel.h"
+#include "suppress.h"
+
+/* (since this one is kinda unique) The Reverberation Suppressor....
+   
+   Reverberation in a measurably live environment displays
+   log amplitude decay with time (linear decay when plotted on a dB
+   scale).
+   
+   In its simplest form, the suppressor follows actual RMS amplitude
+   attacks but chooses a slower-than-actual decay, then expands
+   according to the dB distance between the slow and actual decay.
+   
+   Thus, the suppressor can be used to 'dry out' a very 'wet'
+   reverberative track. */
+    
+extern int input_size;
+extern int input_rate;
+extern int input_ch;
+
+typedef struct {
+  subband_state ss;
+  subband_window sw;
+  
+  iir_filter smooth;
+  iir_filter trigger;
+  iir_filter release;
+  
+  iir_state *iirS[suppress_freqs];
+  iir_state *iirT[suppress_freqs];
+  iir_state *iirR[suppress_freqs];
+
+} suppress_state;
+
+sig_atomic_t suppress_visible;
+sig_atomic_t suppress_active;
+
+suppress_settings sset;
+static suppress_state sss;
+
+void suppress_reset(){
+  int i,j;
+  
+  subband_reset(&sss.ss);
+  
+  for(i=0;i<suppress_freqs;i++){
+    for(j=0;j<input_ch;j++){
+      memset(&sss.iirS[i][j],0,sizeof(iir_state));
+      memset(&sss.iirT[i][j],0,sizeof(iir_state));
+      memset(&sss.iirR[i][j],0,sizeof(iir_state));
+    }
+  }
+}
+
+static void filter_set(float msec,
+		       iir_filter *filter,
+		       int attackp,
+		       int order){
+  float alpha;
+  float corner_freq= 500./msec;
+  
+  /* make sure the chosen frequency doesn't require a lookahead
+     greater than what's available */
+  if(impulse_freq4(input_size*2-sss.ss.qblocksize*3)*1.01>corner_freq && 
+     attackp)
+    corner_freq=impulse_freq4(input_size*2-sss.ss.qblocksize*3);
+  
+  alpha=corner_freq/input_rate;
+  filter->g=mkbessel(alpha,order,filter->c);
+  filter->alpha=alpha;
+  filter->Hz=alpha*input_rate;
+  filter->ms=msec;
+}
+
+int suppress_load(void){
+  int i;
+  int qblocksize=input_size/16;
+  memset(&sss,0,sizeof(sss));
+
+  subband_load(&sss.ss,suppress_freqs,qblocksize);
+  subband_load_freqs(&sss.ss,&sss.sw,suppress_freq_list,suppress_freqs);
+   
+  for(i=0;i<suppress_freqs;i++){
+    sss.iirS[i]=calloc(input_ch,sizeof(iir_state));
+    sss.iirT[i]=calloc(input_ch,sizeof(iir_state));
+    sss.iirR[i]=calloc(input_ch,sizeof(iir_state));
+  }
+  return 0;
+}
+
+static void suppress_work(float **peakfeed,float **rmsfeed){
+  int i,j,k,l;
+  float smoothms=sset.smooth*.1;
+  float triggerms=sset.trigger*.1;
+  float releasems=sset.release*.1;
+  iir_filter *trigger=&sss.trigger;
+  iir_filter *smooth=&sss.smooth;
+  iir_filter *release=&sss.release;
+  int ahead;
+
+  if(smoothms!=smooth->ms)filter_set(smoothms,smooth,1,4);
+  if(triggerms!=trigger->ms)filter_set(triggerms,trigger,0,1);
+  if(releasems!=release->ms)filter_set(releasems,release,0,1);
+
+  ahead=impulse_ahead4(smooth->alpha);
+  
+  for(i=0;i<suppress_freqs;i++){
+
+    if(suppress_active){
+      float fast[input_size];
+      float slow[input_size];
+      float multiplier = 1.-1000./sset.ratio[i];
+
+      for(j=0;j<input_ch;j++){
+	if(sset.linkp){
+	  if(j==0){
+	    memset(fast,0,sizeof(fast));
+	    float scale=1./input_ch;
+	    for(l=0;l<input_ch;l++){
+	      float *x=sss.ss.lap[i][l]+ahead;
+	      for(k=0;k<input_size;k++)
+		fast[k]+=x[k]*x[k];
+	    }
+	    for(k=0;k<input_size;k++)
+	      fast[k]*=scale;
+
+	    //_analysis("rms",i,fast,input_size,0,offset);
+
+	  }
+	  
+	}else{
+	  float *x=sss.ss.lap[i][j]+ahead;
+	  for(k=0;k<input_size;k++)
+	    fast[k]=x[k]*x[k];
+	}
+
+	
+	if(j==0 || sset.linkp==0){
+
+	  compute_iir_symmetric4(fast, input_size, &sss.iirS[i][j],
+				smooth);
+	  
+	  //_analysis("smooth",i,fast,input_size,1,offset);
+	  
+	  compute_iir_freefall1(fast, input_size, &sss.iirT[i][j],
+	  		 trigger);
+	  memcpy(slow,fast,sizeof(slow));
+	  compute_iir_freefall1(slow, input_size, &sss.iirR[i][j],
+			       release);
+	  
+	  //_analysis("fast",i,fast,input_size,1,offset);
+	  //_analysis("slow",i,slow,input_size,1,offset);
+	  for(k=0;k<input_size;k++)
+	    fast[k]=fromdB_a((todB_a(slow+k)-todB_a(fast+k))*.5*multiplier);
+	  //_analysis("adj",i,fast,input_size,1,offset);
+	}
+
+
+	{
+	  float *x=sss.ss.lap[i][j];
+	  for(k=0;k<input_size;k++)
+	    if(fast[k]<1.)
+	      x[k]*=fast[k];
+	}
+      }
+    }
+  }
+}
+
+time_linkage *suppress_read(time_linkage *in){
+  int bypass=!(suppress_active);
+  
+  return subband_read(in,&sss.ss,&sss.sw,
+		      suppress_work,bypass);
+}
+
+

Added: trunk/postfish/suppress.h
===================================================================
--- trunk/postfish/suppress.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/suppress.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,46 @@
+/*
+ *
+ *  postfish
+ *    
+ *      Copyright (C) 2002-2004 Monty and Xiph.Org
+ *
+ *  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.
+ *
+ * 
+ */
+
+#include "postfish.h"
+
+#define suppress_freqs 8
+static const float suppress_freq_list[suppress_freqs+1]={
+  125,250,500,1000,2000,4000,8000,16000,9e10
+};
+
+static char * const suppress_freq_labels[suppress_freqs]={
+  "125","250","500","1k","2k","4k","8k","16k"
+};
+
+typedef struct {
+  sig_atomic_t ratio[suppress_freqs];
+  sig_atomic_t smooth;
+  sig_atomic_t trigger;
+  sig_atomic_t release;
+  sig_atomic_t linkp;
+} suppress_settings;
+
+extern void suppress_reset();
+extern int suppress_load(void);
+extern time_linkage *suppress_read(time_linkage *in);
+

Added: trunk/postfish/suppresspanel.c
===================================================================
--- trunk/postfish/suppresspanel.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/suppresspanel.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,242 @@
+/*
+ *
+ *  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.
+ *
+ * 
+ */
+
+#include "postfish.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "readout.h"
+#include "multibar.h"
+#include "mainpanel.h"
+#include "subpanel.h"
+#include "feedback.h"
+#include "suppress.h"
+#include "suppresspanel.h"
+
+extern sig_atomic_t suppress_active;
+extern sig_atomic_t suppress_visible;
+extern int input_ch;
+extern int input_size;
+extern int input_rate;
+
+extern suppress_settings sset;
+
+typedef struct {
+  GtkWidget *cslider;
+  Readout *readoutc;
+  int number;
+} tbar;
+
+Readout *readoutsmooth;
+Readout *readouttrigger;
+Readout *readoutrelease;
+
+static tbar bars[suppress_freqs+1];
+
+static void compand_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  tbar *bar=(tbar *)in;
+  float val=multibar_get_value(MULTIBAR(w),0);
+
+  if(val==1.){
+    sprintf(buffer,"   off");
+  }else 
+    sprintf(buffer,"%4.2f",val);
+
+  readout_set(bar->readoutc,buffer);
+  
+  sset.ratio[bar->number]=1000./val;
+
+}
+
+static void timing_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  float smooth=multibar_get_value(MULTIBAR(w),0);
+  float trigger=multibar_get_value(MULTIBAR(w),1);
+  float release=multibar_get_value(MULTIBAR(w),2);
+  
+  if(smooth<100){
+    sprintf(buffer,"%4.1fms",smooth);
+  }else if (smooth<1000){
+    sprintf(buffer,"%4.0fms",smooth);
+  }else if (smooth<10000){
+    sprintf(buffer," %4.2fs",smooth/1000.);
+  }else{
+    sprintf(buffer," %4.1fs",smooth/1000.);
+  }
+  readout_set(readoutsmooth,buffer);
+
+  if(trigger<100){
+    sprintf(buffer,"%4.1fms",trigger);
+  }else if (trigger<1000){
+    sprintf(buffer,"%4.0fms",trigger);
+  }else if (trigger<10000){
+    sprintf(buffer," %4.2fs",trigger/1000.);
+  }else{
+    sprintf(buffer," %4.1fs",trigger/1000.);
+  }
+  readout_set(readouttrigger,buffer);
+
+  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(readoutrelease,buffer);
+
+  sset.smooth=rint(smooth*10.);
+  sset.trigger=rint(trigger*10.);
+  sset.release=rint(release*10.);
+}
+
+static void suppress_link(GtkToggleButton *b,gpointer in){
+  int mode=gtk_toggle_button_get_active(b);
+  sset.linkp=mode;
+}
+
+void suppresspanel_create(postfish_mainpanel *mp,
+			  GtkWidget *windowbutton,
+			  GtkWidget *activebutton){
+  int i;
+  float compand_levels[5]={1,1.5,2,3,5};
+  char  *compand_labels[4]={"1.5","2","3","5"};
+
+  float timing_levels[5]={1, 10, 100, 1000, 10000};
+  char  *timing_labels[4]={"10ms","     100ms","1s","10s"};
+  
+  subpanel_generic *panel=subpanel_create(mp,windowbutton,activebutton,
+					  &suppress_active,
+					  &suppress_visible,
+					  "De_verberation filter"," [v] ");
+  
+
+  GtkWidget *table=gtk_table_new(suppress_freqs+4,5,0);
+  GtkWidget *timinglabel=gtk_label_new("suppressor filter timing");
+  GtkWidget *releaselabel=gtk_label_new("release");
+  GtkWidget *smoothlabel=gtk_label_new("smooth");
+  GtkWidget *triggerlabel=gtk_label_new("trigger");
+  GtkWidget *compandlabel=gtk_label_new("suppression depth");
+
+  GtkWidget *linkbutton=
+    gtk_check_button_new_with_mnemonic("_link channels into single image");
+  GtkWidget *linkbox=gtk_hbox_new(0,0);
+
+  gtk_container_add(GTK_CONTAINER(panel->subpanel_box),table);
+
+  gtk_box_pack_end(GTK_BOX(linkbox),linkbutton,0,0,0);
+
+  gtk_table_attach(GTK_TABLE(table),timinglabel,0,2,0,1,
+		   GTK_EXPAND|GTK_FILL,
+		   GTK_EXPAND|GTK_FILL,
+		   0,5);
+  gtk_table_attach(GTK_TABLE(table),smoothlabel,2,3,0,1,
+		   GTK_EXPAND|GTK_FILL,
+		   GTK_EXPAND|GTK_FILL,
+		   0,0);
+  gtk_table_attach(GTK_TABLE(table),triggerlabel,3,4,0,1,
+		   GTK_EXPAND|GTK_FILL,
+		   GTK_EXPAND|GTK_FILL,
+		   0,0);
+  gtk_table_attach(GTK_TABLE(table),releaselabel,4,5,0,1,
+		   GTK_EXPAND|GTK_FILL,
+		   GTK_EXPAND|GTK_FILL,
+		   0,0);
+  gtk_table_attach(GTK_TABLE(table),compandlabel,0,4,2,3,
+		   GTK_EXPAND|GTK_FILL,
+		   GTK_EXPAND|GTK_FILL,
+		   0,5);
+  if(input_ch>1)
+    gtk_table_attach(GTK_TABLE(table),linkbox,0,5,suppress_freqs+3,
+		     suppress_freqs+4,GTK_FILL|GTK_EXPAND,0,0,10);
+
+  gtk_table_set_row_spacing(GTK_TABLE(table),1,5);
+  
+  gtk_misc_set_alignment(GTK_MISC(timinglabel),0,1.);
+  gtk_widget_set_name(timinglabel,"framelabel");
+  gtk_misc_set_alignment(GTK_MISC(smoothlabel),.5,1.);
+  gtk_widget_set_name(smoothlabel,"scalemarker");
+  gtk_misc_set_alignment(GTK_MISC(triggerlabel),.5,1.);
+  gtk_widget_set_name(triggerlabel,"scalemarker");
+  gtk_misc_set_alignment(GTK_MISC(releaselabel),.5,1.);
+  gtk_widget_set_name(releaselabel,"scalemarker");
+  gtk_misc_set_alignment(GTK_MISC(compandlabel),0,1.);
+  gtk_widget_set_name(compandlabel,"framelabel");
+
+  g_signal_connect (G_OBJECT (linkbutton), "clicked",
+		    G_CALLBACK (suppress_link), 0);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linkbutton),1);
+
+  /* timing controls */
+  {
+    GtkWidget *slider=multibar_slider_new(4,timing_labels,timing_levels,3);
+    
+    readoutsmooth=READOUT(readout_new("10.0ms"));
+    readouttrigger=READOUT(readout_new("10.0ms"));
+    readoutrelease=READOUT(readout_new("10.0ms"));
+
+    multibar_callback(MULTIBAR(slider),timing_change,0);
+    
+    multibar_thumb_set(MULTIBAR(slider),20,0);
+    multibar_thumb_set(MULTIBAR(slider),100,1);
+    multibar_thumb_set(MULTIBAR(slider),1000,2);
+
+    gtk_table_attach(GTK_TABLE(table),slider,1,2,1,2,
+		     GTK_FILL|GTK_EXPAND,GTK_EXPAND,5,0);
+    gtk_table_attach(GTK_TABLE(table),GTK_WIDGET(readoutsmooth),2,3,1,2,
+		     0,0,0,0);
+    gtk_table_attach(GTK_TABLE(table),GTK_WIDGET(readouttrigger),3,4,1,2,
+		     0,0,0,0);
+    gtk_table_attach(GTK_TABLE(table),GTK_WIDGET(readoutrelease),4,5,1,2,
+		     0,0,0,0);
+  }
+
+  /* threshold controls */
+
+  for(i=0;i<suppress_freqs;i++){
+    GtkWidget *label=gtk_label_new(suppress_freq_labels[i]);
+    gtk_widget_set_name(label,"scalemarker");
+    
+    bars[i].readoutc=READOUT(readout_new("1.55:1"));
+    bars[i].cslider=multibar_slider_new(4,compand_labels,compand_levels,1);
+    bars[i].number=i;
+
+    multibar_callback(MULTIBAR(bars[i].cslider),compand_change,bars+i);
+    multibar_thumb_set(MULTIBAR(bars[i].cslider),1,0);
+    
+    gtk_misc_set_alignment(GTK_MISC(label),1,.5);
+      
+    gtk_table_attach(GTK_TABLE(table),label,0,1,i+3,i+4,
+		     GTK_FILL,0,0,0);
+
+    gtk_table_attach(GTK_TABLE(table),bars[i].cslider,1,4,i+3,i+4,
+		     GTK_FILL|GTK_EXPAND,GTK_EXPAND,5,0);
+    gtk_table_attach(GTK_TABLE(table),GTK_WIDGET(bars[i].readoutc),4,5,
+		     i+3,i+4,0,0,0,0);
+  }
+  subpanel_show_all_but_toplevel(panel);
+
+}
+

Added: trunk/postfish/suppresspanel.h
===================================================================
--- trunk/postfish/suppresspanel.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/suppresspanel.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -0,0 +1,26 @@
+/*
+ *
+ *  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 void suppresspanel_create(postfish_mainpanel *mp,
+				 GtkWidget *windowbutton,
+				 GtkWidget *activebutton);

Modified: trunk/postfish/version.h
===================================================================
--- trunk/postfish/version.h	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/version.h	2004-04-14 04:50:38 UTC (rev 6506)
@@ -1,2 +1,2 @@
 #define VERSION "$Id: version.h,v 1.44 2004/03/17 10:37:04 xiphmont Exp $ "
-/* DO NOT EDIT: Automated versioning hack [Wed Mar 17 05:34:18 EST 2004] */
+/* DO NOT EDIT: Automated versioning hack [Wed Apr 14 00:46:49 EDT 2004] */

Modified: trunk/postfish/windowbutton.c
===================================================================
--- trunk/postfish/windowbutton.c	2004-04-10 20:07:05 UTC (rev 6505)
+++ trunk/postfish/windowbutton.c	2004-04-14 04:50:38 UTC (rev 6506)
@@ -57,6 +57,11 @@
     } 
     gdk_draw_line(window,light_gc,x,y,x,y+size-2);
     break;
+  case GTK_SHADOW_NONE:
+    break;
+  case GTK_SHADOW_ETCHED_OUT:
+    /* unimplemented, unused */
+    break;
   }
 
 }

--- >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