[xiph-cvs] r6774 - trunk/postfish

xiphmont at xiph.org xiphmont at xiph.org
Fri May 28 18:17:49 PDT 2004



Author: xiphmont
Date: 2004-05-28 21:17:47 -0400 (Fri, 28 May 2004)
New Revision: 6774

Added:
   trunk/postfish/config.c
   trunk/postfish/config.h
   trunk/postfish/outpanel.c
   trunk/postfish/outpanel.h
   trunk/postfish/reverbpanel.c
   trunk/postfish/reverbpanel.h
Modified:
   trunk/postfish/Makefile
   trunk/postfish/clippanel.c
   trunk/postfish/clippanel.h
   trunk/postfish/compandpanel.c
   trunk/postfish/compandpanel.h
   trunk/postfish/declip.c
   trunk/postfish/declip.h
   trunk/postfish/eq.c
   trunk/postfish/eq.h
   trunk/postfish/eqpanel.c
   trunk/postfish/eqpanel.h
   trunk/postfish/feedback.c
   trunk/postfish/input.c
   trunk/postfish/input.h
   trunk/postfish/limit.c
   trunk/postfish/limit.h
   trunk/postfish/limitpanel.c
   trunk/postfish/limitpanel.h
   trunk/postfish/main.c
   trunk/postfish/mainpanel.c
   trunk/postfish/mainpanel.h
   trunk/postfish/mix.c
   trunk/postfish/mix.h
   trunk/postfish/mixpanel.c
   trunk/postfish/mixpanel.h
   trunk/postfish/multicompand.c
   trunk/postfish/multicompand.h
   trunk/postfish/output.c
   trunk/postfish/output.h
   trunk/postfish/postfish-gtkrc
   trunk/postfish/postfish.h
   trunk/postfish/readout.c
   trunk/postfish/reverb.c
   trunk/postfish/reverb.h
   trunk/postfish/singlecomp.c
   trunk/postfish/singlecomp.h
   trunk/postfish/singlepanel.c
   trunk/postfish/singlepanel.h
   trunk/postfish/subband.c
   trunk/postfish/subpanel.c
   trunk/postfish/subpanel.h
   trunk/postfish/suppress.c
   trunk/postfish/suppress.h
   trunk/postfish/suppresspanel.c
   trunk/postfish/suppresspanel.h
   trunk/postfish/version.h
Log:
Having Postfish around in a state where I keep tinkering on it is
still proving distracting.

So that's it.  I'm calling this a prerelease commit and putting it
out of mind.  I'll come back when OggFile hits beta.

Have at.  

<p><p>Modified: trunk/postfish/Makefile
===================================================================
--- trunk/postfish/Makefile	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/Makefile	2004-05-29 01:17:47 UTC (rev 6774)
@@ -3,14 +3,19 @@
 # and Fuck its little dog Libtool too
 
 
-ADD_DEF= -DUGLY_IEEE754_FLOAT32_HACK=1 -maltivec
+# The PPC build *must* use -maltivec, even if the target is a non-altivec machine
 
-# use for PPC with altivec.  IF YOU HAVE ALTIVEC, YOU MUST USE THIS
-# LINE, otherwise FFTW3 will randomly crash whenever it uses Altivec
-# and any math denormalizes.
-
 #ADD_DEF= -DUGLY_IEEE754_FLOAT32_HACK=1 -maltivec
 
+# use the below for x86 and most other platforms where 'float' is 32 bit IEEE754
+
+ADD_DEF= -DUGLY_IEEE754_FLOAT32_HACK=1 
+
+# use the below for anything without IEE754 floats (eg, VAX)
+
+# ADD_DEF=
+
+
 CC=gcc 
 LD=gcc
 INSTALL=install
@@ -23,12 +28,14 @@
         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 suppresspanel.c suppress.c singlecomp.c singlepanel.c \
-	limit.c limitpanel.c mute.c mixpanel.c mix.c reverb.c reverbpanel.c
+	limit.c limitpanel.c mute.c mixpanel.c mix.c reverb.c reverbpanel.c \
+	outpanel.c config.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 suppresspanel.o suppress.o singlecomp.o singlepanel.o \
-	limit.o limitpanel.o mute.o mixpanel.o mix.o reverb.o reverbpanel.o
+	limit.o limitpanel.o mute.o mixpanel.o mix.o reverb.o reverbpanel.o \
+	outpanel.o config.o
 GCF = -DETCDIR=\\\"$(ETCDIR)\\\" `pkg-config --cflags gtk+-2.0` -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED
 
 all:	

Modified: trunk/postfish/clippanel.c
===================================================================
--- trunk/postfish/clippanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/clippanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -29,25 +29,25 @@
 #include "mainpanel.h"
 #include "subpanel.h"
 #include "declip.h"
+#include "config.h"
 
-extern sig_atomic_t *declip_active;
-extern sig_atomic_t declip_visible;
-extern int input_ch;
-extern int input_size;
-extern int input_rate;
-extern sig_atomic_t declip_converge;
+static GtkWidget **feedback_bars;
+static GtkWidget **trigger_bars;
 
-GtkWidget **feedback_bars;
-GtkWidget **trigger_bars;
+static GtkWidget *width_bar;
+static GtkWidget *samplereadout;
+static GtkWidget *msreadout;
+static GtkWidget *hzreadout;
 
-GtkWidget *samplereadout;
-GtkWidget *msreadout;
-GtkWidget *hzreadout;
-GtkWidget *depth_readout;
-GtkWidget *limit_readout;
+static GtkWidget *depth_bar;
+static GtkWidget *depth_readout;
+static GtkWidget *limit_bar;
+static GtkWidget *limit_readout;
 
-GtkWidget *mainpanel_inbar;
+static GtkWidget *mainpanel_inbar;
 
+static subpanel_generic *panel;
+
 typedef struct {
   GtkWidget *slider;
   GtkWidget *readout;
@@ -55,6 +55,37 @@
   int number;
 } clipslider;
 
+void clippanel_state_to_config(int bank){
+  config_set_vector("clippanel_active",bank,0,0,0,input_ch,declip_active);
+  config_set_integer("clippanel_width",bank,0,0,0,0,declip_pending_blocksize);
+  config_set_integer("clippanel_convergence",bank,0,0,0,0,declip_convergence);
+  config_set_integer("clippanel_throttle",bank,0,0,0,0,declip_iterations);
+  config_set_vector("clippanel_trigger",bank,0,0,0,input_ch,declip_chtrigger);
+}
+
+void clippanel_state_from_config(int bank){
+  int i;
+  config_get_vector("clippanel_active",bank,0,0,0,input_ch,declip_active);
+  config_get_sigat("clippanel_width",bank,0,0,0,0,&declip_pending_blocksize);
+  config_get_sigat("clippanel_convergence",bank,0,0,0,0,&declip_convergence);
+  config_get_sigat("clippanel_throttle",bank,0,0,0,0,&declip_iterations);
+  config_get_vector("clippanel_trigger",bank,0,0,0,input_ch,declip_chtrigger);
+
+  {
+    int i=0,j=declip_pending_blocksize;
+    while(j>64){j>>=1;i++;}
+    multibar_thumb_set(MULTIBAR(width_bar),i,0);
+  }
+  multibar_thumb_set(MULTIBAR(depth_bar),declip_convergence*-.1,0);
+  multibar_thumb_set(MULTIBAR(limit_bar),declip_iterations*.1,0);
+
+  for(i=0;i<input_ch;i++){
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->subpanel_activebutton[i]),
+				 declip_active[i]);
+    multibar_thumb_set(MULTIBAR(trigger_bars[i]),declip_chtrigger[i]*.0001,0);
+  }
+}
+
 static void trigger_slider_change(GtkWidget *w,gpointer in){
   char buffer[80];
   clipslider *p=(clipslider *)in;
@@ -66,8 +97,7 @@
   sprintf(buffer,"%3.0fdB",todB(linear));
   readout_set(READOUT(p->readoutdB),buffer);
 
-  declip_settrigger(linear,p->number);
-
+  declip_chtrigger[p->number]=rint(linear*10000.);
 }
 
 static void blocksize_slider_change(GtkWidget *w,gpointer in){
@@ -83,8 +113,8 @@
 
   sprintf(buffer,"%5dHz",(int)rint(input_rate*2./blocksize));
   readout_set(READOUT(hzreadout),buffer);
-  
-  declip_setblock(blocksize);
+
+  declip_pending_blocksize=blocksize;
 }
 
 static void depth_slider_change(GtkWidget *w,gpointer in){
@@ -94,7 +124,7 @@
   sprintf(buffer,"%3ddB",(int)dB);
   readout_set(READOUT(depth_readout),buffer);
 
-  declip_setconvergence(fromdB(-dB));
+  declip_convergence=rint(-dB*10.);
 }
 
 static void limit_slider_change(GtkWidget *w,gpointer in){
@@ -104,7 +134,7 @@
   sprintf(buffer,"%3d%%",(int)percent);
   readout_set(READOUT(limit_readout),buffer);
 
-  declip_setiterations(percent*.01);
+  declip_iterations=rint(percent*10.);
 }
 
 static void active_callback(gpointer in,int activenum){
@@ -120,11 +150,6 @@
   float levels[3]={0.,10.,100.};
   int block_choices=0;
 
-  subpanel_generic *panel=subpanel_create(mp,windowbutton[0],activebutton,
-					  declip_active,&declip_visible,
-					  "_Declipping filter setup",NULL,
-					  0,input_ch);
-  
   GtkWidget *framebox=gtk_hbox_new(1,0);
   GtkWidget *framebox_right=gtk_vbox_new(0,0);
   GtkWidget *blocksize_box=gtk_vbox_new(0,0);
@@ -135,6 +160,11 @@
   GtkWidget *limit_box=gtk_vbox_new(0,0);
   GtkWidget *channel_table=gtk_table_new(input_ch,5,0);
 
+  panel=subpanel_create(mp,windowbutton[0],activebutton,
+			declip_active,&declip_visible,
+			"_Declipping filter setup",NULL,
+			0,input_ch);
+  
   subpanel_set_active_callback(panel,0,active_callback);
 
   gtk_widget_set_name(blocksize_box,"choiceframe");
@@ -182,10 +212,11 @@
     gtk_table_attach(GTK_TABLE(table),hzreadout,1,2,3,4,GTK_FILL,0,5,0);
     gtk_container_add(GTK_CONTAINER(blocksize_box),table);
 
+    width_bar=slider;
     multibar_thumb_increment(MULTIBAR(slider),1.,1.);
     multibar_callback(MULTIBAR(slider),blocksize_slider_change,0);
 
-    multibar_thumb_set(MULTIBAR(slider),2.,0);
+    multibar_thumb_set(MULTIBAR(slider),4.,0);
     
   }
   gtk_container_add(GTK_CONTAINER(blocksize_frame),blocksize_box);
@@ -214,6 +245,7 @@
 
     gtk_container_add(GTK_CONTAINER(converge_box),table);
 
+    depth_bar=slider;
     multibar_thumb_increment(MULTIBAR(slider),1.,10.);
     multibar_callback(MULTIBAR(slider),depth_slider_change,0);
     multibar_thumb_set(MULTIBAR(slider),60.,0);
@@ -244,6 +276,7 @@
 
     gtk_container_add(GTK_CONTAINER(limit_box),table);
 
+    limit_bar=slider;
     multibar_thumb_increment(MULTIBAR(slider),1.,10.);
     multibar_callback(MULTIBAR(slider),limit_slider_change,0);
     multibar_thumb_set(MULTIBAR(slider),100.,0);

Modified: trunk/postfish/clippanel.h
===================================================================
--- trunk/postfish/clippanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/clippanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -28,3 +28,6 @@
                              GtkWidget **activebutton);
 extern void clippanel_feedback(int workp);
 extern void clippanel_reset(void);
+
+extern void clippanel_state_from_config(int bank);
+extern void clippanel_state_to_config(int bank);

Modified: trunk/postfish/compandpanel.c
===================================================================
--- trunk/postfish/compandpanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/compandpanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,14 +31,8 @@
 #include "feedback.h"
 #include "multicompand.h"
 #include "compandpanel.h"
+#include "config.h"
 
-extern int input_ch;
-extern int input_size;
-extern int input_rate;
-
-extern multicompand_settings multi_master_set;
-extern multicompand_settings *multi_channel_set;
-
 typedef struct {
   GtkWidget *label;
   GtkWidget *slider;
@@ -49,11 +43,13 @@
 } cbar;
 
 typedef struct{
+  Multibar *s;
   Readout *r;
   sig_atomic_t *v;
 } callback_arg_rv;
 
 typedef struct{
+  Multibar *s;
   Readout *r0;
   Readout *r1;
   sig_atomic_t *v0;
@@ -66,6 +62,21 @@
 } callback_arg_mi;
 
 typedef struct multi_panel_state{
+  subpanel_generic *panel;
+
+  GtkWidget *over_rms;
+  GtkWidget *over_peak;
+  GtkWidget *over_softknee;
+
+  GtkWidget *under_rms;
+  GtkWidget *under_peak;
+  GtkWidget *under_softknee;
+
+  GtkWidget *base_rms;
+  GtkWidget *base_peak;
+
+  GtkWidget *octave[3];
+
   callback_arg_rv over_compand;
   callback_arg_rv under_compand;
   callback_arg_rv base_compand;
@@ -92,6 +103,127 @@
 static multi_panel_state *master_panel;
 static multi_panel_state **channel_panel;
 
+
+static void compandpanel_state_to_config_helper(int bank,multicompand_settings *s,int A){
+  int i;
+  config_set_integer("multicompand_active",bank,A,0,0,0,s->panel_active);
+  config_set_integer("multicompand_freqbank",bank,A,0,0,0,s->active_bank);
+  for(i=0;i<multicomp_banks;i++){
+    config_set_vector("multicompand_under_thresh",bank,A,i,0,multicomp_freqs_max,s->bc[i].static_u);
+    config_set_vector("multicompand_over_thresh",bank,A,i,0,multicomp_freqs_max,s->bc[i].static_o);
+  }
+
+  config_set_integer("multicompand_over_set",bank,A,0,0,0,s->over_mode);
+  config_set_integer("multicompand_over_set",bank,A,0,0,1,s->over_softknee);
+  config_set_integer("multicompand_over_set",bank,A,0,0,2,s->over_ratio);
+  config_set_integer("multicompand_over_set",bank,A,0,0,3,s->over_attack);
+  config_set_integer("multicompand_over_set",bank,A,0,0,4,s->over_decay);
+  config_set_integer("multicompand_over_set",bank,A,0,0,5,s->over_lookahead);
+
+  config_set_integer("multicompand_under_set",bank,A,0,0,0,s->under_mode);
+  config_set_integer("multicompand_under_set",bank,A,0,0,1,s->under_softknee);
+  config_set_integer("multicompand_under_set",bank,A,0,0,2,s->under_ratio);
+  config_set_integer("multicompand_under_set",bank,A,0,0,3,s->under_attack);
+  config_set_integer("multicompand_under_set",bank,A,0,0,4,s->under_decay);
+  config_set_integer("multicompand_under_set",bank,A,0,0,5,s->under_lookahead);
+
+  config_set_integer("multicompand_base_set",bank,A,0,0,0,s->base_mode);
+  config_set_integer("multicompand_base_set",bank,A,0,0,2,s->base_ratio);
+  config_set_integer("multicompand_base_set",bank,A,0,0,3,s->base_attack);
+  config_set_integer("multicompand_base_set",bank,A,0,0,4,s->base_decay);
+}
+
+void compandpanel_state_to_config(int bank){
+  int i;
+  compandpanel_state_to_config_helper(bank,&multi_master_set,0);
+  for(i=0;i<input_ch;i++)
+    compandpanel_state_to_config_helper(bank,multi_channel_set+i,i+1);
+}
+
+static void static_octave(GtkWidget *w,gpointer in);
+static void compandpanel_state_from_config_helper(int bank,multicompand_settings *s,
+						  multi_panel_state *p,int A){
+
+  int i;
+  config_get_sigat("multicompand_active",bank,A,0,0,0,&s->panel_active);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->panel->subpanel_activebutton[0]),s->panel_active);
+
+  config_get_sigat("multicompand_freqbank",bank,A,0,0,0,&s->active_bank);
+  for(i=0;i<multicomp_banks;i++){
+    config_get_vector("multicompand_under_thresh",bank,A,i,0,multicomp_freqs_max,s->bc[i].static_u);
+    config_get_vector("multicompand_over_thresh",bank,A,i,0,multicomp_freqs_max,s->bc[i].static_o);
+  }
+
+  config_get_sigat("multicompand_over_set",bank,A,0,0,0,&s->over_mode);
+  if(s->over_mode)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->over_peak),1);
+  else
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->over_rms),1);
+
+  config_get_sigat("multicompand_over_set",bank,A,0,0,1,&s->over_softknee);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->over_softknee),s->over_softknee);
+  config_get_sigat("multicompand_over_set",bank,A,0,0,2,&s->over_ratio);
+  multibar_thumb_set(p->over_compand.s,1000./s->over_ratio,0);
+  config_get_sigat("multicompand_over_set",bank,A,0,0,3,&s->over_attack);
+  multibar_thumb_set(p->over_timing.s,s->over_attack*.1,0);
+  config_get_sigat("multicompand_over_set",bank,A,0,0,4,&s->over_decay);
+  multibar_thumb_set(p->over_timing.s,s->over_decay*.1,1);
+  config_get_sigat("multicompand_over_set",bank,A,0,0,5,&s->over_lookahead);
+  multibar_thumb_set(p->over_lookahead.s,s->over_lookahead*.1,0);
+
+  config_get_sigat("multicompand_under_set",bank,A,0,0,0,&s->under_mode);
+  if(s->under_mode)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->under_peak),1);
+  else
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->under_rms),1);
+
+  config_get_sigat("multicompand_under_set",bank,A,0,0,1,&s->under_softknee);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->under_softknee),s->under_softknee);
+  config_get_sigat("multicompand_under_set",bank,A,0,0,2,&s->under_ratio);
+  multibar_thumb_set(p->under_compand.s,1000./s->under_ratio,0);
+  config_get_sigat("multicompand_under_set",bank,A,0,0,3,&s->under_attack);
+  multibar_thumb_set(p->under_timing.s,s->under_attack*.1,0);
+  config_get_sigat("multicompand_under_set",bank,A,0,0,4,&s->under_decay);
+  multibar_thumb_set(p->under_timing.s,s->under_decay*.1,1);
+  config_get_sigat("multicompand_under_set",bank,A,0,0,5,&s->under_lookahead);
+  multibar_thumb_set(p->under_lookahead.s,s->under_lookahead*.1,0);
+
+  config_get_sigat("multicompand_base_set",bank,A,0,0,0,&s->base_mode);
+  if(s->base_mode)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->base_peak),1);
+  else
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->base_rms),1);
+  config_get_sigat("multicompand_base_set",bank,A,0,0,2,&s->base_ratio);
+  multibar_thumb_set(p->base_compand.s,1000./s->base_ratio,0);
+  config_get_sigat("multicompand_base_set",bank,A,0,0,3,&s->base_attack);
+  multibar_thumb_set(p->base_timing.s,s->base_attack*.1,0);
+  config_get_sigat("multicompand_base_set",bank,A,0,0,4,&s->base_decay);
+  multibar_thumb_set(p->base_timing.s,s->base_decay*.1,1);
+
+  /* setting the active bank also redisplays all the sliders */
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->octave[s->active_bank]),1);
+  /* safe to call blindly; if the above already triggered the work, this is a no op */
+  switch(s->active_bank){  
+  case 0:
+    static_octave(0,&p->octave_full);
+    break;
+  case 1:
+    static_octave(0,&p->octave_half);
+    break;
+  case 2:
+    static_octave(0,&p->octave_third);
+    break;
+  }
+}
+
+void compandpanel_state_from_config(int bank){
+  int i;
+  compandpanel_state_from_config_helper(bank,&multi_master_set,master_panel,0);
+  for(i=0;i<input_ch;i++)
+    compandpanel_state_from_config_helper(bank,multi_channel_set+i,channel_panel[i],i+1);
+}
+
+
 static void compand_change(GtkWidget *w,gpointer in){
   callback_arg_rv *ca=(callback_arg_rv *)in;
   char buffer[80];
@@ -509,10 +641,11 @@
   float per_levels[9]={0,12.5,25,37.5,50,62.5,75,87.5,100};
   char  *per_labels[9]={"0%","","25%","","50%","","75%","","100%"};
 
-  multi_panel_state *ps=calloc(1,sizeof(multi_panel_state));
+  multi_panel_state *ps=calloc(1,sizeof(*ps));
   ps->inactive_updatep=1;
   ps->bank_active=2;
   ps->ms=ms;
+  ps->panel=panel;
 
   GtkWidget *hbox=gtk_hbox_new(0,0);
   GtkWidget *sliderbox=gtk_vbox_new(0,0);
@@ -590,6 +723,10 @@
     
     gtk_container_set_border_width(GTK_CONTAINER(sliderframe),4);
 
+    ps->octave[0]=octave_a;
+    ps->octave[1]=octave_b;
+    ps->octave[2]=octave_c;
+
   }
 
   gtk_box_pack_start(GTK_BOX(panel->subpanel_box),hbox,0,0,0);
@@ -637,6 +774,9 @@
     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);
+    ps->under_rms=rms_button;
+    ps->under_peak=peak_button;
+    ps->under_softknee=knee_button;
   }
 
   /* under compand: ratio */
@@ -646,6 +786,7 @@
     GtkWidget *readout=readout_new("1.55:1");
     GtkWidget *slider=multibar_slider_new(9,compand_labels,compand_levels,1);
    
+    ps->under_compand.s=MULTIBAR(slider);
     ps->under_compand.r=READOUT(readout);
     ps->under_compand.v=&ps->ms->under_ratio;
 
@@ -669,6 +810,7 @@
     GtkWidget *readout1=readout_new(" 100ms");
     GtkWidget *slider=multibar_slider_new(6,timing_labels,timing_levels,2);
 
+    ps->under_timing.s=MULTIBAR(slider);
     ps->under_timing.r0=READOUT(readout0);
     ps->under_timing.r1=READOUT(readout1);
     ps->under_timing.v0=&ps->ms->under_attack;
@@ -695,6 +837,7 @@
     GtkWidget *readout=readout_new("100%");
     GtkWidget *slider=multibar_slider_new(9,per_labels,per_levels,1);
 
+    ps->under_lookahead.s=MULTIBAR(slider);
     ps->under_lookahead.r=READOUT(readout);
     ps->under_lookahead.v=&ps->ms->under_lookahead;
 
@@ -733,6 +876,9 @@
     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);
+    ps->over_rms=rms_button;
+    ps->over_peak=peak_button;
+    ps->over_softknee=knee_button;
   }
 
   /* over compand: ratio */
@@ -742,6 +888,7 @@
     GtkWidget *readout=readout_new("1.55:1");
     GtkWidget *slider=multibar_slider_new(9,compand_labels,compand_levels,1);
    
+    ps->over_compand.s=MULTIBAR(slider);
     ps->over_compand.r=READOUT(readout);
     ps->over_compand.v=&ps->ms->over_ratio;
 
@@ -765,6 +912,7 @@
     GtkWidget *readout1=readout_new(" 100ms");
     GtkWidget *slider=multibar_slider_new(6,timing_labels,timing_levels,2);
    
+    ps->over_timing.s=MULTIBAR(slider);
     ps->over_timing.r0=READOUT(readout0);
     ps->over_timing.r1=READOUT(readout1);
     ps->over_timing.v0=&ps->ms->over_attack;
@@ -791,6 +939,7 @@
     GtkWidget *readout=readout_new("100%");
     GtkWidget *slider=multibar_slider_new(9,per_labels,per_levels,1);
    
+    ps->over_lookahead.s=MULTIBAR(slider);
     ps->over_lookahead.r=READOUT(readout);
     ps->over_lookahead.v=&ps->ms->over_lookahead;
 
@@ -825,6 +974,8 @@
                       G_CALLBACK (mode_peak), &ps->ms->base_mode);
     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);
+    ps->base_rms=rms_button;
+    ps->base_peak=peak_button;
   }
 
   /* base compand: ratio */
@@ -834,6 +985,7 @@
     GtkWidget *readout=readout_new("1.55:1");
     GtkWidget *slider=multibar_slider_new(9,compand_labels,compand_levels,1);
    
+    ps->base_compand.s=MULTIBAR(slider);
     ps->base_compand.r=READOUT(readout);
     ps->base_compand.v=&ps->ms->base_ratio;
 
@@ -857,6 +1009,7 @@
     GtkWidget *readout1=readout_new(" 100ms");
     GtkWidget *slider=multibar_slider_new(6,timing_labels,timing_levels,2);
 
+    ps->base_timing.s=MULTIBAR(slider);
     ps->base_timing.r0=READOUT(readout0);
     ps->base_timing.r1=READOUT(readout1);
     ps->base_timing.v0=&ps->ms->base_attack;

Modified: trunk/postfish/compandpanel.h
===================================================================
--- trunk/postfish/compandpanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/compandpanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,5 +31,7 @@
 extern void compandpanel_feedback(int displayit);
 extern void compandpanel_reset(void);
 
+extern void compandpanel_state_to_config(int bank);
+extern void compandpanel_state_from_config(int bank);
 
 

Added: trunk/postfish/config.c
===================================================================
--- trunk/postfish/config.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/config.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -0,0 +1,264 @@
+/*
+ *
+ *  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 "config.h"
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef struct {
+  char *key;
+  int bank;
+  int A;
+  int B;
+  int C;
+  int vals;
+  int *vec;
+  char *string;
+} configentry;
+
+static int configentries=0;
+static configentry *config_list=0;
+
+static int look_for_key(char *key,int bank,int A, int B, int C){
+  int i;
+  for(i=0;i<configentries;i++)
+    if(!strcmp(key,config_list[i].key) && 
+       config_list[i].bank==bank &&
+       config_list[i].A==A &&
+       config_list[i].B==B &&
+       config_list[i].C==C)return i;
+  return -1;
+}
+
+/* query the loaded config; this is just an interface to pre-parsed
+   input */
+const char *config_get_string(char *key,int bank, int A, int B, int C){
+  int i=look_for_key(key,bank,A,B,C);
+  if(i==-1)return NULL;
+
+  return config_list[i].string;
+}
+
+int config_get_integer(char *key,int bank, int A, int B, int C,int valnum, int *val){
+  int i=look_for_key(key,bank,A,B,C);
+  if(i==-1)return -1;
+  if(valnum<config_list[i].vals){
+    *val=config_list[i].vec[valnum];
+    return 0;
+  }
+  return -1;
+}
+
+int config_get_sigat(char *key,int bank, int A, int B, int C,int valnum, sig_atomic_t *val){
+  int ival=*val;
+  int ret=config_get_integer(key, bank, A, B, C,valnum, &ival);
+  *val=ival;
+  return ret;
+}
+
+int config_get_vector(char *key,int bank, int A, int B, int C,int n, sig_atomic_t *v){
+  int i=look_for_key(key,bank,A,B,C),j;
+  if(i==-1)return -1;
+
+  for(j=0;j<n && j<config_list[i].vals;j++)
+    v[j]=config_list[i].vec[j];
+  return 0;
+}
+
+static configentry *old_or_new(char *key,int bank,int A, int B, int C){
+  int i=look_for_key(key,bank,A,B,C);
+
+  if(i==-1){
+    /* create a new entry */
+    i=configentries;
+    configentries++;
+    if(config_list){
+      config_list=realloc(config_list,sizeof(*config_list)*configentries);
+      memset(&config_list[i],0,sizeof(*config_list));
+    }else{
+      config_list=calloc(1,sizeof(*config_list));
+    }
+    config_list[i].key=strdup(key);
+    config_list[i].bank=bank;
+    config_list[i].A=A;
+    config_list[i].B=B;
+    config_list[i].C=C;
+  }
+  return config_list+i;
+}
+
+static void extend_vec(configentry *c,int n){
+  if(n>c->vals){
+    if(!c->vec)
+      c->vec=calloc(n,sizeof(*c->vec));
+    else{
+      c->vec=realloc(c->vec,n*sizeof(*c->vec));
+      memset(c->vec+c->vals,0,(n-c->vals)*sizeof(*c->vec));
+    }
+    c->vals=n;
+  }
+}
+
+/* dump changes back into existing local config state; this is mostly
+   an elaborate means of meging changes into an existing file that may
+   be a superset of what's currently running */
+void config_set_string(char *key,int bank, int A, int B, int C, const char *s){
+  configentry *c=old_or_new(key,bank,A,B,C);
+  c->string=strdup(s);
+}
+
+void config_set_integer(char *key,int bank, int A, int B, int C, int valnum, int val){
+  configentry *c=old_or_new(key,bank,A,B,C);
+  extend_vec(c,valnum+1);
+  c->vec[valnum]=val;
+}
+
+void config_set_vector(char *key,int bank, int A, int B, int C,int n, sig_atomic_t *v){
+  int i;
+  configentry *c=old_or_new(key,bank,A,B,C);
+  extend_vec(c,n);
+  for(i=0;i<n;i++)
+    c->vec[i]=v[i];
+}
+
+int config_load(char *filename){
+  FILE *f=fopen(filename,"r");
+  char key[80];
+  int bank,A,B,C,width,rev;
+  int errflag=0;
+
+  fprintf(stderr,"Loading state configuration file %s... ",filename);
+
+  sprintf(key,"[file beginning]");
+
+  if(!f){
+    fprintf(stderr,"No config file %s; will be created on save/exit.\n",filename);
+    return 0;
+  }
+
+  /* search for magic */
+  if(fscanf(f,"Postfish rev %d",&rev)!=1 || rev!=2){
+    fprintf(stderr,"File %s is not a postfish state configuration file.\n",filename);
+    fclose(f);
+    return -1;
+  }
+
+  /* load file */
+  while(!feof(f)){
+    int c=fgetc(f);
+    switch(c){
+    case '(':  /* string type input */
+
+      if (fscanf(f,"%79s bank%d A%d B%d C%d l%d \"",
+		 key,&bank,&A,&B,&C,&width)==6){
+	char *buffer=calloc(width+1,sizeof(*buffer));
+	for(c=0;c<width;c++)buffer[c]=fgetc(f);
+
+	config_set_string(key,bank,A,B,C,buffer);
+	free(buffer);
+	fscanf(f,"\" )");
+	errflag=0;
+      }else{
+	if(!errflag){
+	  fprintf(stderr,"Configuration file parse error after %s\n",key);
+	  errflag=1;
+	}
+      }
+
+      break;
+    case '[':  /* vector type input */
+      if (fscanf(f,"%79s bank%d A%d B%d C%d v%d \"",
+		 key,&bank,&A,&B,&C,&width)==6){
+	int *vec=calloc(width,sizeof(*vec));
+	for(c=0;c<width;c++){
+	  if(fscanf(f,"%d",vec+c)!=1){
+	    if(!errflag){
+	      fprintf(stderr,"Configuration file parse error after %s\n",key);
+	      errflag=1;
+	      break;
+	    }
+	  }
+	}
+	fscanf(f," ]");
+	
+	config_set_vector(key,bank,A,B,C,width,vec);
+	free(vec);
+	errflag=0;
+      }else{
+	if(!errflag){
+	  fprintf(stderr,"Configuration file parse error after %s\n",key);
+	  errflag=1;
+	}
+      }
+
+      break;
+    default:
+      /* whitespace OK, other characters indicate a parse error */
+      if(!isspace(c) && !errflag && c!=EOF){
+	fprintf(stderr,"Configuration file parse error after %s\n",key);
+	errflag=1;
+      }
+      
+      break;
+    }
+  }
+  
+  fclose(f);
+  fprintf(stderr,"done.\n");
+  return 0;
+}
+
+/* save the config */
+void config_save(char *filename){
+  int i,j;
+  FILE *f=fopen(filename,"w");
+
+  fprintf(stderr,"Saving state to %s ...",filename);
+
+  if(!f){
+    fprintf(stderr,"\nUnable to save config file %s: %s\n",filename,strerror(errno));
+    return;
+  }
+  
+  fprintf(f,"Postfish rev 2\n");
+  
+  for(i=0;i<configentries;i++){
+    configentry *c=config_list+i;
+    if(c->string)
+      fprintf(f,"(%s bank%d A%d B%d C%d l%d \"%s\" )\n",
+	      c->key,c->bank,c->A,c->B,c->C,strlen(c->string),c->string);
+    if(c->vec){
+      fprintf(f,"[%s bank%d A%d B%d C%d v%d ",
+	      c->key,c->bank,c->A,c->B,c->C,c->vals);
+      for(j=0;j<c->vals;j++)
+	fprintf(f,"%d ",c->vec[j]);
+      
+      fprintf(f,"]\n");
+    }
+  }
+  fclose(f);
+  fprintf(stderr," done.\n");
+}
+

Added: trunk/postfish/config.h
===================================================================
--- trunk/postfish/config.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/config.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -0,0 +1,32 @@
+/*
+ *
+ *  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 const char *config_get_string(char *key,int bank, int A, int B, int C);
+extern int config_get_integer(char *key,int bank, int A, int B, int C,int valnum, int *val);
+extern int config_get_sigat(char *key,int bank, int A, int B, int C,int valnum, sig_atomic_t *val);
+extern int config_get_vector(char *key,int bank, int A, int B, int C,int n, sig_atomic_t *v);
+extern void config_set_string(char *key,int bank, int A, int B, int C, const char *s);
+extern void config_set_vector(char *key,int bank, int A, int B, int C,int n, sig_atomic_t *v);
+extern void config_set_integer(char *key,int bank, int A, int B, int C, int valnum, int val);
+extern int config_load(char *filename);
+extern void config_save(char *filename);

Modified: trunk/postfish/declip.c
===================================================================
--- trunk/postfish/declip.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/declip.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,7 +31,6 @@
 extern int input_rate;
 extern int input_ch;
 extern int input_size;
-extern int inbytes;
 
 /* accessed only in playback thread/setup */
 
@@ -60,10 +59,10 @@
 
 sig_atomic_t declip_visible=0;
 
-static float *chtrigger=0;
-static sig_atomic_t pending_blocksize=0;
-static float convergence=0.;
-static float iterations=0.;
+sig_atomic_t *declip_chtrigger=0;
+sig_atomic_t declip_pending_blocksize=0;
+sig_atomic_t declip_convergence=0;
+sig_atomic_t declip_iterations=0;
 
 /* feedback! */
 typedef struct declip_feedback{
@@ -152,9 +151,9 @@
   int i,j;
   declip_active=calloc(input_ch,sizeof(*declip_active));
   declip_prev_active=calloc(input_ch,sizeof(*declip_prev_active));
-  chtrigger=malloc(input_ch*sizeof(*chtrigger));
+  declip_chtrigger=malloc(input_ch*sizeof(*declip_chtrigger));
   for(i=0;i<input_ch;i++)
-    chtrigger[i]=1.;
+    declip_chtrigger[i]=10000;
   
   out.channels=input_ch;
   out.data=malloc(input_ch*sizeof(*out.data));
@@ -170,7 +169,7 @@
   for(i=0;i<input_ch;i++)
     lap[i]=malloc(input_size*sizeof(**lap));
   
-  window=malloc(input_size*2*sizeof(window));
+  window=malloc(input_size*2*sizeof(*window));
 
   {    
     /* alloc for largest possible blocksize */
@@ -178,8 +177,8 @@
     int loestpad=1-rint(fromBark(toBark(0.)-width)*blocksize*2/input_rate);
     int hiestpad=rint(fromBark(toBark(input_rate*.5)+width)*blocksize*2/input_rate)+loestpad;
     widthlookup=malloc((hiestpad+1)*sizeof(*widthlookup));
-    freq=fftwf_malloc((blocksize*2+2)*sizeof(freq));
-    work=fftwf_malloc((blocksize*2)*sizeof(freq));
+    freq=fftwf_malloc((blocksize*2+2)*sizeof(*freq));
+    work=fftwf_malloc((blocksize*2)*sizeof(*work));
 
     for(i=0,j=32;j<=blocksize*2;i++,j*=2){
       fftwf_weight=fftwf_plan_dft_r2c_1d(j,work,
@@ -190,39 +189,10 @@
   }
   reconstruct_init(32,input_size*4);
   
-  pending_blocksize=input_size*2;
+  declip_pending_blocksize=input_size*2;
   return(0);
 }
 
-int declip_setblock(int n){
-  if(n<32)return -1;
-  if(n>input_size*2)return -1;
-  pending_blocksize=n;
-  return 0;
-}
-
-int declip_settrigger(float trigger,int ch){
-  if(ch<0 || ch>=input_ch)return -1;
-  pthread_mutex_lock(&master_mutex);
-  chtrigger[ch]=trigger-(1./(1<<(inbytes*8-1)))-(1./(1<<(inbytes*8-2)));
-  pthread_mutex_unlock(&master_mutex);
-  return 0;
-}
-
-int declip_setiterations(float it){
-  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 */
 int declip_reset(void){
   /* reset cached pipe state */
@@ -312,17 +282,16 @@
   int total[input_ch];
   float peak[input_ch];
   u_int32_t active=0;
-  int next_blocksize=pending_blocksize;
+  int next_blocksize=declip_pending_blocksize;
   int orig_blocksize;
 
   float local_convergence;
   float local_iterations;
   
-  pthread_mutex_lock(&master_mutex);
-  local_convergence=convergence;
-  local_iterations=iterations;
-  memcpy(local_trigger,chtrigger,sizeof(local_trigger));
-  pthread_mutex_unlock(&master_mutex);
+  for(i=0;i<input_ch;i++)
+    local_trigger[i]=declip_chtrigger[i]*.0001;
+  local_iterations=declip_iterations*.0001;
+  local_convergence=fromdB(declip_convergence*.1);
 
   memset(count,0,sizeof(count));
   memset(peak,0,sizeof(peak));
@@ -379,9 +348,9 @@
           }
         }
 
-      }else{
-	/* no declipping to do, so direct cache/lap buffer rotation */
+      }//else no declipping to do, so direct cache/lap buffer rotation */
 
+      {
         float *temp=cache[i];
         cache[i]=in->data[i];
         in->data[i]=temp;

Modified: trunk/postfish/declip.h
===================================================================
--- trunk/postfish/declip.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/declip.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -22,10 +22,14 @@
  */
 
 extern int declip_load(void);
-extern int declip_setblock(int n);
-extern int declip_settrigger(float trigger,int ch);
-extern int declip_setiterations(float x);
-extern int declip_setconvergence(float x);
 extern int declip_reset(void);
 extern time_linkage *declip_read(time_linkage *in);
 extern int pull_declip_feedback(int *clip,float *peak,int *total);
+
+extern sig_atomic_t *declip_active;
+extern sig_atomic_t declip_pending_blocksize;
+extern sig_atomic_t *declip_chtrigger;
+extern sig_atomic_t declip_convergence;
+extern sig_atomic_t declip_iterations;
+extern sig_atomic_t declip_visible;
+

Modified: trunk/postfish/eq.c
===================================================================
--- trunk/postfish/eq.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/eq.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -27,8 +27,6 @@
 #include "freq.h"
 #include "eq.h"
 
-extern int input_size;
-
 typedef struct{
 
   freq_state eq;

Modified: trunk/postfish/eq.h
===================================================================
--- trunk/postfish/eq.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/eq.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -56,3 +56,7 @@
 extern void eq_set(eq_settings *eq,int freq, float value);
 extern time_linkage *eq_read_master(time_linkage *in);
 extern time_linkage *eq_read_channel(time_linkage *in);
+
+extern eq_settings eq_master_set;
+extern eq_settings *eq_channel_set;
+

Modified: trunk/postfish/eqpanel.c
===================================================================
--- trunk/postfish/eqpanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/eqpanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,14 +31,8 @@
 #include "feedback.h"
 #include "freq.h"
 #include "eq.h"
+#include "config.h"
 
-extern int input_ch;
-extern int input_size;
-extern int input_rate;
-
-extern eq_settings eq_master_set;
-extern eq_settings *eq_channel_set;
-
 typedef struct {
   GtkWidget *slider;
   GtkWidget *readout;
@@ -46,9 +40,46 @@
   int number;
 } bar;
 
-static bar *m_bars;
-static bar **c_bars;
+typedef struct {
+  subpanel_generic *panel;
+  bar *bars;
+} eq_panel_state;
 
+static eq_panel_state *master_panel;
+static eq_panel_state **channel_panel;
+
+static void eqpanel_state_to_config_helper(int bank,eq_settings *s,int A){
+  config_set_integer("eq_active",bank,A,0,0,0,s->panel_active);
+  config_set_vector("eq_settings",bank,A,0,0,eq_freqs,s->settings);
+}
+
+void eqpanel_state_to_config(int bank){
+  int i;
+  eqpanel_state_to_config_helper(bank,&eq_master_set,0);
+  for(i=0;i<input_ch;i++)
+    eqpanel_state_to_config_helper(bank,eq_channel_set+i,i+1);
+}
+
+static void eqpanel_state_from_config_helper(int bank,eq_settings *s,
+						 eq_panel_state *p,int A){
+
+  int i;
+  config_get_sigat("eq_active",bank,A,0,0,0,&s->panel_active);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->panel->subpanel_activebutton[0]),s->panel_active);
+
+  config_get_vector("eq_settings",bank,A,0,0,eq_freqs,s->settings);
+  for(i=0;i<eq_freqs;i++)
+    multibar_thumb_set(MULTIBAR(p->bars[i].slider),s->settings[i]*.1,0);
+
+}
+
+void eqpanel_state_from_config(int bank){
+  int i;
+  eqpanel_state_from_config_helper(bank,&eq_master_set,master_panel,0);
+  for(i=0;i<input_ch;i++)
+    eqpanel_state_from_config_helper(bank,eq_channel_set+i,channel_panel[i],i+1);
+}
+
 static void slider_change(GtkWidget *w,gpointer in){
   char buffer[80];
   bar *b=(bar *)in;
@@ -61,7 +92,7 @@
 
 }
 
-static bar *eqpanel_create_helper(postfish_mainpanel *mp,
+static eq_panel_state *eqpanel_create_helper(postfish_mainpanel *mp,
                            subpanel_generic *panel,
                            eq_settings *es){
 
@@ -73,7 +104,11 @@
 
   GtkWidget *slidertable=gtk_table_new(eq_freqs,3,0);
   bar *bars=calloc(eq_freqs,sizeof(*bars));
-  
+  eq_panel_state *p=calloc(1,sizeof(*p));
+
+  p->bars=bars;
+  p->panel=panel;
+
   for(i=0;i<eq_freqs;i++){
     const char *labeltext=eq_freq_labels[i];
     
@@ -104,7 +139,7 @@
   gtk_box_pack_start(GTK_BOX(panel->subpanel_box),slidertable,1,1,4);
   subpanel_show_all_but_toplevel(panel);
 
-  return bars;
+  return p;
 }
 
 void eqpanel_create_master(postfish_mainpanel *mp,
@@ -118,14 +153,14 @@
                                           "_Equalizer (master)",shortcut,
                                           0,1);
   
-  m_bars=eqpanel_create_helper(mp,panel,&eq_master_set);
+  master_panel=eqpanel_create_helper(mp,panel,&eq_master_set);
 }
 
 void eqpanel_create_channel(postfish_mainpanel *mp,
                                  GtkWidget **windowbutton,
                                  GtkWidget **activebutton){
   int i;
-  c_bars=malloc(input_ch*sizeof(*c_bars));
+  channel_panel=malloc(input_ch*sizeof(*channel_panel));
 
   /* a panel for each channel */
   for(i=0;i<input_ch;i++){
@@ -139,7 +174,7 @@
                           &eq_channel_set[i].panel_visible,
                           buffer,0,i,1);
   
-    c_bars[i]=eqpanel_create_helper(mp,panel,eq_channel_set+i);
+    channel_panel[i]=eqpanel_create_helper(mp,panel,eq_channel_set+i);
   }
 }
 
@@ -161,7 +196,7 @@
   
   if(pull_eq_feedback_master(peakfeed,rmsfeed)==1)
     for(i=0;i<eq_freqs;i++)
-      multibar_set(MULTIBAR(m_bars[i].slider),rmsfeed[i],peakfeed[i],
+      multibar_set(MULTIBAR(master_panel->bars[i].slider),rmsfeed[i],peakfeed[i],
                    OUTPUT_CHANNELS,(displayit && eq_master_set.panel_visible));
   
 
@@ -176,7 +211,7 @@
         rms[j]=rmsfeed[i][j];
         peak[j]=peakfeed[i][j];
         
-	multibar_set(MULTIBAR(c_bars[j][i].slider),rms,peak,
+	multibar_set(MULTIBAR(channel_panel[j]->bars[i].slider),rms,peak,
                      input_ch,(displayit && eq_channel_set[j].panel_visible));
       }
     }
@@ -186,10 +221,10 @@
 void eqpanel_reset(void){
   int i,j;
   for(i=0;i<eq_freqs;i++)
-    multibar_reset(MULTIBAR(m_bars[i].slider));
+    multibar_reset(MULTIBAR(master_panel->bars[i].slider));
   
   for(i=0;i<eq_freqs;i++)
     for(j=0;j<input_ch;j++)
-      multibar_reset(MULTIBAR(c_bars[j][i].slider));
+      multibar_reset(MULTIBAR(channel_panel[j]->bars[i].slider));
 }
 

Modified: trunk/postfish/eqpanel.h
===================================================================
--- trunk/postfish/eqpanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/eqpanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -30,3 +30,6 @@
                                    GtkWidget **activebutton);
 extern void eqpanel_feedback(int workp);
 extern void eqpanel_reset(void);
+
+extern void eqpanel_state_to_config(int bank);
+extern void eqpanel_state_from_config(int bank);

Modified: trunk/postfish/feedback.c
===================================================================
--- trunk/postfish/feedback.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/feedback.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -24,18 +24,20 @@
 #include "postfish.h"
 #include "feedback.h"
 
+static pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
 feedback_generic *feedback_new(feedback_generic_pool *pool,
                                feedback_generic *(*constructor)(void)){
   feedback_generic *ret;
   
-  pthread_mutex_lock(&master_mutex);
+  pthread_mutex_lock(&feedback_mutex);
   if(pool->feedback_pool){
     ret=pool->feedback_pool;
     pool->feedback_pool=pool->feedback_pool->next;
-    pthread_mutex_unlock(&master_mutex);
+    pthread_mutex_unlock(&feedback_mutex);
     return ret;
   }
-  pthread_mutex_unlock(&master_mutex);
+  pthread_mutex_unlock(&feedback_mutex);
 
   ret=constructor();
   return ret;
@@ -45,7 +47,7 @@
                    feedback_generic *f){
   f->next=NULL;
 
-  pthread_mutex_lock(&master_mutex);
+  pthread_mutex_lock(&feedback_mutex);
   if(!pool->feedback_list_tail){
     pool->feedback_list_tail=f;
     pool->feedback_list_head=f;
@@ -53,13 +55,13 @@
     pool->feedback_list_head->next=f;
     pool->feedback_list_head=f;
   }
-  pthread_mutex_unlock(&master_mutex);
+  pthread_mutex_unlock(&feedback_mutex);
 }
 
 feedback_generic *feedback_pull(feedback_generic_pool *pool){
   feedback_generic *f;
 
-  pthread_mutex_lock(&master_mutex);
+  pthread_mutex_lock(&feedback_mutex);
   if(pool->feedback_list_tail){
     
     f=pool->feedback_list_tail;
@@ -67,33 +69,33 @@
     if(!pool->feedback_list_tail)pool->feedback_list_head=0;
 
   }else{
-    pthread_mutex_unlock(&master_mutex);
+    pthread_mutex_unlock(&feedback_mutex);
     return 0;
   }
-  pthread_mutex_unlock(&master_mutex);
+  pthread_mutex_unlock(&feedback_mutex);
   return(f);
 }
 
 void feedback_old(feedback_generic_pool *pool,
                   feedback_generic *f){
   
-  pthread_mutex_lock(&master_mutex);
+  pthread_mutex_lock(&feedback_mutex);
   f->next=pool->feedback_pool;
   pool->feedback_pool=f;
-  pthread_mutex_unlock(&master_mutex);
+  pthread_mutex_unlock(&feedback_mutex);
 }
 
 /* are there multiple feedback outputs waiting or just one (a metric
    of 'are we behind?') */
 int feedback_deep(feedback_generic_pool *pool){
   if(pool){
-    pthread_mutex_lock(&master_mutex);
+    pthread_mutex_lock(&feedback_mutex);
     if(pool->feedback_list_tail)
       if(pool->feedback_list_tail->next){
-	pthread_mutex_unlock(&master_mutex);
+	pthread_mutex_unlock(&feedback_mutex);
         return 1;
       }
-    pthread_mutex_unlock(&master_mutex);
+    pthread_mutex_unlock(&feedback_mutex);
   }
   return 0;
 }

Modified: trunk/postfish/input.c
===================================================================
--- trunk/postfish/input.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/input.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -30,14 +30,14 @@
 static off_t        cursor=0;
 
 sig_atomic_t loop_active;
-int seekable;
+int input_seekable;
 
 int input_rate;
 int input_ch;
-int inbytes;
-static int signp;
 int input_size;
 
+pthread_mutex_t input_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
 typedef struct {
   FILE *f;
   
@@ -45,8 +45,27 @@
   off_t end;
   off_t data;
 
+  int bytes;
+  int signp;
+  int endian;
+  char *name;
+  
 } file_entry;
 
+typedef struct{
+  int ch;
+
+  int files;
+  file_entry *file_list;
+
+  int current_file_entry_number;
+  file_entry *current_file_entry;
+
+} group_entry;
+
+static int groups=0;
+static group_entry *group_list=0;
+
 typedef struct input_feedback{
   feedback_generic parent_class;
   off_t   cursor;
@@ -56,22 +75,18 @@
 
 static feedback_generic_pool feedpool;
 
-static file_entry *file_list=NULL;
-static int file_entries=0;
-static int current_file_entry_number=-1;
-static file_entry *current_file_entry=NULL;
 static time_linkage out;
 
 void input_Acursor_set(off_t c){
-  pthread_mutex_lock(&master_mutex);
+  pthread_mutex_lock(&input_mutex);
   Acursor=c;
-  pthread_mutex_unlock(&master_mutex);
+  pthread_mutex_unlock(&input_mutex);
 }
 
 void input_Bcursor_set(off_t c){
-  pthread_mutex_lock(&master_mutex);
+  pthread_mutex_lock(&input_mutex);
   Bcursor=c;
-  pthread_mutex_unlock(&master_mutex);
+  pthread_mutex_unlock(&input_mutex);
 }
 
 off_t input_time_to_cursor(const char *t){
@@ -125,7 +140,7 @@
   if(h<0)h=0;
 
   return ((off_t)hd + (off_t)s*100 + (off_t)m*60*100 + (off_t)h*60*60*100) *
-    input_rate / 100 * inbytes * input_ch;
+    input_rate / 100;
 }
 
 void time_fix(char *buffer){
@@ -145,7 +160,6 @@
 
 void input_cursor_to_time(off_t cursor,char *t){
   int h,m,s,hd;
-  cursor/=input_ch*inbytes;
 
   h=cursor/60/60/input_rate;
   cursor%=(off_t)60*60*input_rate;
@@ -159,151 +173,388 @@
   time_fix(t);
 }
 
-int input_load(int n,char *list[]){
-  char *fname="stdin";
-  int stdinp=0,i,j,ch=0,rate=0;
-  off_t total=0;
+void input_parse(char *filename,int newgroup){
+  if(newgroup){
+    /* add a group */
+    
+    if(!groups){
+      group_list=calloc(1,sizeof(*group_list));
+    }else{
+      group_list=realloc(group_list,sizeof(*group_list)*(groups+1));
+      memset(group_list+groups,0,sizeof(*group_list));
+    }
+    groups++;
+  }
+      
+  {
+    group_entry *g=group_list+groups-1;
+    file_entry *fe;
+    
+    if(g->files==0){
+      g->file_list=calloc(1,sizeof(*g->file_list));
+    }else{
+      g->file_list=realloc(g->file_list,
+			   sizeof(*g->file_list)*(g->files+1));
+      memset(g->file_list+g->files,0,sizeof(*g->file_list));
+    }
+    fe=g->file_list+g->files;
+    g->files++;
+    
+    fe->name=strdup(filename);
+  }
+}
 
-  if(n==0){
+/* Macros to read header data */
+#define READ_U32_LE(buf) \
+        (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U16_LE(buf) \
+        (((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U32_BE(buf) \
+        (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
+
+#define READ_U16_BE(buf) \
+        (((buf)[0]<<8)|((buf)[1]&0xff))
+
+double read_IEEE80(unsigned char *buf){
+  int s=buf[0]&0xff;
+  int e=((buf[0]&0x7f)<<8)|(buf[1]&0xff);
+  double f=((unsigned long)(buf[2]&0xff)<<24)|
+    ((buf[3]&0xff)<<16)|
+    ((buf[4]&0xff)<<8) |
+    (buf[5]&0xff);
+  
+  if(e==32767){
+    if(buf[2]&0x80)
+      return HUGE_VAL; /* Really NaN, but this won't happen in reality */
+    else{
+      if(s)
+	return -HUGE_VAL;
+      else
+	return HUGE_VAL;
+    }
+  }
+  
+  f=ldexp(f,32);
+  f+= ((buf[6]&0xff)<<24)|
+    ((buf[7]&0xff)<<16)|
+    ((buf[8]&0xff)<<8) |
+    (buf[9]&0xff);
+  
+  return ldexp(f, e-16446);
+}
+
+static int find_chunk(FILE *in, char *type, unsigned int *len, int endian){
+  unsigned int i;
+  unsigned char buf[8];
+
+  while(1){
+    if(fread(buf,1,8,in) <8)return 0;
+
+    if(endian)
+      *len = READ_U32_BE(buf+4);
+    else
+      *len = READ_U32_LE(buf+4);
+
+    if(memcmp(buf,type,4)){
+
+      if((*len) & 0x1)(*len)++;
+      
+      for(i=0;i<*len;i++)
+	if(fgetc(in)==EOF)return 0;
+
+    }else return 1;
+  }
+}
+
+int input_load(void){
+
+  int stdinp=0,i,k;
+
+  input_ch=0;
+
+  if(groups==0){
     /* look at stdin... is it a file, pipe, tty...? */
     if(isatty(STDIN_FILENO)){
       fprintf(stderr,
               "Postfish requires input either as a list of contiguous WAV\n"
               "files on the command line, or WAV data piped|redirected to\n"
-	      "stdin.\n");
+	      "stdin. postfish -h will give more details.\n");
       return 1;
     }
     stdinp=1;    /* file coming in via stdin */
-    file_entries=1;
-  }else
-    file_entries=n;
-
-  file_list=calloc(file_entries,sizeof(file_entry));
-  for(i=0;i<file_entries;i++){
-    FILE *f;
     
-    if(stdinp){
-      int newfd=dup(STDIN_FILENO);
-      f=fdopen(newfd,"rb");
-    }else{
-      f=fopen(list[i],"rb");
-      fname=list[i];
-    }
+    group_list=calloc(1,sizeof(*group_list));
+    group_list[0].file_list=calloc(1,sizeof(*group_list[0].file_list));
 
-    if(f){
-      unsigned char buffer[81];
-      off_t filelength;
-      int datap=0;
-      int fmtp=0;
-      file_list[i].f=f;
-      
-      /* parse header (well, sort of) and get file size */
-      seekable=(fseek(f,0,SEEK_CUR)?0:1);
-      if(!seekable){
-	filelength=-1;
+    groups=1;
+    group_list[0].files=1;
+    group_list[0].file_list[0].name="stdin";
+  }
+
+  for(k=0;k<groups;k++){
+    group_entry *g=group_list+k;
+    off_t total=0;
+
+    for(i=0;i<g->files;i++){
+      file_entry *fe=g->file_list+i;
+      FILE *f;
+      char *fname="stdin";
+
+      if(stdinp){
+	int newfd=dup(STDIN_FILENO);
+	f=fdopen(newfd,"rb");
       }else{
-	fseek(f,0,SEEK_END);
-	filelength=ftello(f);
-	fseek(f,0,SEEK_SET);
+	fname=g->file_list[i].name;
+	f=fopen(fname,"rb");
       }
 
-      fread(buffer,1,12,f);
-      if(strncmp(buffer,"RIFF",4) || strncmp(buffer+8,"WAVE",4)){
-	fprintf(stderr,"%s: Not a WAVE file.\n",fname);
-	return 1;
-      }
+      /* Crappy! Use a lib to do this for pete's sake! */
+      if(f){
+	unsigned char headerid[12];
+	off_t filelength;
+	fe->f=f;
+	
+	/* parse header (well, sort of) and get file size */
+	input_seekable=(fseek(f,0,SEEK_CUR)?0:1);
+	if(!input_seekable){
+	  filelength=-1;
+	}else{
+	  fseek(f,0,SEEK_END);
+	  filelength=ftello(f);
+	  fseek(f,0,SEEK_SET);
+	}
 
-      while(fread(buffer,1,8,f)==8){
-	unsigned long chunklen=
-	  buffer[4]|(buffer[5]<<8)|(buffer[6]<<16)|(buffer[7]<<24);
+	fread(headerid,1,12,f);
+	if(!strncmp(headerid,"RIFF",4) && !strncmp(headerid+8,"WAVE",4)){
+	  unsigned int chunklen;
 
-	if(!strncmp(buffer,"fmt ",4)){
-	  int ltype;
+	  if(find_chunk(f,"fmt ",&chunklen,0)){
+	    int ltype;
+	    int lch;
+	    int lrate;
+	    int lbits;
+	    unsigned char *buf=alloca(chunklen);
+	    
+	    fread(buf,1,chunklen,f);
+	    
+	    ltype = READ_U16_LE(buf); 
+	    lch =   READ_U16_LE(buf+2); 
+	    lrate = READ_U32_LE(buf+4);
+	    lbits = READ_U16_LE(buf+14);
+	    
+	    if(ltype!=1){
+	      fprintf(stderr,"%s:\n\tWAVE file not PCM.\n",fname);
+	      return 1;
+	    }
+	      
+	    fe->bytes=(lbits+7)/8;
+	    fe->signp=0;
+	    fe->endian=0;
+	    if(fe->bytes>1)fe->signp=1;
+	    
+	    if(lrate<4000 || lrate>192000){
+	      fprintf(stderr,"%s:\n\tSampling rate out of bounds\n",fname);
+	      return 1;
+	    }
+	    
+	    if(k==0 && i==0){
+	      input_rate=lrate;
+	    }else if(input_rate!=lrate){
+	      fprintf(stderr,"%s:\n\tInput files must all be same sampling rate.\n",fname);
+	      return 1;
+	    }
+	    
+	    if(i==0){
+	      g->ch=lch;
+	      input_ch+=lch;
+	    }else{
+	      if(g->ch!=lch){
+		fprintf(stderr,"%s:\n\tInput files must all have same number of channels.\n",fname);
+		return 1;
+	      }
+	    }
+
+	    if(find_chunk(f,"data",&chunklen,0)){
+	      off_t pos=ftello(f);
+	      
+	      if(input_seekable)
+		filelength=
+		  (filelength-pos)/
+		  (g->ch*fe->bytes)*
+		  (g->ch*fe->bytes)+pos;
+	      
+	      if(chunklen==0UL ||
+		 chunklen==0x7fffffffUL || 
+		 chunklen==0xffffffffUL){
+		if(filelength==-1){
+		  fe->begin=total;
+		  total=fe->end=-1;
+		  fprintf(stderr,"%s: Incomplete header; assuming stream.\n",fname);
+		}else{
+		  fe->begin=total;
+		  total=fe->end=total+(filelength-pos)/(g->ch*fe->bytes);
+		  fprintf(stderr,"%s: Incomplete header; using actual file size.\n",fname);
+		}
+	      }else if(filelength==-1 || chunklen+pos<=filelength){
+		fe->begin=total;
+		total=fe->end=total+ (chunklen/(g->ch*fe->bytes));
+		fprintf(stderr,"%s: Using declared file size.\n",fname);
+
+	      }else{
+		fe->begin=total;
+		total=fe->end=total+(filelength-pos)/(g->ch*fe->bytes);
+		fprintf(stderr,"%s: File truncated; Using actual file size.\n",fname);
+	      }
+	      fe->data=ftello(f);
+	    } else {
+	      fprintf(stderr,"%s: WAVE file has no \"data\" chunk following \"fmt \".\n",fname);
+	      return 1;
+	    }
+	  }else{
+	    fprintf(stderr,"%s: WAVE file has no \"fmt \" chunk.\n",fname);
+	    return 1;
+	  }
+
+	}else if(!strncmp(headerid,"FORM",4) && !strncmp(headerid+8,"AIF",3)){
+	  unsigned int len;
+	  int aifc=0;
+	  if(headerid[11]=='C')aifc=1;
+	  unsigned char *buffer;
+	  char buf2[8];
+
           int lch;
+	  int lbits;
           int lrate;
-	  int lbits;
-
-	  if(chunklen>80){
-	    fprintf(stderr,"%s: WAVE file fmt chunk too large to parse.\n",fname);
+	  
+	  /* look for COMM */
+	  if(!find_chunk(f, "COMM", &len,1)){
+	    fprintf(stderr,"%s: AIFF file has no \"COMM\" chunk.\n",fname);
             return 1;
           }
-	  fread(buffer,1,chunklen,f);
           
-	  ltype=buffer[0] |(buffer[1]<<8);
-	  lch=  buffer[2] |(buffer[3]<<8);
-	  lrate=buffer[4] |(buffer[5]<<8)|(buffer[6]<<16)|(buffer[7]<<24);
-	  lbits=buffer[14]|(buffer[15]<<8);
+	  if(len < 18 || (aifc && len<22)) {
+	    fprintf(stderr,"%s: AIFF COMM chunk is truncated.\n",fname);
+	    return 1;
+	  }
+	  
+	  buffer = alloca(len);
 
-	  if(ltype!=1){
-	    fprintf(stderr,"%s: WAVE file not PCM.\n",fname);
+	  if(fread(buffer,1,len,f) < len){
+	    fprintf(stderr, "%s: Unexpected EOF in reading AIFF header\n",fname);
             return 1;
           }
 
+	  lch = READ_U16_BE(buffer);
+	  lbits = READ_U16_BE(buffer+6);
+	  lrate = (int)read_IEEE80(buffer+8);
+
+	  fe->endian = 1; // default
+	      
+	  fe->bytes=(lbits+7)/8;
+	  fe->signp=1;
+	    
+	  if(lrate<4000 || lrate>192000){
+	    fprintf(stderr,"%s:\n\tSampling rate out of bounds\n",fname);
+	    return 1;
+	  }
+
+	  if(k==0 && i==0){
+	    input_rate=lrate;
+	  }else if(input_rate!=lrate){
+	    fprintf(stderr,"%s:\n\tInput files must all be same sampling rate.\n",fname);
+	    return 1;
+	  }
+	    
           if(i==0){
-	    ch=lch;
-	    rate=lrate;
-	    inbytes=lbits/8;
-	    if(inbytes>1)signp=1;
+	    g->ch=lch;
+	    input_ch+=lch;
           }else{
-	    if(ch!=lch){
-	      fprintf(stderr,"%s: WAVE files must all have same number of channels.\n",fname);
+	    if(g->ch!=lch){
+	      fprintf(stderr,"%s:\n\tInput files must all have same number of channels.\n",fname);
               return 1;
             }
-	    if(rate!=lrate){
-	      fprintf(stderr,"%s: WAVE files must all be same sampling rate.\n",fname);
+	  }
+
+	  if(aifc){
+	    if(!memcmp(buffer+18, "NONE", 4)) {
+	      fe->endian = 1;
+	    }else if(!memcmp(buffer+18, "sowt", 4)) {
+	      fe->endian = 0;
+	    }else{
+	      fprintf(stderr, "%s: Postfish supports only linear PCM AIFF-C files.\n",fname);
               return 1;
             }
-	    if(inbytes!=lbits/8){
-	      fprintf(stderr,"%s: WAVE files must all be same sample width.\n",fname);
-	      return 1;
-	    }
           }
-	  fmtp=1;
-	} else if(!strncmp(buffer,"data",4)){
-	  off_t pos=ftello(f);
-	  if(!fmtp){
-	    fprintf(stderr,"%s: WAVE fmt chunk must preceed data chunk.\n",fname);
+
+	  if(!find_chunk(f, "SSND", &len, 1)){
+	    fprintf(stderr,"%s: AIFF file has no \"SSND\" chunk.\n",fname);
             return 1;
           }
-	  datap=1;
+
+	  if(fread(buf2,1,8,f) < 8){
+	    fprintf(stderr,"%s: Unexpected EOF reading AIFF header\n",fname);
+	    return 1;
+	  }
           
-	  if(seekable)
-	    filelength=(filelength-pos)/(ch*inbytes)*(ch*inbytes)+pos;
+	  {
+	    int loffset = READ_U32_BE(buf2);
+	    int lblocksize = READ_U32_BE(buf2+4);
 
-	  if(chunklen==0UL ||
-	     chunklen==0x7fffffffUL || 
-	     chunklen==0xffffffffUL){
-	    file_list[i].begin=total;
-	    total=file_list[i].end=0;
-	    fprintf(stderr,"%s: Incomplete header; assuming stream.\n",fname);
-	  }else if(filelength==-1 || chunklen+pos<=filelength){
-	    file_list[i].begin=total;
-	    total=file_list[i].end=total+chunklen;
-	    fprintf(stderr,"%s: Using declared file size.\n",fname);
-	  }else{
-	    file_list[i].begin=total;
-	    total=file_list[i].end=total+filelength-pos;
-	    fprintf(stderr,"%s: Using actual file size.\n",fname);
+	    /* swallow some data */
+	    for(i=0;i<loffset;i++)
+	      if(fgetc(f)==EOF)break;
+	    
+	    if( lblocksize == 0 && (lbits == 32 || lbits == 24 || lbits == 16 || lbits == 8)){
+
+	      off_t pos=ftello(f);
+	      
+	      if(input_seekable)
+		filelength=
+		  (filelength-pos)/
+		  (g->ch*fe->bytes)*
+		  (g->ch*fe->bytes)+pos;
+	      
+	      if(len==0UL ||
+		 len==0x7fffffffUL || 
+		 len==0xffffffffUL){
+		if(filelength==-1){
+		  fe->begin=total;
+		  total=fe->end=-1;
+		  fprintf(stderr,"%s: Incomplete header; assuming stream.\n",fname);
+		}else{
+		  fe->begin=total;
+		  total=fe->end=total+(filelength-pos)/(g->ch*fe->bytes);
+		  fprintf(stderr,"%s: Incomplete header; using actual file size.\n",fname);
+		}
+	      }else if(filelength==-1 || (len+pos-loffset-8)<=filelength){
+		fe->begin=total;
+		total=fe->end=total+ ((len-loffset-8)/(g->ch*fe->bytes));
+		fprintf(stderr,"%s: Using declared file size.\n",fname);
+
+	      }else{
+		fe->begin=total;
+		total=fe->end=total+(filelength-pos)/(g->ch*fe->bytes);
+		fprintf(stderr,"%s: File truncated; Using actual file size.\n",fname);
+	      }
+	      fe->data=pos;
+	    }else{
+	      fprintf(stderr, "%s: Postfish supports only linear PCM AIFF-C files.\n",fname);
+	      return 1;
+	    }
           }
-	  file_list[i].data=ftello(f);
-	  
-	  break;
+
         } else {
-	  fprintf(stderr,"%s: Unknown chunk type %c%c%c%c; skipping.\n",fname,
-		  buffer[0],buffer[1],buffer[2],buffer[3]);
-	  for(j=0;j<(int)chunklen;j++)
-	    if(fgetc(f)==EOF)break;
+
+	  fprintf(stderr,"%s: Postfish supports only linear PCM WAV and AIFF[-C] files.\n",fname);
+	  return 1;
         }
-      }
 
-      if(!datap){
-	fprintf(stderr,"%s: WAVE file has no data chunk.\n",fname);
+      }else{
+	fprintf(stderr,"%s: Unable to open file.\n",fname);
         return 1;
       }
-      
-    }else{
-      fprintf(stderr,"%s: Unable to open file.\n",fname);
-      return 1;
     }
   }
 
@@ -326,230 +577,349 @@
 
        4000:  256 */
 
-  if(rate<6000){
+  if(input_rate<6000){
     input_size=256;
-  }else if(rate<15000){
+  }else if(input_rate<15000){
     input_size=512;
-  }else if(rate<25000){
+  }else if(input_rate<25000){
     input_size=1024;
-  }else if(rate<50000){
+  }else if(input_rate<50000){
     input_size=2048;
-  }else if(rate<100000){
+  }else if(input_rate<100000){
     input_size=4096;
   }else
     input_size=8192;
 
-  input_ch=out.channels=ch;
-  input_rate=rate;
-  out.data=malloc(sizeof(*out.data)*ch);
-  for(i=0;i<ch;i++)
-    out.data[i]=malloc(sizeof(*out.data[0])*input_size);
+  out.channels=input_ch;
+
+  out.data=malloc(sizeof(*out.data)*input_ch);
+  for(i=0;i<input_ch;i++)
+    out.data[i]=malloc(sizeof(**out.data)*input_size);
   
   return 0;
 }
 
-off_t input_seek(off_t pos){
-  int i;
+off_t input_seek_i(off_t pos,int ps){
+  int i,k;
+  int flag=0;
+  off_t maxpos=0;
 
   if(pos<0)pos=0;
-  if(!seekable){
-    current_file_entry=file_list;
-    current_file_entry_number=0;
+  if(!input_seekable){
+    for(i=0;i<groups;i++){
+      group_list[i].current_file_entry=group_list[i].file_list;
+      group_list[i].current_file_entry_number=0;
+    }
     return -1;
   }
 
-  for(i=0;i<file_entries;i++){
-    current_file_entry=file_list+i;
-    current_file_entry_number=i;
-    if(current_file_entry->begin<=pos && current_file_entry->end>pos){
-      fseeko(current_file_entry->f,
-	     pos-current_file_entry->begin+current_file_entry->data,
-	     SEEK_SET);
-      pthread_mutex_lock(&master_mutex);
-      cursor=pos;
-      playback_seeking=1;
-      pthread_mutex_unlock(&master_mutex);
-      return cursor;
+  pthread_mutex_lock(&input_mutex);
+  if(ps)playback_seeking=1;
+
+  /* seek has to happen correctly in all groups */
+  for(k=0;k<groups;k++){
+    group_entry *g=group_list+k;
+
+    for(i=0;i<g->files;i++){
+      file_entry *fe=g->current_file_entry=g->file_list+i;
+      g->current_file_entry_number=i;
+
+      if(fe->begin<=pos && fe->end>pos){
+	flag=1;
+	fseeko(fe->f, 
+	       (pos-fe->begin)*(g->ch*fe->bytes)+fe->data,
+	       SEEK_SET);
+	break;
+      }
     }
+    
+    if(i==g->files){
+      /* this group isn't that long; seek to the end of it */
+      file_entry *fe=g->current_file_entry;
+      
+      fseeko(fe->f,(fe->end-fe->begin)*(g->ch*fe->bytes)+fe->data,SEEK_SET);
+      
+      if(fe->end>maxpos)maxpos=fe->end;
+    }
   }
-  i--;
 
-  pos=current_file_entry->end;
-  fseeko(current_file_entry->f,
-	 pos-current_file_entry->begin+current_file_entry->data,
-	 SEEK_SET);
-  pthread_mutex_lock(&master_mutex);
-  cursor=pos;
-      playback_seeking=1;
-  pthread_mutex_unlock(&master_mutex);
+  if(flag){
+    cursor=pos;
+    pthread_mutex_unlock(&input_mutex);
+  }else{
+    cursor=maxpos;
+    pthread_mutex_unlock(&input_mutex);
+  }
+
   return cursor;
 }
 
+off_t input_seek(off_t pos){
+  return input_seek_i(pos,1);
+}
+
 off_t input_time_seek_rel(float s){
-  off_t ret;
-  pthread_mutex_lock(&master_mutex);
-  ret=input_seek(cursor+input_rate*inbytes*input_ch*s);
-  pthread_mutex_unlock(&master_mutex);
-  return ret;
+  return input_seek(cursor+input_rate*s);
 }
 
 static feedback_generic *new_input_feedback(void){
   input_feedback *ret=malloc(sizeof(*ret));
-  ret->rms=malloc((input_ch+2)*sizeof(*ret->rms));
-  ret->peak=malloc((input_ch+2)*sizeof(*ret->peak));
+  ret->rms=malloc(input_ch*sizeof(*ret->rms));
+  ret->peak=malloc(input_ch*sizeof(*ret->peak));
   return (feedback_generic *)ret;
 }
 
 static void push_input_feedback(float *peak,float *rms, off_t cursor){
-  int n=input_ch+2;
   input_feedback *f=(input_feedback *)
     feedback_new(&feedpool,new_input_feedback);
   f->cursor=cursor;
-  memcpy(f->rms,rms,n*sizeof(*rms));
-  memcpy(f->peak,peak,n*sizeof(*peak));
+  memcpy(f->rms,rms,input_ch*sizeof(*rms));
+  memcpy(f->peak,peak,input_ch*sizeof(*peak));
   feedback_push(&feedpool,(feedback_generic *)f);
 }
 
 int pull_input_feedback(float *peak,float *rms,off_t *cursor){
   input_feedback *f=(input_feedback *)feedback_pull(&feedpool);
-  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);
+  if(rms)memcpy(rms,f->rms,sizeof(*rms)*input_ch);
+  if(peak)memcpy(peak,f->peak,sizeof(*peak)*input_ch);
   if(cursor)*cursor=f->cursor;
   feedback_old(&feedpool,(feedback_generic *)f);
   return 1;
 }
 
+static void LEconvert(float **data,
+		      unsigned char *readbuf, int dataoff,
+		      int ch,int bytes, int signp, int n){
+  int i,j,k=0;
+  int32_t val;
+  int32_t xor=(signp?0:0x80000000UL);
+  float scale=1./2147483648.;
+
+  k=0;
+  switch(bytes){
+  case 1:
+    
+    for(i=dataoff;i<dataoff+n;i++)
+      for(j=0;j<ch;j++){
+	val=(readbuf[k]<<24)^xor;
+	data[j][i]=(val==0x7f000000?1.:val*scale);
+	k++;
+      }
+    break;
+
+  case 2:
+
+    for(i=dataoff;i<dataoff+n;i++)
+      for(j=0;j<ch;j++){
+	val=((readbuf[k]<<16)|(readbuf[k+1]<<24))^xor;
+	data[j][i]=(val==0x7fff0000?1.:val*scale);
+	k+=2;
+      }
+    break;
+
+  case 3:
+
+    for(i=dataoff;i<dataoff+n;i++)
+      for(j=0;j<ch;j++){
+	val=((readbuf[k]<<8)|(readbuf[k+1]<<16)|(readbuf[k+2]<<24))^xor;
+	data[j][i]=(val==0x7fffff00?1.:val*scale);
+	k+=3;
+      }
+    break;
+
+  case 4:
+
+    for(i=dataoff;i<dataoff+n;i++)
+      for(j=0;j<ch;j++){
+	val=((readbuf[k])|(readbuf[k+1]<<8)|(readbuf[k+2]<<16)|(readbuf[k+3]<<24))^xor;
+	data[j][i]=(val==0x7fffffff?1.:val*scale);
+	k+=4;
+      }
+    break;
+  }
+}
+
+static void BEconvert(float **data,
+		      unsigned char *readbuf, int dataoff,
+		      int ch,int bytes, int signp, int n){
+  int i,j,k=0;
+  int32_t val;
+  int32_t xor=(signp?0:0x80000000UL);
+  float scale=1./2147483648.;
+
+  k=0;
+  switch(bytes){
+  case 1:
+    
+    for(i=dataoff;i<dataoff+n;i++)
+      for(j=0;j<ch;j++){
+	val=(readbuf[k]<<24)^xor;
+	data[j][i]=(val==0x7f000000?1.:val*scale);
+	k++;
+      }
+    break;
+
+  case 2:
+
+    for(i=dataoff;i<dataoff+n;i++)
+      for(j=0;j<ch;j++){
+	val=((readbuf[k+1]<<16)|(readbuf[k]<<24))^xor;
+	data[j][i]=(val==0x7fff0000?1.:val*scale);
+	k+=2;
+      }
+    break;
+
+  case 3:
+
+    for(i=dataoff;i<dataoff+n;i++)
+      for(j=0;j<ch;j++){
+	val=((readbuf[k+2]<<8)|(readbuf[k+1]<<16)|(readbuf[k]<<24))^xor;
+	data[j][i]=(val==0x7fffff00?1.:val*scale);
+	k+=3;
+      }
+    break;
+
+  case 4:
+
+    for(i=dataoff;i<dataoff+n;i++)
+      for(j=0;j<ch;j++){
+	val=((readbuf[k+3])|(readbuf[k+2]<<8)|(readbuf[k+1]<<16)|(readbuf[k]<<24))^xor;
+	data[j][i]=(val==0x7fffffff?1.:val*scale);
+	k+=4;
+      }
+    break;
+  }
+}
+
+static void zero(float **data, int dataoff, int ch, int n){
+  int i,j;
+  
+  for(i=dataoff;i<dataoff+n;i++)
+    for(j=0;j<ch;j++)
+      data[j][i]=0;
+}
+
+/* no locking within as the only use of input_read is locked in the
+   playback thread (must be locked there because the real lock needs
+   to avoid a seeking race) */
+
 time_linkage *input_read(void){
-  int read_b=0,i,j,k;
-  int toread_b=input_size*out.channels*inbytes;
-  unsigned char *readbuf;
-  float *rms=alloca(sizeof(*rms)*(out.channels+2));
-  float *peak=alloca(sizeof(*peak)*(out.channels+2));
+  int h,i,j;
+  int groupread_s=0;
 
-  memset(rms,0,sizeof(*rms)*(out.channels+2));
-  memset(peak,0,sizeof(*peak)*(out.channels+2));
+  float *rms=alloca(sizeof(*rms)*(out.channels));
+  float *peak=alloca(sizeof(*peak)*(out.channels));
 
-  pthread_mutex_lock(&master_mutex);
+  memset(rms,0,sizeof(*rms)*(out.channels));
+  memset(peak,0,sizeof(*peak)*(out.channels));
+
   out.samples=0;
 
   /* the non-streaming case */
-  if(!loop_active && 
-     cursor>=current_file_entry->end &&
-     current_file_entry->end!=-1){
-    pthread_mutex_unlock(&master_mutex);
-    goto tidy_up;
+  if(!loop_active && input_seekable){
+    for(i=0;i<groups;i++)
+      if(cursor<group_list[i].file_list[group_list[i].files-1].end)
+	break;
+    if(i==groups)goto tidy_up;
   }
 
   /* the streaming case */
-  if(feof(current_file_entry->f) && 
-     current_file_entry_number+1>=file_entries){
-    pthread_mutex_unlock(&master_mutex);
+  if(!input_seekable && feof(group_list[0].current_file_entry->f)){ 
     goto tidy_up;
   }
-  pthread_mutex_unlock(&master_mutex);
 
-  readbuf=alloca(toread_b);
+  /* If we're A-B looping, we might need several loops/seeks */
+  while(groupread_s<input_size){
+    int chcount=0;
+    int max_read_s=0;
 
-  while(toread_b){
-    off_t ret;
-    off_t read_this_loop=current_file_entry->end-cursor;
-    if(read_this_loop>toread_b)read_this_loop=toread_b;
+    if(loop_active && cursor>=Bcursor){
+      input_seek_i(Acursor,0);
+    }
 
-    ret=fread(readbuf+read_b,1,read_this_loop,current_file_entry->f);
+    /* each read section is by group */
+    for(h=0;h<groups;h++){
+      group_entry *g=group_list+h;
+      int toread_s=input_size-groupread_s;
+      int fileread_s=0;
+      
+      if(input_seekable && loop_active && toread_s>Bcursor-cursor)
+	toread_s = Bcursor-cursor;
+      
+      /* inner loop in case the read spans multiple files within the group */
+      while(toread_s){
+	file_entry *fe=g->current_file_entry;
+	off_t ret;
 
-    pthread_mutex_lock(&master_mutex);
+	/* span forward to next file entry in the group? */
+	if(cursor+fileread_s>=fe->end && 
+	   g->current_file_entry_number+1<g->files){
+	  fe=++g->current_file_entry;
+	  g->current_file_entry_number++;
+	  fseeko(fe->f,fe->data,SEEK_SET);
+	}
 
-    if(ret>0){
-      read_b+=ret;
-      toread_b-=ret;
-      cursor+=ret;
-    }else{
-      if(current_file_entry_number+1>=file_entries){
+	/* perform read/conversion of this file entry */
+	{
+	  off_t read_this_loop=fe->end-cursor-fileread_s;
+	  unsigned char readbuf[input_size*(g->ch*fe->bytes)];
+	  if(read_this_loop>toread_s)read_this_loop=toread_s;
+	  
+	  ret=fread(readbuf,1,read_this_loop*(g->ch*fe->bytes),fe->f);
+	  
+	  if(ret>0){
+	    ret/=(g->ch*fe->bytes);
+	    
+	    if(fe->endian)
+	      BEconvert(out.data+chcount,readbuf,
+			fileread_s+groupread_s,g->ch,fe->bytes,fe->signp,ret);
+	    else
+	      LEconvert(out.data+chcount,readbuf,
+			fileread_s+groupread_s,g->ch,fe->bytes,fe->signp,ret);
+	    
+	    fileread_s+=ret;
+	    toread_s-=ret;
 
-	/* end of file before full frame */
-	memset(readbuf+read_b,0,toread_b);
-	toread_b=0;
+	  }else{
+	    if(g->current_file_entry_number+1>=g->files){
 
+	      /* end of group before full frame */	      
+	      zero(out.data+chcount,fileread_s+groupread_s,g->ch,toread_s);
+	      toread_s=0;
+	    }
+	  }
+	}
       }
-    }
 
-    if(loop_active && cursor>=Bcursor){
-      pthread_mutex_unlock(&master_mutex);
-      input_seek(Acursor);
-    }else{
-      if(cursor>=current_file_entry->end){
-	pthread_mutex_unlock(&master_mutex);
-	if(current_file_entry_number+1<file_entries){
-	  current_file_entry_number++;
-	  current_file_entry++;
-	  fseeko(current_file_entry->f,current_file_entry->data,SEEK_SET);
-	}
-      }else
-	pthread_mutex_unlock(&master_mutex);
+      if(max_read_s<fileread_s)max_read_s=fileread_s;
+      chcount+=g->ch;
     }
-  }
 
-  out.samples=read_b/out.channels/inbytes;
-  
-  k=0;
-  for(i=0;i<out.samples;i++){
-    float mean=0.;
-    float divrms=0.;
+    groupread_s+=max_read_s;
+    cursor+=max_read_s;
 
-    for(j=0;j<out.channels;j++){
-      float dval;
-      long val=0;
-      switch(inbytes){
-      case 1:
-	val=(readbuf[k]<<24);
-	break;
-      case 2:
-	val=(readbuf[k]<<16)|(readbuf[k+1]<<24);
-	break;
-      case 3:
-	val=(readbuf[k]<<8)|(readbuf[k+1]<<16)|(readbuf[k+2]<<24);
-	break;
-      case 4:
-	val=(readbuf[k])|(readbuf[k+1]<<8)|(readbuf[k+2]<<16)|(readbuf[k+3]<<24);
-	break;
-      }
-      if(signp)
-	dval=out.data[j][i]=val/2147483648.;
-      else
-	dval=out.data[j][i]=(val^0x80000000UL)/2147483648.;
+    if(!loop_active || cursor<Bcursor) break;
 
-      if(fabs(dval)>peak[j])peak[j]=fabs(dval);
-      rms[j]+= dval*dval;
-      mean+=dval;
+  }
 
-      k+=inbytes;
-    }
+  out.samples=groupread_s;
 
-    /* mean */
-    mean/=j;
-    if(fabs(mean)>peak[j])peak[j]=fabs(mean);
-    rms[j]+= mean*mean;
-
-    /* div */
+  for(i=0;i<groupread_s;i++)
     for(j=0;j<out.channels;j++){
-      float dval=mean-out.data[j][i];
-      if(fabs(dval)>peak[out.channels+1])peak[out.channels+1]=fabs(dval);
-      divrms+=dval*dval;
+      float dval=out.data[j][i];
+      dval*=dval;
+      if(dval>peak[j])peak[j]=dval;
+      rms[j]+= dval;
     }
-    rms[out.channels+1]+=divrms/out.channels;
-      
-  }    
   
-  for(j=0;j<out.channels+2;j++){
+  for(j=0;j<out.channels;j++)
     rms[j]/=out.samples;
-    rms[j]=sqrt(rms[j]);
-  }
 
   push_input_feedback(peak,rms,cursor);
 
  tidy_up:
+
   {
     int tozero=input_size-out.samples;
     if(tozero)
@@ -567,3 +937,4 @@
 
 
 
+

Modified: trunk/postfish/input.h
===================================================================
--- trunk/postfish/input.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/input.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -5,7 +5,8 @@
 extern void time_fix(char *buffer);
 extern off_t input_seek(off_t pos);
 extern time_linkage *input_read(void);
-extern int input_load(int n,char *list[]);
+extern void input_parse(char *filename,int newgroup);
+extern int input_load(void);
 extern int pull_input_feedback(float *peak,float *rms,off_t *cursor);
 extern void input_reset(void);
 extern off_t input_time_seek_rel(float s);

Modified: trunk/postfish/limit.c
===================================================================
--- trunk/postfish/limit.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/limit.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -41,6 +41,9 @@
 
   int prev_active;
   int initted;
+
+  float pthresh;
+  float pdepth;
 } limit_state;
 
 limit_settings limitset;
@@ -109,7 +112,7 @@
 int limit_reset(void){
   /* reset cached pipe state */
   while(pull_limit_feedback(NULL,NULL));
-  memset(limitstate.iir,0,limitstate.out.channels*sizeof(&limitstate.iir));
+  memset(limitstate.iir,0,limitstate.out.channels*sizeof(*limitstate.iir));
   limitstate.initted=0;
   return 0;
 }
@@ -133,6 +136,7 @@
 
   float thresh=limitset.thresh/10.-.01;
   float depth=limitset.depth;
+
   float localpeak;
   float localatt;
 
@@ -144,14 +148,17 @@
     return &limitstate.out;
   }
 
+  depth=depth*.2;
+  depth*=depth;
+
   if(!limitstate.initted){
     limitstate.initted=1;
     limitstate.prev_active=activeC;
+
+    limitstate.pthresh=thresh;
+    limitstate.pdepth=depth;
   }
 
-  depth=depth*.2;
-  depth*=depth;
-
   for(i=0;i<ch;i++){
     localpeak=0.;
     localatt=0.;
@@ -162,16 +169,26 @@
       
       float *inx=in->data[i];
       float *x=limitstate.out.data[i];
+
+      float prev_thresh=limitstate.pthresh;
+      float prev_depth=limitstate.pdepth;
+
+      float thresh_add=(thresh-prev_thresh)/input_size;
+      float depth_add=(depth-prev_depth)/input_size;
       
+
       /* '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 knee=limit_knee(dB-prev_thresh,prev_depth)+prev_thresh;
         float att=dB-knee;
         
         if(att>localatt)localatt=att;
         
         x[k]=att;
+
+	prev_depth+=depth_add;
+	prev_thresh+=thresh_add;
       }
         
       compute_iir_decayonly2(x,input_size,limitstate.iir+i,&limitstate.decay);
@@ -242,6 +259,9 @@
 
   limitstate.out.active=in->active;
   limitstate.prev_active=activeC;
+  limitstate.pthresh=thresh;
+  limitstate.pdepth=depth;
+
   return &limitstate.out;
 }
 

Modified: trunk/postfish/limit.h
===================================================================
--- trunk/postfish/limit.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/limit.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,3 +31,7 @@
 extern int limit_load(int ch);
 extern int limit_reset(void);
 extern time_linkage *limit_read(time_linkage *in);
+
+extern sig_atomic_t limit_active;
+extern sig_atomic_t limit_visible;
+extern limit_settings limitset;

Modified: trunk/postfish/limitpanel.c
===================================================================
--- trunk/postfish/limitpanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/limitpanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,17 +31,35 @@
 #include "feedback.h"
 #include "limit.h"
 #include "limitpanel.h"
+#include "config.h"
 
-extern sig_atomic_t limit_active;
-extern sig_atomic_t limit_visible;
-extern int input_size;
-extern int input_rate;
-
-extern limit_settings limitset;
-
+static GtkWidget *active;
 static GtkWidget *t_slider;
+static GtkWidget *k_slider;
+static GtkWidget *d_slider;
 static GtkWidget *a_slider;
 
+void limitpanel_state_to_config(int bank){
+  config_set_integer("limit_active",bank,0,0,0,0,limit_active);
+  config_set_integer("limit_set",bank,0,0,0,0,limitset.thresh);
+  config_set_integer("limit_set",bank,0,0,0,1,limitset.depth);
+  config_set_integer("limit_set",bank,0,0,0,2,limitset.decay);
+}
+
+void limitpanel_state_from_config(int bank){
+  config_get_sigat("limit_active",bank,0,0,0,0,&limit_active);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(active),limit_active);
+
+  config_get_sigat("limit_set",bank,0,0,0,0,&limitset.thresh);
+  multibar_thumb_set(MULTIBAR(t_slider),limitset.thresh*.1,0);
+
+  config_get_sigat("limit_set",bank,0,0,0,1,&limitset.depth);
+  multibar_thumb_set(MULTIBAR(k_slider),limitset.depth*.1,0);
+
+  config_get_sigat("limit_set",bank,0,0,0,2,&limitset.decay);
+  multibar_thumb_set(MULTIBAR(d_slider),limitset.decay*.1,0);
+}
+
 static void limit_change(GtkWidget *w,gpointer in){
   char buffer[80];
   Readout *r=(Readout *)in;
@@ -126,9 +144,14 @@
   GtkWidget *slider2=multibar_slider_new(5,labels2,levels2,1);
   GtkWidget *slider3=multibar_slider_new(6,timing_labels,timing_levels,1);
 
+  active=activebutton;
+
   t_slider=multibar_new(9,labels,levels,1,HI_DECAY);
   a_slider=multibar_new(4,rlabels,rlevels,0,0);
 
+  k_slider=slider2;
+  d_slider=slider3;
+
   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);

Modified: trunk/postfish/limitpanel.h
===================================================================
--- trunk/postfish/limitpanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/limitpanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -28,3 +28,5 @@
 extern void limitpanel_reset(void);
 
 
+extern void limitpanel_state_to_config(int bank);
+extern void limitpanel_state_from_config(int bank);

Modified: trunk/postfish/main.c
===================================================================
--- trunk/postfish/main.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/main.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -28,8 +28,11 @@
 
 /* sound playback code is OSS-specific for now */
 #include "postfish.h"
+#include <signal.h>
+#include <getopt.h>
 #include <fenv.h>  // Thank you C99!
 #include <fftw3.h>
+#include <gtk/gtk.h>
 #include "input.h"
 #include "output.h"
 #include "declip.h"
@@ -41,19 +44,168 @@
 #include "mute.h"
 #include "mix.h"
 #include "reverb.h"
+#include "version.h"
+#include "config.h"
+#include "mainpanel.h"
 
-pthread_mutex_t master_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
-
-int outfileno=-1;
 int eventpipe[2];
+sig_atomic_t main_looping;
+char *configfile="postfish-staterc";
+char *version;
 
+static void cleanup(void){
+  save_state();
+}
+
+void clean_exit(int sig){
+  signal(sig,SIG_IGN);
+  if(sig!=SIGINT){
+    fprintf(stderr,
+	    "\nTrapped signal %d; saving state and exiting!\n"
+	    "This signal almost certainly indicates a bug in the Postfish;\n"
+	    "If this version of Postfish is newer than a few months old,\n"
+	    "please email a detailed report of the crash along with\n"
+	    "processor type, OS version, FFTW3 version, and as much\n"
+	    "information as possible about what caused the crash.  The best\n"
+	    "possible report will outline the exact steps needed to\n"
+	    "reproduce the error, ensuring that we at Xiph can fix the\n"
+	    "bug as quickly as possible.\n\n"
+	    "-- monty at xiph.org, Postfish revision %s\n\n",sig,version);
+    configfile="postfish-staterc-crashsave";
+    cleanup();
+    exit(0);
+    
+  }
+
+  /* otherwise we want a clean SIGINT exit */
+  if(main_looping)
+    gtk_main_quit();
+  else{
+    cleanup();
+    exit(0);
+  }
+}
+
+const char *optstring = "-c:gh";
+
+struct option options [] = {
+        {"configuration-file",required_argument,NULL,'c'},
+        {"group",no_argument,NULL,'g'},
+        {"help",no_argument,NULL,'h'},
+
+        {NULL,0,NULL,0}
+};
+
+static void usage(FILE *f){
+  fprintf( f,
+"\nthe Postfish, revision %s\n\n"
+
+"USAGE:\n"
+"  postfish [options] infile [infile]+ [-g infile [infile]+]+ > output\n\n"
+
+"OPTIONS:\n"
+"  -c --configuration-file    : load state from alternate configuration file\n"
+"  -g --group                 : place following input files in a new channel\n"
+"                               grouping\n"
+"  -h --help                  : print this help\n\n"
+"INPUT:\n\n"
+
+" Postfish takes WAV/AIFF input either from stdin or from a list of files\n"
+" specified on the command line.  A list of input files is handled as\n"
+" time-continguous entries, each holding audio data that continues at\n"
+" the instant the previous file ends.  Files may also be arranged into\n"
+" groups with -g; each group represents additional input channels\n"
+" parallel to preceeding groups. All input files must be the same\n"
+" sampling rate.  Files in a group must have the same number of\n"
+" channels.\n\n"
+
+" Examples:\n\n"
+" Files a.wav, b.wav, c.wav and d.wav are all four channels and\n"
+" ten minutes each.\n\n"
+
+" postfish a.wav b.wav c.wav d.wav \n"
+"   This command line treats the input as forty minutes of four channel\n"
+"   audio in the order a.wav, b.wav, c.wav, d.wav.\n\n"
+
+" postfish a.wav b.wav -g c.wav d.wav \n"
+"   This command line treats the input as twenty minutes of eight channel\n"
+"   audio.  Channels 1-4 are taken from files a.wav and b.wav while channels\n"
+"   5-8 are taken from files c.wav  and d.wav.\n\n"
+
+" cat a.wav | postfish \n"
+"   This command line sends a.wav to Postfish as a non-seekable stream\n"
+"   of four-channel data. If the WAV (or AIFF) header is complete, Postfish\n"
+"   obeys the length encoded in the header and halts after processing to\n"
+"   that length.  If the data length in the header is unset (0 or -1),\n"
+"   Postfish will continue processing data until EOF on stdin.\n\n"
+
+"OUTPUT:\n\n"
+
+" Postfish writes output to stdout.\n\n" 
+
+" If stdout is piped, the output is nonseekable and Postfish marks the\n"
+" produced header incomplete (length of -1).  Stopping and re-starting\n"
+" processing writes a fresh stream to stdout.\n\n"
+
+" If stdout is redirected to a file, Postfish will write a complete header\n"
+" upon processing halt or program exit.  If processing halts and restarts,\n"
+" the file is re-written from scratch.\n\n"
+
+" If stdout is a pipe or redirected to a file, the user may specify\n"
+" parallel audio monitor through the audio device using the 'mOn' activator\n"
+" button in the main panel's 'master' section, or on the output config\n"
+" panel.  The audio device selected for playback is configurable on the\n"
+" output config panel.\n\n"
+
+" If stdout is redirected to an audio device, output is sent to that audio\n"
+" device exclusively and the 'mOn' activator on the main panel will not\n"
+" be available.\n\n"
+
+"STATE/CONFIG:\n\n"
+
+" By default, persistent panel state is loaded from the file \n"
+" 'postfish-staterc' in the current working directory.  Postfish rewrites\n"
+" this file with all current panel state upon exit.  -c specifies loading\n"
+" from and saving to an alternate configuration file name.\n\n",version);
+
+}
+
+void parse_command_line(int argc, char **argv){
+  int c,long_option_index;
+  int newgroup=1;
+
+  while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
+    switch(c){
+    case 1:
+      /* file name that belongs to current group */
+      input_parse(optarg,newgroup);      
+      newgroup=0;
+      break;
+    case 'c':
+      /* alternate configuration file */
+      configfile=strdup(optarg);
+      break;
+    case 'g':
+      /* start a new file/channel group */
+      newgroup=1;
+      break;
+    case 'h':
+      usage(stdout);
+      exit(0);
+    default:
+      usage(stderr);
+      exit(0);
+    }
+  }
+}
+
 int look_for_wisdom(char *filename){
   int ret;
   FILE *f=fopen(filename,"r");
   if(!f)return 0;
   ret=fftwf_import_wisdom_from_file(f);
   fclose(f);
-
+ 
   if(ret)
     fprintf(stderr,"Found valid postfish-wisdomrc file at %s\n",filename);
   else
@@ -61,10 +213,33 @@
   return(ret);
 }
 
+static int sigill=0;
+void sigill_handler(int sig){
+  /* make sure */
+  if(sig==SIGILL)sigill=1;
+}
+
 int main(int argc, char **argv){
-  int configfd=-1;
   int wisdom=0;
 
+  version=strstr(VERSION,"version.h");
+  if(version){
+    char *versionend=strchr(version,' ');
+    if(versionend)versionend=strchr(versionend+1,' ');
+    if(versionend)versionend=strchr(versionend+1,' ');
+    if(versionend)versionend=strchr(versionend+1,' ');
+    if(versionend){
+      int len=versionend-version-9;
+      version=strdup(version+10);
+      version[len-1]=0;
+    }
+  }else{
+    version="";
+  }
+
+  /* parse command line and open all the input files */
+  parse_command_line(argc, argv);
+
   /* We do not care about FPEs; rather, underflow is nominal case, and
      its better to ignore other traps in production than to crash the
      app.  Please inform the FPU of this. */
@@ -74,14 +249,16 @@
 
   /* Linux Altivec support has a very annoying problem; by default,
      math on denormalized floats will simply crash the program.  FFTW3
-     uses Altivec, so boom.
+     uses Altivec, so boom, but only random booms.
      
      By the C99 spec, the above exception configuration is also
      supposed to handle Altivec config, but doesn't.  So we use the
-     below ugliness. */
+     below ugliness to both handle altivec and non-alitvec PPC. */
 
 #ifdef __PPC
 #include <altivec.h>
+  signal(SIGILL,sigill_handler);
+  
 #if (defined __GNUC__) && (__GNUC__ == 3) && ! (defined __APPLE_CC__)
   __vector unsigned short noTrap = 
     (__vector unsigned short){0,0,0,0,0,0,0x1,0};
@@ -139,8 +316,16 @@
             "window because this information must be regenerated each time Postfish runs.\n");
   }
 
-  /* parse command line and open all the input files */
-  if(input_load(argc-1,argv+1))exit(1);
+  /* probe outputs */
+  output_probe_stdout(STDOUT_FILENO);
+  output_probe_monitor();
+
+  /* open all the input files */
+  if(input_load())exit(1);
+
+  /* load config file */
+  if(config_load(configfile))exit(1);
+
   /* set up filter chains */
   if(declip_load())exit(1);
   if(eq_load(OUTPUT_CHANNELS))exit(1);
@@ -152,21 +337,6 @@
   if(mix_load(OUTPUT_CHANNELS))exit(1);
   if(plate_load(OUTPUT_CHANNELS))exit(1);
 
-  /* look at stdout... do we have a file or device? */
-  if(!isatty(STDOUT_FILENO)){
-    /* assume this is the file/device for output */
-    outfileno=dup(STDOUT_FILENO);
-    dup2(STDERR_FILENO,STDOUT_FILENO);
-  }
-
-  /* load config */
-#if 0
-  {
-    configfd=open(".postfishrc",O_RDWR|O_CREAT,0666);
-    if(configfd>=0)load_settings(configfd);
-  }
-#endif
-
   /* easiest way to inform gtk of changes and not deal with locking
      issues around the UI */
   if(pipe(eventpipe)){
@@ -177,13 +347,17 @@
   }
 
   input_seek(0);
+
+  main_looping=0;
+
+  signal(SIGINT,clean_exit);
+  signal(SIGSEGV,clean_exit);
+
   mainpanel_go(argc,argv,input_ch);
 
   output_halt_playback();
 
-  //save_settings(configfd);
-  if(configfd>=0)close(configfd);
-
+  cleanup();
   return(0);
 }
 

Modified: trunk/postfish/mainpanel.c
===================================================================
--- trunk/postfish/mainpanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/mainpanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -28,12 +28,94 @@
 #include "buttonicons.h"
 #include "multibar.h"
 #include "readout.h"
-#include "version.h"
 #include "input.h"
 #include "output.h"
 #include "mainpanel.h"
 #include "windowbutton.h"
+#include "config.h"
 
+static postfish_mainpanel p;
+extern char *configfile;
+
+static void action_setb_to(postfish_mainpanel *p,const char *time);
+static void action_seta_to(postfish_mainpanel *p,const char *time);
+
+
+static void mainpanel_state_to_config(int bank){
+  int i;
+  float f;
+
+  f=multibar_get_value(MULTIBAR(p.masterdB_s),0);
+  i=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p.masterdB_a));
+
+  config_set_integer("mainpanel_master_att",bank,0,0,0,0,rint(f*10));
+  config_set_integer("mainpanel_master_att_active",bank,0,0,0,0,i);
+
+  config_set_string("mainpanel_cue_A",0,0,0,0,gtk_entry_get_text(GTK_ENTRY(p.entry_a)));
+  config_set_string("mainpanel_cue_B",0,0,0,0,gtk_entry_get_text(GTK_ENTRY(p.entry_b)));
+  config_set_integer("mainpanel_loop",0,0,0,0,0,
+		     gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p.cue_b)));
+
+  for(i=0;i<input_ch || i<OUTPUT_CHANNELS;i++)
+    config_set_integer("mainpanel_VU_show",0,0,0,0,i,
+		       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p.channelshow[i])));
+
+  clippanel_state_to_config(bank);
+  compandpanel_state_to_config(bank);
+  singlepanel_state_to_config(bank);
+  suppresspanel_state_to_config(bank);
+  eqpanel_state_to_config(bank);
+  reverbpanel_state_to_config(bank);
+  limitpanel_state_to_config(bank);
+  outpanel_state_to_config(bank);
+  mixpanel_state_to_config(bank);
+
+}
+
+static void mainpanel_state_from_config(int bank){
+
+  int val,i;
+  const char *string;
+
+  if(!config_get_integer("mainpanel_master_att",bank,0,0,0,0,&val))
+    multibar_thumb_set(MULTIBAR(p.masterdB_s),val*.1,0);
+
+  if(!config_get_integer("mainpanel_master_att_active",bank,0,0,0,0,&val))
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p.masterdB_a),val);
+
+  /* A/B state are saved but *not* banked */
+  if((string=config_get_string("mainpanel_cue_A",0,0,0,0)))
+    action_seta_to(&p,string);
+  if((string=config_get_string("mainpanel_cue_B",0,0,0,0)))
+    action_setb_to(&p,string);
+  if(!config_get_integer("mainpanel_loop",0,0,0,0,0,&val))
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p.cue_b),val);
+
+  for(i=0;i<input_ch || i<OUTPUT_CHANNELS;i++)
+    if(!config_get_integer("mainpanel_VU_show",0,0,0,0,i,&val))
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p.channelshow[i]),val);
+
+  clippanel_state_from_config(bank);
+  compandpanel_state_from_config(bank);
+  singlepanel_state_from_config(bank);
+  suppresspanel_state_from_config(bank);
+  eqpanel_state_from_config(bank);
+  reverbpanel_state_from_config(bank);
+  limitpanel_state_from_config(bank);
+  outpanel_state_from_config(bank);
+  mixpanel_state_from_config(bank);
+
+}
+
+void save_state(){
+  mainpanel_state_to_config(0);
+  config_save(configfile);
+}
+
+static void savestatei(GtkWidget *dummy, gpointer in){
+  save_state();
+}
+
 static void meterhold_reset(postfish_mainpanel *p){
   p->inpeak=-200;
   p->outpeak=-200;
@@ -210,8 +292,7 @@
     
 }
 
-static void action_seta(GtkWidget *widget,postfish_mainpanel *p){
-  const char *time=readout_get(READOUT(p->cue));
+static void action_seta_to(postfish_mainpanel *p,const char *time){
   gtk_entry_set_text(GTK_ENTRY(p->entry_a),time);
 
   {
@@ -224,38 +305,42 @@
       loop_active=0;
       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->cue_b),0);
     }
+    input_Acursor_set(cursora);
   }
 
 }
 
-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);
+static void action_seta(GtkWidget *widget,postfish_mainpanel *p){
+  const char *time=readout_get(READOUT(p->cue));
+  action_seta_to(p,time);
+}
+
+static void action_setb_to(postfish_mainpanel *p,const char *time){
+  off_t cursora,cursorb;
   
-  time=readout_get(READOUT(p->cue));
   cursorb=input_time_to_cursor(time);
   gtk_entry_set_text(GTK_ENTRY(p->entry_b),time);
     
-  {
-    const char *time=gtk_entry_get_text(GTK_ENTRY(p->entry_a));
-    off_t cursora=input_time_to_cursor(time),cursorb;
-    time=gtk_entry_get_text(GTK_ENTRY(p->entry_b));
-    cursorb=input_time_to_cursor(time);
-    
-    if(cursora>=cursorb && loop_active){
-      loop_active=0;
-      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->cue_b),0);
-    }
+  time=gtk_entry_get_text(GTK_ENTRY(p->entry_a));
+  cursora=input_time_to_cursor(time);
+
+  if(cursora>=cursorb && loop_active){
+    loop_active=0;
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->cue_b),0);
   }
+  input_Bcursor_set(cursorb);
+ 
 }
 
+static void action_setb(GtkWidget *widget,postfish_mainpanel *p){
+  const char *time=  time=readout_get(READOUT(p->cue));
+  action_setb_to(p,time);
+}
+
 static void shutdown(void){
   gtk_main_quit ();
 }
 
-sig_atomic_t master_att;
 static void masterdB_change(GtkWidget *dummy, gpointer in){
   postfish_mainpanel *p=in;
   char buf[80];
@@ -334,6 +419,8 @@
   case GDK_3:
   case GDK_4:
   case GDK_5:
+    if(pos==4 || pos==7 || pos==10)pos++;
+
     if(pos>12)return TRUE;
     {
       char buffer[15];
@@ -551,6 +638,7 @@
 
 #define CHANNEL_EFFECTS 7
 #define MASTER_EFFECTS 7
+extern char *version;
 void mainpanel_create(postfish_mainpanel *panel,char **chlabels){
   char *text_bar[7]={"[bksp]","[<]","[,]","[space]","[.]","[>]","[end]"};
 
@@ -559,12 +647,13 @@
   GdkBitmap *xbm_bar[7];
   GtkWidget *gim_bar[7];
 
-  GtkWidget *topplace,*topal;
+  GtkWidget *topplace,*topal,*topalb;
 
   GtkWidget *topframe=gtk_frame_new (NULL);
   GtkWidget *toplabel=gtk_label_new (NULL);
 
   GtkWidget *quitbutton=gtk_button_new_with_mnemonic("_quit");
+  GtkWidget *savebutton=gtk_button_new_with_label("^save");
 
   GtkWidget *mainbox=gtk_hbox_new(0,6);
   GtkWidget *masterback=gtk_event_box_new();
@@ -591,20 +680,6 @@
   panel->channeleffects=CHANNEL_EFFECTS;
   
   char versionmarkup[240];
-  char *version=strstr(VERSION,"version.h");
-  if(version){
-    char *versionend=strchr(version,' ');
-    if(versionend)versionend=strchr(versionend+1,' ');
-    if(versionend)versionend=strchr(versionend+1,' ');
-    if(versionend)versionend=strchr(versionend+1,' ');
-    if(versionend){
-      int len=versionend-version-9;
-      version=strdup(version+10);
-      version[len-1]=0;
-    }
-  }else{
-    version="";
-  }
   snprintf(versionmarkup,240," <span size=\"large\" weight=\"bold\" "
            "style=\"italic\" foreground=\"dark blue\">"
            "Postfish</span>  <span size=\"small\" foreground=\"#606060\">"
@@ -613,10 +688,15 @@
 
 
   topplace=gtk_table_new(1,1,0);
+  topalb=gtk_hbox_new(0,0);
   topal=gtk_alignment_new(1,0,0,0);
 
   gtk_widget_set_name(quitbutton,"quitbutton");
-  gtk_container_add (GTK_CONTAINER(topal),quitbutton);
+
+  gtk_box_pack_start(GTK_BOX(topalb),savebutton,0,0,0);
+  gtk_box_pack_start(GTK_BOX(topalb),quitbutton,0,0,0);
+  gtk_container_add (GTK_CONTAINER(topal),topalb);
+
   
   gtk_table_attach_defaults(GTK_TABLE(topplace),
                             topal,0,1,0,1);
@@ -625,11 +705,17 @@
     
   gtk_container_add (GTK_CONTAINER (panel->toplevel), topplace);
   gtk_container_set_border_width (GTK_CONTAINER (quitbutton), 3);
+  gtk_container_set_border_width (GTK_CONTAINER (savebutton), 3);
 
   g_signal_connect (G_OBJECT (quitbutton), "clicked",
                     G_CALLBACK (shutdown), NULL);
+
+  g_signal_connect (G_OBJECT (savebutton), "clicked",
+		    G_CALLBACK (savestatei), 0);
   
+  gtk_widget_add_accelerator (savebutton, "activate", panel->group, GDK_s, GDK_CONTROL_MASK, 0);
 
+
   g_signal_connect (G_OBJECT (panel->toplevel), "key-press-event",
                     G_CALLBACK (mainpanel_keybinding), panel);
 
@@ -783,6 +869,7 @@
       gtk_widget_add_accelerator (panel->masterdB_a, "activate", panel->group, GDK_t, 0, 0);
       g_signal_connect_after (G_OBJECT(panel->masterdB_a), "clicked",
                         G_CALLBACK(masterdB_change), (gpointer)panel);
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->masterdB_a),1);
     }
 
     /* master action bar */
@@ -833,15 +920,24 @@
                         G_CALLBACK (action_ff), panel);
       g_signal_connect (G_OBJECT (panel->deckactive[6]), "clicked",
                         G_CALLBACK (action_end), panel);
-
-      gtk_widget_add_accelerator (panel->deckactive[0], "activate", panel->group, GDK_BackSpace, 0, 0);
-      gtk_widget_add_accelerator (panel->deckactive[1], "activate", panel->group, GDK_less, 0, 0);
-      gtk_widget_add_accelerator (panel->deckactive[2], "activate", panel->group, GDK_comma, 0, 0);
+      
       gtk_widget_add_accelerator (panel->deckactive[3], "activate", panel->group, GDK_space, 0, 0);
-      gtk_widget_add_accelerator (panel->deckactive[4], "activate", panel->group, GDK_period, 0, 0);
-      gtk_widget_add_accelerator (panel->deckactive[5], "activate", panel->group, GDK_greater, 0, 0);
-      gtk_widget_add_accelerator (panel->deckactive[6], "activate", panel->group, GDK_End, 0, 0);
 
+      if(!input_seekable){
+	gtk_widget_set_sensitive(panel->deckactive[0],FALSE);
+	gtk_widget_set_sensitive(panel->deckactive[1],FALSE);
+	gtk_widget_set_sensitive(panel->deckactive[2],FALSE);
+	gtk_widget_set_sensitive(panel->deckactive[4],FALSE);
+	gtk_widget_set_sensitive(panel->deckactive[5],FALSE);
+	gtk_widget_set_sensitive(panel->deckactive[6],FALSE);
+      }else{
+	gtk_widget_add_accelerator (panel->deckactive[0], "activate", panel->group, GDK_BackSpace, 0, 0);
+	gtk_widget_add_accelerator (panel->deckactive[1], "activate", panel->group, GDK_less, 0, 0);
+	gtk_widget_add_accelerator (panel->deckactive[2], "activate", panel->group, GDK_comma, 0, 0);
+	gtk_widget_add_accelerator (panel->deckactive[4], "activate", panel->group, GDK_period, 0, 0);
+	gtk_widget_add_accelerator (panel->deckactive[5], "activate", panel->group, GDK_greater, 0, 0);
+	gtk_widget_add_accelerator (panel->deckactive[6], "activate", panel->group, GDK_End, 0, 0);
+      }
 
     }
 
@@ -869,6 +965,7 @@
       gtk_box_pack_start(GTK_BOX(cuebox),framea,1,1,3);
       gtk_box_pack_start(GTK_BOX(cuebox),temp,0,0,0);
       gtk_box_pack_start(GTK_BOX(cuebox),panel->entry_a,0,0,0);
+      if(!input_seekable)gtk_widget_set_sensitive(temp,FALSE);
 
       temp=gtk_button_new_with_label(" A ");
       gtk_widget_add_accelerator (temp, "activate", panel->group, GDK_A, GDK_SHIFT_MASK, 0);
@@ -882,6 +979,7 @@
                         G_CALLBACK (action_entryb), panel);
       gtk_box_pack_start(GTK_BOX(cuebox),frameb,1,1,3);
       gtk_box_pack_start(GTK_BOX(cuebox),temp,0,0,0);
+      if(!input_seekable)gtk_widget_set_sensitive(temp,FALSE);
 
       temp=gtk_button_new_with_label(" B ");
       gtk_widget_add_accelerator (temp, "activate", panel->group, GDK_B, GDK_SHIFT_MASK, 0);
@@ -998,7 +1096,7 @@
     GtkWidget *ww=windowbutton_new("_Output ");
 
     GtkWidget *std=gtk_toggle_button_new_with_label(" o ");
-    GtkWidget *ply=gtk_toggle_button_new_with_label("play");
+    GtkWidget *ply=gtk_toggle_button_new_with_label("mOn");
     GtkWidget *box=gtk_hbox_new(0,0);
     GtkWidget *box2=gtk_hbox_new(1,0);
 
@@ -1009,7 +1107,8 @@
     gtk_widget_set_sensitive(fw,FALSE);
       
     gtk_widget_add_accelerator (std, "activate", panel->group, GDK_o, 0, 0);
-    gtk_widget_add_accelerator (ply, "activate", panel->group, GDK_p, 0, 0);
+    gtk_widget_add_accelerator (ply, "activate", panel->group, GDK_O, 
+				GDK_SHIFT_MASK, 0);
       
     gtk_box_pack_start(GTK_BOX(box),ww,0,0,0);
     gtk_box_pack_start(GTK_BOX(box),box2,1,1,2);
@@ -1022,7 +1121,10 @@
     gtk_table_attach_defaults(GTK_TABLE(mastertable),box,0,1,7,8);
     gtk_table_attach_defaults(GTK_TABLE(mastertable),std,1,2,7,8);
 
-    //if(panel_create)(*panel_create)(p,ww,(shortcut?wa:0));
+    {
+      GtkWidget *active[2]={ply,std};
+      outpanel_create(panel,ww,active);
+    }
   }
 
   g_signal_connect (G_OBJECT (panel->toplevel), "delete_event",
@@ -1051,28 +1153,27 @@
   if(!playback_seeking){
     int     current_p=!output_feedback_deep();
     off_t   time_cursor;
-    int     n=(input_ch>1?input_ch+2:input_ch);
-    float *rms=alloca(sizeof(*rms)*(input_ch+2));
-    float *peak=alloca(sizeof(*peak)*(input_ch+2));
+    float *rms=alloca(sizeof(*rms)*(input_ch+OUTPUT_CHANNELS));
+    float *peak=alloca(sizeof(*peak)*(input_ch+OUTPUT_CHANNELS));
     
     if(pull_output_feedback(peak,rms)){
       char buffer[14];
       int i;
 
-      for(i=0;i<n;i++){
-	if(i<input_ch && peak[i]>=1.)
+      for(i=0;i<OUTPUT_CHANNELS;i++){
+	if(peak[i]>=1.)
           multibar_setwarn(MULTIBAR(panel->outbar),current_p);
 
         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->channelshow[i]))){
-	  peak[i]=todB(peak[i]);
-	  rms[i]=todB(rms[i]);
-
+	  peak[i]=todB(peak[i])*.5;
+	  rms[i]=todB(rms[i])*.5;
+	  
           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;
@@ -1080,14 +1181,14 @@
         
       }
       
-      multibar_set(MULTIBAR(panel->outbar),rms,peak,n,current_p);
+      multibar_set(MULTIBAR(panel->outbar),rms,peak,input_ch,current_p);
       
       if(pull_input_feedback(peak,rms,&time_cursor)){
-	for(i=0;i<n;i++){
+	for(i=0;i<input_ch;i++){
           if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->channelshow[i]))){
-	    peak[i]=todB(peak[i]);
-	    rms[i]=todB(rms[i]);
-
+	    peak[i]=todB(peak[i])*.5;
+	    rms[i]=todB(rms[i])*.5;
+	    
             if(peak[i]>panel->inpeak){
               panel->inpeak=ceil(peak[i]);
               sprintf(buffer,"%+4.0fdB",panel->inpeak);
@@ -1100,7 +1201,7 @@
           }
         }
         
-	multibar_set(MULTIBAR(panel->inbar),rms,peak,n,current_p);
+	multibar_set(MULTIBAR(panel->inbar),rms,peak,input_ch,current_p);
         input_cursor_to_time(time_cursor,buffer);
         readout_set(READOUT(panel->cue),buffer);
       }
@@ -1136,10 +1237,10 @@
 }
 
 #include <stdlib.h>
+extern sig_atomic_t main_looping;
 void mainpanel_go(int argc,char *argv[], int ch){
-  postfish_mainpanel p;
   char *homedir=getenv("HOME");
-  char *labels[11];
+  char *labels[33];
   char  buffer[20];
   int i;
   int found=0;
@@ -1196,30 +1297,21 @@
   gtk_init (&argc, &argv);
 
   memset(labels,0,sizeof(labels));
-  switch(ch){
-  case 1:
+  if(ch==1){
     labels[0]="_1 mono";
-    break;
-  case 2:
+
+    for(i=1;i<OUTPUT_CHANNELS;i++){
+      sprintf(buffer,"_%d",i+1);
+      labels[i]=strdup(buffer);
+    }
+  }else if (ch==2){
     labels[0]="_1 left";
     labels[1]="_2 right";
-    labels[2]="_y mid";
-    labels[3]="_z side";
-    break;
-  case 3:
-  case 4:
-  case 5:
-  case 6:
-  case 7:
-  case 8:
-  case 9:
-  case 10:
-  case 11:
-  case 12:
-  case 13:
-  case 14:
-  case 15:
-  case 16:
+    for(i=2;i<OUTPUT_CHANNELS;i++){
+      sprintf(buffer,"_%d",i+1);
+      labels[i]=strdup(buffer);
+    }
+  }else if (ch>0 && ch<=MAX_INPUT_CHANNELS){
     for(i=0;i<ch && i<9;i++){
       sprintf(buffer,"_%d",i+1);
       labels[i]=strdup(buffer);
@@ -1228,17 +1320,19 @@
       sprintf(buffer,"%d_%d",(i+1)/10,(i+1)%10);
       labels[i]=strdup(buffer);
     }
-    labels[i++]=strdup("_y mid");
-    labels[i++]=strdup("_z div");
+    for(;i<OUTPUT_CHANNELS;i++){
+      sprintf(buffer,"_%d",i+1);
+      labels[i]=strdup(buffer);
+    }
 
-    break;
-  default:
-    fprintf(stderr,"\nPostfish currently supports inputs of one to sixteen\n"
-	    "channels (current input request: %d channels)\n\n",ch);
+  }else{
+    fprintf(stderr,"\nPostfish currently supports inputs of 1 to %d\n"
+	    "channels (current input request: %d channels)\n\n",(int)MAX_INPUT_CHANNELS,ch);
     exit(1);
   }
-
+  
   mainpanel_create(&p,labels);
+  mainpanel_state_from_config(0);
   animate_fish(&p);
 
   /* set up watching the event pipe */
@@ -1256,9 +1350,9 @@
 
   }
 
-
+  /* the slight race here is cosmetic, so I'm not worrying about it */
+  main_looping=1;
   gtk_main ();
 
 }
 
-

Modified: trunk/postfish/mainpanel.h
===================================================================
--- trunk/postfish/mainpanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/mainpanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -34,6 +34,7 @@
 #include "limitpanel.h"
 #include "mixpanel.h"
 #include "reverbpanel.h"
+#include "outpanel.h"
 
 struct postfish_mainpanel{
   GtkWidget *twirlimage;
@@ -55,8 +56,6 @@
   GtkWidget *buttonactive[7];
   GtkWidget *deckactive[7];
 
-  GtkWidget *cue_b;
-
   GtkWidget *inbar;
   GtkWidget *outbar;
   GtkWidget *inreadout;
@@ -65,16 +64,19 @@
   float inpeak;
   float outpeak;
 
-  GtkWidget *channelshow[18]; /* support only up to 16 + mid/side */
+  GtkWidget *channelshow[MAX_INPUT_CHANNELS]; 
   int channelrotate[10]; /* up to 16 channels, but only base 10 digits on the keyboard */
   GtkWidget ***channel_wa;
   int channeleffects;
 
   GtkWidget *cue;
+  GtkWidget *cue_a;
   GtkWidget *entry_a;
+  GtkWidget *set_a;
+  GtkWidget *cue_b;
   GtkWidget *entry_b;
+  GtkWidget *set_b;
 
-
   /* ui state */
   int fishframe;
   int fishframe_init;
@@ -83,4 +85,5 @@
 
 };
 
-extern gboolean slider_keymodify(GtkWidget *w,GdkEventKey *event,gpointer in);
+extern void mainpanel_go(int n,char *list[],int ch);
+extern void save_state();

Modified: trunk/postfish/mix.c
===================================================================
--- trunk/postfish/mix.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/mix.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -262,6 +262,9 @@
   memset(peak,0,sizeof(peak));
   memset(rms,0,sizeof(rms));
 
+  /* eliminate asynch change possibility */
+  memcpy(ms.curr,mix_set,sizeof(*mix_set)*input_ch);
+
   /* fillstate here is only used for lazy initialization/reset */
   if(ms.fillstate==0){
     /* zero the cache */
@@ -269,6 +272,7 @@
       memset(ms.cacheP[i],0,sizeof(**ms.cacheP)*input_size);
       memset(ms.cachePP[i],0,sizeof(**ms.cachePP)*input_size);
     }
+    memcpy(ms.prev,ms.curr,sizeof(*mix_set)*input_ch);
     ms.fillstate=1;
   }
 
@@ -276,9 +280,6 @@
   for(i=0;i<outch;i++)
     memset(ms.out.data[i],0,sizeof(**ms.out.data)*input_size);
 
-  /* eliminate asynch change possibility */
-  memcpy(ms.curr,mix_set,sizeof(*mix_set)*input_ch);
-
   /* a bit of laziness that may actually save CPU time by avoiding
      special-cases later */
   for(i=0;i<input_ch;i++){
@@ -292,8 +293,8 @@
 
     /* input-by-input */
   for(i=0;i<input_ch;i++){
-    int feedit=mixpanel_visible[i] && mixpanel_active[i];
-    int feeditM=atten_visible && mixpanel_active[i];
+    int feedit=mixpanel_visible[i];
+    int feeditM=atten_visible;
 
     /* master feedback is a bit of a pain; the metrics we need aren't
        produced by any of the mixdowns below. Do it by hand */
@@ -496,24 +497,30 @@
         if(sourceM || sourceMP)
           mixwork(in->data[i],ms.cacheP[i],ms.cachePP[i],
                   mix,
-		  att,del,ms.curr[i].insert_invert[k],
-		  attP,delP,ms.prev[i].insert_invert[k]);
+		  (sourceM ? att : 0),
+		  del,ms.curr[i].insert_invert[k],
+		  (sourceMP ? attP : 0),
+		  delP,ms.prev[i].insert_invert[k]);
 
         /* reverbA */
         if(sourceA || sourceAP)
           if(inA)
             mixwork(inA->data[i],ms.cachePA[i],ms.cachePPA[i],
                     mix,
-		    att,del,ms.curr[i].insert_invert[k],
-		    attP,delP,ms.prev[i].insert_invert[k]);
+		    (sourceA ? att : 0),
+		    del,ms.curr[i].insert_invert[k],
+		    (sourceAP ? attP : 0),
+		    delP,ms.prev[i].insert_invert[k]);
 
         /* reverbB */
         if(sourceB || sourceBP)
           if(inB)
             mixwork(inB->data[i],ms.cachePB[i],ms.cachePPB[i],
                     mix,
-		    att,del,ms.curr[i].insert_invert[k],
-		    attP,delP,ms.prev[i].insert_invert[k]);
+		    (sourceB ? att : 0),
+		    del,ms.curr[i].insert_invert[k],
+		    (sourceBP ? attP : 0),
+		    delP,ms.prev[i].insert_invert[k]);
 
         /* mix into output */
         for(j=0;j<OUTPUT_CHANNELS;j++){

Modified: trunk/postfish/mix.h
===================================================================
--- trunk/postfish/mix.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/mix.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -49,3 +49,8 @@
                               time_linkage *inB); // reverb channel
 extern int pull_mix_feedback(float **peak,float **rms);
 
+extern mix_settings *mix_set;
+extern sig_atomic_t atten_visible;
+extern sig_atomic_t *mixpanel_active;
+extern sig_atomic_t *mixpanel_visible;
+

Modified: trunk/postfish/mixpanel.c
===================================================================
--- trunk/postfish/mixpanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/mixpanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -30,16 +30,8 @@
 #include "subpanel.h"
 #include "feedback.h"
 #include "mix.h"
+#include "config.h"
 
-extern int input_ch;
-extern int input_size;
-extern int input_rate;
-
-extern mix_settings *mix_set;
-extern sig_atomic_t atten_visible;
-extern sig_atomic_t *mixpanel_active;
-extern sig_atomic_t *mixpanel_visible;
-
 typedef struct {
   GtkWidget *s;
   GtkWidget *r;
@@ -48,10 +40,26 @@
 
 /* only the sliders we need to save for feedback */
 typedef struct {
+  subpanel_generic *panel;
+  slider_readout_pair **att;
+  slider_readout_pair **del;
+
   GtkWidget **master;
 } atten_panelsave;
 
 typedef struct {
+  GtkWidget *insert_source[MIX_BLOCKS][3];
+  GtkWidget *insert_invert[MIX_BLOCKS];
+  slider_readout_pair *insert_att[MIX_BLOCKS];
+  slider_readout_pair *insert_del[MIX_BLOCKS];
+  GtkWidget *insert_dest[MIX_BLOCKS][OUTPUT_CHANNELS];
+  
+  GtkWidget *destA[OUTPUT_CHANNELS];
+  GtkWidget *destB[OUTPUT_CHANNELS];
+  slider_readout_pair *place_AB;
+  slider_readout_pair *place_atten;
+  slider_readout_pair *place_delay;
+
   GtkWidget *place[2];
   GtkWidget *sub[MIX_BLOCKS];
 } mix_panelsave;
@@ -59,6 +67,98 @@
 static atten_panelsave atten_panel;
 static mix_panelsave **mix_panels;
 
+static void mixblock_state_to_config(int bank, mix_settings *s,int A,int B){
+  config_set_vector("mixblock_source",bank,A,B,0,3,s->insert_source[B]);
+  config_set_integer("mixblock_set",bank,A,B,0,0,s->insert_invert[B]);
+  config_set_integer("mixblock_set",bank,A,B,0,1,s->insert_att[B]);
+  config_set_integer("mixblock_set",bank,A,B,0,2,s->insert_delay[B]);
+  config_set_vector("mixblock_dest",bank,A,B,0,OUTPUT_CHANNELS,s->insert_dest[B]);
+}
+
+static void mixdown_state_to_config(int bank, mix_settings *s,int A){
+  int i;
+  config_set_vector("mixplace_dest",bank,A,0,0,OUTPUT_CHANNELS,s->placer_destA);
+  config_set_vector("mixplace_dest",bank,A,1,0,OUTPUT_CHANNELS,s->placer_destB);
+  config_set_integer("mixplace_set",bank,A,0,0,0,s->placer_place);
+  config_set_integer("mixplace_set",bank,A,0,0,1,s->placer_att);
+  config_set_integer("mixplace_set",bank,A,0,0,2,s->placer_delay);
+  for(i=0;i<MIX_BLOCKS;i++)
+    mixblock_state_to_config(bank,s,A,i);
+}
+
+void mixpanel_state_to_config(int bank){
+  int i;
+  config_set_vector("mixdown_active",bank,0,0,0,input_ch,mixpanel_active);
+
+  for(i=0;i<input_ch;i++){
+    config_set_integer("mixdown_master_attenuate",bank,0,0,0,i,mix_set[i].master_att);
+    config_set_integer("mixdown_master_delay",bank,0,0,0,i,mix_set[i].master_delay);
+
+    mixdown_state_to_config(bank,mix_set+i,i);
+  }
+}
+
+static void mixblock_state_from_config(int bank, mix_settings *s,mix_panelsave *p,int A,int B){
+  int i;
+  config_get_vector("mixblock_source",bank,A,B,0,3,s->insert_source[B]);
+  for(i=0;i<3;i++)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->insert_source[B][i]),
+				 s->insert_source[B][i]);
+
+  config_get_sigat("mixblock_set",bank,A,B,0,0,&s->insert_invert[B]);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->insert_invert[B]),
+			       s->insert_invert[B]);
+  
+  config_get_sigat("mixblock_set",bank,A,B,0,1,&s->insert_att[B]);
+  multibar_thumb_set(MULTIBAR(p->insert_att[B]->s),s->insert_att[B]*.1,0);
+  
+  config_get_sigat("mixblock_set",bank,A,B,0,2,&s->insert_delay[B]);
+  multibar_thumb_set(MULTIBAR(p->insert_del[B]->s),s->insert_delay[B]*.01,0);
+  
+  config_get_vector("mixblock_dest",bank,A,B,0,OUTPUT_CHANNELS,s->insert_dest[B]);
+  for(i=0;i<OUTPUT_CHANNELS;i++)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->insert_dest[B][i]),
+				 s->insert_dest[B][i]);
+}
+
+static void mixdown_state_from_config(int bank, mix_settings *s, mix_panelsave *p, int A){
+  int i;
+  config_get_vector("mixplace_dest",bank,A,0,0,OUTPUT_CHANNELS,s->placer_destA);
+  config_get_vector("mixplace_dest",bank,A,1,0,OUTPUT_CHANNELS,s->placer_destB);
+  config_get_sigat("mixplace_set",bank,A,0,0,0,&s->placer_place);
+  config_get_sigat("mixplace_set",bank,A,0,0,1,&s->placer_att);
+  config_get_sigat("mixplace_set",bank,A,0,0,2,&s->placer_delay);
+
+  for(i=0;i<OUTPUT_CHANNELS;i++){
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->destA[i]),s->placer_destA[i]);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->destB[i]),s->placer_destB[i]);
+  }
+  multibar_thumb_set(MULTIBAR(p->place_AB->s),s->placer_place,0);
+  multibar_thumb_set(MULTIBAR(p->place_atten->s),s->placer_att*.1,0);
+  multibar_thumb_set(MULTIBAR(p->place_delay->s),s->placer_delay*.01,0);
+
+  for(i=0;i<MIX_BLOCKS;i++)
+    mixblock_state_from_config(bank,s,p,A,i);
+}
+
+void mixpanel_state_from_config(int bank){
+  int i;
+  config_get_vector("mixdown_active",bank,0,0,0,input_ch,mixpanel_active);
+
+  for(i=0;i<input_ch;i++){
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(atten_panel.panel->subpanel_activebutton[i]),
+						   mixpanel_active[i]);
+
+    config_get_sigat("mixdown_master_attenuate",bank,0,0,0,i,&mix_set[i].master_att);
+    config_get_sigat("mixdown_master_delay",bank,0,0,0,i,&mix_set[i].master_delay);
+
+    multibar_thumb_set(MULTIBAR(atten_panel.att[i]->s),mix_set[i].master_att*.1,0);
+    multibar_thumb_set(MULTIBAR(atten_panel.del[i]->s),mix_set[i].master_delay*.01,0);
+
+    mixdown_state_from_config(bank,mix_set+i,mix_panels[i],i);
+  }
+}
+
 static void dB_slider_change(GtkWidget *w,gpointer in){
   char buffer[80];
   slider_readout_pair *p=(slider_readout_pair *)in;
@@ -108,7 +208,7 @@
   *val=gtk_toggle_button_get_active(b);
 }
 
-static char *labels_dB[11]={"","  60","40","20","10","0","10","20","40","60","80"};
+static char *labels_dB[11]={""," -60","-40","-20","-10","0","10","20","40","60","80"};
 static float levels_dB[11]={-80,-60,-40,-20,-10,0,10,20,40,60,80};
 
 static char *labels_dBn[6]={"","-40","-20","-10","0","+10"};
@@ -151,9 +251,9 @@
 
   /* crossplace controls */
   {
-    slider_readout_pair *AB=calloc(1,sizeof(AB));
-    slider_readout_pair *att=calloc(1,sizeof(att));
-    slider_readout_pair *del=calloc(1,sizeof(del));
+    slider_readout_pair *AB=calloc(1,sizeof(*AB));
+    slider_readout_pair *att=calloc(1,sizeof(*att));
+    slider_readout_pair *del=calloc(1,sizeof(*del));
     GtkWidget *boxA=gtk_hbox_new(1,0);
     GtkWidget *boxB=gtk_hbox_new(1,0);
 
@@ -175,6 +275,10 @@
     att->val=&m->placer_att;
     del->val=&m->placer_delay;
 
+    ps->place_AB=AB;
+    ps->place_atten=att;
+    ps->place_delay=del;
+
     multibar_callback(MULTIBAR(AB->s),AB_slider_change,AB);
     multibar_thumb_set(MULTIBAR(AB->s),100,0);
     multibar_callback(MULTIBAR(att->s),dB_slider_change,att);
@@ -206,6 +310,8 @@
                         G_CALLBACK (toggle_callback), 
                         (gpointer)&m->placer_destB[i]);
 
+      ps->destA[i]=bA;
+      ps->destB[i]=bB;
     }
     
     gtk_table_attach(GTK_TABLE(table),lA,0,2,6,7,
@@ -268,8 +374,8 @@
   }
 
   for(i=0;i<MIX_BLOCKS;i++){
-    slider_readout_pair *att=calloc(1,sizeof(att));
-    slider_readout_pair *del=calloc(1,sizeof(del));
+    slider_readout_pair *att=calloc(1,sizeof(*att));
+    slider_readout_pair *del=calloc(1,sizeof(*del));
 
     GtkWidget *boxA=gtk_hbox_new(0,0);
     GtkWidget *boxB=gtk_hbox_new(1,0);
@@ -286,6 +392,11 @@
     gtk_misc_set_alignment(GTK_MISC(latt),1,.5);
     gtk_misc_set_alignment(GTK_MISC(ldel),1,.5);
 
+    ps->insert_source[i][0]=bM;
+    ps->insert_source[i][1]=bA;
+    ps->insert_source[i][2]=bB;
+    ps->insert_invert[i]=bI;
+
     att->s=multibar_slider_new(11,labels_dB,levels_dB,1);
     del->s=multibar_slider_new(6,labels_del,levels_del,1);
     att->r=readout_new("+00.0dB");
@@ -296,6 +407,9 @@
     ps->sub[i]=multibar_new(6,labels_dBn,levels_dBn,0,
                             LO_ATTACK|LO_DECAY|HI_DECAY);
     
+    ps->insert_att[i]=att;
+    ps->insert_del[i]=del;
+
     multibar_callback(MULTIBAR(att->s),dB_slider_change,att);
     multibar_callback(MULTIBAR(del->s),ms_slider_change,del);
 
@@ -336,6 +450,8 @@
       if(thisch%2 == j && i == 0)
         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b),1);
 
+      ps->insert_dest[i][j]=b;
+
       gtk_box_pack_start(GTK_BOX(boxB),b,1,1,0);
     }
     
@@ -393,6 +509,7 @@
                           buffer,0,i,1);
   
     mix_panels[i]=mixpanel_create_helper(mp,panel,mix_set+i,i);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(activebutton[i]),1);
   }
 }
 
@@ -408,8 +525,11 @@
 
   GtkWidget *table=gtk_table_new(MIX_BLOCKS*3,5,0);
   mix_panelsave *ps=calloc(1,sizeof(*ps));
-  atten_panel.master=calloc(input_ch,sizeof(*atten_panel.master));
-  
+  atten_panel.master=calloc(input_ch,sizeof(*atten_panel.master));  
+  atten_panel.att=calloc(input_ch,sizeof(*atten_panel.att));
+  atten_panel.del=calloc(input_ch,sizeof(*atten_panel.del));
+  atten_panel.panel=panel;
+
   for(i=0;i<input_ch;i++){
     char buffer[80];
     GtkWidget *l1=gtk_label_new("attenuation ");
@@ -423,11 +543,13 @@
     sprintf(buffer,"channel/reverb %d VU",i+1);
     GtkWidget *lV=gtk_label_new(buffer);
     
-    slider_readout_pair *att=calloc(1,sizeof(att));
-    slider_readout_pair *del=calloc(1,sizeof(del));
+    slider_readout_pair *att=calloc(1,sizeof(*att));
+    slider_readout_pair *del=calloc(1,sizeof(*del));
     
     atten_panel.master[i]=multibar_new(6,labels_dBn,levels_dBn,0,
                                        LO_ATTACK|LO_DECAY|HI_DECAY);
+    atten_panel.att[i]=att;
+    atten_panel.del[i]=del;
 
     att->s=multibar_slider_new(11,labels_dB,levels_dB,1);
     att->r=readout_new("+00.0dB");
@@ -471,7 +593,6 @@
     gtk_table_attach(GTK_TABLE(table),del->r,3,4,1+i*3,2+i*3,
                      GTK_FILL|GTK_EXPAND,0,0,0);
     
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(activebutton[i]),1);
   }
 
   gtk_box_pack_start(GTK_BOX(panel->subpanel_box),table,1,1,4);

Modified: trunk/postfish/mixpanel.h
===================================================================
--- trunk/postfish/mixpanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/mixpanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,3 +31,6 @@
                               GtkWidget **activebutton);
 extern void mixpanel_feedback(int displayit);
 extern void mixpanel_reset(void);
+
+extern void mixpanel_state_to_config(int bank);
+extern void mixpanel_state_from_config(int bank);

Modified: trunk/postfish/multicompand.c
===================================================================
--- trunk/postfish/multicompand.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/multicompand.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -28,10 +28,6 @@
 #include "subband.h"
 #include "bessel.h"
 
-extern int input_size;
-extern int input_rate;
-extern int input_ch;
-
 /* feedback! */
 typedef struct multicompand_feedback{
   feedback_generic parent_class;
@@ -47,6 +43,16 @@
 } peak_state;
 
 typedef struct {
+  sig_atomic_t static_u[multicomp_freqs_max];
+  sig_atomic_t under_ratio;
+
+  sig_atomic_t static_o[multicomp_freqs_max];
+  sig_atomic_t over_ratio;
+  
+  sig_atomic_t base_ratio;
+} atten_cache;
+
+typedef struct {
   feedback_generic_pool feedpool;
   subband_state ss;
   
@@ -66,10 +72,14 @@
   peak_state *over_peak[multicomp_freqs_max];
   peak_state *under_peak[multicomp_freqs_max];
   peak_state *base_peak[multicomp_freqs_max];
+
+  atten_cache *prevset;
+  atten_cache *currset;
   
   float **peak;
   float **rms;
   int ch;
+  int initstate;
 } multicompand_state;
 
 multicompand_settings multi_master_set;
@@ -139,12 +149,14 @@
   reset_filters(&master_state);
   reset_filters(&channel_state);
 
+  master_state.initstate=0;
+  channel_state.initstate=0;
 }
 
 static int multicompand_load_helper(multicompand_state *ms,int ch){
   int i;
   int qblocksize=input_size/8;
-  memset(ms,0,sizeof(ms));
+  memset(ms,0,sizeof(*ms));
   
   ms->ch=ch;
   subband_load(&ms->ss,multicomp_freqs_max,qblocksize,ch);
@@ -171,6 +183,9 @@
   ms->rms=calloc(multicomp_freqs_max,sizeof(*ms->rms));
   for(i=0;i<multicomp_freqs_max;i++)ms->peak[i]=malloc(ms->ch*sizeof(**ms->peak));
   for(i=0;i<multicomp_freqs_max;i++)ms->rms[i]=malloc(ms->ch*sizeof(**ms->rms));
+
+  ms->prevset=malloc(ch*sizeof(*ms->prevset));
+  ms->currset=malloc(ch*sizeof(*ms->currset));
   
   return 0;
 }
@@ -296,11 +311,14 @@
   return (x>0.f?-x:0.f);
 }
 
-static void over_compand(float *lx,float zerocorner,
+static void over_compand(float *lx,
+			 float zerocorner,
+			 float currcorner,
                          iir_filter *attack, iir_filter *decay,
                          iir_state *iir, peak_state *ps,
                          float lookahead,int mode,int knee,
-			 int ratio,
+			 float prevratio,
+			 float currratio,
                          float *adj){
   int k;
   float overdB[input_size];
@@ -308,13 +326,34 @@
   run_filter(overdB,lx,input_size,lookahead,mode,iir,attack,decay,ps);
   
   if(adj){
-    float corner_multiplier= 1.- 1000./ratio;
-    if(knee){
-      for(k=0;k<input_size;k++)
-	adj[k]+=soft_knee(overdB[k]-zerocorner)*corner_multiplier;
+    float ratio_multiplier= 1.- 1000./prevratio;
+    
+    if(zerocorner!=currcorner || prevratio!=currratio){
+      /* slew limit these attenuators */
+      float ratio_add= ((1.- 1000./currratio)-ratio_multiplier)/input_size;
+      float corner_add= (currcorner-zerocorner)/input_size;
+      
+      if(knee){
+	for(k=0;k<input_size;k++){
+	  adj[k]+=soft_knee(overdB[k]-zerocorner)*ratio_multiplier;
+	  ratio_multiplier+=ratio_add;
+	  zerocorner+=corner_add;
+	}
+      }else{
+	for(k=0;k<input_size;k++){
+	  adj[k]+=hard_knee(overdB[k]-zerocorner)*ratio_multiplier;
+	  ratio_multiplier+=ratio_add;
+	  zerocorner+=corner_add;
+	}
+      }
     }else{
-      for(k=0;k<input_size;k++)
-	adj[k]+=hard_knee(overdB[k]-zerocorner)*corner_multiplier;
+      if(knee){
+	for(k=0;k<input_size;k++)
+	  adj[k]+=soft_knee(overdB[k]-zerocorner)*ratio_multiplier;
+      }else{
+	for(k=0;k<input_size;k++)
+	  adj[k]+=hard_knee(overdB[k]-zerocorner)*ratio_multiplier;
+      }
     }
   }
 }
@@ -323,7 +362,8 @@
                          iir_filter *attack, iir_filter *decay,
                          iir_state *iir, peak_state *ps,
                          int mode,
-			 int ratio,
+			 float prevratio,
+			 float currratio,
                          float *adj){
   int k;
   float basedB[input_size];
@@ -332,17 +372,32 @@
              iir,attack,decay,ps);
   
   if(adj){
-    float base_multiplier=1.-1000./ratio;
-    for(k=0;k<input_size;k++)
-      adj[k]-=(basedB[k]+adj[k])*base_multiplier;
+    float ratio_multiplier=1.-1000./prevratio;
+
+    if(prevratio!=currratio){
+      /* slew limit the attenuators */
+      float ratio_add= ((1.- 1000./currratio)-ratio_multiplier)/input_size;
+
+      for(k=0;k<input_size;k++){
+	adj[k]-=(basedB[k]+adj[k])*ratio_multiplier;
+	ratio_multiplier+=ratio_add;
+      }
+
+    }else{
+      for(k=0;k<input_size;k++)
+	adj[k]-=(basedB[k]+adj[k])*ratio_multiplier;
+    }
   }
 }
 
-static void under_compand(float *x,float zerocorner,
+static void under_compand(float *x,
+			 float zerocorner,
+			 float currcorner,
                          iir_filter *attack, iir_filter *decay,
                          iir_state *iir, peak_state *ps,
                          float lookahead,int mode,int knee,
-			 int ratio,
+			 float prevratio,
+			 float currratio,
                          float *adj){
   int k;
   float underdB[input_size];
@@ -351,13 +406,35 @@
              iir,attack,decay,ps);
   
   if(adj){
-    float corner_multiplier=1000./ratio -1.;
-    if(knee){
-      for(k=0;k<input_size;k++)
-	adj[k]=soft_knee(zerocorner-underdB[k])*corner_multiplier;
+    float ratio_multiplier=1000./prevratio - 1.;
+    
+    if(zerocorner!=currcorner || prevratio!=currratio){
+      /* slew limit these attenuators */
+      float ratio_add= ((1000./currratio - 1.)-ratio_multiplier)/input_size;
+      float corner_add= (currcorner-zerocorner)/input_size;
+      
+      if(knee){
+	for(k=0;k<input_size;k++){
+	  adj[k]=soft_knee(zerocorner-underdB[k])*ratio_multiplier;
+	  ratio_multiplier+=ratio_add;
+	  zerocorner+=corner_add;
+	}
+      }else{
+	for(k=0;k<input_size;k++){
+	  adj[k]=hard_knee(zerocorner-underdB[k])*ratio_multiplier;
+	  ratio_multiplier+=ratio_add;
+	  zerocorner+=corner_add;
+	}
+      }
+
     }else{
-      for(k=0;k<input_size;k++)
-	adj[k]=hard_knee(zerocorner-underdB[k])*corner_multiplier;
+      if(knee){
+	for(k=0;k<input_size;k++)
+	  adj[k]=soft_knee(zerocorner-underdB[k])*ratio_multiplier;
+      }else{
+	for(k=0;k<input_size;k++)
+	  adj[k]=hard_knee(zerocorner-underdB[k])*ratio_multiplier;
+      }
     }
   }
 }
@@ -370,11 +447,13 @@
 }
 
 static int multicompand_work_perchannel(multicompand_state *ms, 
-				     float **peakfeed,
-				     float **rmsfeed,
-				     int maxbands,
-				     int channel,
-				     multicompand_settings *c){
+					float **peakfeed,
+					float **rmsfeed,
+					int maxbands,
+					int channel,
+					atten_cache *prevset,
+					atten_cache *currset,
+					multicompand_settings *c){
   subband_state *ss=&ms->ss;
   int active=(ss->effect_active1[channel] || 
               ss->effect_active0[channel] || 
@@ -391,7 +470,22 @@
   }else if(w==&sw[1]){
     bank=1;
   }else bank=2;
-  
+
+  if(active){
+    for(i=0;i<multicomp_freqs_max;i++){
+      currset->static_u[i]=c->bc[bank].static_u[i];
+      currset->static_o[i]=c->bc[bank].static_o[i];
+    }
+    
+    currset->under_ratio=c->under_ratio;
+    currset->over_ratio=c->over_ratio;
+    currset->base_ratio=c->base_ratio;
+    
+    /* don't slew from an unknown value */
+    if(!ss->effect_activeP[channel] || !ms->initstate) 
+      memcpy(prevset,currset,sizeof(*currset));
+  }
+
   for(i=0;i<maxbands;i++){
     
     float *x=ss->lap[i][channel];
@@ -405,7 +499,8 @@
          settings.  There are allocated, but unset; if overrange,
          they're ignored in the compand worker */
       under_compand(x,  
-		    c->bc[bank].static_u[i], 
+		    prevset->static_u[i],
+		    currset->static_u[i],
                     &ms->under_attack[channel],
                     &ms->under_decay[channel],
                     &ms->under_iir[i][channel],
@@ -413,11 +508,13 @@
                     c->under_lookahead/1000.,
                     c->under_mode,
                     c->under_softknee,
-		    c->under_ratio,
+		    prevset->under_ratio,
+		    currset->under_ratio,
                     (i>=w->freq_bands?0:adj));
       
       over_compand(x,  
-		   c->bc[bank].static_o[i],
+		   prevset->static_o[i],
+		   currset->static_o[i],
                    &ms->over_attack[channel],
                    &ms->over_decay[channel],
                    &ms->over_iir[i][channel],
@@ -425,34 +522,38 @@
                    c->over_lookahead/1000.,
                    c->over_mode,
                    c->over_softknee,
-		   c->over_ratio,
+		   prevset->over_ratio,
+		   currset->over_ratio,
                    (i>=w->freq_bands?0:adj));
       
     }
     
-    if(ss->visible1[channel] && !mute_channel_muted(ss->mutemask1,channel)){
-      /* determine rms and peak for feedback */
-      float max=-1.;
-      int maxpos=-1;
-      float rms=0.;
+    if(ss->visible1[channel]){
 
       feedback_p=1;
-      
-      for(k=0;k<input_size;k++){
-	float val=x[k]*x[k];
-	if(val>max){
-	  max=val;
-	  maxpos=k;
+	
+      if(!mute_channel_muted(ss->mutemask1,channel)){
+	/* determine rms and peak for feedback */
+	float max=-1.;
+	int maxpos=-1;
+	float rms=0.;
+	
+	for(k=0;k<input_size;k++){
+	  float val=x[k]*x[k];
+	  if(val>max){
+	    max=val;
+	    maxpos=k;
+	  }
+	  rms+=val;
         }
-	rms+=val;
+	if(active){
+	  peakfeed[i][channel]=todB(max)*.5+adj[maxpos];
+	  rmsfeed[i][channel]=todB(rms/input_size)*.5+adj[maxpos];
+	}else{
+	  peakfeed[i][channel]=todB(max)*.5;
+	  rmsfeed[i][channel]=todB(rms/input_size)*.5;
+	}
       }
-      if(active){
-	peakfeed[i][channel]=todB(max)*.5+adj[maxpos];
-	rmsfeed[i][channel]=todB(rms/input_size)*.5+adj[maxpos];
-      }else{
-	peakfeed[i][channel]=todB(max)*.5;
-	rmsfeed[i][channel]=todB(rms/input_size)*.5;
-      }
     }
     
     if(active){
@@ -462,7 +563,8 @@
                    &ms->base_iir[i][channel],
                    &ms->base_peak[i][channel],
                    c->base_mode,
-		   c->base_ratio,
+		   prevset->base_ratio,
+		   currset->base_ratio,
                    (i>=w->freq_bands?0:adj));
       
       if(ss->effect_activeC[channel]){
@@ -540,9 +642,16 @@
   for(i=0;i<ms->ch;i++){
     int maxbands=find_maxbands(&ms->ss,i);
     if(maxbands>maxmaxbands)maxmaxbands=maxbands;
-    if(multicompand_work_perchannel(ms, ms->peak, ms->rms, maxbands, i, &multi_master_set))
+    if(multicompand_work_perchannel(ms, ms->peak, ms->rms, maxbands, i, 
+				    &ms->prevset[i], &ms->currset[i], &multi_master_set))
       bypass_visible=0;
   }
+  {
+    atten_cache *temp=ms->prevset;
+    ms->prevset=ms->currset;
+    ms->currset=temp;
+    ms->initstate=1;
+  }
 
   push_feedback(ms,bypass_visible,maxmaxbands);
 }
@@ -562,9 +671,16 @@
   for(i=0;i<ms->ch;i++){
     int maxbands=find_maxbands(&ms->ss,i);
     if(maxbands>maxmaxbands)maxmaxbands=maxbands;
-    if(multicompand_work_perchannel(ms, ms->peak, ms->rms, maxbands, i, multi_channel_set+i))
+    if(multicompand_work_perchannel(ms, ms->peak, ms->rms, maxbands, i, 
+				    &ms->prevset[i], &ms->currset[i], multi_channel_set+i))
       bypass_visible=0;
   }
+  {
+    atten_cache *temp=ms->prevset;
+    ms->prevset=ms->currset;
+    ms->currset=temp;
+    ms->initstate=1;
+  }
 
   push_feedback(ms,bypass_visible,maxmaxbands);
 }
@@ -607,7 +723,6 @@
 
   return subband_read(in, &master_state.ss, w, visible,active,
                       multicompand_work_master,&master_state);
-  
 }
 
 time_linkage *multicompand_read_channel(time_linkage *in){

Modified: trunk/postfish/multicompand.h
===================================================================
--- trunk/postfish/multicompand.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/multicompand.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -60,7 +60,6 @@
   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;
@@ -73,7 +72,6 @@
   sig_atomic_t under_attack;
   sig_atomic_t under_decay;
   sig_atomic_t under_lookahead;
-  sig_atomic_t under_trim;
 
   sig_atomic_t active_bank;
 
@@ -90,4 +88,7 @@
 extern int pull_multicompand_feedback_channel(float **peak,float **rms,int *bands);
 extern int pull_multicompand_feedback_master(float **peak,float **rms,int *bands);
 
+extern multicompand_settings multi_master_set;
+extern multicompand_settings *multi_channel_set;
 
+

Added: trunk/postfish/outpanel.c
===================================================================
--- trunk/postfish/outpanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/outpanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -0,0 +1,341 @@
+/*
+ *
+ *  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 "output.h"
+#include "outpanel.h"
+#include "config.h"
+
+typedef struct {
+  GtkWidget *source[OUTPUT_CHANNELS];
+  GtkWidget *device;
+  GtkWidget *format;
+  GtkWidget *ch;
+  GtkWidget *depth;
+} outsub_state;
+
+typedef struct {
+  GtkWidget *monitor_active;
+  GtkWidget *stdout_active;
+
+  outsub_state monitor;
+  outsub_state stdout;
+} outpanel_state;
+
+outpanel_state state;
+
+void outpanel_state_to_config(int bank){
+  config_set_vector("output_active",bank,0,0,0,2,outset.panel_active);
+
+  config_set_vector("output_monitor_source",bank,0,0,0,OUTPUT_CHANNELS,outset.monitor.source);
+  config_set_integer("output_monitor_set",bank,0,0,0,0,outset.monitor.device);
+  config_set_integer("output_monitor_set",bank,0,0,0,1,outset.monitor.bytes);
+  config_set_integer("output_monitor_set",bank,0,0,0,2,outset.monitor.ch);
+  config_set_integer("output_monitor_set",bank,0,0,0,3,outset.monitor.format);
+
+  config_set_vector("output_stdout_source",bank,0,0,0,OUTPUT_CHANNELS,outset.stdout.source);
+  config_set_integer("output_stdout_set",bank,0,0,0,0,outset.stdout.device);
+  config_set_integer("output_stdout_set",bank,0,0,0,1,outset.stdout.bytes);
+  config_set_integer("output_stdout_set",bank,0,0,0,2,outset.stdout.ch);
+  config_set_integer("output_stdout_set",bank,0,0,0,3,outset.stdout.format);
+}
+
+void outpanel_state_from_config(int bank){
+  int i;
+
+  config_get_vector("output_active",bank,0,0,0,2,outset.panel_active);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state.monitor_active),outset.panel_active[0]);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state.stdout_active),outset.panel_active[1]);
+
+  config_get_vector("output_monitor_source",bank,0,0,0,OUTPUT_CHANNELS,outset.monitor.source);
+  for(i=0;i<OUTPUT_CHANNELS;i++)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state.monitor.source[i]),
+				 outset.monitor.source[i]);
+
+  config_get_sigat("output_monitor_set",bank,0,0,0,0,&outset.monitor.device);
+  if(state.monitor.device)
+    gtk_option_menu_set_history(GTK_OPTION_MENU(state.monitor.device),outset.monitor.device);
+  config_get_sigat("output_monitor_set",bank,0,0,0,1,&outset.monitor.bytes);
+  if(state.monitor.depth)
+    gtk_option_menu_set_history(GTK_OPTION_MENU(state.monitor.depth),outset.monitor.bytes);
+  config_get_sigat("output_monitor_set",bank,0,0,0,2,&outset.monitor.ch);
+  if(state.monitor.ch)
+    gtk_option_menu_set_history(GTK_OPTION_MENU(state.monitor.ch),outset.monitor.ch);
+  config_get_sigat("output_monitor_set",bank,0,0,0,3,&outset.monitor.format);
+  if(state.monitor.format)
+    gtk_option_menu_set_history(GTK_OPTION_MENU(state.monitor.format),outset.monitor.format);
+
+
+  config_get_vector("output_stdout_source",bank,0,0,0,OUTPUT_CHANNELS,outset.stdout.source);
+  for(i=0;i<OUTPUT_CHANNELS;i++)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state.stdout.source[i]),
+				 outset.stdout.source[i]);
+
+  config_get_sigat("output_stdout_set",bank,0,0,0,0,&outset.stdout.device);
+  if(state.stdout.device)
+    gtk_option_menu_set_history(GTK_OPTION_MENU(state.stdout.device),outset.stdout.device);
+  config_get_sigat("output_stdout_set",bank,0,0,0,1,&outset.stdout.bytes);
+  if(state.stdout.depth)
+    gtk_option_menu_set_history(GTK_OPTION_MENU(state.stdout.depth),outset.stdout.bytes);
+  config_get_sigat("output_stdout_set",bank,0,0,0,2,&outset.stdout.ch);
+  if(state.stdout.ch)
+    gtk_option_menu_set_history(GTK_OPTION_MENU(state.stdout.ch),outset.stdout.ch);
+  config_get_sigat("output_stdout_set",bank,0,0,0,3,&outset.stdout.format);
+  if(state.stdout.format)
+    gtk_option_menu_set_history(GTK_OPTION_MENU(state.stdout.format),outset.stdout.format);
+
+
+}
+
+static void menuchange(GtkWidget *w,gpointer in){
+  sig_atomic_t *var=(sig_atomic_t *)in;
+
+  *var=gtk_option_menu_get_history(GTK_OPTION_MENU(w));
+}
+
+static void buttonchange(GtkWidget *w,gpointer in){
+  sig_atomic_t *var=(sig_atomic_t *)in;
+
+  *var=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
+}
+
+
+static GtkWidget *outpanel_subpanel(postfish_mainpanel *mp,
+				    subpanel_generic *panel, 
+				    output_subsetting *set,
+				    char *prompt,
+				    int devp,
+				    int active,
+				    outsub_state *os){
+
+  GtkWidget *frame=gtk_frame_new(prompt);
+  GtkWidget *table=gtk_table_new(4,3,0);
+  
+  GtkWidget *l1=gtk_label_new("active channels ");
+  GtkWidget *l2=gtk_label_new(devp?"output device ":"output format ");
+  GtkWidget *l3=gtk_label_new("output channels ");
+  GtkWidget *l4=gtk_label_new("output bit depth ");
+
+  GtkWidget *b1=gtk_hbox_new(1,0);
+  GtkWidget *b2=gtk_hbox_new(1,0);
+  GtkWidget *b3=gtk_hbox_new(1,0);
+  GtkWidget *b4=gtk_hbox_new(1,0);
+
+  gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_IN);
+  gtk_container_set_border_width(GTK_CONTAINER(table),4);
+
+  gtk_misc_set_alignment(GTK_MISC(l1),1.,.5);
+  gtk_misc_set_alignment(GTK_MISC(l2),1.,.5);
+  gtk_misc_set_alignment(GTK_MISC(l3),1.,.5);
+  gtk_misc_set_alignment(GTK_MISC(l4),1.,.5);
+
+  gtk_table_attach_defaults(GTK_TABLE(table),l1,0,1,0,1);
+  gtk_table_attach_defaults(GTK_TABLE(table),l2,0,1,1,2);
+  gtk_table_attach_defaults(GTK_TABLE(table),l3,0,1,2,3);
+  gtk_table_attach_defaults(GTK_TABLE(table),l4,0,1,3,4);
+
+  /* channel buttonx0rz */
+  {
+    int i;
+    char buffer[80];
+    for(i=0;i<OUTPUT_CHANNELS;i++){
+      GtkWidget *b;
+      sprintf(buffer," %d ",i+1);
+
+      b=gtk_toggle_button_new_with_label(buffer);
+      
+      gtk_box_pack_start(GTK_BOX(b1),b,0,0,0);
+      // careful; this breaks (cosmetically) for OUTPUT_CHANNELS>8
+      gtk_widget_add_accelerator (b, "activate", mp->group, GDK_1+i, 0, 0); 
+      gtk_widget_set_sensitive(b,active);
+      g_signal_connect (G_OBJECT (b), "clicked",
+			G_CALLBACK (buttonchange), &set->source[i]);
+
+      if(i<2)gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b),1);
+      os->source[i]=b;
+    }
+    gtk_table_attach_defaults(GTK_TABLE(table),b1,1,3,0,1);
+  }
+
+
+  {
+    int i;
+    GtkWidget *option_menu=gtk_option_menu_new();
+    GtkWidget *menu=gtk_menu_new();
+    
+    if(devp){
+      /* device selection */
+      for(i=0;i<monitor_entries;i++){
+	GtkWidget *item=gtk_menu_item_new_with_label(monitor_list[i].name);
+	gtk_menu_shell_append (GTK_MENU_SHELL(menu),item);
+      }
+      
+      if(i==0){
+	GtkWidget *item=gtk_menu_item_new_with_label("stdout");
+	gtk_menu_shell_append (GTK_MENU_SHELL(menu),item);
+      }
+
+      g_signal_connect (G_OBJECT (option_menu), "changed",
+			G_CALLBACK (menuchange), &set->device);
+
+      os->device=option_menu;
+      os->format=0;
+    }else{
+      
+      /* format selection */
+      int i;
+      char *formats[]={"WAV","AIFF-C","raw (little endian)","raw (big endian)"};
+      
+      for(i=0;i<4;i++){
+	GtkWidget *item=gtk_menu_item_new_with_label(formats[i]);
+	gtk_menu_shell_append (GTK_MENU_SHELL(menu),item);
+      }
+      g_signal_connect (G_OBJECT (option_menu), "changed",
+			G_CALLBACK (menuchange), &set->format);
+      os->format=option_menu;
+      os->device=0;
+    }
+
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu),menu);
+    gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu),0);
+    
+    gtk_box_pack_start(GTK_BOX(b2),option_menu,1,1,0);
+
+    gtk_widget_set_sensitive(option_menu,active);
+    gtk_widget_set_sensitive(menu,active);
+
+  }
+  gtk_table_attach(GTK_TABLE(table),b2,1,2,1,2,GTK_FILL,0,0,0);
+
+
+  {
+    /* channels selection */
+    int i;
+    GtkWidget *option_menu=gtk_option_menu_new();
+    GtkWidget *menu=gtk_menu_new();
+    char *formats[]={"auto","1","2","4","6","8"};
+    
+    for(i=0;i<6;i++){
+      GtkWidget *item=gtk_menu_item_new_with_label(formats[i]);
+      gtk_menu_shell_append (GTK_MENU_SHELL(menu),item);
+    }
+
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu),menu);
+    gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu),0);
+    
+    gtk_box_pack_start(GTK_BOX(b3),option_menu,1,1,0);
+    gtk_widget_set_sensitive(option_menu,active);
+    gtk_widget_set_sensitive(menu,active);
+    g_signal_connect (G_OBJECT (option_menu), "changed",
+		      G_CALLBACK (menuchange), &set->ch);
+    os->ch=option_menu;
+  }
+  gtk_table_attach(GTK_TABLE(table),b3,1,2,2,3,GTK_FILL,0,0,0);
+  
+
+  {
+    /* bit depth selection */
+    int i;
+    GtkWidget *option_menu=gtk_option_menu_new();
+    GtkWidget *menu=gtk_menu_new();
+    char *formats[]={"auto","8","16","24"};
+    
+    for(i=0;i<4;i++){
+      GtkWidget *item=gtk_menu_item_new_with_label(formats[i]);
+      gtk_menu_shell_append (GTK_MENU_SHELL(menu),item);
+    }
+
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu),menu);
+    gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu),0);
+    
+    gtk_box_pack_start(GTK_BOX(b4),option_menu,1,1,0);
+    gtk_widget_set_sensitive(option_menu,active);
+    gtk_widget_set_sensitive(menu,active);
+
+    g_signal_connect (G_OBJECT (option_menu), "changed",
+		      G_CALLBACK (menuchange), &set->bytes);
+
+    os->depth=option_menu;
+  }
+  gtk_table_attach(GTK_TABLE(table),b4,1,2,3,4,GTK_FILL,0,0,0);
+
+  gtk_table_set_row_spacing(GTK_TABLE(table),0,5);
+
+  gtk_container_add(GTK_CONTAINER(frame),table);
+  return frame;
+}
+
+void outpanel_create(postfish_mainpanel *mp,
+		      GtkWidget *windowbutton,
+		      GtkWidget **activebutton){
+
+  char *shortcuts[]={"mOn"," o "};
+
+  subpanel_generic *panel=subpanel_create(mp,windowbutton,activebutton,
+					  outset.panel_active,
+					  &outset.panel_visible,
+					  "_Output configuration",shortcuts,
+					  0,2);
+  
+  GtkWidget *box=gtk_hbox_new(1,0);
+  GtkWidget *monitor_panel=outpanel_subpanel(mp,panel,&outset.monitor,
+					     " audio monitor output ",
+					     1,output_monitor_available,
+					     &state.monitor);
+  GtkWidget *stdout_panel=outpanel_subpanel(mp,panel,&outset.stdout,
+					    " standard output ",
+					    output_stdout_device,
+					    output_stdout_available,
+					    &state.stdout);
+  
+  gtk_box_pack_start(GTK_BOX(box),monitor_panel,0,0,0);
+  gtk_box_pack_start(GTK_BOX(box),stdout_panel,0,0,2);
+
+  if(!output_monitor_available)gtk_widget_set_sensitive(activebutton[0],0);
+  if(!output_monitor_available)gtk_widget_set_sensitive(panel->subpanel_activebutton[0],0);
+  if(!output_stdout_available)gtk_widget_set_sensitive(activebutton[1],0);
+  if(!output_stdout_available)gtk_widget_set_sensitive(panel->subpanel_activebutton[1],0);
+
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(activebutton[0]),1);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(activebutton[1]),1);
+
+  gtk_container_add(GTK_CONTAINER(panel->subpanel_box),box);
+  subpanel_show_all_but_toplevel(panel);
+
+  state.monitor_active=activebutton[0];
+  state.stdout_active=activebutton[1];
+}
+
+void outpanel_monitor_off(){
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state.monitor_active),0);
+}
+
+void outpanel_stdout_off(){
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state.stdout_active),0);
+}

Added: trunk/postfish/outpanel.h
===================================================================
--- trunk/postfish/outpanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/outpanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -0,0 +1,29 @@
+/*
+ *
+ *  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 outpanel_create(postfish_mainpanel *mp,
+			    GtkWidget *windowbutton,
+			    GtkWidget **activebutton);
+
+extern void outpanel_state_to_config(int bank);
+extern void outpanel_state_from_config(int bank);

Modified: trunk/postfish/output.c
===================================================================
--- trunk/postfish/output.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/output.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -38,11 +38,14 @@
 #include "mix.h"
 #include "reverb.h"
 
+output_settings outset;
+
 extern int input_size;
 extern int input_rate;
 sig_atomic_t playback_active=0;
 sig_atomic_t playback_exit=0;
 sig_atomic_t playback_seeking=0;
+sig_atomic_t master_att;
 
 void output_reset(void){
   /* empty feedback queues */
@@ -81,27 +84,25 @@
 
 static feedback_generic *new_output_feedback(void){
   output_feedback *ret=malloc(sizeof(*ret));
-  ret->rms=malloc((input_ch+2)*sizeof(*ret->rms));
-  ret->peak=malloc((input_ch+2)*sizeof(*ret->peak));
+  ret->rms=malloc(OUTPUT_CHANNELS*sizeof(*ret->rms));
+  ret->peak=malloc(OUTPUT_CHANNELS*sizeof(*ret->peak));
   return (feedback_generic *)ret;
 }
 
 static void push_output_feedback(float *peak,float *rms){
-  int n=input_ch+2;
   output_feedback *f=(output_feedback *)
     feedback_new(&feedpool,new_output_feedback);
   
-  memcpy(f->rms,rms,n*sizeof(*rms));
-  memcpy(f->peak,peak,n*sizeof(*peak));
+  memcpy(f->rms,rms,OUTPUT_CHANNELS*sizeof(*rms));
+  memcpy(f->peak,peak,OUTPUT_CHANNELS*sizeof(*peak));
   feedback_push(&feedpool,(feedback_generic *)f);
 }
 
 int pull_output_feedback(float *peak,float *rms){
   output_feedback *f=(output_feedback *)feedback_pull(&feedpool);
-  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);
+  if(rms)memcpy(rms,f->rms,sizeof(*rms)*OUTPUT_CHANNELS);
+  if(peak)memcpy(peak,f->peak,sizeof(*peak)*OUTPUT_CHANNELS);
   feedback_old(&feedpool,(feedback_generic *)f);
   return 1;
 }
@@ -118,6 +119,70 @@
   }
 }
 
+static void PutNumBE(long num,FILE *f,int bytes){
+  switch(bytes){
+  case 4:
+    fputc((num>>24)&0xff,f);
+  case 3:
+    fputc((num>>16)&0xff,f);
+  case 2:
+    fputc((num>>8)&0xff,f);
+  case 1:
+    fputc(num&0xff,f);
+  }
+}
+
+/* Borrowed from sox */
+# define FloatToUnsigned(f) ((u_int32_t)(((int32_t)(f - 2147483648.0)) + 2147483647L) + 1)
+static void ConvertToIeeeExtended(double num, char *bytes){
+  int    sign;
+  int expon;
+  double fMant, fsMant;
+  u_int32_t hiMant, loMant;
+  
+  if (num < 0) {
+    sign = 0x8000;
+    num *= -1;
+  } else {
+    sign = 0;
+  }
+  
+  if (num == 0) {
+    expon = 0; hiMant = 0; loMant = 0;
+  }
+  else {
+    fMant = frexp(num, &expon);
+    if ((expon > 16384) || !(fMant < 1)) {    /* Infinity or NaN */
+      expon = sign|0x7FFF; hiMant = 0; loMant = 0; /* infinity */
+    }
+    else {    /* Finite */
+      expon += 16382;
+      if (expon < 0) {    /* denormalized */
+	fMant = ldexp(fMant, expon);
+	expon = 0;
+      }
+      expon |= sign;
+      fMant = ldexp(fMant, 32);          
+      fsMant = floor(fMant); 
+      hiMant = FloatToUnsigned(fsMant);
+      fMant = ldexp(fMant - fsMant, 32); 
+      fsMant = floor(fMant); 
+      loMant = FloatToUnsigned(fsMant);
+    }
+  }
+  
+  bytes[0] = expon >> 8;
+  bytes[1] = expon;
+  bytes[2] = hiMant >> 24;
+  bytes[3] = hiMant >> 16;
+  bytes[4] = hiMant >> 8;
+  bytes[5] = hiMant;
+  bytes[6] = loMant >> 24;
+  bytes[7] = loMant >> 16;
+  bytes[8] = loMant >> 8;
+  bytes[9] = loMant;
+}
+
 static void WriteWav(FILE *f,long channels,long rate,long bits,long duration){
   if(ftell(f)>0)
     if(fseek(f,0,SEEK_SET))
@@ -136,14 +201,72 @@
   PutNumLE(duration,f,4);
 }
 
-static int isachr(FILE *f){
+void WriteAifc(FILE *f,long channels,long rate,long bits,long duration){
+  int bytes=(bits+7)/8;
+  long size=duration+86;
+  long frames=duration/bytes/channels;
+  char ieee[10];
+
+  if(ftell(f)>0)
+    if(fseek(f,0,SEEK_SET))
+      return;
+  
+  /* Again, quick and dirty */
+  
+  fprintf(f,"FORM"); 
+  if(duration>0)
+    PutNumBE(size-8,f,4);
+  else
+    PutNumBE(0,f,4);
+  fprintf(f,"AIFC");       
+  fprintf(f,"FVER");       
+  PutNumBE(4,f,4);
+  PutNumBE(2726318400UL,f,4);
+
+  fprintf(f,"COMM");
+  PutNumBE(38,f,4);
+  PutNumBE(channels,f,2);
+  if(duration>0)
+    PutNumBE(frames,f,4);
+  else
+    PutNumBE(-1,f,4);
+
+  PutNumBE(bits,f,2);
+  ConvertToIeeeExtended(rate,ieee);
+  fwrite(ieee,1,10,f);
+
+  fprintf(f,"NONE");
+  PutNumBE(14,f,1);
+  fprintf(f,"not compressed");
+  PutNumBE(0,f,1);
+
+  fprintf(f,"SSND");
+  if(duration>0)
+    PutNumBE(duration+8,f,4);
+  else
+    PutNumBE(0,f,4);
+
+  PutNumBE(0,f,4);
+  PutNumBE(0,f,4);
+
+}
+
+static int isachr(int f){
   struct stat s;
 
-  if(!fstat(fileno(f),&s))
+  if(!fstat(f,&s))
     if(S_ISCHR(s.st_mode)) return 1;
   return 0;
 }
 
+static int isareg(int f){
+  struct stat s;
+
+  if(!fstat(f,&s))
+    if(S_ISREG(s.st_mode)) return 1;
+  return 0;
+}
+
 static int ilog(long x){
   int ret=-1;
 
@@ -154,93 +277,559 @@
   return ret;
 }
 
-static int outbytes;
-static FILE *playback_startup(int outfileno, int ch, int r, int *bep){
-  FILE *playback_fd=NULL;
-  int rate=r,channels=ch,ret;
+static int is_oss(int f){
+  struct stat s;
 
-  if(outfileno==-1){
-    playback_fd=fopen("/dev/dsp","wb");
+  if(isachr(f))
+    if(!fstat(f,&s)){
+      int major=(int)(s.st_rdev>>8)&0xff;
+      int minor=(int)(s.st_rdev&0xff);
+
+      // is this a Linux OSS audio device (Major 14) ?
+      if(major==14 && ((minor&0xf)==3 || (minor&0xf)==4))return 1;
+    }
+
+  return 0;
+}
+
+static int is_alsa(int f){
+  struct stat s;
+
+  if(isachr(f))
+    if(!fstat(f,&s)){
+      int type=(int)(s.st_rdev>>8);
+
+      // is this a Linux ALSA audio device (Major 116) ?
+      if(type==116)return 1;
+    }
+
+  return 0;
+}
+
+static int isaudio(int outfileno){
+  if(is_oss(outfileno))return 1;
+  if(is_alsa(outfileno))return 2;
+  return 0;
+}
+
+int output_stdout_available=0;
+int output_stdout_device=0;    /* 0== file, 1==OSS, 2==ALSA */
+int output_monitor_available=0;
+
+int output_probe_stdout(int outfileno){
+  int ret;
+
+  if(isatty(outfileno)){
+    /* stdout is the terminal; disable stdout */
+    output_stdout_available=0;
+
+  }else if (isareg(outfileno)){
+    /* stdout is a regular file */
+    output_stdout_available=1;
+    output_stdout_device=0;
+
+  }else if((ret==isaudio(outfileno))){
+    /* stdout is an audio device */
+
+    output_stdout_available=1;
+    output_stdout_device=ret;
+
+    if(ret==2){
+      fprintf(stderr,
+	      "\nStdout is pointing to an ALSA sound device;\n"
+	      "Postfish does not yet support ALSA playback.\n\n");
+      exit(1);
+    }
   }else{
-    playback_fd=fdopen(dup(outfileno),"wb");
+    /* God only knows what stdout is.  It might be /dev/null or some other.... treat it similar to file */
+
+    output_stdout_available=1;
+    output_stdout_device=0;
+
   }
 
-  if(!playback_fd){
-    fprintf(stderr,"Unable to open output for playback\n");
-    return NULL;
+  return 0;
+}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+output_monitor_entry *monitor_list;
+int monitor_entries;
+
+static char *audio_dev_type[]={"file","OSS"};
+
+/* look for sound devices that actually exist */
+
+static void add_monitor(char *file, char *name,int type){
+  monitor_entries++;
+  if(monitor_list){
+    monitor_list=realloc(monitor_list,
+			 monitor_entries*sizeof(*monitor_list));
+  }else
+    monitor_list=malloc(monitor_entries*sizeof(*monitor_list));
+  
+  monitor_list[monitor_entries-1].file=strdup(file);
+  monitor_list[monitor_entries-1].name=strdup(name);
+  monitor_list[monitor_entries-1].type=type;
+  
+  fprintf(stderr,"Found an output device (type %s): %s\n",
+	  audio_dev_type[type],file);
+}
+
+static void output_probe_monitor_OSS(){
+  /* open /dev and search of major 14 */
+  DIR *d=opendir("/dev");
+  struct dirent *de;
+  
+  if(d==NULL){
+    fprintf(stderr,"Odd; could not open /dev to look for audio devices.\n");
+    return;
   }
+  
+  while((de=readdir(d))){
+    struct stat s;
+    char buf[PATH_MAX];
+    snprintf(buf,PATH_MAX,"/dev/%s",de->d_name);
+    if(!stat(buf,&s)){
+      
+      int major=(int)(s.st_rdev>>8)&0xff;
+      int minor=(int)(s.st_rdev&0xff);
+      
+      // is this a Linux OSS dsp audio device (Major 14, minor 3,19,35...) ?
+      if(major==14 && (minor&0xf)==3){
+	int f=open(buf,O_RDWR|O_NONBLOCK);
+	if(f!=-1){
+	  add_monitor(buf,de->d_name,1);
+	  close(f);
+	}
+      }
+    }
+  }
+	
+  closedir(d);
+}
 
-  /* is this file a block device? */
-  if(isachr(playback_fd)){
-    long bytesperframe=input_size*ch*2;
-    int fd=fileno(playback_fd);
-    int format=AFMT_S16_NE;
-    int fraglog=ilog(bytesperframe);
-    int fragment=0x00040000|fraglog;
-    outbytes=2;
+static int moncomp(const void *a, const void *b){
+  output_monitor_entry *ma=(output_monitor_entry *)a;
+  output_monitor_entry *mb=(output_monitor_entry *)b;
+  return(strcmp(ma->name,mb->name));
+}
 
+int output_probe_monitor(void ){
+  if(output_stdout_device!=0){
+    output_monitor_available=0;
+    return 0;
+  }
+  
+  output_probe_monitor_OSS();
+  
+  if(monitor_entries>0){
+    output_monitor_available=1;
+
+    /* sort the entries alphabetically by name */
+    qsort(monitor_list, monitor_entries, sizeof(*monitor_list), moncomp);
+  }
+
+  return 0;
+}
+
+#ifndef AFMT_S24_LE
+#       define AFMT_S24_LE              0x00000800      
+#       define AFMT_S24_BE              0x00001000      
+#       define AFMT_U24_LE              0x00002000      
+#       define AFMT_U24_BE              0x00004000
+#endif
+
+static int OSS_playback_startup(FILE *f, int rate, int *ch, int *endian, int *bits,int *signp){
+  int fd=fileno(f);
+  int downgrade=0;
+  int failflag=1;
+  int local_ch=*ch;
+  int local_bits=*bits;
+
+  *endian=(AFMT_S16_NE==AFMT_S16_BE?1:0);
+  
+  /* try to configure requested playback.  If it fails, keep dropping back
+     until one works */
+  while(local_ch || local_bits>16){
+    int format;
+    
+    switch(local_bits){
+    case 8:
+      format=AFMT_U8;
+      *signp=0;
+      break;
+    case 16:
+      format=AFMT_S16_NE;
+      *signp=1;
+      break;
+    case 24:
+      format=AFMT_S24_LE;
+      if(*endian)format=AFMT_S24_BE;
+      *signp=1;
+      break;
+    }
+    
     /* try to lower the DSP delay; this ioctl may fail gracefully */
-    ret=ioctl(fd,SNDCTL_DSP_SETFRAGMENT,&fragment);
-    if(ret){
-      fprintf(stderr,"Could not set DSP fragment size; continuing.\n");
+    {
+      
+      long bytesperframe=input_size*local_ch*2;
+      int fraglog=ilog(bytesperframe);
+      int fragment=0x00040000|fraglog;
+      
+      int ret=ioctl(fd,SNDCTL_DSP_SETFRAGMENT,&fragment);
+      if(ret){
+	fprintf(stderr,"Could not set DSP fragment size; continuing.\n");
+      }
     }
+    
+    /* format, channels, rate */
+    {
+      int temp=format;
+      int ret=ioctl(fd,SNDCTL_DSP_SETFMT,&temp);
+      if(ret || format!=temp) goto failset;
+    }
+    
+    {
+      int temp=local_ch;
+      int ret=ioctl(fd,SNDCTL_DSP_CHANNELS,&temp);
+      if(ret || temp!=local_ch) goto failset;
+    }
+    
+    {
+      int temp=rate;
+      int ret=ioctl(fd,SNDCTL_DSP_SPEED,&temp);
+      if(ret || temp!=rate) goto failset;
+    }
+    
+    failflag=0;
+    break;
+    
+  failset:
+    downgrade=1;
+    
+    /* first try decreasing bit depth */
+    if(local_bits==24){
+      local_bits=16;
+    }else{
+      
+      /* next, drop channels */
+      local_bits=*bits;
+      local_ch--;
+    }
+      
+    ioctl(fd,SNDCTL_DSP_RESET);
+  }
 
-    ret=ioctl(fd,SNDCTL_DSP_SETFMT,&format);
-    if(ret || format!=AFMT_S16_NE){
-      fprintf(stderr,"Could not set AFMT_S16_NE playback\n");
-      exit(1);
+  if(failflag || downgrade)
+    fprintf(stderr,"Unable to set playback for %d bits, %d channels, %dHz\n",
+	    *bits,*ch,rate);
+  if(downgrade && !failflag)
+    fprintf(stderr,"\tUsing %d bits, %d channels, %dHz instead\n",
+	    local_bits,local_ch,rate);
+  if(failflag)return -1;
+  
+  *bits=local_bits;
+  *ch=local_ch;
+  
+  return 0;
+}
+
+static int output_startup(FILE *f, int devtype, int format, int rate, int *ch, int *endian, int *bits,int *signp){
+  switch(devtype){
+  case 0: // pipe, regular file or file device 
+    switch(format){
+    case 0:
+      /* WAVE format */
+      ftruncate(fileno(f),0);
+      WriteWav(f,*ch,rate,*bits,-1);
+      *endian=0;
+      *signp=1;
+      if(*bits==8)*signp=0;
+      break;
+    case 1:
+      /* AIFF-C format */
+      ftruncate(fileno(f),0);
+      WriteAifc(f,*ch,rate,*bits,-1);
+      *endian=1;
+      *signp=1;
+      break;
+    case 2:
+      /* raw little endian */
+      *endian=0;
+      *signp=1;
+      if(*bits==8)*signp=0;
+      break;
+    case 3:
+      /* raw big endian */
+      *endian=1;
+      *signp=1;
+      if(*bits==8)*signp=0;
+      break;
+    default:
+      fprintf(stderr,"Unsupported output file format selected!\n");
+      return -1;
     }
-    ret=ioctl(fd,SNDCTL_DSP_CHANNELS,&channels);
-    if(ret || channels!=ch){
-      fprintf(stderr,"Could not set %d channel playback\n",ch);
-      exit(1);
+    return 0;
+
+  case 1: // OSS playback
+    return OSS_playback_startup(f, rate, ch, endian, bits, signp);
+
+  default: // undefined for now
+    fprintf(stderr,"Unsupported playback device selected\n");
+    return -1;
+  }
+}
+
+static void output_halt(FILE *f,int devtype,int format, int rate, int ch, int endian, int bits,int signp,
+			off_t bytecount){
+  switch(devtype){
+  case 0:
+    switch(format){
+    case 0:
+      WriteWav(f,ch,rate,bits,bytecount); // will complete header only if stream is seekable
+      break;
+    case 1:
+      WriteAifc(f,ch,rate,bits,bytecount); // will complete header only if stream is seekable
+      break;
     }
-    ret=ioctl(fd,SNDCTL_DSP_SPEED,&rate);
-    if(ret || r!=rate){
-      fprintf(stderr,"Could not set %dHz playback\n",r);
-      exit(1);
+    break;
+  case 1: // OSS
+    {
+      int fd=fileno(f);
+      ioctl(fd,SNDCTL_DSP_RESET);
     }
-  }else{
-    outbytes=3;
-    WriteWav(playback_fd,ch,r,24,-1);
-    *bep=0;
   }
+}
 
-  return playback_fd;
+static int outpack(time_linkage *link,unsigned char *audiobuf,
+		   int ch,int bits,int endian,
+		   int signp,int *source){
+  int bytes=(bits+7)>>3;
+  int32_t signxor=(signp?0:0x800000L);
+  int bytestep=bytes*(ch-1);
+
+  int endbytecase=endian*3+(bytes-1);
+  int j,i;
+  for(j=0;j<ch;j++){
+    unsigned char *o=audiobuf+bytes*j;
+    if(!mute_channel_muted(link->active,j) && source[j]){
+      
+      float *d=link->data[j];
+
+      for(i=0;i<link->samples;i++){
+	float dval=d[i];
+	int32_t val=rint(dval*8388608.);
+	if(val>8388607)val=8388607;
+	if(val<-8388608)val=-8388608;
+	val ^= signxor;
+  
+	switch(endbytecase){
+	case 2:
+	  /* LE 24 */
+	  *o++=val;
+	  // fall through
+	case 1:
+	  /* LE 16 */
+	  *o++=val>>8;
+	  // fall through
+	case 0:case 3:
+	  /* 8 */
+	  *o++=val>>16;
+	  break;
+	case 4:
+	  /* BE 16 */
+	  *o++=val>>16;
+	  *o++=val>>8;
+	  break;
+	case 5:
+	  /* BE 24 */
+	  *o++=val>>16;
+	  *o++=val>>8;
+	  *o++=val;
+	  break;
+	}
+
+	o+=bytestep;
+      }
+    }else{
+      for(i=0;i<link->samples;i++){
+	int32_t val = signxor;
+  
+	switch(endbytecase){
+	case 2:case 5:
+	  /* 24 */
+	  *o++=val;
+	  // fall through
+	case 1:case 4:
+	  /* LE 16 */
+	  *o++=val>>8;
+	  // fall through
+	case 0:case 3:
+	  /* 8 */
+	  *o++=val>>16;
+	  break;
+	}
+	
+	o+=bytestep;
+      }
+    }
+  }
+  return bytes*ch*link->samples;
 }
 
-/* playback must be halted to change blocksize. */
+extern pthread_mutex_t input_mutex;
+extern mix_settings *mix_set;
+
+/* playback must be halted for new output settings to take hold */
 void *playback_thread(void *dummy){
-  int audiobufsize=0,i,j,k;
-  unsigned char *audiobuf=NULL;
+  int i,j,k;
+  unsigned char *audiobuf;
   int bigendianp=(AFMT_S16_NE==AFMT_S16_BE?1:0);
-  FILE *playback_fd=NULL;
-  int setupp=0;
   time_linkage *link;
   int result;
-  off_t count=0;
+  off_t output_bytecount=0;
 
-  int ch=-1;
-  long rate=-1;
+  int att_last=master_att;
 
   /* for output feedback */
-  float *rms=alloca(sizeof(*rms)*(input_ch+2));
-  float *peak=alloca(sizeof(*peak)*(input_ch+2));
+  float *rms=alloca(sizeof(*rms)*OUTPUT_CHANNELS);
+  float *peak=alloca(sizeof(*peak)*OUTPUT_CHANNELS);
 
+  long offset=0;
+
+  /* monitor setup */
+  FILE *monitor_fd=NULL;
+  int monitor_bits;
+  int monitor_ch;
+  int monitor_endian=bigendianp;
+  int monitor_signp=1;
+  int monitor_started=0;
+  int monitor_devicenum=outset.monitor.device;
+
+  int stdout_bits;
+  int stdout_ch;
+  int stdout_endian;
+  int stdout_signp=1;
+  int stdout_started=0;
+  int stdout_format=outset.stdout.format;
+
+  /* inspect mixdown; how many channels are in use? */
+  {
+    for(j=OUTPUT_CHANNELS-1;j>=0;j--){
+      for(i=0;i<input_ch;i++){
+	mix_settings *m=mix_set+i;
+      
+	if(m->placer_destA[j] ||
+	   m->placer_destB[j])break;
+
+	for(k=0;k<MIX_BLOCKS;k++)
+	  if(m->insert_dest[k][j])break;
+	if(k<MIX_BLOCKS)break;
+      }
+      if(i<input_ch)break;
+    }
+    monitor_ch=stdout_ch=j+1;
+  }
+
+  if(output_monitor_available){
+    int ch=outset.monitor.ch;
+    int bits=outset.monitor.bytes;
+
+    /* channels */
+    switch(ch){
+    case 0:
+      break;
+    case 1:case 2:
+      monitor_ch=ch;
+      break;
+    default:
+      monitor_ch=(ch-1)*2;
+      break;
+    }
+
+    /* bits */
+    switch(bits){
+    case 0:case 2:
+      /* 'auto', 16 */
+      monitor_bits=16;
+      break;
+    case 1:
+      monitor_bits=8;
+      break;
+    case 3:
+      monitor_bits=24;
+      break;
+    }
+  }
+  
+  if(output_stdout_available){
+    int ch=outset.stdout.ch;
+    int bits=outset.stdout.bytes;
+
+    /* channels */
+    switch(ch){
+    case 0:
+      break;
+    case 1:case 2:
+      stdout_ch=ch;
+      break;
+    default:
+      stdout_ch=(ch-1)*2;
+      break;
+    }
+
+    /* bits */
+    switch(bits){
+    case 0:
+      if(output_stdout_device)
+	stdout_bits=16;
+      else
+	stdout_bits=24;
+      break;
+    case 1:
+      stdout_bits=8;
+      break;
+    case 2:
+      stdout_bits=16;
+      break;
+    case 3:
+      stdout_bits=24;
+      break;
+    }
+
+  }
+
+  audiobuf=malloc(input_size*OUTPUT_CHANNELS*4); // largest possible need
+  
   while(1){
+
+    /* the larger lock against seeking is primarily cosmetic, but
+       keeps the metadata strictly in sync.  This lock is only against
+       seeks. */
+    pthread_mutex_lock(&input_mutex);
+
     if(playback_seeking){
       pipeline_reset();
       playback_seeking=0;
     }
+    
+    if(playback_exit){
+      pthread_mutex_unlock(&input_mutex);
+      break;
+    }
+    
+    offset+=input_size;
 
-    if(playback_exit)break;
-
     /* get data */
     link=input_read();
     result=link->samples;
+    pthread_mutex_unlock(&input_mutex);
+
+    /* channel pipeline */
     link=mute_read(link);
     result|=link->samples;
-
     link=declip_read(link);
     result|=link->samples;
     link=multicompand_read_channel(link);
@@ -263,6 +852,7 @@
       result|=link->samples;
     }
 
+    /* master pipeline */
     link=multicompand_read_master(link);
     result|=link->samples;
     link=singlecomp_read_master(link);
@@ -271,18 +861,33 @@
     result|=link->samples;
     link=plate_read_master(link);
     result|=link->samples;
-    
+
     if(!result)break;
     /************/
     
     /* master att */
     if(link->samples>0){
-      float scale=fromdB(master_att/10.);
-      for(i=0;i<link->samples;i++)
-	for(j=0;j<link->channels;j++)
-	  link->data[j][i]*=scale;
-    }    
+      int att=master_att;
 
+      if(att==att_last){
+	float scale=fromdB(att/10.);
+	
+	for(i=0;i<link->samples;i++)
+	  for(j=0;j<link->channels;j++)
+	    link->data[j][i]*=scale;
+      }else{
+	/* slew-limit the scaling */
+	float scale=fromdB(att_last*.1);
+	float mult=fromdB((att-att_last)*.1 / input_size);
+	
+	for(i=0;i<link->samples;i++){
+	  for(j=0;j<link->channels;j++)
+	    link->data[j][i]*=scale;
+	  scale*=mult;
+	}
+	att_last=att;
+      }
+    }
 
     link=limit_read(link);
 
@@ -290,132 +895,141 @@
 
     if(link->samples>0){
 
-      memset(rms,0,sizeof(*rms)*(input_ch+2));
-      memset(peak,0,sizeof(*peak)*(input_ch+2));
-      ch=link->channels;
-      rate=input_rate;
+      /* final limiting and conversion */
+      
+      /* monitor output */
+      if(output_monitor_available){
+	if(outset.panel_active[0]){
+	  
+	  /* lazy playback init */
+	  if(!monitor_started){
 
-      /* temporary! */
-      if(ch>2)ch=2;
-      
-      /* lazy playbak setup; we couldn't do it until we had rate and
-	 channel information from the pipeline */
-      if(!setupp){
-	playback_fd=playback_startup(outfileno,ch,rate,&bigendianp);
-	if(!playback_fd){
-	  playback_active=0;
-	  playback_exit=0;
-	  return NULL;
+	    /* nonblocking open... just in case this is an exclusive
+               use device currently used by something else */
+	    int mfd=open(monitor_list[monitor_devicenum].file,O_RDWR|O_NONBLOCK);
+	    if(mfd==-1){
+	      fprintf(stderr,"unable to open audio monitor device %s for playback.\n",
+		      monitor_list[monitor_devicenum].file);
+	      outpanel_monitor_off();
+	    }else{
+	      fcntl(mfd,F_SETFL,0); /* unset non-blocking */
+	      monitor_fd=fdopen(dup(mfd),"wb");
+	      close(mfd);
+	      if(monitor_fd==NULL){
+		fprintf(stderr,"unable to fdopen audio monitor device %s for playback.\n",
+			monitor_list[monitor_devicenum].file);
+		outpanel_monitor_off();
+	      }else{
+		if(output_startup(monitor_fd,monitor_list[monitor_devicenum].type,0,input_rate,
+				  &monitor_ch,&monitor_endian,&monitor_bits,&monitor_signp)){
+		  outpanel_monitor_off();
+		  fclose(monitor_fd);
+		  monitor_fd=NULL;
+		}else
+		  monitor_started=1;
+	      }
+	    }
+	  }
+
+	  if(monitor_started){
+	    int outbytes=outpack(link,audiobuf,monitor_ch,
+				 monitor_bits,monitor_endian,monitor_signp,
+				 outset.monitor.source);
+	    
+	    fwrite(audiobuf,1,outbytes,monitor_fd);
+	  }
+	}else{
+	  if(monitor_started){
+	    /* halt playback */
+	    output_halt(monitor_fd,monitor_list[monitor_devicenum].type,0,input_rate,monitor_ch,
+			monitor_endian,monitor_bits,monitor_signp,-1);
+	    fclose(monitor_fd);
+	    monitor_fd=NULL;
+	    monitor_started=0;
+	  }
         }
-	setupp=1;
       }
       
-      if(audiobufsize<ch*link->samples*outbytes){
-	audiobufsize=ch*link->samples*outbytes;
-	if(audiobuf)
-	  audiobuf=realloc(audiobuf,sizeof(*audiobuf)*audiobufsize);
-	else
-	  audiobuf=malloc(sizeof(*audiobuf)*audiobufsize);
-      }
-      
-      /* final limiting and conversion */
+      /* standard output */
+      if(output_stdout_available){
+	if(outset.panel_active[1]){
 
-      for(k=0,i=0;i<link->samples;i++){
-	float mean=0.;
-	float divrms=0.;
-	
-	for(j=0;j<ch;j++){
-	  float dval=link->data[j][i];
+	  /* lazy playback/header init */
+	  if(!stdout_started){
+	    if(output_startup(stdout,output_stdout_device,stdout_format,input_rate,
+			      &stdout_ch,&stdout_endian,&stdout_bits,&stdout_signp))
+	      outpanel_stdout_off();
+	    else
+	      stdout_started=1;
+	  }
 
-	  switch(outbytes){
-	  case 3:
-	    {
-	      int32_t val=rint(dval*8388608.);
-	      if(val>8388607)val=8388607;
-	      if(val<-8388608)val=-8388608;
-	      
-	      if(bigendianp){
-		audiobuf[k++]=val>>16;
-		audiobuf[k++]=val>>8;
-		audiobuf[k++]=val;
-	      }else{
-		audiobuf[k++]=val;
-		audiobuf[k++]=val>>8;
-		audiobuf[k++]=val>>16;
-	      }
+	  if(stdout_started){
+	    int outbytes=outpack(link,audiobuf,stdout_ch,
+				 stdout_bits,stdout_endian,stdout_signp,
+				 outset.stdout.source);
+	    
+	    output_bytecount+=fwrite(audiobuf,1,outbytes,stdout);
+	  }
+	}else{
+	  if(stdout_started){
+	    /* if stdout is a device, halt playback.  Otherwise, write nothing */
+	    if(output_stdout_device){
+	      /* halt playback */
+	      output_halt(stdout,output_stdout_device,0,input_rate,stdout_ch,stdout_endian,
+			  stdout_bits,stdout_signp,-1);
+	      stdout_started=0;
             }
-	    break;
-	  case 2:
-	    {
-	      int32_t val=rint(dval*32768.);
-	      if(val>32767)val=32767;
-	      if(val<-32768)val=-32768;
-	      if(bigendianp){
-		audiobuf[k++]=val>>8;
-		audiobuf[k++]=val;
-	      }else{
-		audiobuf[k++]=val;
-		audiobuf[k++]=val>>8;
-	      }
-	    }
-	    break;
-	  }	  
-
-	  if(fabs(dval)>peak[j])peak[j]=fabs(dval);
-	  rms[j]+= dval*dval;
-	  mean+=dval;
-	  
+	  }
         }
-	
-	/* mean */
-	mean/=j;
-	if(fabs(mean)>peak[input_ch])peak[input_ch]=fabs(mean);
-	rms[input_ch]+= mean*mean;
-	
-	/* div */
-	for(j=0;j<ch;j++){
-	  float dval=mean-link->data[j][i];
-	  if(fabs(dval)>peak[input_ch+1])peak[input_ch+1]=fabs(dval);
-	  divrms+=dval*dval;
-	}
-	rms[input_ch+1]+=divrms/ch;
-	
       }
 
-      for(j=0;j<input_ch+2;j++){
+      /* feedback */
+      memset(rms,0,sizeof(*rms)*OUTPUT_CHANNELS);
+      memset(peak,0,sizeof(*peak)*OUTPUT_CHANNELS);
+      
+      for(j=0;j<OUTPUT_CHANNELS;j++){
+	if(!mute_channel_muted(link->active,j))
+	  for(i=0;i<link->samples;i++){
+	    float dval=link->data[j][i];
+	    dval*=dval;
+	    if(dval>peak[j])peak[j]=dval;
+	    rms[j]+= dval;
+	  }
+      }
+
+      for(j=0;j<OUTPUT_CHANNELS;j++)
         rms[j]/=link->samples;
-	rms[j]=sqrt(rms[j]);
-      }
-      
-      count+=fwrite(audiobuf,1,ch*link->samples*outbytes,playback_fd);
-      
+
       /* inform Lord Vader his shuttle is ready */
       push_output_feedback(peak,rms);
-
+      
       write(eventpipe[1],"",1);
-      
     }
+  }
 
+  /* shut down monitor */
+  if(monitor_fd){
+    if(monitor_started)
+      output_halt(monitor_fd,monitor_list[monitor_devicenum].type,0,input_rate,monitor_ch,
+		  monitor_endian,monitor_bits,monitor_signp,-1);
+    fclose(monitor_fd);
   }
 
-  if(playback_fd){
-    if(isachr(playback_fd)){
-      int fd=fileno(playback_fd);
-      ioctl(fd,SNDCTL_DSP_RESET);
-    }else{
-      if(ch>-1)
-	WriteWav(playback_fd,ch,rate,24,count);
-    } 
-    fclose(playback_fd);
-  }
+  /* shut down stdout playback or write final header */
+  if(stdout_started)
+    output_halt(stdout,output_stdout_device,stdout_format,input_rate,stdout_ch,stdout_endian,
+		stdout_bits,stdout_signp,output_bytecount);
+
   pipeline_reset();
   playback_active=0;
   playback_exit=0;
   if(audiobuf)free(audiobuf);
+
   write(eventpipe[1],"",1);
   return(NULL);
 }
 
+/* for access from UI */
 void output_halt_playback(void){
   if(playback_active){
     playback_exit=1;

Modified: trunk/postfish/output.h
===================================================================
--- trunk/postfish/output.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/output.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -21,9 +21,47 @@
  * 
  */
 
+typedef struct {
+  sig_atomic_t device;
+  sig_atomic_t source[OUTPUT_CHANNELS];
+  sig_atomic_t bytes;
+  sig_atomic_t ch;
+  sig_atomic_t format; /* WAV, AIFC, LE, BE */
+} output_subsetting;
+
+typedef struct {
+  sig_atomic_t panel_active[2];
+  sig_atomic_t panel_visible;
+
+  output_subsetting stdout;
+  output_subsetting monitor;
+} output_settings;
+
+typedef struct {
+  int type;
+  char *name;
+  char *file;
+} output_monitor_entry;
+
+extern int output_stdout_available;
+extern int output_stdout_device;
+extern int output_monitor_available;
+extern output_monitor_entry *monitor_list;
+extern int monitor_entries;
+
+extern sig_atomic_t master_att;
+
 extern int pull_output_feedback(float *peak,float *rms);
 extern void *playback_thread(void *dummy);
 extern void output_halt_playback(void);
 extern void output_reset(void);
 extern void playback_request_seek(off_t cursor);
 extern int output_feedback_deep(void);
+
+extern int output_probe_stdout(int outfileno);
+extern int output_probe_monitor(void );
+
+extern void outpanel_monitor_off();
+extern void outpanel_stdout_off();
+
+extern output_settings outset;

Modified: trunk/postfish/postfish-gtkrc
===================================================================
--- trunk/postfish/postfish-gtkrc	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/postfish-gtkrc	2004-05-29 01:17:47 UTC (rev 6774)
@@ -3,6 +3,14 @@
         bg[ACTIVE]="#c0f0ff" 
         bg[PRELIGHT]="#c0f0ff" 
 
+	text[NORMAL]="#000000"
+	text[ACTIVE]="#000000"
+	text[PRELIGHT]="#000000" 
+
+	fg[NORMAL]="#000000"
+	fg[ACTIVE]="#000000"
+	fg[PRELIGHT]="#000000" 
+
         font_name = "sans 8"
         GtkButton::relief = none
 
@@ -173,6 +181,8 @@
 widget "*.smallreadout" style "small-readout"
 widget "*.GtkEntry" style "readout"
 widget "*.GtkHScale" style "slider"
+widget "*.GtkMenu*" style "button-poppy"
+widget "*.GtkOptionMenu*" style "button-poppy"
 widget "*.GtkToggleButton*" style "button-poppy"
 widget "*.GtkButton*" style "button-poppy"
 widget "*.GtkCheckButton" style "check-poppy"

Modified: trunk/postfish/postfish.h
===================================================================
--- trunk/postfish/postfish.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/postfish.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -50,7 +50,8 @@
 #include <signal.h>
 #include <fcntl.h>
 
-#define OUTPUT_CHANNELS 6
+#define OUTPUT_CHANNELS 8     // UI code assumes this is <=8
+#define MAX_INPUT_CHANNELS 32 // engine code requires <= 32 
 
 static inline float todB(float x){
   return logf((x)*(x)+1e-30f)*4.34294480f;
@@ -67,7 +68,7 @@
 }
 
 static inline float fromdB_a(float x){
-  int y=1.39331762961e+06f*((x<-400?-400:x)+764.6161886f);
+  int y=(x < -300 ? 0 : 1.39331762961e+06f*(x+764.6161886f));
   return *(float *)&y;
 }
 
@@ -100,20 +101,19 @@
   u_int32_t active; /* active channel bitmask */
 } time_linkage;
 
-extern pthread_mutex_t master_mutex;
-
 extern sig_atomic_t loop_active;
 extern sig_atomic_t playback_active;
 extern sig_atomic_t playback_exit;
 extern sig_atomic_t playback_seeking;
 extern sig_atomic_t master_att;
 extern int outfileno;
-extern int seekable;
+extern int input_seekable;
 extern int eventpipe[2];
 extern int input_ch;
+extern int input_size;
+extern int input_rate;
 
-extern void mainpanel_go(int n,char *list[],int ch);
 extern int mute_channel_muted(u_int32_t bitmap,int i);
-
+extern void clean_exit(int sig);
 #endif
 

Modified: trunk/postfish/readout.c
===================================================================
--- trunk/postfish/readout.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/readout.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -153,7 +153,6 @@
   GtkWidget *ret= GTK_WIDGET (g_object_new (readout_get_type (), NULL));
   Readout *r=READOUT(ret);
 
-  r->layout=calloc(1,sizeof(r->layout));
   r->layout=gtk_widget_create_pango_layout(ret,markup);
 
   return ret;

Modified: trunk/postfish/reverb.c
===================================================================
--- trunk/postfish/reverb.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/reverb.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -52,7 +52,10 @@
   int size;
   float *buffer[2];
   int ptr;
+  int dptr;
+  int dptr_pending;
   int delay;
+  int delay_pending;
   float fc;
   float lp[2];
   float a1a;
@@ -63,6 +66,8 @@
 typedef struct {
   float *outbuffer;
   waveguide_nl w[8];
+  int prevwet;
+  int initstate;
 } plate;
 
 typedef struct {
@@ -90,10 +95,13 @@
 static void waveguide_init(waveguide_nl *wg,
                            int size, float fc, float da, float db){
   wg->size = size;
-  wg->delay = size;
+  wg->delay_pending = size;
+  wg->delay = -1;
   wg->buffer[0] = calloc(size, sizeof(float));
   wg->buffer[1] = calloc(size, sizeof(float));
   wg->ptr = 0;
+  wg->dptr = 0;
+  wg->dptr_pending = 0;
   wg->fc = fc;
   wg->lp[0] = 0.0f;
   wg->lp[1] = 0.0f;
@@ -101,25 +109,33 @@
   wg->zm1[1] = 0.0f;
   wg->a1a = (1.0f - da) / (1.0f + da);
   wg->a1b = (1.0f - db) / (1.0f + db);
+
 }
 
 static void waveguide_nl_reset(waveguide_nl *wg){
-  memset(wg->buffer[0], 0, wg->size * sizeof(float));
-  memset(wg->buffer[1], 0, wg->size * sizeof(float));
+  memset(wg->buffer[0], 0, wg->size * sizeof(**wg->buffer));
+  memset(wg->buffer[1], 0, wg->size * sizeof(**wg->buffer));
   wg->lp[0] = 0.0f;
   wg->lp[1] = 0.0f;
   wg->zm1[0] = 0.0f;
   wg->zm1[1] = 0.0f;
+
+  wg->ptr=0;
+  wg->dptr = wg->delay;
+  wg->dptr_pending = wg->delay_pending;
 }
 
 static void waveguide_nl_set_delay(waveguide_nl *wg, int delay){
   if (delay > wg->size) {
-    wg->delay = wg->size;
+    wg->delay_pending = wg->size;
   } else if (delay < 1) {
-    wg->delay = 1;
+    wg->delay_pending = 1;
   } else {
-    wg->delay = delay;
+    wg->delay_pending = delay;
   }
+
+  wg->dptr_pending = wg->ptr + wg->delay_pending;
+  if(wg->dptr_pending >= wg->size) wg->dptr_pending -= wg->size;
 }
 
 static void waveguide_nl_set_fc(waveguide_nl *wg, float fc){
@@ -127,17 +143,45 @@
 }
 
 static void waveguide_nl_process_lin(waveguide_nl *wg, float in0, float in1, 
-				     float *out0, float *out1){
+				     float *out0, float *out1, int i){
   float tmp;
+
+  *out0 = wg->buffer[0][wg->dptr];
+  *out0 = wg->lp[0] * (wg->fc - 1.0f) + wg->fc * *out0;
+  wg->lp[0] = *out0;
+  tmp = *out0 * -(wg->a1a) + wg->zm1[0];
+  wg->zm1[0] = tmp * wg->a1a + *out0;
+  *out0 = tmp;
   
-  *out0 = wg->buffer[0][(wg->ptr + wg->delay) % wg->size];
+  *out1 = wg->buffer[1][wg->dptr];
+  *out1 = wg->lp[1] * (wg->fc - 1.0f) + wg->fc * *out1;
+  wg->lp[1] = *out1;
+  tmp = *out1 * -(wg->a1a) + wg->zm1[1];
+  wg->zm1[1] = tmp * wg->a1a + *out1;
+  *out1 = tmp;
+  
+  wg->buffer[0][wg->ptr] = in0;
+  wg->buffer[1][wg->ptr] = in1;
+  wg->ptr--;
+  wg->dptr--;
+  if (wg->ptr < 0) wg->ptr += wg->size;
+  if (wg->dptr < 0) wg->dptr += wg->size;
+}
+
+static void waveguide_nl_process_trans(waveguide_nl *wg, float in0, float in1, 
+				       float *out0, float *out1, int i){
+  float tmp;
+
+  *out0 = wg->buffer[0][wg->dptr]*(1.-frame_window[i]) +
+    wg->buffer[0][wg->dptr_pending]*frame_window[i];
   *out0 = wg->lp[0] * (wg->fc - 1.0f) + wg->fc * *out0;
   wg->lp[0] = *out0;
   tmp = *out0 * -(wg->a1a) + wg->zm1[0];
   wg->zm1[0] = tmp * wg->a1a + *out0;
   *out0 = tmp;
   
-  *out1 = wg->buffer[1][(wg->ptr + wg->delay) % wg->size];
+  *out1 = wg->buffer[1][wg->dptr]*(1.-frame_window[i]) +
+    wg->buffer[1][wg->dptr_pending]*frame_window[i];
   *out1 = wg->lp[1] * (wg->fc - 1.0f) + wg->fc * *out1;
   wg->lp[1] = *out1;
   tmp = *out1 * -(wg->a1a) + wg->zm1[1];
@@ -147,7 +191,11 @@
   wg->buffer[0][wg->ptr] = in0;
   wg->buffer[1][wg->ptr] = in1;
   wg->ptr--;
+  wg->dptr--;
+  wg->dptr_pending--;
   if (wg->ptr < 0) wg->ptr += wg->size;
+  if (wg->dptr < 0) wg->dptr += wg->size;
+  if (wg->dptr_pending < 0) wg->dptr_pending += wg->size;
 }
 
 /* model the plate reverb as a set of eight linear waveguides */
@@ -155,17 +203,9 @@
 #define LP_INNER 0.96f
 #define LP_OUTER 0.983f
 
-#define RUN_WG(n, junct_a, junct_b) waveguide_nl_process_lin(&w[n], junct_a - out[n*2+1], junct_b - out[n*2], out+n*2, out+n*2+1)
+#define RUN_WG(n, junct_a, junct_b, i) process(&w[n], junct_a - out[n*2+1], junct_b - out[n*2], out+n*2, out+n*2+1, i)
 
 static void plate_reset_helper(plate_state *ps){
-  int i,j;
-
-  for(i=0;i<ps->out.channels;i++){
-    for (j = 0; j < 8; j++) 
-      waveguide_nl_reset(&ps->plates[i].w[j]);
-    memset(ps->plates[i].outbuffer,0,
-	   32*sizeof(*ps->plates[i].outbuffer));
-  }
   ps->fillstate=0;
 }
 
@@ -236,10 +276,24 @@
   waveguide_nl *w = ps->w;
   float *out=ps->outbuffer;
 
+  void (*process)(waveguide_nl *, float, float, float*, float*, int)=
+    waveguide_nl_process_lin;
+  
   int pos;
   const float scale = powf(p->time*.0009999f, 1.34f);
   const float lpscale = 1.0f - p->damping*.001f * 0.93f;
-  const float wet = p->wet*.001f;
+  int curwet=p->wet;
+  float wet;
+  float wet_e;
+  
+  if(ps->initstate==0){
+    wet = fromdB(curwet*.1f);
+    wet_e = 1.;
+    ps->initstate=1;
+  }else{
+    wet = fromdB(ps->prevwet*.1f);
+    wet_e = fromdB((curwet-ps->prevwet)*.1f/count);
+  }
 
   /* waveguide reconfig */
   for (pos=0; pos<8; pos++) 
@@ -248,31 +302,44 @@
     waveguide_nl_set_fc(&w[pos], LP_INNER * lpscale);
   for (; pos<8; pos++) 
     waveguide_nl_set_fc(&w[pos], LP_OUTER * lpscale);
-	
+
+  if(w[0].delay!=w[0].delay_pending)
+    process=waveguide_nl_process_trans;
+
   for (pos = 0; pos < count; pos++) {
-    const float alpha = (out[0] + out[2] + out[4] + out[6]) * 0.5f + in[pos];
+    const float alpha = (out[0] + out[2] + out[4] + out[6]) * 0.5f + 
+      (in?in[pos]:0.f);
     const float beta = (out[1] + out[9] + out[14]) * 0.666666666f;
     const float gamma = (out[3] + out[8] + out[11]) * 0.666666666f;
     const float delta = (out[5] + out[10] + out[13]) * 0.666666666f;
     const float epsilon = (out[7] + out[12] + out[15]) * 0.666666666f;
     
-    RUN_WG(0, beta, alpha);
-    RUN_WG(1, gamma, alpha);
-    RUN_WG(2, delta, alpha);
-    RUN_WG(3, epsilon, alpha);
-    RUN_WG(4, beta, gamma);
-    RUN_WG(5, gamma, delta);
-    RUN_WG(6, delta, epsilon);
-    RUN_WG(7, epsilon, beta);
+    RUN_WG(0, beta, alpha, pos);
+    RUN_WG(1, gamma, alpha, pos);
+    RUN_WG(2, delta, alpha, pos);
+    RUN_WG(3, epsilon, alpha, pos);
+    RUN_WG(4, beta, gamma, pos);
+    RUN_WG(5, gamma, delta, pos);
+    RUN_WG(6, delta, epsilon, pos);
+    RUN_WG(7, epsilon, beta, pos);
     
     if(!outB || !outC){
-      outA[pos]=in[pos] * (1.0f - wet) +  beta * wet;
+      outA[pos]=  beta * wet;
     }else{
-      outA[pos]=in[pos] * (1.0f - wet);
       outB[pos]= beta * wet;
       outC[pos]= gamma * wet;
     }
+    wet*=wet_e;
   }
+  ps->prevwet=curwet;
+
+  if(w[0].delay!=w[0].delay_pending){
+    int i;
+    for(i=0;i<8;i++){
+      w[i].delay=w[i].delay_pending;
+      w[i].dptr=w[i].dptr_pending;
+    }
+  }
 }
 
 time_linkage *plate_read_channel(time_linkage *in,
@@ -280,6 +347,7 @@
                                  time_linkage **revB){
   int i,j,ch=in->channels;
   plate_state *ps=&channel;
+  int active[ch];
   
   *revA=&ps->outA;
   *revB=&ps->outB;
@@ -288,52 +356,78 @@
     ps->out.samples=0;
     ps->outA.samples=0;
     ps->outB.samples=0;
+    return &ps->out;
   }
 
+  for(i=0;i<ch;i++)
+    active[i]=plate_channel_set[i].panel_active;
+
   ps->outA.active=0;
   ps->outB.active=0;
 
   if(ps->fillstate==0){
     for(i=0;i<ch;i++)
-      ps->prevactive[i]=plate_channel_set[i].panel_active;
-    ps->fillstate=1;
+      ps->prevactive[i]=active[i];
   }
 
   for(i=0;i<ch;i++){
-    if(plate_channel_set[i].panel_active || ps->prevactive[i]){
+    if(active[i] || ps->prevactive[i]){
       float *x=in->data[i];
       float *y=ps->out.data[i];
       float *yA=ps->outA.data[i];
       float *yB=ps->outB.data[i];
       
       /* clear the waveguides of old state if they were inactive */
-      if (!ps->prevactive[i]){
+      if (!ps->prevactive[i] || !ps->fillstate){
         for (j = 0; j < 8; j++) 
           waveguide_nl_reset(&ps->plates[i].w[j]);
+	ps->plates[i].initstate=0;
         memset(ps->plates[i].outbuffer,0,
                32*sizeof(*ps->plates[i].outbuffer));
       }
 
       /* process this plate */
-      plate_compute(&plate_channel_set[i], &ps->plates[i], 
-		    x,y,yA,yB,input_size);
-      
-      if(!plate_channel_set[i].panel_active){
+      if(mute_channel_muted(in->active,i)){
+	plate_compute(&plate_channel_set[i], &ps->plates[i], 
+		      0,0,yA,yB,input_size);
+      }else{
+	
+	/* state transition?  If so, the input needs to be smoothed to
+           avoid a step transition ringing through the plate */
+	if(!ps->fillstate || 
+	   !active[i] ||
+	   !ps->prevactive[i]){
+	  
+	  memcpy(y,x,sizeof(*x)*input_size);
+	  if(!active[i]){
+	    /* transition to inactive */
+	    for(j=0;j<input_size;j++)
+	      x[j] *= (1.f - frame_window[j]);
+	    
+	  }else if (!ps->prevactive[i] || !ps->fillstate){
+	    /* transition to active */
+	    for(j=0;j<input_size;j++)
+	      x[j]*= frame_window[j];
+	  }
+	}else{
+	  ps->out.data[i]=x;
+	  in->data[i]=y;
+	}
+
+	plate_compute(&plate_channel_set[i], &ps->plates[i], 
+		      x,0,yA,yB,input_size);
+
+
+      }
+
+      if(!active[i]){
         /* transition to inactive */
-	for(j=0;j<input_ch;j++){
-	  y[j]= y[j]*(1.f - frame_window[j]) + x[j] * frame_window[j];
-	  yA[j]= yA[j]*(1.f - frame_window[j]);
-	  yB[j]= yB[j]*(1.f - frame_window[j]);
+	for(j=0;j<input_size;j++){
+	  yA[j] *= (1.f - frame_window[j]);
+	  yB[j] *= (1.f - frame_window[j]);
         }
+      }
         
-      }else if (!ps->prevactive[i]){
-	/* transition to active */
-	for(j=0;j<input_ch;j++){
-	  y[j]=   y[j]* frame_window[j] + x[j] * (1. - frame_window[j]);
-	  yA[j]= yA[j]* frame_window[j];
-	  yB[j]= yB[j]* frame_window[j];
-	}
-      }
       ps->outA.active |= 1<<i;
       ps->outB.active |= 1<<i;
 
@@ -343,67 +437,92 @@
       ps->out.data[i]=in->data[i];
       in->data[i]=temp;
     }
-    ps->prevactive[i]=plate_channel_set[i].panel_active;
+    ps->prevactive[i]=active[i];
   }
 
   ps->out.active=in->active;
   ps->out.samples=in->samples;
   ps->outA.samples=in->samples;
   ps->outB.samples=in->samples;
+  ps->fillstate=1;
   return &ps->out;
 }
 
 time_linkage *plate_read_master(time_linkage *in){
   int i,j,ch=in->channels;
   plate_state *ps=&master;
-  
+  int active=plate_master_set.panel_active;
+
   if(in->samples==0){
     ps->out.samples=0;
+    return &ps->out;
   }
 
   if(ps->fillstate==0){
-    ps->prevactive[0]=plate_master_set.panel_active;
-    ps->fillstate=1;
+    ps->prevactive[0]=active;
   }
 
   for(i=0;i<ch;i++){
-    if(plate_master_set.panel_active || ps->prevactive[0]){
+    if(active || ps->prevactive[0]){
       float *x=in->data[i];
       float *y=ps->out.data[i];
       
       /* clear the waveguides of old state if they were inactive */
-      if (!ps->prevactive[0]){
+      if (!ps->prevactive[0] || !ps->fillstate){
         for (j = 0; j < 8; j++) 
           waveguide_nl_reset(&ps->plates[i].w[j]);
         memset(ps->plates[i].outbuffer,0,
                32*sizeof(*ps->plates[i].outbuffer));
       }
-      
+
       /* process this plate */
-      plate_compute(&plate_master_set, &ps->plates[i], 
-		    x,y,0,0,input_size);
-      
-      if(!plate_master_set.panel_active){
+      if(mute_channel_muted(in->active,i)){
+	plate_compute(&plate_master_set, &ps->plates[i], 
+		      0,y,0,0,input_size);
+      }else{
+
+	memcpy(y,x,sizeof(*x)*input_size);
+
+	/* must be done to the input, else the transition impulse
+           will ring through the plate */
+	if(!active){
+	  /* transition to inactive */
+	  for(j=0;j<input_size;j++)
+	    y[j] *= (1.f - frame_window[j]);
+	  
+	}else if (!ps->prevactive[0] || !ps->fillstate){
+	  /* transition to active */
+	  for(j=0;j<input_size;j++)
+	    y[j]*= frame_window[j];
+	}
+	
+	plate_compute(&plate_master_set, &ps->plates[i], 
+		      y,y,0,0,input_size);
+      }
+
+      if(!active){
         /* transition to inactive */
-	for(j=0;j<input_ch;j++)
-	  y[j]= y[j]*(1.f - frame_window[j]) + x[j] * frame_window[j];
+	for(j=0;j<input_size;j++)
+	  y[j]*= 1.f - frame_window[j];
+
+      }
+      if(!mute_channel_muted(in->active,i))
+	for(j=0;j<input_size;j++)
+	  y[j]+=x[j];
         
-      }else if (!ps->prevactive[0]){
-	/* transition to active */
-	for(j=0;j<input_ch;j++)
-	  y[j]=   y[j]* frame_window[j] + x[j] * (1. - frame_window[j]);
+      ps->out.active |= 1<<i;
 
-      }
     }else{
       /* fully inactive */
       float *temp=ps->out.data[i];
       ps->out.data[i]=in->data[i];
       in->data[i]=temp;
+      ps->out.active |= in->active & (1<<i);
     }
   }
-  ps->prevactive[0]=plate_master_set.panel_active;
+  ps->prevactive[0]=active;
 
-  ps->out.active=in->active;
   ps->out.samples=in->samples;
+  ps->fillstate=1;
   return &ps->out;
 }

Modified: trunk/postfish/reverb.h
===================================================================
--- trunk/postfish/reverb.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/reverb.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -36,3 +36,7 @@
                                         time_linkage **revA,
                                         time_linkage **revB);
 extern time_linkage *plate_read_master(time_linkage *in);
+
+extern plate_set *plate_channel_set;
+extern plate_set plate_master_set;
+

Added: trunk/postfish/reverbpanel.c
===================================================================
--- trunk/postfish/reverbpanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/reverbpanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -0,0 +1,228 @@
+/*
+ *
+ *  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 "reverb.h"
+#include "reverbpanel.h"
+#include "config.h"
+
+typedef struct {
+  GtkWidget *s;
+  GtkWidget *r;
+  sig_atomic_t *val;
+} slider_readout_pair;
+
+typedef struct{
+  subpanel_generic *panel;
+  slider_readout_pair *time;
+  slider_readout_pair *damp;
+  slider_readout_pair *wet;
+} reverb_panel_state;
+
+reverb_panel_state *master_panel;
+reverb_panel_state **channel_panel;
+
+static void reverbpanel_state_to_config_helper(int bank,plate_set *s,int A){
+  config_set_integer("reverb_active",bank,A,0,0,0,s->panel_active);
+
+  config_set_integer("reverb_set",bank,A,0,0,0,s->time);
+  config_set_integer("reverb_set",bank,A,0,0,1,s->damping);
+  config_set_integer("reverb_set",bank,A,0,0,2,s->wet);
+}
+
+void reverbpanel_state_to_config(int bank){
+  int i;
+  reverbpanel_state_to_config_helper(bank,&plate_master_set,0);
+  for(i=0;i<input_ch;i++)
+    reverbpanel_state_to_config_helper(bank,plate_channel_set+i,i+1);
+}
+
+static void reverbpanel_state_from_config_helper(int bank,plate_set *s,
+						 reverb_panel_state *p,int A){
+
+  config_get_sigat("reverb_active",bank,A,0,0,0,&s->panel_active);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->panel->subpanel_activebutton[0]),s->panel_active);
+
+
+  config_get_sigat("reverb_set",bank,A,0,0,0,&s->time);
+    multibar_thumb_set(MULTIBAR(p->time->s),s->time,0);
+
+  config_get_sigat("reverb_set",bank,A,0,0,1,&s->damping);
+    multibar_thumb_set(MULTIBAR(p->damp->s),s->damping,0);
+
+  config_get_sigat("reverb_set",bank,A,0,0,2,&s->wet);
+    multibar_thumb_set(MULTIBAR(p->wet->s),s->wet,0);
+
+}
+
+void reverbpanel_state_from_config(int bank){
+  int i;
+  reverbpanel_state_from_config_helper(bank,&plate_master_set,master_panel,0);
+  for(i=0;i<input_ch;i++)
+    reverbpanel_state_from_config_helper(bank,plate_channel_set+i,channel_panel[i],i+1);
+}
+
+static void slider_change(GtkWidget *w,gpointer in){
+  char buffer[80];
+  slider_readout_pair *p=(slider_readout_pair *)in;
+  float val=multibar_get_value(MULTIBAR(p->s),0);
+  
+  sprintf(buffer,"  %5.2f",val*.01);
+  readout_set(READOUT(p->r),buffer);
+  
+  *p->val=rint(val);
+}
+
+static void slider_change_dB(GtkWidget *w,gpointer in){
+  char buffer[80];
+  slider_readout_pair *p=(slider_readout_pair *)in;
+  float val=multibar_get_value(MULTIBAR(p->s),0);
+  
+  sprintf(buffer,"%+4.1fdB",val*.1);
+  readout_set(READOUT(p->r),buffer);
+  
+  *p->val=rint(val);
+}
+
+static reverb_panel_state *reverbpanel_create_helper (postfish_mainpanel *mp,
+				       subpanel_generic *panel,
+				       plate_set *ps){
+  
+  char *labels[11]={"","1","2","3","4","5","6","7","8","9","  10"};
+  float levels[11]={0,100,200,300,400,500,600,700,800,900,1000};
+
+  char *labelsdB[11]={"","-40","-20","-10","-3","0","+3","+10","+20","+40","  +80"};
+  float levelsdB[11]={-800,-400,-200,-100,-30,0,30,100,200,400,800};
+
+  GtkWidget *table=gtk_table_new(3,3,0);
+  slider_readout_pair *p1=malloc(sizeof(*p1));
+  slider_readout_pair *p2=malloc(sizeof(*p2));
+  slider_readout_pair *p3=malloc(sizeof(*p3));
+
+  GtkWidget *l1=gtk_label_new("Plate size ");
+  GtkWidget *l2=gtk_label_new("Damping ");
+  GtkWidget *l3=gtk_label_new("Wet level ");
+
+  reverb_panel_state *p=calloc(1,sizeof(*p));
+  p->panel=panel;
+
+  gtk_misc_set_alignment(GTK_MISC(l1),1,.5);
+  gtk_misc_set_alignment(GTK_MISC(l2),1,.5);
+  gtk_misc_set_alignment(GTK_MISC(l3),1,.5);
+
+  p->time=p1;
+  p1->s=multibar_slider_new(11,labels,levels,1);
+  p1->r=readout_new("10.00");
+  p1->val=&ps->time;
+
+  p->damp=p2;
+  p2->s=multibar_slider_new(11,labels,levels,1);
+  p2->r=readout_new("10.00");
+  p2->val=&ps->damping;
+
+  p->wet=p3;
+  p3->s=multibar_slider_new(11,labelsdB,levelsdB,1);
+  p3->r=readout_new("10.00");
+  p3->val=&ps->wet;
+
+  gtk_table_attach(GTK_TABLE(table),l1,0,1,0,1,
+		   GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+  gtk_table_attach(GTK_TABLE(table),p1->s,1,2,0,1,
+		   GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+  gtk_table_attach(GTK_TABLE(table),p1->r,2,3,0,1,
+		   GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+
+  gtk_table_attach(GTK_TABLE(table),l2,0,1,1,2,
+		   GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+  gtk_table_attach(GTK_TABLE(table),p2->s,1,2,1,2,
+		   GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+  gtk_table_attach(GTK_TABLE(table),p2->r,2,3,1,2,
+		   GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+
+  gtk_table_attach(GTK_TABLE(table),l3,0,1,2,3,
+		   GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+  gtk_table_attach(GTK_TABLE(table),p3->s,1,2,2,3,
+		   GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+  gtk_table_attach(GTK_TABLE(table),p3->r,2,3,2,3,
+		   GTK_FILL,GTK_FILL|GTK_EXPAND,2,0);
+
+  gtk_container_add(GTK_CONTAINER(panel->subpanel_box),table);
+
+  multibar_callback(MULTIBAR(p1->s),slider_change,p1);
+  multibar_callback(MULTIBAR(p2->s),slider_change,p2);
+  multibar_callback(MULTIBAR(p3->s),slider_change_dB,p3);
+
+  multibar_thumb_set(MULTIBAR(p1->s),200,0);
+  multibar_thumb_set(MULTIBAR(p2->s),200,0);
+  multibar_thumb_set(MULTIBAR(p3->s),0,0);
+
+  multibar_thumb_increment(MULTIBAR(p1->s),1,10);
+  multibar_thumb_increment(MULTIBAR(p2->s),1,10);
+  multibar_thumb_increment(MULTIBAR(p3->s),1,10);
+  
+  subpanel_show_all_but_toplevel(panel);
+  return p;
+}
+
+void reverbpanel_create_master(postfish_mainpanel *mp,
+                               GtkWidget *windowbutton,
+                               GtkWidget *activebutton){
+  
+  char *shortcut[]={" r "};
+  subpanel_generic *panel=subpanel_create(mp,windowbutton,&activebutton,
+					  &plate_master_set.panel_active,
+					  &plate_master_set.panel_visible,
+                                          "_Plate Reverb (master)",shortcut,
+                                          0,1);
+  master_panel=reverbpanel_create_helper(mp,panel,&plate_master_set);
+}
+
+void reverbpanel_create_channel(postfish_mainpanel *mp,
+                                GtkWidget **windowbutton,
+                                GtkWidget **activebutton){
+  int i;
+  /* a panel for each channel */
+  channel_panel=calloc(input_ch,sizeof(*channel_panel));
+
+  for(i=0;i<input_ch;i++){
+    subpanel_generic *panel;
+    char buffer[80];
+    
+    sprintf(buffer,"_Plate Reverb (channel %d)",i+1);
+    panel=subpanel_create(mp,windowbutton[i],activebutton+i,
+                          &plate_channel_set[i].panel_active,
+                          &plate_channel_set[i].panel_visible,
+                          buffer,NULL,
+                          i,1);
+    
+    channel_panel[i]=reverbpanel_create_helper(mp,panel,plate_channel_set+i);
+  }
+}
+
+

Added: trunk/postfish/reverbpanel.h
===================================================================
--- trunk/postfish/reverbpanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/reverbpanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -0,0 +1,34 @@
+/*
+ *
+ *  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 reverbpanel_create_master(postfish_mainpanel *mp,
+                               GtkWidget *windowbutton,
+                               GtkWidget *activebutton);
+extern void reverbpanel_create_channel(postfish_mainpanel *mp,
+                                GtkWidget **windowbutton,
+				       GtkWidget **activebutton);
+
+
+
+extern void reverbpanel_state_to_config(int bank);
+extern void reverbpanel_state_from_config(int bank);

Modified: trunk/postfish/singlecomp.c
===================================================================
--- trunk/postfish/singlecomp.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/singlecomp.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -35,6 +35,16 @@
   float val;
 } peak_state;
 
+typedef struct {
+  sig_atomic_t u_thresh;
+  sig_atomic_t u_ratio;
+
+  sig_atomic_t o_thresh;
+  sig_atomic_t o_ratio;
+  
+  sig_atomic_t b_ratio;
+} atten_cache;
+
 typedef struct{
   time_linkage out;
   feedback_generic_pool feedpool;
@@ -64,6 +74,9 @@
   int mutemaskP;
   int mutemask0;
   int ch;
+
+  atten_cache *prevset;
+  atten_cache *currset;
 } singlecomp_state;
 
 static float *window;
@@ -112,7 +125,7 @@
 
 static void singlecomp_load_helper(singlecomp_state *scs,int ch){
   int i;
-  memset(scs,0,sizeof(scs));
+  memset(scs,0,sizeof(*scs));
 
   scs->ch=ch;
   scs->activeP=calloc(scs->ch,sizeof(*scs->activeP));
@@ -143,6 +156,8 @@
   for(i=0;i<scs->ch;i++)
     scs->cache[i]=malloc(input_size*sizeof(**scs->cache));
 
+  scs->prevset=malloc(ch*sizeof(*scs->prevset));
+  scs->currset=malloc(ch*sizeof(*scs->currset));
 }
 
 /* called only by initial setup */
@@ -187,12 +202,12 @@
 }
 
 static void reset_filter(singlecomp_state *scs){
-  memset(scs->o_peak,0,scs->ch*sizeof(&scs->o_peak));
-  memset(scs->u_peak,0,scs->ch*sizeof(&scs->u_peak));
-  memset(scs->b_peak,0,scs->ch*sizeof(&scs->b_peak));
-  memset(scs->o_iir,0,scs->ch*sizeof(&scs->o_iir));
-  memset(scs->u_iir,0,scs->ch*sizeof(&scs->u_iir));
-  memset(scs->b_iir,0,scs->ch*sizeof(&scs->b_iir));
+  memset(scs->o_peak,0,scs->ch*sizeof(*scs->o_peak));
+  memset(scs->u_peak,0,scs->ch*sizeof(*scs->u_peak));
+  memset(scs->b_peak,0,scs->ch*sizeof(*scs->b_peak));
+  memset(scs->o_iir,0,scs->ch*sizeof(*scs->o_iir));
+  memset(scs->u_iir,0,scs->ch*sizeof(*scs->u_iir));
+  memset(scs->b_iir,0,scs->ch*sizeof(*scs->b_iir));
 }
 
 /* called only in playback thread */
@@ -291,7 +306,10 @@
 }
 
 static void over_compand(float *A,float *B,float *adj,
-			 float zerocorner,float multiplier,
+			 float zerocorner,
+			 float currcorner,
+			 float multiplier,
+			 float currmultiplier,
                          float lookahead,int mode,int softknee,
                          iir_filter *attack, iir_filter *decay,
                          iir_state *iir, peak_state *ps,
@@ -306,18 +324,39 @@
   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;
+    if(multiplier!=currmultiplier || zerocorner!=currcorner){
+      float multiplier_add=(currmultiplier-multiplier)/input_size;
+      float zerocorner_add=(currcorner-zerocorner)/input_size;
+
+      if(softknee){
+	for(k=0;k<input_size;k++){
+	  adj[k]+=soft_knee(work[k]-zerocorner)*multiplier;
+	  multiplier+=multiplier_add;
+	  zerocorner+=zerocorner_add;
+	}
+      }else{
+	for(k=0;k<input_size;k++){
+	  adj[k]+=hard_knee(work[k]-zerocorner)*multiplier;
+	  multiplier+=multiplier_add;
+	  zerocorner+=zerocorner_add;
+	}
+      }
+
     }else{
-      for(k=0;k<input_size;k++)
-        adj[k]+=hard_knee(work[k]-zerocorner)*multiplier;
+      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 zerocorner,float currcorner,
+			  float multiplier,float currmultiplier,
                           float lookahead,int mode,int softknee,
                           iir_filter *attack, iir_filter *decay,
                           iir_state *iir, peak_state *ps,
@@ -331,20 +370,41 @@
   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;
+    if(multiplier!=currmultiplier || zerocorner!=currcorner){
+      float multiplier_add=(currmultiplier-multiplier)/input_size;
+      float zerocorner_add=(currcorner-zerocorner)/input_size;
+
+      if(softknee){
+	for(k=0;k<input_size;k++){
+	  adj[k]= -soft_knee(zerocorner-work[k])*multiplier;
+	  multiplier+=multiplier_add;
+	  zerocorner+=zerocorner_add;
+	}
+      }else{
+	for(k=0;k<input_size;k++){
+	  adj[k]= -hard_knee(zerocorner-work[k])*multiplier;
+	  multiplier+=multiplier_add;
+	  zerocorner+=zerocorner_add;
+	}
+      }
+
     }else{
-      for(k=0;k<input_size;k++)
-        adj[k]= -hard_knee(zerocorner-work[k])*multiplier;
+      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,
+			 float multiplier,float currmultiplier,
+			 int mode,
                          iir_filter *attack, iir_filter *decay,
                          iir_state *iir, peak_state *ps,
                          int active){
@@ -355,11 +415,20 @@
   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;
-
+  
+  if(active){
+    if(multiplier!=currmultiplier){
+      float multiplier_add=(currmultiplier-multiplier)/input_size;
+      
+      for(k=0;k<input_size;k++){
+	adj[k]-=(work[k]+adj[k])*multiplier;
+	multiplier+=multiplier_add;
+      }
+    }else{ 
+      for(k=0;k<input_size;k++)
+	adj[k]-=(work[k]+adj[k])*multiplier;
+    }
+  }
 }
 
 static void work_and_lapping(singlecomp_state *scs,
@@ -401,9 +470,9 @@
     if(u_decayms!=scs->u_decay[i].ms)filter_set(u_decayms,&scs->u_decay[i],0);
     if(b_attackms!=scs->b_attack[i].ms)filter_set(b_attackms,&scs->b_attack[i],1);
     if(b_decayms!=scs->b_decay[i].ms)filter_set(b_decayms,&scs->b_decay[i],0);
-  
+    
     if(!active0 && !activeC){
-
+      
       if(activeP) reset_filter(scs); /* just became inactive; reset all filters */
 
       /* feedabck */
@@ -435,13 +504,26 @@
         
 
     }else if(active0 || activeC){
+      float adj[input_size]; // under will set it
 
+      scs->currset[i].u_thresh=scset[i]->u_thresh;
+      scs->currset[i].o_thresh=scset[i]->o_thresh;
+      scs->currset[i].u_ratio=scset[i]->u_ratio;
+      scs->currset[i].o_ratio=scset[i]->o_ratio;
+      scs->currset[i].b_ratio=scset[i]->b_ratio;
+      
+      /* don't slew from an unknown value */
+
+      if(!activeP || !scs->fillstate) 
+	memcpy(scs->prevset+i,scs->currset+i,sizeof(*scs->currset));
+
       /* run the filters */
-      float adj[input_size]; // under will set it
       
       under_compand(scs->cache[i],in->data[i],adj,
-		    (float)(scset[i]->u_thresh),
-		    1.-1000./scset[i]->u_ratio,
+		    scs->prevset[i].u_thresh,
+		    scs->currset[i].u_thresh,
+		    1.-1000./scs->prevset[i].u_ratio,
+		    1.-1000./scs->currset[i].u_ratio,
                     scset[i]->u_lookahead/1000.,
                     scset[i]->u_mode,
                     scset[i]->u_softknee,
@@ -450,8 +532,10 @@
                     active0);
       
       over_compand(scs->cache[i],in->data[i],adj,
-		   (float)(scset[i]->o_thresh),
-		   1.-1000./scset[i]->o_ratio,
+		   scs->prevset[i].o_thresh,
+		   scs->currset[i].o_thresh,
+		   1.-1000./scs->prevset[i].o_ratio,
+		   1.-1000./scs->currset[i].o_ratio,
                    scset[i]->o_lookahead/1000.,
                    scset[i]->o_mode,
                    scset[i]->o_softknee,
@@ -484,7 +568,8 @@
       }
       
       base_compand(scs->cache[i],in->data[i],adj,
-		   1.-1000./scset[i]->b_ratio,
+		   1.-1000./scs->prevset[i].b_ratio,
+		   1.-1000./scs->currset[i].b_ratio,
                    scset[i]->b_mode,
                    scs->b_attack+i,scs->b_decay+i,
                    scs->b_iir+i,scs->b_peak+i,
@@ -563,6 +648,12 @@
     out->active=mutemask0;
     out->samples=scs->cache_samples;
   }
+
+  {
+    atten_cache *temp=scs->prevset;
+    scs->prevset=scs->currset;
+    scs->currset=temp;
+  }
    
   scs->cache_samples=in->samples;
   scs->mutemaskP=mutemask0;

Modified: trunk/postfish/singlecomp.h
===================================================================
--- trunk/postfish/singlecomp.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/singlecomp.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -56,3 +56,7 @@
 extern int singlecomp_reset(void);
 extern time_linkage *singlecomp_read_master(time_linkage *in);
 extern time_linkage *singlecomp_read_channel(time_linkage *in);
+
+extern singlecomp_settings singlecomp_master_set;
+extern singlecomp_settings *singlecomp_channel_set;
+

Modified: trunk/postfish/singlepanel.c
===================================================================
--- trunk/postfish/singlepanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/singlepanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,14 +31,8 @@
 #include "feedback.h"
 #include "singlecomp.h"
 #include "singlepanel.h"
+#include "config.h"
 
-extern singlecomp_settings singlecomp_master_set;
-extern singlecomp_settings *singlecomp_channel_set;
-
-extern int input_ch;
-extern int input_size;
-extern int input_rate;
-
 typedef struct {
   GtkWidget *slider;
   GtkWidget *readouto;
@@ -48,11 +42,13 @@
 } cbar;
 
 typedef struct{
+  Multibar *s;
   Readout *r;
   sig_atomic_t *v;
 } callback_arg_rv;
 
 typedef struct{
+  Multibar *s;
   Readout *r0;
   Readout *r1;
   sig_atomic_t *v0;
@@ -60,6 +56,17 @@
 } callback_arg_rv2;
 
 typedef struct singlecomp_panel_state{
+  subpanel_generic *panel;
+
+  GtkWidget *o_peak;
+  GtkWidget *o_rms;
+  GtkWidget *o_knee;
+  GtkWidget *u_peak;
+  GtkWidget *u_rms;
+  GtkWidget *u_knee;
+  GtkWidget *b_peak;
+  GtkWidget *b_rms;
+
   callback_arg_rv over_compand;
   callback_arg_rv under_compand;
   callback_arg_rv base_compand;
@@ -79,6 +86,106 @@
 static singlecomp_panel_state *master_panel;
 static singlecomp_panel_state **channel_panel;
 
+static void singlepanel_state_to_config_helper(int bank,singlecomp_settings *s,int A){
+  int i;
+  config_set_integer("singlecompand_active",bank,A,0,0,0,s->panel_active);
+  config_set_integer("singlecompand_thresh",bank,A,i,0,0,s->u_thresh);
+  config_set_integer("singlecompand_thresh",bank,A,i,0,1,s->o_thresh);
+
+  config_set_integer("singlecompand_over_set",bank,A,0,0,0,s->o_mode);
+  config_set_integer("singlecompand_over_set",bank,A,0,0,1,s->o_softknee);
+  config_set_integer("singlecompand_over_set",bank,A,0,0,2,s->o_ratio);
+  config_set_integer("singlecompand_over_set",bank,A,0,0,3,s->o_attack);
+  config_set_integer("singlecompand_over_set",bank,A,0,0,4,s->o_decay);
+  config_set_integer("singlecompand_over_set",bank,A,0,0,5,s->o_lookahead);
+
+  config_set_integer("singlecompand_under_set",bank,A,0,0,0,s->u_mode);
+  config_set_integer("singlecompand_under_set",bank,A,0,0,1,s->u_softknee);
+  config_set_integer("singlecompand_under_set",bank,A,0,0,2,s->u_ratio);
+  config_set_integer("singlecompand_under_set",bank,A,0,0,3,s->u_attack);
+  config_set_integer("singlecompand_under_set",bank,A,0,0,4,s->u_decay);
+  config_set_integer("singlecompand_under_set",bank,A,0,0,5,s->u_lookahead);
+
+  config_set_integer("singlecompand_base_set",bank,A,0,0,0,s->b_mode);
+  config_set_integer("singlecompand_base_set",bank,A,0,0,2,s->b_ratio);
+  config_set_integer("singlecompand_base_set",bank,A,0,0,3,s->b_attack);
+  config_set_integer("singlecompand_base_set",bank,A,0,0,4,s->b_decay);
+}
+
+void singlepanel_state_to_config(int bank){
+  int i;
+  singlepanel_state_to_config_helper(bank,&singlecomp_master_set,0);
+  for(i=0;i<input_ch;i++)
+    singlepanel_state_to_config_helper(bank,singlecomp_channel_set+i,i+1);
+}
+
+static void singlepanel_state_from_config_helper(int bank,singlecomp_settings *s,
+						 singlecomp_panel_state *p,int A){
+
+  int i;
+  config_get_sigat("singlecompand_active",bank,A,0,0,0,&s->panel_active);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->panel->subpanel_activebutton[0]),s->panel_active);
+
+  config_get_sigat("singlecompand_thresh",bank,A,i,0,0,&s->u_thresh);
+  multibar_thumb_set(MULTIBAR(p->bar.slider),s->u_thresh,0);
+  config_get_sigat("singlecompand_thresh",bank,A,i,0,1,&s->o_thresh);
+  multibar_thumb_set(MULTIBAR(p->bar.slider),s->o_thresh,1);
+
+  config_get_sigat("singlecompand_over_set",bank,A,0,0,0,&s->o_mode);
+  if(s->o_mode)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->o_peak),1);
+  else
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->o_rms),1);
+
+  config_get_sigat("singlecompand_over_set",bank,A,0,0,1,&s->o_softknee);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->o_knee),s->o_softknee);
+  config_get_sigat("singlecompand_over_set",bank,A,0,0,2,&s->o_ratio);
+  multibar_thumb_set(p->over_compand.s,1000./s->o_ratio,0);
+  config_get_sigat("singlecompand_over_set",bank,A,0,0,3,&s->o_attack);
+  multibar_thumb_set(p->over_timing.s,s->o_attack*.1,0);
+  config_get_sigat("singlecompand_over_set",bank,A,0,0,4,&s->o_decay);
+  multibar_thumb_set(p->over_timing.s,s->o_decay*.1,1);
+  config_get_sigat("singlecompand_over_set",bank,A,0,0,5,&s->o_lookahead);
+  multibar_thumb_set(p->over_lookahead.s,s->o_lookahead*.1,0);
+
+  config_get_sigat("singlecompand_under_set",bank,A,0,0,0,&s->u_mode);
+  if(s->u_mode)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->u_peak),1);
+  else
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->u_rms),1);
+
+  config_get_sigat("singlecompand_under_set",bank,A,0,0,1,&s->u_softknee);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->u_knee),s->u_softknee);
+  config_get_sigat("singlecompand_under_set",bank,A,0,0,2,&s->u_ratio);
+  multibar_thumb_set(p->under_compand.s,1000./s->u_ratio,0);
+  config_get_sigat("singlecompand_under_set",bank,A,0,0,3,&s->u_attack);
+  multibar_thumb_set(p->under_timing.s,s->u_attack*.1,0);
+  config_get_sigat("singlecompand_under_set",bank,A,0,0,4,&s->u_decay);
+  multibar_thumb_set(p->under_timing.s,s->u_decay*.1,1);
+  config_get_sigat("singlecompand_under_set",bank,A,0,0,5,&s->u_lookahead);
+  multibar_thumb_set(p->under_lookahead.s,s->u_lookahead*.1,0);
+
+  config_get_sigat("singlecompand_base_set",bank,A,0,0,0,&s->b_mode);
+  if(s->b_mode)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->b_peak),1);
+  else
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->b_rms),1);
+  config_get_sigat("singlecompand_base_set",bank,A,0,0,2,&s->b_ratio);
+  multibar_thumb_set(p->base_compand.s,1000./s->b_ratio,0);
+  config_get_sigat("singlecompand_base_set",bank,A,0,0,3,&s->b_attack);
+  multibar_thumb_set(p->base_timing.s,s->b_attack*.1,0);
+  config_get_sigat("singlecompand_base_set",bank,A,0,0,4,&s->b_decay);
+  multibar_thumb_set(p->base_timing.s,s->b_decay*.1,1);
+
+}
+
+void singlepanel_state_from_config(int bank){
+  int i;
+  singlepanel_state_from_config_helper(bank,&singlecomp_master_set,master_panel,0);
+  for(i=0;i<input_ch;i++)
+    singlepanel_state_from_config_helper(bank,singlecomp_channel_set+i,channel_panel[i],i+1);
+}
+
 static void compand_change(GtkWidget *w,gpointer in){
   callback_arg_rv *ca=(callback_arg_rv *)in;
   char buffer[80];
@@ -202,6 +309,7 @@
 
   singlecomp_panel_state *ps=calloc(1,sizeof(singlecomp_panel_state));
   ps->ms=scset;
+  ps->panel=panel;
 
   GtkWidget *sliderframe=gtk_frame_new(NULL);
   GtkWidget *allbox=gtk_vbox_new(0,0);
@@ -293,6 +401,10 @@
     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);
+
+    ps->u_rms=rms_button;
+    ps->u_peak=peak_button;
+    ps->u_knee=knee_button;
   }
 
   /* under compand: ratio */
@@ -302,6 +414,7 @@
     GtkWidget *readout=readout_new("1.55:1");
     GtkWidget *slider=multibar_slider_new(9,compand_labels,compand_levels,1);
    
+    ps->under_compand.s=MULTIBAR(slider);
     ps->under_compand.r=READOUT(readout);
     ps->under_compand.v=&ps->ms->u_ratio;
 
@@ -325,6 +438,7 @@
     GtkWidget *readout1=readout_new(" 100ms");
     GtkWidget *slider=multibar_slider_new(6,timing_labels,timing_levels,2);
 
+    ps->under_timing.s=MULTIBAR(slider);
     ps->under_timing.r0=READOUT(readout0);
     ps->under_timing.r1=READOUT(readout1);
     ps->under_timing.v0=&ps->ms->u_attack;
@@ -351,6 +465,7 @@
     GtkWidget *readout=readout_new("100%");
     GtkWidget *slider=multibar_slider_new(9,per_labels,per_levels,1);
     
+    ps->under_lookahead.s=MULTIBAR(slider);
     ps->under_lookahead.r=READOUT(readout);
     ps->under_lookahead.v=&ps->ms->u_lookahead;
     
@@ -390,6 +505,9 @@
     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);
+    ps->o_rms=rms_button;
+    ps->o_peak=peak_button;
+    ps->o_knee=knee_button;
   }
 
   /* over compand: ratio */
@@ -399,6 +517,7 @@
     GtkWidget *readout=readout_new("1.55:1");
     GtkWidget *slider=multibar_slider_new(9,compand_labels,compand_levels,1);
 
+    ps->over_compand.s=MULTIBAR(slider);
     ps->over_compand.r=READOUT(readout);
     ps->over_compand.v=&ps->ms->o_ratio;
 
@@ -422,6 +541,7 @@
     GtkWidget *readout1=readout_new(" 100ms");
     GtkWidget *slider=multibar_slider_new(6,timing_labels,timing_levels,2);
    
+    ps->over_timing.s=MULTIBAR(slider);
     ps->over_timing.r0=READOUT(readout0);
     ps->over_timing.r1=READOUT(readout1);
     ps->over_timing.v0=&ps->ms->o_attack;
@@ -448,6 +568,7 @@
     GtkWidget *readout=readout_new("100%");
     GtkWidget *slider=multibar_slider_new(9,per_labels,per_levels,1);
    
+    ps->over_lookahead.s=MULTIBAR(slider);
     ps->over_lookahead.r=READOUT(readout);
     ps->over_lookahead.v=&ps->ms->o_lookahead;
     
@@ -483,6 +604,8 @@
 
     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);
+    ps->b_rms=rms_button;
+    ps->b_peak=peak_button;
   }
 
   /* base compand: ratio */
@@ -492,6 +615,7 @@
     GtkWidget *readout=readout_new("1.55:1");
     GtkWidget *slider=multibar_slider_new(9,compand_labels,compand_levels,1);
    
+    ps->base_compand.s=MULTIBAR(slider);
     ps->base_compand.r=READOUT(readout);
     ps->base_compand.v=&ps->ms->b_ratio;
 
@@ -515,6 +639,7 @@
     GtkWidget *readout1=readout_new(" 100ms");
     GtkWidget *slider=multibar_slider_new(6,timing_labels,timing_levels,2);
 
+    ps->base_timing.s=MULTIBAR(slider);
     ps->base_timing.r0=READOUT(readout0);
     ps->base_timing.r1=READOUT(readout1);
     ps->base_timing.v0=&ps->ms->b_attack;

Modified: trunk/postfish/singlepanel.h
===================================================================
--- trunk/postfish/singlepanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/singlepanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,3 +31,5 @@
 extern void singlepanel_reset(void);
 
 
+extern void singlepanel_state_to_config(int bank);
+extern void singlepanel_state_from_config(int bank);

Modified: trunk/postfish/subband.c
===================================================================
--- trunk/postfish/subband.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/subband.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -525,10 +525,9 @@
        would have been in the previous frame */
     for(i=0;i<ch;i++){
       int set=(visible[i]||active[i]) && !mute_channel_muted(in->active,i);
-      memset(f->lap_activeP,set,sizeof(*f->lap_activeP)*ch);
-      memset(f->lap_active1,set,sizeof(*f->lap_active1)*ch);
-      memset(f->lap_active0,set,sizeof(*f->lap_active0)*ch);
-      //memset(f->lap_activeC,1,sizeof(*f->lap_activeC)*ch);
+      f->lap_activeP[i]=set;
+      f->lap_active1[i]=set;
+      f->lap_active0[i]=set;
 
       f->wP[i]=w[i];
       f->w1[i]=w[i];

Modified: trunk/postfish/subpanel.c
===================================================================
--- trunk/postfish/subpanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/subpanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -167,7 +167,7 @@
   GtkWidget *toplabelwb=windowbutton_new(prompt);
   GtkWidget *toplabelab[num];
   int i;
-  
+
   for(i=0;i<num;i++){
     if(shortcut && shortcut[i]){
       toplabelab[i]=gtk_toggle_button_new_with_label(shortcut[i]);
@@ -207,6 +207,9 @@
   panel->subpanel_toplevel=gtk_window_new (GTK_WINDOW_TOPLEVEL);
   panel->mainpanel=mp;
 
+  panel->group = gtk_accel_group_new ();
+  gtk_window_add_accel_group (GTK_WINDOW(panel->subpanel_toplevel), panel->group);
+
   gtk_container_add (GTK_CONTAINER (panel->subpanel_toplevel), panel->subpanel_topframe);
   gtk_container_add (GTK_CONTAINER (panel->subpanel_topframe), panel->subpanel_box);
   gtk_container_set_border_width (GTK_CONTAINER (panel->subpanel_topframe), 3);
@@ -229,6 +232,9 @@
                     G_CALLBACK (subpanel_hide), 
                     panel);
 
+  gtk_widget_add_accelerator(windowbutton, "clicked", panel->group, GDK_W, GDK_MOD1_MASK, 0);
+
+
   /* link the mainpanel and subpanel buttons */
   g_signal_connect_after (G_OBJECT (panel->mainpanel_windowbutton), "clicked",
                           G_CALLBACK (windowbutton_action), panel);

Modified: trunk/postfish/subpanel.h
===================================================================
--- trunk/postfish/subpanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/subpanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -30,6 +30,7 @@
   GtkWidget *subpanel_topframe;
   GtkWidget *subpanel_box;
   sig_atomic_t *activevar;
+  GtkAccelGroup *group;
 
   int active_button_count; /* silliness around the rotating non-alt-shortcut */
   int active_button_start; /* silliness around the rotating non-alt-shortcut */

Modified: trunk/postfish/suppress.c
===================================================================
--- trunk/postfish/suppress.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/suppress.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -56,6 +56,8 @@
   iir_state *iirT[suppress_freqs];
   iir_state *iirR[suppress_freqs];
 
+  float prevratio[suppress_freqs];
+
 } suppress_state;
 
 suppress_settings suppress_channel_set;
@@ -182,8 +184,23 @@
           
           //_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);
+
+	  if(multiplier==sss->prevratio[i]){
+
+	    for(k=0;k<input_size;k++)
+	      fast[k]=fromdB_a((todB_a(slow+k)-todB_a(fast+k))*.5*multiplier);
+
+	  }else{
+	    float multiplier_add=(multiplier-sss->prevratio[i])/input_size;
+	    multiplier=sss->prevratio[i];
+
+	    for(k=0;k<input_size;k++){
+	      fast[k]=fromdB_a((todB_a(slow+k)-todB_a(fast+k))*.5*multiplier);
+	      multiplier+=multiplier_add;
+	    }
+
+	  }
+
           //_analysis("adj",i,fast,input_size,1,offset);
 
           if(sset->linkp && firstlink==1){
@@ -214,6 +231,9 @@
         memset(&sss->iirR[i][j],0,sizeof(iir_state));
       }
     }
+
+    sss->prevratio[i]=multiplier;
+
   }
 }
 

Modified: trunk/postfish/suppress.h
===================================================================
--- trunk/postfish/suppress.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/suppress.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -47,3 +47,5 @@
 extern int suppress_load(void);
 extern time_linkage *suppress_read_channel(time_linkage *in);
 
+extern suppress_settings suppress_channel_set;
+

Modified: trunk/postfish/suppresspanel.c
===================================================================
--- trunk/postfish/suppresspanel.c	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/suppresspanel.c	2004-05-29 01:17:47 UTC (rev 6774)
@@ -31,13 +31,8 @@
 #include "feedback.h"
 #include "suppress.h"
 #include "suppresspanel.h"
+#include "config.h"
 
-extern int input_ch;
-extern int input_size;
-extern int input_rate;
-
-extern suppress_settings suppress_channel_set;
-
 typedef struct {
   GtkWidget *cslider;
   Readout *readoutc;
@@ -47,6 +42,7 @@
 } tbar;
 
 typedef struct{
+  Multibar *s;
   Readout *r0;
   Readout *r1;
   Readout *r2;
@@ -56,12 +52,50 @@
 } callback_arg_rv3;
 
 typedef struct suppress_panel_state{
-  callback_arg_rv3 timing;
-  tbar             bars[suppress_freqs+1];
+  subpanel_generic *panel;
+
+  GtkWidget        *link;
+  callback_arg_rv3  timing;
+  tbar              bars[suppress_freqs];
 } suppress_panel_state;
 
 static suppress_panel_state *channel_panel;
 
+void suppresspanel_state_to_config(int bank){
+  config_set_vector("suppresspanel_active",bank,0,0,0,input_ch,suppress_channel_set.active);
+  config_set_vector("suppresspanel_ratio",bank,0,0,0,suppress_freqs,suppress_channel_set.ratio);
+  config_set_integer("suppresspanel_set",bank,0,0,0,0,suppress_channel_set.linkp);
+  config_set_integer("suppresspanel_set",bank,0,0,0,1,suppress_channel_set.smooth);
+  config_set_integer("suppresspanel_set",bank,0,0,0,2,suppress_channel_set.trigger);
+  config_set_integer("suppresspanel_set",bank,0,0,0,3,suppress_channel_set.release);
+}
+
+void suppresspanel_state_from_config(int bank){
+  int i;
+
+  config_get_vector("suppresspanel_active",bank,0,0,0,input_ch,suppress_channel_set.active);
+  for(i=0;i<input_ch;i++)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(channel_panel->panel->subpanel_activebutton[i]),
+				 suppress_channel_set.active[i]);
+
+  config_get_vector("suppresspanel_ratio",bank,0,0,0,suppress_freqs,suppress_channel_set.ratio);
+  for(i=0;i<suppress_freqs;i++)
+    multibar_thumb_set(MULTIBAR(channel_panel->bars[i].cslider),
+		       1000./suppress_channel_set.ratio[i],0);
+
+  config_get_sigat("suppresspanel_set",bank,0,0,0,0,&suppress_channel_set.linkp);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(channel_panel->link),suppress_channel_set.linkp);
+  
+  config_get_sigat("suppresspanel_set",bank,0,0,0,1,&suppress_channel_set.smooth);
+  multibar_thumb_set(MULTIBAR(channel_panel->timing.s),suppress_channel_set.smooth*.1,0);
+
+  config_get_sigat("suppresspanel_set",bank,0,0,0,2,&suppress_channel_set.trigger);
+  multibar_thumb_set(MULTIBAR(channel_panel->timing.s),suppress_channel_set.trigger*.1,1);
+
+  config_get_sigat("suppresspanel_set",bank,0,0,0,3,&suppress_channel_set.release);
+  multibar_thumb_set(MULTIBAR(channel_panel->timing.s),suppress_channel_set.release*.1,2);
+}
+
 static void compand_change(GtkWidget *w,gpointer in){
   char buffer[80];
   tbar *bar=(tbar *)in;
@@ -149,6 +183,7 @@
   GtkWidget *linkbox=gtk_hbox_new(0,0);
 
   suppress_panel_state *ps=calloc(1,sizeof(suppress_panel_state));
+  ps->panel=panel;
 
   gtk_container_add(GTK_CONTAINER(panel->subpanel_box),table);
 
@@ -194,11 +229,14 @@
   g_signal_connect (G_OBJECT (linkbutton), "clicked",
                     G_CALLBACK (suppress_link), &sset->linkp);
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linkbutton),1);
+  ps->link=linkbutton;
 
   /* timing controls */
   {
     GtkWidget *slider=multibar_slider_new(5,timing_labels,timing_levels,3);
 
+    ps->timing.s=MULTIBAR(slider);
+
     ps->timing.r0=READOUT(readout_new("10.0ms"));
     ps->timing.r1=READOUT(readout_new("10.0ms"));
     ps->timing.r2=READOUT(readout_new("10.0ms"));
@@ -209,9 +247,9 @@
 
     multibar_callback(MULTIBAR(slider),timing_change,&ps->timing);
     
-    multibar_thumb_set(MULTIBAR(slider),20,0);
+    multibar_thumb_set(MULTIBAR(slider),80,0);
     multibar_thumb_set(MULTIBAR(slider),100,1);
-    multibar_thumb_set(MULTIBAR(slider),1000,2);
+    multibar_thumb_set(MULTIBAR(slider),2000,2);
 
     gtk_table_attach(GTK_TABLE(table),slider,1,2,1,2,
                      GTK_FILL|GTK_EXPAND,GTK_EXPAND,5,0);

Modified: trunk/postfish/suppresspanel.h
===================================================================
--- trunk/postfish/suppresspanel.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/suppresspanel.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -24,3 +24,6 @@
 extern void suppresspanel_create_channel(postfish_mainpanel *mp,
                                          GtkWidget **windowbutton,
                                          GtkWidget **activebutton);
+
+extern void suppresspanel_state_to_config(int bank);
+extern void suppresspanel_state_from_config(int bank);

Modified: trunk/postfish/version.h
===================================================================
--- trunk/postfish/version.h	2004-05-28 21:46:02 UTC (rev 6773)
+++ trunk/postfish/version.h	2004-05-29 01:17:47 UTC (rev 6774)
@@ -1,2 +1,2 @@
 #define VERSION "$Id$ "
-/* DO NOT EDIT: Automated versioning hack [Mon May 17 17:49:56 EDT 2004] */
+/* DO NOT EDIT: Automated versioning hack [Fri May 28 21:14:38 EDT 2004] */

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