[xiph-commits] r18353 - trunk/spectrum

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Sun Jun 3 20:26:49 PDT 2012


Author: xiphmont
Date: 2012-06-03 20:26:48 -0700 (Sun, 03 Jun 2012)
New Revision: 18353

Modified:
   trunk/spectrum/analyzer.h
   trunk/spectrum/io.c
   trunk/spectrum/io.h
   trunk/spectrum/spec_panel.c
   trunk/spectrum/spec_plot.h
   trunk/spectrum/spec_process.c
   trunk/spectrum/spectrum.c
   trunk/spectrum/version.h
   trunk/spectrum/wave_panel.c
   trunk/spectrum/wave_plot.c
   trunk/spectrum/wave_plot.h
   trunk/spectrum/wave_process.c
   trunk/spectrum/waveform.c
   trunk/spectrum/waveform.h
Log:
Add on-the-fly pipe reconfiguration to waveform viewer as well



Modified: trunk/spectrum/analyzer.h
===================================================================
--- trunk/spectrum/analyzer.h	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/analyzer.h	2012-06-04 03:26:48 UTC (rev 18353)
@@ -85,8 +85,7 @@
 extern void process_dump(int mode);
 extern void rundata_clear();
 extern fetchdata *process_fetch(int scale, int mode, int link,
-                                 int *process, int process_n,
-                                 int height, int width);
+                                 int *process, int height, int width);
 
 extern sig_atomic_t acc_rewind;
 extern sig_atomic_t acc_loop;

Modified: trunk/spectrum/io.c
===================================================================
--- trunk/spectrum/io.c	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/io.c	2012-06-04 03:26:48 UTC (rev 18353)
@@ -26,7 +26,7 @@
 #include <unistd.h>
 #include "io.h"
 
-pthread_mutex_t ioparam_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t ioparam_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 int bits[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
 int bigendian[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
 int channels[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
@@ -58,6 +58,8 @@
 int global_seekable=0;
 int total_ch=0;
 
+static void (*lc)(void)=NULL;
+
 static int host_is_big_endian() {
   int32_t pattern = 0xfeedface; /* deadbeef */
   unsigned char *bytewise = (unsigned char *)&pattern;
@@ -131,12 +133,11 @@
 
 /* used to load or reload an input */
 static int load_one_input(int fi){
-  int i,reload=0;
+  int i;
 
   if(f[fi]){
     fclose(f[fi]);
     f[fi]=NULL;
-    reload=1;
     blockbufferfill[fi]=0;
     readbufferptr[fi]=0;
     readbufferfill[fi]=0;
@@ -401,7 +402,7 @@
   return 0;
 }
 
-int input_load(void){
+int input_load(void (*load_callback)(void)){
   int fi;
   if(inputs==0){
     /* look at stdin... is it a file, pipe, tty...? */
@@ -426,6 +427,11 @@
       exit(1);
   }
 
+  if(load_callback){
+    lc = load_callback;
+    load_callback();
+  }
+
   return 0;
 }
 
@@ -441,6 +447,8 @@
       fprintf(stderr,"reloading....\n");
       ret=1;
     }
+
+  if(ret && lc)lc();
   return ret;
 }
 

Modified: trunk/spectrum/io.h
===================================================================
--- trunk/spectrum/io.h	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/io.h	2012-06-04 03:26:48 UTC (rev 18353)
@@ -46,10 +46,9 @@
 #define MAX_FILES 16
 #define readbuffersize 512
 
-extern pthread_mutex_t ioparam_mutex;
-extern int input_load(void);
-extern int pipe_reload(void);
+extern int input_load(void (*load_callback)(void));
 extern void set_blockslice(int slice, int fi);
+extern int pipe_reload(void);
 extern int input_read(int loop_p, int partial_p);
 extern int rewind_files(void);
 

Modified: trunk/spectrum/spec_panel.c
===================================================================
--- trunk/spectrum/spec_panel.c	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/spec_panel.c	2012-06-04 03:26:48 UTC (rev 18353)
@@ -572,7 +572,8 @@
 }
 
 /* rate, channels, bits, channel active buttons */
-static void create_chbuttons(int *active){
+static void create_chbuttons(int *bits, int *rate, int *channels,
+                             int *active){
   int i,fi;
   int ch=0;
   char buffer[160];
@@ -649,21 +650,12 @@
   /* update the spectral display; send new data */
   fetchdata *f = process_fetch
     (plot_scale, plot_mode, plot_link,
-     process, plot_ch, plot_height(PLOT(plot)), plot_width(PLOT(plot)));
+     process, plot_height(PLOT(plot)), plot_width(PLOT(plot)));
 
-  fprintf(stderr,"%f %f %d\n",f->ymax,f->data[0][0],f->active[0]);
-
   /* the fetched data may indicate the underlying file data has
      changed... */
   if(f->reload){
 
-    /* update group block slices */
-    for(i=0;i<f->groups;i++){
-      int sl=f->rate[i]/10;
-      while(sl>blocksize/2)sl/=2;
-      set_blockslice(sl,i);
-    }
-
     /* remove old group labels and channel buttons */
     destroy_chbuttons();
 
@@ -672,12 +664,12 @@
 
     /* create new buttons/labels */
     {
-      int newprocess[total_ch];
+      int newprocess[f->total_ch];
       for(i=0;i<plot_ch && i<old_ch;i++)
         newprocess[i]=process[i];
       for(;i<plot_ch;i++)
         newprocess[i]=0;
-      create_chbuttons(newprocess);
+      create_chbuttons(f->bits,f->rate,f->channels,newprocess);
     }
   }
 
@@ -861,7 +853,7 @@
     gtk_table_set_row_spacing (GTK_TABLE (toptable), 0, 6);
   }
 
-  create_chbuttons(NULL);
+  create_chbuttons(bits,rate,channels,NULL);
 
   /* add the action buttons */
   GtkWidget *bbox=gtk_vbox_new(0,0);

Modified: trunk/spectrum/spec_plot.h
===================================================================
--- trunk/spectrum/spec_plot.h	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/spec_plot.h	2012-06-04 03:26:48 UTC (rev 18353)
@@ -29,17 +29,6 @@
 #include <gtk/gtk.h>
 #include "io.h"
 
-G_BEGIN_DECLS
-
-#define PLOT_TYPE            (plot_get_type ())
-#define PLOT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PLOT_TYPE, Plot))
-#define PLOT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PLOT_TYPE, PlotClass))
-#define IS_PLOT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PLOT_TYPE))
-#define IS_PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PLOT_TYPE))
-
-typedef struct _Plot       Plot;
-typedef struct _PlotClass  PlotClass;
-
 typedef struct {
   int bits[MAX_FILES];
   int channels[MAX_FILES];
@@ -71,6 +60,17 @@
   float bold;
 } plotparams;
 
+G_BEGIN_DECLS
+
+#define PLOT_TYPE            (plot_get_type ())
+#define PLOT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PLOT_TYPE, Plot))
+#define PLOT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PLOT_TYPE, PlotClass))
+#define IS_PLOT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PLOT_TYPE))
+#define IS_PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PLOT_TYPE))
+
+typedef struct _Plot       Plot;
+typedef struct _PlotClass  PlotClass;
+
 struct _Plot{
 
   GtkDrawingArea canvas;

Modified: trunk/spectrum/spec_process.c
===================================================================
--- trunk/spectrum/spec_process.c	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/spec_process.c	2012-06-04 03:26:48 UTC (rev 18353)
@@ -29,26 +29,26 @@
 static fftwf_plan plan=NULL;
 static int prev_total_ch=-1;
 
-pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
-int feedback_increment=0;
+static pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static int feedback_increment=0;
 
-float *feedback_count=NULL;
-float *process_work=NULL;
+static float *feedback_count=NULL;
+static float *process_work=NULL;
 
-float **feedback_acc=NULL;
-float **feedback_max=NULL;
-float **feedback_instant=NULL;
+static float **feedback_acc=NULL;
+static float **feedback_max=NULL;
+static float **feedback_instant=NULL;
 
-float **ph_acc=NULL;
-float **ph_max=NULL;
-float **ph_instant=NULL;
+static float **ph_acc=NULL;
+static float **ph_max=NULL;
+static float **ph_instant=NULL;
 
-float **xmappingL=NULL;
-float **xmappingM=NULL;
-float **xmappingH=NULL;
-int metascale = -1;
-int metawidth = -1;
-int metareload = 0;
+static float **xmappingL=NULL;
+static float **xmappingM=NULL;
+static float **xmappingH=NULL;
+static int metascale = -1;
+static int metawidth = -1;
+static int metareload = 0;
 
 sig_atomic_t acc_rewind=0;
 sig_atomic_t acc_loop=0;
@@ -343,8 +343,7 @@
 /* the data returned is now 2 vals per bin; a min and a max.  The spec
    plot merely draws a vertical line between. */
 fetchdata *process_fetch(int scale, int mode, int link,
-                         int *process_in, int process_n,
-                         int height, int width){
+                         int *process_in, int height, int width){
   int ch,ci,i,j,fi;
   float **data;
   float **ph;

Modified: trunk/spectrum/spectrum.c
===================================================================
--- trunk/spectrum/spectrum.c	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/spectrum.c	2012-06-04 03:26:48 UTC (rev 18353)
@@ -1,24 +1,24 @@
 /*
  *
  *  gtk2 spectrum analyzer
- *    
+ *
  *      Copyright (C) 2004-2012 Monty
  *
  *  This analyzer 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.
- *   
+ *
  *  The analyzer 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 "analyzer.h"
@@ -214,9 +214,17 @@
   if(sig==SIGILL)sigill=1;
 }
 
-int main(int argc, char **argv){
+void blockslice_callback(void){
   int fi;
+  /* select the full-block slice size: ~10fps */
+  for(fi=0;fi<inputs;fi++){
+    blockslice[fi]=rate[fi]/10;
+    while(blockslice[fi]>blocksize/2)blockslice[fi]/=2;
+  }
+}
 
+int main(int argc, char **argv){
+
   version=strstr(VERSION,"version.h");
   if(version){
     char *versionend=strchr(version,' ');
@@ -294,14 +302,8 @@
   //signal(SIGINT,handler);
   signal(SIGSEGV,handler);
 
-  if(input_load())exit(1);
+  if(input_load(blockslice_callback))exit(1);
 
-  /* select the full-block slice size: ~10fps */
-  for(fi=0;fi<inputs;fi++){
-    blockslice[fi]=rate[fi]/10;
-    while(blockslice[fi]>blocksize/2)blockslice[fi]/=2;
-  }
-
   /* go */
   panel_go(argc,argv);
 

Modified: trunk/spectrum/version.h
===================================================================
--- trunk/spectrum/version.h	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/version.h	2012-06-04 03:26:48 UTC (rev 18353)
@@ -1,2 +1,2 @@
 #define VERSION "$Id$ "
-/* DO NOT EDIT: Automated versioning hack [Sun Jun  3 00:12:33 EDT 2012] */
+/* DO NOT EDIT: Automated versioning hack [Sun Jun  3 23:19:05 EDT 2012] */

Modified: trunk/spectrum/wave_panel.c
===================================================================
--- trunk/spectrum/wave_panel.c	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/wave_panel.c	2012-06-04 03:26:48 UTC (rev 18353)
@@ -1,187 +1,146 @@
 /*
  *
  *  gtk2 waveform viewer
- *    
+ *
  *      Copyright (C) 2004-2012 Monty
  *
  *  This analyzer 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.
- *   
+ *
  *  The analyzer 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 "waveform.h"
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 #include "fisharray.h"
-#include "io.h"
 #include "wave_plot.h"
 
-sig_atomic_t increment_fish=0;
+GtkWidget *twirlimage;
+GdkPixmap *ff[19];
+GdkBitmap *fb[19];
 
-static struct panel {
-  GtkWidget *twirlimage;
-  GdkPixmap *ff[19];
-  GdkBitmap *fb[19];
+GtkAccelGroup *group;
+GtkWidget *toplevel;
 
-  GtkAccelGroup *group;
-  GtkWidget *toplevel;
+guint fishframe_timer;
+int fishframe_init;
+int fishframe;
 
+GtkWidget *rightbox;
+GtkWidget *plot;
+GtkWidget *run;
+GtkWidget **chbuttons;
+GtkWidget **groupboxes;
+GtkWidget *rangemenu;
 
-  guint fishframe_timer;
-  int fishframe_init;
-  int fishframe;
+int plot_ch=0;
+int plot_inputs=0;
 
-  GtkWidget *plot;
-  GtkWidget *run;
-  GtkWidget **chbuttons;
-  GtkWidget *rangemenu;
-} p;
-
-float plot_range=0;
 int plot_scale=0;
 int plot_span=0;
+float plot_range=0;
 int plot_rchoice=0;
 int plot_schoice=0;
 int plot_spanchoice=0;
 int plot_interval=0;
 int plot_trigger=0;
+int plot_type=0;
 int plot_hold=0;
 int plot_bold=0;
 int plot_sep=0;
+int plot_rate[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
 
-int plot_type=0;
-int plot_last_update=0;
-int *active;
+/* first up... the Fucking Fish */
+sig_atomic_t increment_fish=0;
 
-int overslice[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+static int reanimate_fish(void){
+  if(process_active || (fishframe>0 && fishframe<12)){
+    /* continue spinning */
+    if(increment_fish || fishframe>0)fishframe++;
+    if(fishframe==1)increment_fish=0;
+    if(fishframe>=12)fishframe=0;
 
-static void override_base(GtkWidget *w, int active){
-  gtk_widget_modify_base (w, GTK_STATE_NORMAL, &w->style->bg[active?GTK_STATE_ACTIVE:GTK_STATE_NORMAL]);
-}
+    gtk_image_set_from_pixmap(GTK_IMAGE(twirlimage),
+			      ff[fishframe],
+			      fb[fishframe]);
 
-static void set_slices(int interval, int span){
-
-  /* update interval limited to < 25fps */
-  //int temp = (interval < 50000 ? 50000:interval),fi;
-
-  int temp=interval,fi;
-
-  pthread_mutex_lock(&ioparam_mutex);
-  if(temp <= span){
-    /* if the fps-limited update interval is shorter than or equal to
-       the span, we simply frame limit */
-    for(fi=0;fi<inputs;fi++){
-      blockslice[fi]=rint(rate[fi]/1000000.*temp);
-      overslice[fi]=rint(rate[fi]/1000000.*temp);
-    }
-  }else{
-    /* if the limited update interval is longer than the span, we
-       overdraw */
-    for(fi=0;fi<inputs;fi++)
-      blockslice[fi]=rint(rate[fi]/1000000.*temp);
-    for(fi=0;fi<inputs;fi++){
-      overslice[fi]=rint(rate[fi]/1000000.*interval);
-    }
-  }
-  pthread_mutex_unlock(&ioparam_mutex);
-}
-
-static void replot(struct panel *p){
-  /* update the waveform display; send new data */
-  if(!plot_hold){
-    pthread_mutex_lock(&feedback_mutex);
-    plot_refresh(PLOT(p->plot));
-    plot_last_update=feedback_increment;
-    pthread_mutex_unlock(&feedback_mutex);
-  }
-}
-
-static void shutdown(void){
-  gtk_main_quit();
-}
-
-/* gotta have the Fucking Fish */
-static int reanimate_fish(struct panel *p){
-  if(process_active || (p->fishframe>0 && p->fishframe<12)){
-    /* continue spinning */
-    if(increment_fish)p->fishframe++;
-    if(p->fishframe>=12)p->fishframe=0;
-    
-    gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
-			      p->ff[p->fishframe],
-			      p->fb[p->fishframe]);
-    
-    if(p->fishframe==0 && !process_active){
+    if(fishframe==0 && !process_active){
       /* reschedule to blink */
-      p->fishframe_timer=
-	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+      fishframe_timer=
+	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,NULL);
       return FALSE;
     }
 
   }else{
-    p->fishframe++;
-    if(p->fishframe<=1)p->fishframe=12;
-    if(p->fishframe>=19)p->fishframe=0;
+    fishframe++;
+    if(fishframe<=1)fishframe=12;
+    if(fishframe>=19)fishframe=0;
 
-    gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
-			      p->ff[p->fishframe],
-			      p->fb[p->fishframe]);
+    gtk_image_set_from_pixmap(GTK_IMAGE(twirlimage),
+			      ff[fishframe],
+			      fb[fishframe]);
 
 
-    if(p->fishframe==12){
+    if(fishframe==12){
       /* reschedule to animate */
-      p->fishframe_timer=
-	g_timeout_add(10,(GSourceFunc)reanimate_fish,p);
+      fishframe_timer=
+	g_timeout_add(10,(GSourceFunc)reanimate_fish,NULL);
       return FALSE;
     }
-    if(p->fishframe==0){
+    if(fishframe==0){
       /* reschedule to blink */
-      p->fishframe_timer=
-	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+      fishframe_timer=
+	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,NULL);
       return FALSE;
     }
   }
   return TRUE;
 }
 
-static void animate_fish(struct panel *p){
-  if(p->fishframe_init){
-    g_source_remove(p->fishframe_timer);
-    p->fishframe_timer=
-      g_timeout_add(80,(GSourceFunc)reanimate_fish,p);
+static void animate_fish(void){
+  if(fishframe_init){
+    g_source_remove(fishframe_timer);
+    fishframe_timer=
+      g_timeout_add(80,(GSourceFunc)reanimate_fish,NULL);
   }else{
-    p->fishframe_init=1;
-    p->fishframe_timer=
-      g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+    fishframe_init=1;
+    fishframe_timer=
+      g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,NULL);
   }
 }
 
-static void set_fg(GtkWidget *c, gpointer in){
-  GdkColor *rgb = in;
-  gtk_widget_modify_fg(c,GTK_STATE_NORMAL,rgb);
-  gtk_widget_modify_fg(c,GTK_STATE_ACTIVE,rgb);
-  gtk_widget_modify_fg(c,GTK_STATE_PRELIGHT,rgb);
-  gtk_widget_modify_fg(c,GTK_STATE_SELECTED,rgb);
+static void set_slices(int interval, int span){
+  int fi;
+  /* update interval limited to < 25fps */
+  //int temp = (interval < 50000 ? 50000:interval),fi;
+  for(fi=0;fi<plot_inputs;fi++)
+    set_blockslice(rint(plot_rate[fi]/1000000.*interval),fi);
+}
 
-  /* buttons usually have internal labels */
-  if(GTK_IS_CONTAINER(c))
-    gtk_container_forall (GTK_CONTAINER(c),set_fg,in);
+static void override_base(GtkWidget *w, int active){
+  gtk_widget_modify_base
+    (w, GTK_STATE_NORMAL,
+     &w->style->bg[active?GTK_STATE_ACTIVE:GTK_STATE_NORMAL]);
 }
 
+static void chlabels(GtkWidget *widget,gpointer in){
+  replot();
+}
+
 static int rangechange_ign=0;
-static void rangechange(GtkWidget *widget,struct panel *p){
+static void rangechange(GtkWidget *widget,gpointer in){
   if(!rangechange_ign){
     int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
     plot_rchoice=choice;
@@ -217,11 +176,11 @@
       plot_range=.001;
       break;
     }
-    plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,plot_type,NULL,NULL);
+    replot();
   }
 }
 
-static void scalechange(GtkWidget *widget,struct panel *p){
+static void scalechange(GtkWidget *widget,gpointer in){
   int i;
   int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
   plot_schoice=choice;
@@ -257,8 +216,8 @@
       "0.01",
       "0.001"};
     for(i=0;i<10;i++){
-      gtk_combo_box_remove_text (GTK_COMBO_BOX (p->rangemenu), i);
-      gtk_combo_box_insert_text (GTK_COMBO_BOX (p->rangemenu), i, entries[i]);
+      gtk_combo_box_remove_text (GTK_COMBO_BOX (rangemenu), i);
+      gtk_combo_box_insert_text (GTK_COMBO_BOX (rangemenu), i, entries[i]);
     }
 
   }else{
@@ -274,17 +233,16 @@
       "-40dB",
       "-60dB"};
     for(i=0;i<10;i++){
-      gtk_combo_box_remove_text (GTK_COMBO_BOX (p->rangemenu), i);
-      gtk_combo_box_insert_text (GTK_COMBO_BOX (p->rangemenu), i, entries[i]);
+      gtk_combo_box_remove_text (GTK_COMBO_BOX (rangemenu), i);
+      gtk_combo_box_insert_text (GTK_COMBO_BOX (rangemenu), i, entries[i]);
     }
   }
-  gtk_combo_box_set_active(GTK_COMBO_BOX(p->rangemenu),plot_rchoice);
+  gtk_combo_box_set_active(GTK_COMBO_BOX(rangemenu),plot_rchoice);
   rangechange_ign=0;
-
-  plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,plot_type,NULL,NULL);
+  replot();
 }
 
-static void spanchange(GtkWidget *widget,struct panel *p){
+static void spanchange(GtkWidget *widget,gpointer in){
   int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
   plot_spanchoice=choice;
   switch(choice){
@@ -330,12 +288,10 @@
   }
 
   set_slices(plot_interval,plot_span);
-
-  plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,plot_type,blockslice,overslice);
+  replot();
 }
 
-/* intervals that are >= the span and would result in > 25fps are overdrawn */
-static void intervalchange(GtkWidget *widget,struct panel *p){
+static void intervalchange(GtkWidget *widget,gpointer in){
   int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
   switch(choice){
   case 0:
@@ -380,78 +336,175 @@
   }
 
   set_slices(plot_interval,plot_span);
-
-  plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,plot_type,blockslice,overslice);
+  replot();
 }
 
-static void triggerchange(GtkWidget *widget,struct panel *p){
-  int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+static void triggerchange(GtkWidget *widget,gpointer in){
+  //int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
   /* nothing but free-run supported right now */
 }
 
-static void runchange(GtkWidget *widget,struct panel *p){
+static void runchange(GtkWidget *widget,gpointer in){
   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))){
     if(!process_active){
       pthread_t thread_id;
       process_active=1;
       process_exit=0;
-      animate_fish(p);
+      animate_fish();
       pthread_create(&thread_id,NULL,&process_thread,NULL);
     }
   }else{
     process_exit=1;
-    while(process_active)sched_yield();
   }
 }
 
-static void holdchange(GtkWidget *widget,struct panel *p){
+static void holdchange(GtkWidget *widget,gpointer in){
   plot_hold=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
   override_base(widget,plot_bold);
-  replot(p);
-  plot_draw(PLOT(p->plot));
+  replot();
 }
 
-static void boldchange(GtkWidget *widget,struct panel *p){
+static void boldchange(GtkWidget *widget,gpointer in){
   plot_bold=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
   override_base(widget,plot_bold);
-  plot_set_bold(PLOT(p->plot),plot_bold);
+  replot();
 }
 
-static void sepchange(GtkWidget *widget,struct panel *p){
+static void sepchange(GtkWidget *widget,gpointer in){
   plot_sep=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
   override_base(widget,plot_sep);
-  plot_set_sep(PLOT(p->plot),plot_sep);
+  replot();
 }
 
-static void plotchange(GtkWidget *widget,struct panel *p){
+static void plotchange(GtkWidget *widget,gpointer in){
   plot_type=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
-  plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,plot_type,blockslice,overslice);
+  replot();
 }
 
-static void loopchange(GtkWidget *widget,struct panel *p){
+static void loopchange(GtkWidget *widget,gpointer in){
   acc_loop=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
 }
 
-static void rewindchange(GtkWidget *widget,struct panel *p){
+static void rewindchange(GtkWidget *widget,gpointer in){
   acc_rewind=1;
 }
 
-static void chlabels(GtkWidget *widget,struct panel *p){
-  /* scan state, update labels on channel buttons, set sensitivity
-     based on grouping and mode */
-  int fi,ch,i;
-  char buf[80];
+static void create_chbuttons(int *bits, int *rate, int *channels,
+                             int *active){
+  int i,fi;
+  int ch=0;
+  char buffer[160];
 
-  for(i=0;i<total_ch;i++)
-    active[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
+  groupboxes = calloc(plot_inputs,sizeof(*groupboxes));
+  chbuttons = calloc(plot_ch,sizeof(*chbuttons));
+  for(fi=0;fi<plot_inputs;fi++){
+    GtkWidget *al=groupboxes[fi]=gtk_alignment_new(0,0,1,0);
+    GtkWidget *vbox=gtk_vbox_new(0,0);
+    GtkWidget *label;
 
-  plot_set_active(PLOT(p->plot),active);
+    char *lastslash = strrchr(inputname[fi],'/');
+    sprintf(buffer,"%s",(lastslash?lastslash+1:inputname[fi]));
+    label=gtk_label_new(buffer);
+    gtk_widget_set_name(label,"readout");
+    gtk_box_pack_start(GTK_BOX(vbox),label,0,0,0);
+
+    sprintf(buffer,"%dHz %dbit",rate[fi],bits[fi]);
+    label=gtk_label_new(buffer);
+    gtk_widget_set_name(label,"readout");
+    gtk_box_pack_start(GTK_BOX(vbox),label,0,0,0);
+
+    for(i=ch;i<ch+channels[fi];i++){
+      GtkWidget *button=chbuttons[i]=gtk_toggle_button_new();
+
+      sprintf(buffer,"channel %d", i-ch);
+      gtk_button_set_label(GTK_BUTTON(button),buffer);
+
+      if(active)
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),active[i]);
+      else
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),1);
+      g_signal_connect (G_OBJECT (button), "clicked",
+                        G_CALLBACK (chlabels), NULL);
+      gtk_box_pack_start(GTK_BOX(vbox),button,0,0,0);
+    }
+
+    gtk_container_add(GTK_CONTAINER(al),vbox);
+    gtk_alignment_set_padding(GTK_ALIGNMENT(al),0,10,0,0);
+    gtk_box_pack_start(GTK_BOX(rightbox),al,0,0,0);
+
+    ch+=channels[fi];
+    gtk_widget_show_all(al);
+  }
 }
 
+static void destroy_chbuttons(){
+  int fi;
+
+  for(fi=0;fi<plot_inputs;fi++){
+    gtk_widget_destroy(groupboxes[fi]);
+    groupboxes[fi]=NULL;
+  }
+
+  free(groupboxes);
+  free(chbuttons);
+  groupboxes=NULL;
+  chbuttons=NULL;
+}
+
+static plotparams pp;
+void replot(void){
+  int i,process[plot_ch],old_ch=plot_ch;
+
+  for(i=0;i<plot_ch;i++)
+    process[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chbuttons[i]));
+
+  /* update the waveform display; send new data */
+  fetchdata *f = process_fetch (plot_span, plot_scale, plot_range, process);
+  if(!f)return;
+
+  /* the fetched data may indicate the underlying file data has
+     changed... */
+  if(f->reload){
+
+    /* update group block slices */
+    memcpy(plot_rate,f->rate,sizeof(plot_rate));
+    set_slices(plot_interval,plot_span);
+
+    /* remove old group labels and channel buttons */
+    destroy_chbuttons();
+
+    plot_ch=f->total_ch;
+    plot_inputs=f->groups;
+
+    /* create new buttons/labels */
+    {
+      int newprocess[f->total_ch];
+      for(i=0;i<plot_ch && i<old_ch;i++)
+        newprocess[i]=process[i];
+      for(;i<plot_ch;i++)
+        newprocess[i]=0;
+      create_chbuttons(f->bits,f->rate,f->channels,newprocess);
+    }
+  }
+
+  pp.bold=plot_bold;
+  pp.trace_sep=plot_sep;
+  pp.span=plot_span;
+  pp.plotchoice=plot_type;
+  pp.spanchoice=plot_spanchoice;
+  pp.rangechoice=plot_rchoice;
+  pp.scalechoice=plot_schoice;
+
+  plot_draw(PLOT(plot),f,&pp);
+}
+
+static void shutdown(void){
+  gtk_main_quit();
+}
+
 static gint watch_keyboard(GtkWidget *grab_widget,
                            GdkEventKey *event,
-                           gpointer func_data){
-  struct panel *p=(struct panel *)func_data;
+                           gpointer in){
 
   if(event->type == GDK_KEY_PRESS){
     if(event->state == GDK_CONTROL_MASK){
@@ -463,7 +516,7 @@
 }
 
 extern char *version;
-void panel_create(struct panel *panel){
+void panel_create(void){
   int i;
 
   GdkWindow *root=gdk_get_default_root_window();
@@ -471,21 +524,20 @@
   GtkWidget *rightframe=gtk_frame_new (NULL);
   GtkWidget *righttopbox=gtk_vbox_new(0,0);
   GtkWidget *rightframebox=gtk_event_box_new();
-  GtkWidget *rightbox=gtk_vbox_new(0,0);
   GtkWidget *lefttable=gtk_table_new(4,2,0);
   GtkWidget *plot_control_al;
   GtkWidget *wbold;
 
-  active = calloc(total_ch,sizeof(*active));
+  rightbox=gtk_vbox_new(0,0);
 
-  panel->toplevel=gtk_window_new (GTK_WINDOW_TOPLEVEL);
-  panel->group = gtk_accel_group_new ();
-  gtk_window_add_accel_group (GTK_WINDOW(panel->toplevel), panel->group);
-  gtk_window_set_title(GTK_WINDOW(panel->toplevel),(const gchar *)"Waveform Viewer");
-  gtk_window_set_default_size(GTK_WINDOW(panel->toplevel),1024,400);
-  //gtk_widget_set_size_request(GTK_WIDGET(panel->toplevel),1024,400);
-  gtk_container_add (GTK_CONTAINER (panel->toplevel), topbox);
-  g_signal_connect (G_OBJECT (panel->toplevel), "delete_event",
+  toplevel=gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  group = gtk_accel_group_new ();
+  gtk_window_add_accel_group (GTK_WINDOW(toplevel), group);
+  gtk_window_set_title(GTK_WINDOW(toplevel),(const gchar *)"Waveform Viewer");
+  gtk_window_set_default_size(GTK_WINDOW(toplevel),1024,400);
+  //gtk_widget_set_size_request(GTK_WIDGET(toplevel),1024,400);
+  gtk_container_add (GTK_CONTAINER (toplevel), topbox);
+  g_signal_connect (G_OBJECT (toplevel), "delete_event",
 		    G_CALLBACK (shutdown), NULL);
   gtk_widget_set_name(topbox,"panel");
 
@@ -497,8 +549,10 @@
   {
     GtkWidget *al=plot_control_al=gtk_alignment_new(0,0,0,0);
     GtkWidget *box=gtk_hbox_new(0,6);
-    GtkWidget *hold_display=gtk_check_button_new_with_mnemonic("_hold display");
-    GtkWidget *trace_sep=gtk_check_button_new_with_mnemonic("trace _separation");
+    GtkWidget *hold_display=
+      gtk_check_button_new_with_mnemonic("_hold display");
+    GtkWidget *trace_sep=
+      gtk_check_button_new_with_mnemonic("trace _separation");
     wbold=gtk_check_button_new_with_mnemonic("_bold");
     gtk_table_attach(GTK_TABLE (lefttable), al,0,1,1,2,GTK_FILL,GTK_FILL,0,0);
     gtk_container_add(GTK_CONTAINER (al),box);
@@ -509,18 +563,20 @@
     gtk_widget_set_name(hold_display,"top-control");
     gtk_widget_set_name(wbold,"top-control");
     gtk_widget_set_name(trace_sep,"top-control");
-    g_signal_connect (G_OBJECT (hold_display), "clicked", G_CALLBACK (holdchange), panel);
-    gtk_widget_add_accelerator (hold_display, "activate", panel->group, GDK_h, 0, 0);
-    g_signal_connect (G_OBJECT (wbold), "clicked", G_CALLBACK (boldchange), panel);
-    gtk_widget_add_accelerator (wbold, "activate", panel->group, GDK_b, 0, 0);
-    g_signal_connect (G_OBJECT (trace_sep), "clicked", G_CALLBACK (sepchange), panel);
-    gtk_widget_add_accelerator (trace_sep, "activate", panel->group, GDK_s, 0, 0);
-
+    g_signal_connect (G_OBJECT (hold_display), "clicked",
+                      G_CALLBACK (holdchange), NULL);
+    gtk_widget_add_accelerator (hold_display, "activate", group, GDK_h, 0, 0);
+    g_signal_connect (G_OBJECT (wbold), "clicked",
+                      G_CALLBACK (boldchange), NULL);
+    gtk_widget_add_accelerator (wbold, "activate", group, GDK_b, 0, 0);
+    g_signal_connect (G_OBJECT (trace_sep), "clicked",
+                      G_CALLBACK (sepchange), NULL);
+    gtk_widget_add_accelerator (trace_sep, "activate", group, GDK_s, 0, 0);
   }
 
   /* add the waveform plot box */
-  panel->plot=plot_new(blocksize,inputs,channels,rate);
-  gtk_table_attach_defaults (GTK_TABLE (lefttable), panel->plot,0,1,2,3);
+  plot=plot_new();
+  gtk_table_attach_defaults (GTK_TABLE (lefttable), plot,0,1,2,3);
   gtk_table_set_row_spacing (GTK_TABLE (lefttable), 2, 4);
   gtk_table_set_col_spacing (GTK_TABLE (lefttable), 0, 2);
 
@@ -533,7 +589,6 @@
   gtk_container_add (GTK_CONTAINER (rightframebox),rightframe);
   gtk_container_add (GTK_CONTAINER (rightframe), rightbox);
 
-
   /* the Fucking Fish */
   {
     GtkWidget *toptable = gtk_table_new(2,1,0);
@@ -550,10 +605,10 @@
     w/=19;
 
     for(i=0;i<19;i++){
-      panel->ff[i]=gdk_pixmap_new(tp,w,h,-1);
-      panel->fb[i]=gdk_pixmap_new(tb,w,h,-1);
-      gdk_draw_drawable(panel->ff[i],cgc,tp,i*w,0,0,0,w,h);
-      gdk_draw_drawable(panel->fb[i],bgc,tb,i*w,0,0,0,w,h);
+      ff[i]=gdk_pixmap_new(tp,w,h,-1);
+      fb[i]=gdk_pixmap_new(tb,w,h,-1);
+      gdk_draw_drawable(ff[i],cgc,tp,i*w,0,0,0,w,h);
+      gdk_draw_drawable(fb[i],bgc,tb,i*w,0,0,0,w,h);
     }
 
     g_object_unref(cgc);
@@ -561,69 +616,24 @@
     g_object_unref(tp);
     g_object_unref(tb);
 
-    panel->twirlimage=gtk_image_new_from_pixmap(panel->ff[0],panel->fb[0]);
+    twirlimage=gtk_image_new_from_pixmap(ff[0],fb[0]);
 
     gtk_container_set_border_width (GTK_CONTAINER (toptable), 1);
     gtk_box_pack_start(GTK_BOX(righttopbox),toptable,0,0,0);
     gtk_container_add (GTK_CONTAINER (sepbox), topsep);
-    gtk_container_add(GTK_CONTAINER(fishbox),panel->twirlimage);
+    gtk_container_add(GTK_CONTAINER(fishbox),twirlimage);
     gtk_table_attach_defaults (GTK_TABLE (toptable), fishbox,0,1,0,1);
     gtk_table_attach_defaults (GTK_TABLE (toptable), sepbox,0,1,0,1);
     gtk_table_set_row_spacing (GTK_TABLE (toptable), 0, 6);
   }
 
-  /* rate */
-  /* channels */
-  /* bits */
-  {
-    int fi;
-    int ch=0;
-    char buffer[160];
-    GtkWidget *label;
+  create_chbuttons(bits,rate,channels,NULL);
 
-    panel->chbuttons = calloc(total_ch,sizeof(*panel->chbuttons));
-    for(fi=0;fi<inputs;fi++){
-      GtkWidget *al=gtk_alignment_new(0,0,1,0);
-      GtkWidget *vbox=gtk_vbox_new(0,0);
-      
-      char *lastslash = strrchr(inputname[fi],'/');
-      sprintf(buffer,"%s",(lastslash?lastslash+1:inputname[fi]));
-      label=gtk_label_new(buffer);
-      gtk_widget_set_name(label,"readout");
-      gtk_box_pack_start(GTK_BOX(vbox),label,0,0,0);
-      
-      sprintf(buffer,"%dHz %dbit",rate[fi],bits[fi]);
-      label=gtk_label_new(buffer);
-      gtk_widget_set_name(label,"readout");
-      gtk_box_pack_start(GTK_BOX(vbox),label,0,0,0);
-
-      for(i=ch;i<ch+channels[fi];i++){
-	GtkWidget *button=panel->chbuttons[i]=gtk_toggle_button_new();
-
-        sprintf(buffer,"channel %d", i-ch);
-        gtk_button_set_label(GTK_BUTTON(button),buffer);
-
-	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),1);  
-	g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (chlabels), panel);
-	gtk_box_pack_start(GTK_BOX(vbox),button,0,0,0);
-      }
-
-      gtk_container_add(GTK_CONTAINER(al),vbox);
-      gtk_alignment_set_padding(GTK_ALIGNMENT(al),0,10,0,0);
-      gtk_box_pack_start(GTK_BOX(rightbox),al,0,0,0);
-
-      ch+=channels[fi];
-
-    }
-    chlabels(NULL,panel);
-  }
-
   /* add the action buttons */
   GtkWidget *bbox=gtk_vbox_new(0,0);
 
-  /* add the action buttons */
-  /* range */
   {
+    /* range */
     GtkWidget *box=gtk_hbox_new(1,1);
 
     GtkWidget *menu=gtk_combo_box_new_text();
@@ -631,15 +641,15 @@
     for(i=0;i<10;i++)
       gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
     g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (rangechange), panel);
-    panel->rangemenu = menu;
+		      G_CALLBACK (rangechange), NULL);
+    rangemenu = menu;
 
     GtkWidget *menu2=gtk_combo_box_new_text();
     char *entries2[]={"linear","-65dB","-96dB","-120dB","-160dB"};
     for(i=0;i<5;i++)
       gtk_combo_box_append_text (GTK_COMBO_BOX (menu2), entries2[i]);
     g_signal_connect (G_OBJECT (menu2), "changed",
-		      G_CALLBACK (scalechange), panel);
+		      G_CALLBACK (scalechange), NULL);
 
     gtk_box_pack_start(GTK_BOX(box),menu2,1,1,0);
     gtk_box_pack_start(GTK_BOX(box),menu,1,1,0);
@@ -664,7 +674,7 @@
     for(i=0;i<13;i++)
       gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
     g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (spanchange), panel);
+		      G_CALLBACK (spanchange), NULL);
     gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
 
 
@@ -683,7 +693,7 @@
       gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
     gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
     g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (triggerchange), panel);
+		      G_CALLBACK (triggerchange), NULL);
 
     /* interval */
 
@@ -697,7 +707,7 @@
     for(i=0;i<13;i++)
       gtk_combo_box_append_text (GTK_COMBO_BOX (menu2), entries2[i]);
     g_signal_connect (G_OBJECT (menu2), "changed",
-		      G_CALLBACK (intervalchange), panel);
+		      G_CALLBACK (intervalchange), NULL);
     gtk_combo_box_set_active(GTK_COMBO_BOX(menu2),3);
 
 
@@ -716,7 +726,7 @@
       gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
     gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
     g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (plotchange), panel);
+		      G_CALLBACK (plotchange), NULL);
     gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
   }
 
@@ -728,11 +738,12 @@
   /* run/pause */
   {
     GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_run");
-    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_space, 0, 0);
-    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_r, 0, 0);
-    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (runchange), panel);
+    gtk_widget_add_accelerator (button, "activate", group, GDK_space, 0, 0);
+    gtk_widget_add_accelerator (button, "activate", group, GDK_r, 0, 0);
+    g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (runchange), NULL);
     gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
-    panel->run=button;
+    run=button;
   }
 
   /* loop */
@@ -740,33 +751,34 @@
   {
     GtkWidget *box=gtk_hbox_new(1,1);
     GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_loop");
-    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_l, 0, 0);
-    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (loopchange), panel);
+    gtk_widget_add_accelerator (button, "activate", group, GDK_l, 0, 0);
+    g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (loopchange), NULL);
     gtk_widget_set_sensitive(button,global_seekable);
     gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
 
     button=gtk_button_new_with_mnemonic("re_wind");
-    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_w, 0, 0);
-    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (rewindchange), panel);
+    gtk_widget_add_accelerator (button, "activate", group, GDK_w, 0, 0);
+    g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (rewindchange), NULL);
     gtk_widget_set_sensitive(button,global_seekable);
     gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
     gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
   }
 
   gtk_box_pack_end(GTK_BOX(rightbox),bbox,0,0,0);
-  gtk_widget_show_all(panel->toplevel);
+  gtk_widget_show_all(toplevel);
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wbold),plot_bold);
 
-  gtk_key_snooper_install(watch_keyboard,panel);
+  gtk_key_snooper_install(watch_keyboard,NULL);
 
-  gtk_alignment_set_padding(GTK_ALIGNMENT(plot_control_al),0,0,plot_get_left_pad(PLOT(panel->plot)),0);
+  gtk_alignment_set_padding(GTK_ALIGNMENT(plot_control_al),0,0,plot_get_left_pad(PLOT(plot)),0);
 
 }
 
 static gboolean async_event_handle(GIOChannel *channel,
 				   GIOCondition condition,
 				   gpointer data){
-  struct panel *panel=data;
   char buf[1];
 
   /* read all pending */
@@ -775,24 +787,20 @@
   increment_fish=1;
 
   /* check playback status and update the run button if needed */
-  if(process_active && panel->run && 
-     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->run)))
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->run),1);
-  if(!process_active && panel->run && 
-     gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->run)))
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->run),0);
+  if(process_active && run && 
+     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(run)))
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(run),1);
+  if(!process_active && run && 
+     gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(run)))
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(run),0);
 
   /* update the waveform display; send new data */
-  pthread_mutex_lock(&feedback_mutex);
-  if(plot_last_update!=feedback_increment){
-    pthread_mutex_unlock(&feedback_mutex);
-    replot(panel);
-    plot_draw(PLOT(panel->plot));
+  if(!plot_hold)replot();
 
-    while (gtk_events_pending())
-      gtk_main_iteration();
-  }else
-    pthread_mutex_unlock(&feedback_mutex);
+  /* if we're near CPU limit, service the rest of Gtk over next async
+     update request */
+  while (gtk_events_pending())
+    gtk_main_iteration();
 
   return TRUE;
 }
@@ -810,7 +818,6 @@
 void panel_go(int argc,char *argv[]){
   char *homedir=getenv("HOME");
   int found=0;
-  memset(&p,0,sizeof(p));
 
   found|=look_for_gtkrc(STR(ETCDIR)"/waveform-gtkrc");
   {
@@ -837,16 +844,20 @@
 
   if(!found){
   
-    fprintf(stderr,"Could not find the waveform-gtkrc configuration file normally\n"
+    fprintf(stderr,
+            "Could not find the waveform-gtkrc configuration file normally\n"
 	    "installed in one of the following places:\n"
 
 	    "\t./waveform-gtkrc\n"
 	    "\t$(SPECTRUM_RCDIR)/waveform-gtkrc\n"
 	    "\t~/.spectrum/waveform-gtkrc\n\t"
 	    STR(ETCDIR)"/wavegform-gtkrc\n"
-	    "This configuration file is used to tune the color, font and other detail aspects\n"
-	    "of the user interface.  Although the viewer will work without it, the UI\n"
-	    "appearence will likely make the application harder to use due to missing visual\n"
+	    "This configuration file is used to tune the color, "
+            "font and other detail aspects\n"
+	    "of the user interface.  Although the viewer will "
+            "work without it, the UI\n"
+	    "appearence will likely make the application harder to "
+            "use due to missing visual\n"
 	    "cues.\n");
   }
 
@@ -862,32 +873,25 @@
   gtk_rc_add_default_file("waveform-gtkrc");
   gtk_init (&argc, &argv);
 
-  panel_create(&p);
-  animate_fish(&p);
+  plot_ch = total_ch; /* true now, won't necessarily be true later */
+  plot_inputs = inputs; /* true now, won't necessarily be true later */
+  memcpy(plot_rate,rate,sizeof(plot_rate));
 
+  panel_create();
+  animate_fish();
+
   /* set up watching the event pipe */
   {
     GIOChannel *channel = g_io_channel_unix_new (eventpipe[0]);
-    guint id;
-
     g_io_channel_set_encoding (channel, NULL, NULL);
     g_io_channel_set_buffered (channel, FALSE);
     g_io_channel_set_close_on_unref (channel, TRUE);
-
-    id = g_io_add_watch (channel, G_IO_IN, async_event_handle, &p);
-
+    g_io_add_watch (channel, G_IO_IN, async_event_handle, NULL);
     g_io_channel_unref (channel);
+  }
 
-  }
-  
   /* we want to be running by default */
-  {
-    pthread_t thread_id;
-    animate_fish(&p);
-    process_active=1;
-    pthread_create(&thread_id,NULL,&process_thread,NULL);
-  }
-
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(run),TRUE);
   gtk_main ();
 
 }

Modified: trunk/spectrum/wave_plot.c
===================================================================
--- trunk/spectrum/wave_plot.c	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/wave_plot.c	2012-06-04 03:26:48 UTC (rev 18353)
@@ -1,24 +1,24 @@
 /*
  *
  *  gtk2 waveform viewer
- *    
+ *
  *      Copyright (C) 2004-2012 Monty
  *
  *  This analyzer 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.
- *   
+ *
  *  The analyzer 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 "waveform.h"
@@ -31,20 +31,23 @@
 
 static GtkDrawingAreaClass *parent_class = NULL;
 
-static void compute_metadata(GtkWidget *widget){
-  Plot *p=PLOT(widget);
+static void compute_xgrid(Plot *p){
+  GtkWidget *widget=GTK_WIDGET(p);
   int width=widget->allocation.width-p->padx;
-  int i,j;
+  if(p->width != width){
+    int i,j;
 
-  p->xgrids=11;
-  p->xtics=30;
+    p->xgrids=11;
+    p->xtics=30;
 
-  for(i=0;i<p->xgrids;i++)
-    p->xgrid[i]=rint(i/(float)(p->xgrids-1) * (width-1))+p->padx;
+    for(i=0;i<p->xgrids;i++)
+      p->xgrid[i]=rintf(i/(float)(p->xgrids-1) * (width-1))+p->padx;
 
-  for(i=0,j=0;i<p->xtics;i++,j++){
-    if(j%4==0)j++;
-    p->xtic[i]=rint(j/(float)((p->xgrids-1)*4) * (width-1))+p->padx;
+    for(i=0,j=0;i<p->xtics;i++,j++){
+      if(j%4==0)j++;
+      p->xtic[i]=rintf(j/(float)((p->xgrids-1)*4) * (width-1))+p->padx;
+    }
+    p->width=width;
   }
 }
 
@@ -88,38 +91,48 @@
     rgb.blue=0xe000;
     break;
   }
-  
+
   return rgb;
 }
 
-static void draw(GtkWidget *widget){
+void plot_draw(Plot *p, fetchdata *f, plotparams *pp){
   int i;
-  Plot *p=PLOT(widget);
+  GtkWidget *widget=GTK_WIDGET(p);
+  GtkWidget *parent=gtk_widget_get_parent(widget);
   int height=widget->allocation.height;
   int width=widget->allocation.width;
-  GtkWidget *parent=gtk_widget_get_parent(widget);
   int padx = p->padx;
   int num_active=0;
+  float center = (height-p->pady)/2.;
 
+  if(!GDK_IS_DRAWABLE(p->backing))return;
+  if(!pp)return;
+  if(!f)return;
+
   /* how many channels actually active right now?  Need to know if
      trace sep is enabled */
-  if(p->trace_sep){
+  if(pp->trace_sep){
     int fi,ch=0;
-    for(fi=0;fi<p->groups;fi++){
-      for(i=ch;i<ch+p->ch[fi];i++)
-        if(p->ch_active[i])
+    for(fi=0;fi<f->groups;fi++){
+      for(i=ch;i<ch+f->channels[fi];i++)
+        if(f->active[i])
           num_active++;
-      ch+=p->ch[fi];
+      ch+=f->channels[fi];
     }
   }
 
   if(!p->drawgc){
-    GdkGCValues values;
     p->drawgc=gdk_gc_new(p->backing);
     p->twogc=gdk_gc_new(p->backing);
     gdk_gc_copy(p->drawgc,widget->style->black_gc);
     gdk_gc_copy(p->twogc,widget->style->black_gc);
-    gdk_gc_set_line_attributes(p->twogc,p->bold+1,GDK_LINE_SOLID,
+  }
+
+  if(pp->plotchoice==2){
+    gdk_gc_set_line_attributes(p->twogc,pp->bold+1,GDK_LINE_SOLID,
+                               GDK_CAP_BUTT,GDK_JOIN_MITER);
+  }else{
+    gdk_gc_set_line_attributes(p->twogc,pp->bold+1,GDK_LINE_SOLID,
                                GDK_CAP_PROJECTING,GDK_JOIN_MITER);
   }
 
@@ -133,6 +146,8 @@
     gdk_draw_rectangle(p->backing,gc,1,padx,0,width-padx,height-p->pady);
   }
 
+  compute_xgrid(p);
+
   /* draw the light x grid */
   {
     int i;
@@ -147,7 +162,7 @@
       gdk_draw_line(p->backing,p->drawgc,p->xtic[i],0,p->xtic[i],height-p->pady-1);
   }
 
-  PangoLayout **proper=p->x_layout[p->spanchoice];
+  PangoLayout **proper=p->x_layout[pp->spanchoice];
 
   for(i=0;i<p->xgrids;i++){
     int px,py;
@@ -159,11 +174,9 @@
                      proper[i]);
   }
 
-
   /* draw the light y grid */
   {
     GdkColor rgb={0,0,0,0};
-    int center = (height-p->pady)/2;
 
     rgb.red=0xc000;
     rgb.green=0xff00;
@@ -171,8 +184,8 @@
     gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
 
     for(i=1;i<8;i++){
-      int y1=rint(center + center*i/9);
-      int y2=rint(center - center*i/9);
+      int y1=rintf(center + center*i/9.);
+      int y2=rintf(center - center*i/9.);
       gdk_draw_line(p->backing,p->drawgc,padx,y1,width,y1);
       gdk_draw_line(p->backing,p->drawgc,padx,y2,width,y2);
     }
@@ -181,11 +194,10 @@
   /* dark y grid */
   {
     GdkColor rgb={0,0,0,0};
-    int center = (height-p->pady)/2;
     int px,py;
 
-    gdk_draw_line(p->backing,p->drawgc,padx,center-1,width,center-1);
-    gdk_draw_line(p->backing,p->drawgc,padx,center+1,width,center+1);
+    gdk_draw_line(p->backing,p->drawgc,padx,rintf(center-1),width,rintf(center-1));
+    gdk_draw_line(p->backing,p->drawgc,padx,rintf(center+1),width,rintf(center+1));
 
     rgb.red=0x0000;
     rgb.green=0xc000;
@@ -193,12 +205,12 @@
     gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
 
     for(i=-8;i<9;i+=4){
-      int y=rint(center + center*i/9);
+      int y=rintf(center + center*i/9.);
 
       gdk_draw_line(p->backing,p->drawgc,padx,y,width,y);
 
-      pango_layout_get_pixel_size(p->y_layout[p->rchoice][p->schoice][i/4+2],
-                                  &px,&py);
+      pango_layout_get_pixel_size
+        (p->y_layout[pp->rangechoice][pp->scalechoice][i/4+2],&px,&py);
 
       if(i<=0){
         rgb.red=0x0000;
@@ -214,7 +226,7 @@
       gdk_draw_layout (p->backing,
                        p->drawgc,
                        padx-px-2, y-py/2,
-                       p->y_layout[p->rchoice][p->schoice][i/4+2]);
+                       p->y_layout[pp->rangechoice][pp->scalechoice][i/4+2]);
 
       rgb.red=0x0000;
       rgb.green=0xc000;
@@ -235,226 +247,204 @@
     gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
 
     for(i=0;i<p->xgrids;i++)
-      gdk_draw_line(p->backing,p->drawgc,p->xgrid[i],0,p->xgrid[i],height-p->pady-1);
+      gdk_draw_line(p->backing,p->drawgc,p->xgrid[i],0,p->xgrid[i],
+                    height-p->pady-1);
   }
 
-  /* zero line */
+  /* center/zero line */
   {
-    int center = (height-p->pady)/2;
-    gdk_draw_line(p->backing,widget->style->black_gc,padx,center,width,center);
+    gdk_draw_line(p->backing,widget->style->black_gc,padx,
+                  rintf(center),width,rintf(center));
   }
 
   /* draw actual data */
-  if(p->ydata){
-    int ch=0,fi,i,j,k;
+  if(f->data){
+    int ch=0,ach=0,fi,i,k;
     const GdkRectangle clip = {p->padx,0,width-p->padx,height-p->pady};
-    const GdkRectangle noclip = {0,0,width,height};
     GdkColor rgb;
-    int y_sep=0;
-    int cp;
 
     gdk_gc_set_clip_rectangle (p->twogc, &clip);
 
-    if(p->trace_sep && num_active>1){
-      y_sep = (height-p->pady)/18*16/num_active;
-      cp = y_sep/2+(height-p->pady)/18;
-    }else{
-      cp = (height-p->pady)/2;
-    }
+    for(fi=0;fi<f->groups;fi++){
+      int spann = ceil(f->rate[fi]/1000000.*pp->span)+1;
 
-    for(fi=0;fi<p->groups;fi++){
-      int copies = (int)ceil(p->blockslice[fi]/p->overslice[fi]);
-      int spann = ceil(p->rate[fi]/1000000.*p->span)+1;
+      for(i=ch;i<ch+f->channels[fi];i++){
 
-      for(i=ch;i<ch+p->ch[fi];i++){
-        if(p->ch_active[i]){
-          int offset=0;
+        if(f->active[i]){
+          float *data=f->data[i];
+          int wp=width-p->padx;
+          float spani = 1000000./f->span/f->rate[fi]*wp;
+          int hp=height-p->pady;
+          float ym=hp*-8./18;
+          float cp = pp->trace_sep ?
+            (height-p->pady)*(16*i+8)/(float)(18*num_active)+
+            (height-p->pady)/18. : center;
+
+          ach++;
           rgb = chcolor(i);
           gdk_gc_set_rgb_fg_color(p->twogc,&rgb);
 
-          for(j=0;j<copies;j++){
-            float *data=p->ydata[i]+offset;
-            int wp=width-p->padx;
-            float spani = 1000000./p->span/p->rate[fi]*wp;
-            int hp=height-p->pady;
-            float ym=hp*-8./18;
+          switch(pp->plotchoice){
+          case 0: /* zero-hold */
+            {
+              int x0=-1;
+              float yH=NAN,yL=NAN,y0=NAN;
+              int acc=0;
+              for(k=0;k<spann;k++){
+                int x1 = rintf(k*spani);
+                float y1 = data[k]*ym;
 
-            switch(p->type){
-            case 0: /* zero-hold */
-              {
-                int x0=-1;
-                float yH=NAN,yL=NAN,y0=NAN;
-                int acc=0;
-                for(k=0;k<spann;k++){
-                  int x1 = rint(k*spani);
-                  float y1 = data[k]*ym;
-
-                  if(x1>x0){
-                    if(acc>1){
-                      if(!isnan(yL)&&!isnan(yH))
-                        gdk_draw_line(p->backing,p->twogc,
-                                      x0+padx,rint(yL)+cp,x0+padx,
-                                      rint(yH)+cp);
-                    }
-                    if(!isnan(y0)){
+                if(x1>x0){
+                  if(acc>1){
+                    if(!isnan(yL)&&!isnan(yH))
                       gdk_draw_line(p->backing,p->twogc,
-                                    x0+padx,rint(y0)+cp,x1+padx,rint(y0)+cp);
-
-                      if(!isnan(y1))
-                        gdk_draw_line(p->backing,p->twogc,
-                                      x1+padx,rint(y0)+cp,x1+padx,rint(y1)+cp);
-                    }
-
-                    acc=1;
-                    yH=yL=y1;
-                  }else{
-                    acc++;
-                    if(!isnan(y1)){
-                      if(y1<yL || isnan(yL))yL=y1;
-                      if(y1>yH || isnan(yH))yH=y1;
-                    }
+                                    x0+padx,rintf(yL+cp),x0+padx,
+                                    rintf(yH+cp));
                   }
-                  x0=x1;
-                  y0=y1;
-                }
-                {
-                  int x1 = rint(k*spani);
+                  if(!isnan(y0)){
+                    gdk_draw_line(p->backing,p->twogc,
+                                  x0+padx,rintf(y0+cp),x1+padx,rintf(y0+cp));
 
-                  if(x1<=x0 || acc>1){
-                    if(!isnan(yL)&&!isnan(yH))
+                    if(!isnan(y1))
                       gdk_draw_line(p->backing,p->twogc,
-                                    x0+padx,rint(yL)+cp,x0+padx,rint(yH)+cp);
+                                    x1+padx,rintf(y0+cp),x1+padx,rintf(y1+cp));
                   }
+
+                  acc=1;
+                  yH=yL=y1;
+                }else{
+                  acc++;
+                  if(!isnan(y1)){
+                    if(y1<yL || isnan(yL))yL=y1;
+                    if(y1>yH || isnan(yH))yH=y1;
+                  }
                 }
+                x0=x1;
+                y0=y1;
               }
-              break;
-            case 1: /* linear interpolation */
               {
-                int x0=-1;
-                float yH=NAN,yL=NAN,y0=NAN;
-                int acc=0;
-                for(k=0;k<spann;k++){
-                  int x1 = rint(k*spani);
-                  float y1 = data[k]*ym;
+                int x1 = rintf(k*spani);
 
-                  if(x1>x0){
-                    if(acc>1){
-                      if(!isnan(yL) && !isnan(yH))
-                        gdk_draw_line(p->backing,p->twogc,
-                                      x0+padx,rint(yL)+cp,x0+padx,
-                                      rint(yH)+cp);
-                    }
-                    if(!isnan(y0) && !isnan(y1)){
-                      gdk_draw_line(p->backing,p->twogc,
-                                    x0+padx,rint(y0)+cp,x1+padx,rint(y1)+cp);
-                    }
-
-                    acc=1;
-                    yH=yL=y1;
-                  }else{
-                    acc++;
-                    if(!isnan(y1)){
-                      if(y1<yL || isnan(yL))yL=y1;
-                      if(y1>yH || isnan(yH))yH=y1;
-                    }
-                  }
-                  x0=x1;
-                  y0=y1;
+                if(x1<=x0 || acc>1){
+                  if(!isnan(yL)&&!isnan(yH))
+                    gdk_draw_line(p->backing,p->twogc,
+                                  x0+padx,rintf(yL+cp),x0+padx,rintf(yH+cp));
                 }
-                {
-                  int x1 = rint(k*spani);
+              }
+            }
+            break;
+          case 1: /* linear interpolation (first-order hold) */
+            {
+              int x0=-1;
+              float yH=NAN,yL=NAN,y0=NAN;
+              int acc=0;
+              for(k=0;k<spann;k++){
+                int x1 = rintf(k*spani);
+                float y1 = data[k]*ym;
 
-                  if(x1<=x0 || acc>1){
+                if(x1>x0){
+                  if(acc>1){
                     if(!isnan(yL) && !isnan(yH))
                       gdk_draw_line(p->backing,p->twogc,
-                                    x0+padx,rint(yL)+cp,x0+padx,rint(yH)+cp);
+                                    x0+padx,rintf(yL+cp),x0+padx,
+                                    rintf(yH+cp));
                   }
+                  if(!isnan(y0) && !isnan(y1)){
+                    gdk_draw_line(p->backing,p->twogc,
+                                  x0+padx,rintf(y0+cp),x1+padx,rintf(y1+cp));
+                  }
+
+                  acc=1;
+                  yH=yL=y1;
+                }else{
+                  acc++;
+                  if(!isnan(y1)){
+                    if(y1<yL || isnan(yL))yL=y1;
+                    if(y1>yH || isnan(yH))yH=y1;
+                  }
                 }
+                x0=x1;
+                y0=y1;
               }
-              break;
-            case 2: /* lollipop */
               {
-                int x0=-1;
-                float yH=NAN,yL=NAN;
-                int acc=0;
+                int x1 = rintf(k*spani);
 
-                rgb.red=0x8000;
-                rgb.green=0x8000;
-                rgb.blue=0x8000;
-                gdk_gc_set_rgb_fg_color(p->twogc,&rgb);
+                if(x1<=x0 || acc>1){
+                  if(!isnan(yL) && !isnan(yH))
+                    gdk_draw_line(p->backing,p->twogc,
+                                  x0+padx,rintf(yL+cp),x0+padx,rintf(yH+cp));
+                }
+              }
+            }
+            break;
+          case 2: /* lollipop */
+            {
+              int x0=-1;
+              float yH=NAN,yL=NAN;
 
-                for(k=0;k<spann;k++){
-                  int x1 = rint(k*spani);
-                  float y1 = data[k]*ym;
+              rgb.red=0x8000;
+              rgb.green=0x8000;
+              rgb.blue=0x8000;
+              gdk_gc_set_rgb_fg_color(p->twogc,&rgb);
 
-                  if(x1>x0){
-                    if(!isnan(yL) || !isnan(yH)){
-                      if(isnan(yL) || yL>0)yL=0;
-                      if(isnan(yH) || yH<0)yH=0;
-                      gdk_draw_line(p->backing,p->twogc,
-                                    x0+padx,rint(yL)+cp,x0+padx,
-                                    rint(yH)+cp);
-                    }
-                    yH=yL=y1;
-                  }else{
-                    if(!isnan(y1)){
-                      if(y1<yL || isnan(yL))yL=y1;
-                      if(y1>yH || isnan(yH))yH=y1;
-                    }
-                  }
-                  x0=x1;
-                }
-                {
-                  int x1 = rint(k*spani);
+              for(k=0;k<spann;k++){
+                int x1 = rintf(k*spani);
+                float y1 = data[k]*ym;
+
+                if(x1>x0){
                   if(!isnan(yL) || !isnan(yH)){
                     if(isnan(yL) || yL>0)yL=0;
                     if(isnan(yH) || yH<0)yH=0;
                     gdk_draw_line(p->backing,p->twogc,
-                                  x0+padx,rint(yL)+cp,x0+padx,
-                                  rint(yH)+cp);
+                                  x0+padx,rintf(yL+cp),x0+padx,
+                                  rintf(yH+cp));
                   }
+                  yH=yL=y1;
+                }else{
+                  if(!isnan(y1)){
+                    if(y1<yL || isnan(yL))yL=y1;
+                    if(y1>yH || isnan(yH))yH=y1;
+                  }
                 }
+                x0=x1;
+              }
+              {
+                if(!isnan(yL) || !isnan(yH)){
+                  if(isnan(yL) || yL>0)yL=0;
+                  if(isnan(yH) || yH<0)yH=0;
+                  gdk_draw_line(p->backing,p->twogc,
+                                x0+padx,rintf(yL+cp),x0+padx,
+                                rintf(yH+cp));
+                }
+              }
 
-                rgb = chcolor(i);
-                gdk_gc_set_rgb_fg_color(p->twogc,&rgb);
+              rgb = chcolor(i);
+              gdk_gc_set_rgb_fg_color(p->twogc,&rgb);
 
-                for(k=0;k<spann;k++){
-                  int x = rint(k*spani);
-                  float y = data[k]*ym;
-                  if(!isnan(y)){
-                    gdk_draw_arc(p->backing,p->twogc,
-                                 0,x+padx-5,rint(y)+cp-5,
-                                 9,9,0,23040);
-                  }
+              for(k=0;k<spann;k++){
+                int x = rintf(k*spani);
+                float y = data[k]*ym;
+                if(!isnan(y)){
+                  gdk_draw_arc(p->backing,p->twogc,
+                               0,x+padx-5,rintf(y+cp-5),
+                               9,9,0,23040);
                 }
               }
-              break;
             }
-
-            offset+=spann;
+            break;
           }
-          cp+=y_sep;
         }
       }
-      ch+=p->ch[fi];
+      ch+=f->channels[fi];
     }
-    //gdk_gc_set_clip_rectangle (p->drawgc, &noclip);
   }
-}
 
-static void draw_and_expose(GtkWidget *widget){
-  Plot *p=PLOT(widget);
-  if(!GDK_IS_DRAWABLE(p->backing))return;
-  draw(widget);
-  if(!GTK_WIDGET_DRAWABLE(widget))return;
-  if(!GDK_IS_DRAWABLE(widget->window))return;
   gdk_draw_drawable(widget->window,
-		    widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
-		    p->backing,
-		    0, 0,
-		    0, 0,
-		    widget->allocation.width,
-		    widget->allocation.height);
+                    widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+                    p->backing,
+                    0, 0,
+                    0, 0,
+                    width, height);
 }
 
 static gboolean expose( GtkWidget *widget, GdkEventExpose *event ){
@@ -473,7 +463,7 @@
   Plot *p=PLOT(widget);
   requisition->width = 400;
   requisition->height = 200;
-  int axisy=0,axisx=0,pady=0,padx=0,phax=0,px,py,i;
+  int axisy=0,axisx=0,pady=0,padx=0,px,py,i;
 
   /* find max X layout */
   {
@@ -506,12 +496,10 @@
   if(requisition->height<axisy+pady)requisition->height=axisy+pady;
   p->padx=padx;
   p->pady=pady;
-  p->phax=phax;
 }
 
 static gboolean configure(GtkWidget *widget, GdkEventConfigure *event){
   Plot *p=PLOT(widget);
-
   if (p->backing)
     g_object_unref(p->backing);
 
@@ -520,13 +508,8 @@
 			      widget->allocation.height,
 			      -1);
 
-  p->ydata=NULL;
   p->configured=1;
-
-  compute_metadata(widget);
-  plot_refresh(p);
-  draw_and_expose(widget);
-
+  replot();
   return TRUE;
 }
 
@@ -557,28 +540,19 @@
       (GInstanceInitFunc) plot_init,
       0
     };
-    
-    m_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, "Plot", &m_info, 0);
+
+    m_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, "Plot",
+                                     &m_info, 0);
   }
 
   return m_type;
 }
 
-GtkWidget* plot_new (int size, int groups, int *channels, int *rate){
+GtkWidget* plot_new (void){
   GtkWidget *ret= GTK_WIDGET (g_object_new (plot_get_type (), NULL));
   Plot *p=PLOT(ret);
-  int g,i,j;
-  int ch=0;
-  p->groups = groups;
-  for(g=0;g<groups;g++)
-    ch+=channels[g];
+  int i,j;
 
-  p->total_ch = ch;
-
-  p->ch=channels;
-  p->rate=rate;
-  p->size=size;
-
   /* generate all the text layouts we'll need */
   /* linear X scale */
   {
@@ -648,104 +622,9 @@
     }
   }
 
-  p->ch_active=calloc(ch,sizeof(*p->ch_active));
-
-  p->autoscale=1;
-
-  plot_clear(p);
   return ret;
 }
 
-void plot_refresh (Plot *p){
-  float ymax,pmax,pmin;
-  int width=GTK_WIDGET(p)->allocation.width-p->padx;
-  int height=GTK_WIDGET(p)->allocation.height-p->pady;
-  float **data;
-  float *floor;
-
-  if(!p->configured)return;
-
-  data = process_fetch(p->blockslice, p->overslice, p->span,p->scale,p->range);
-  p->ydata=data;
-}
-
-void plot_clear (Plot *p){
-  GtkWidget *widget=GTK_WIDGET(p);
-  int width=GTK_WIDGET(p)->allocation.width-p->padx;
-  int i,j;
-
-  if(p->ydata)
-    for(i=0;i<p->total_ch;i++)
-      for(j=0;j<p->size;j++)
-	p->ydata[i][j]=NAN;
-  draw_and_expose(widget);
-}
-
-float **plot_get (Plot *p){
-  return(p->ydata);
-}
-
-void plot_setting (Plot *p, float range, int scale, int interval, int span, int rangechoice, int scalechoice, int spanchoice,int type,
-                   int *blockslice, int *overslice){
-  GtkWidget *widget=GTK_WIDGET(p);
-  p->range=range;
-  p->scale=scale;
-  p->span=span;
-  p->interval=interval;
-  p->rchoice=rangechoice;
-  p->schoice=scalechoice;
-  p->spanchoice=spanchoice;
-  p->type=type;
-
-  if(blockslice){
-    if(!p->blockslice)
-      p->blockslice=calloc(p->groups,sizeof(*p->blockslice));
-    memcpy(p->blockslice, blockslice, p->groups*sizeof(*p->blockslice));
-  }
-  if(overslice){
-    if(!p->overslice)
-      p->overslice=calloc(p->groups,sizeof(*p->overslice));
-    memcpy(p->overslice, overslice, p->groups*sizeof(*p->blockslice));
-  }
-
-  compute_metadata(widget);
-  plot_refresh(p);
-  draw_and_expose(widget);
-}
-
-void plot_draw (Plot *p){
-  GtkWidget *widget=GTK_WIDGET(p);
-  draw_and_expose(widget);
-}
-
-void plot_set_active(Plot *p, int *a){
-  GtkWidget *widget=GTK_WIDGET(p);
-  memcpy(p->ch_active,a,p->total_ch*sizeof(*a));
-  plot_refresh(p);
-  draw_and_expose(widget);
-}
-
-void plot_set_autoscale(Plot *p, int a){
-  GtkWidget *widget=GTK_WIDGET(p);
-  p->autoscale=a;
-  plot_refresh(p);
-  draw_and_expose(widget);
-}
-
-void plot_set_bold(Plot *p, int b){
-  GtkWidget *widget=GTK_WIDGET(p);
-  p->bold=b;
-  gdk_gc_set_line_attributes(p->twogc,p->bold+1,GDK_LINE_SOLID,
-                             GDK_CAP_PROJECTING,GDK_JOIN_MITER);
-  draw_and_expose(widget);
-}
-
-void plot_set_sep(Plot *p, int b){
-  GtkWidget *widget=GTK_WIDGET(p);
-  p->trace_sep=b;
-  draw_and_expose(widget);
-}
-
 int plot_get_left_pad (Plot *m){
   return m->padx;
 }

Modified: trunk/spectrum/wave_plot.h
===================================================================
--- trunk/spectrum/wave_plot.h	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/wave_plot.h	2012-06-04 03:26:48 UTC (rev 18353)
@@ -1,24 +1,24 @@
 /*
  *
  *  gtk2 waveform viewer
- *    
+ *
  *      Copyright (C) 2004-2012 Monty
  *
  *  This analyzer 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.
- *   
+ *
  *  The analyzer 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.
  *
- * 
+ *
  */
 
 #ifndef __PLOT_H__
@@ -27,7 +27,37 @@
 #include <glib.h>
 #include <glib-object.h>
 #include <gtk/gtk.h>
+#include "io.h"
 
+typedef struct {
+  int bits[MAX_FILES];
+  int channels[MAX_FILES];
+  int rate[MAX_FILES];
+  int groups;
+  int total_ch;
+  int maxrate;
+  int reload;
+
+  int span;
+  int scale;
+  int range;
+
+  float **data;
+  int *active;
+  int increment;
+} fetchdata;
+
+typedef struct {
+  int bold;
+  int trace_sep;
+  int span;
+  int plotchoice;
+  int spanchoice;
+  int rangechoice;
+  int scalechoice;
+} plotparams;
+
+
 G_BEGIN_DECLS
 
 #define PLOT_TYPE            (plot_get_type ())
@@ -40,8 +70,7 @@
 typedef struct _PlotClass  PlotClass;
 
 struct _Plot{
-
-  GtkDrawingArea canvas;  
+  GtkDrawingArea canvas;
   GdkPixmap *backing;
   GdkGC     *drawgc;
   GdkGC     *twogc;
@@ -50,29 +79,10 @@
   PangoLayout ****y_layout;
 
   int configured;
-  float **ydata;
-  int size;
 
-  int groups;
-  int *ch;
-  int *ch_active;
-  int total_ch;
-
-  float range;
   int scale;
-  int interval;
-  int span;
-  int type;
+  int width;
 
-  int rchoice;
-  int schoice;
-  int spanchoice;
-
-  int *blockslice;
-  int *overslice;
-
-  int *rate;
-
   int xgrid[20];
   int xgrids;
   int xtic[200];
@@ -84,12 +94,7 @@
   int ytics;
 
   float padx;
-  float phax;
   float pady;
-
-  int bold;
-  int autoscale;
-  int trace_sep;
 };
 
 struct _PlotClass{
@@ -99,23 +104,12 @@
 };
 
 GType          plot_get_type        (void);
-GtkWidget*     plot_new             (int n, int inputs, int *channels, int *rate);
-void	       plot_refresh         (Plot *m);
-void	       plot_setting         (Plot *p, float range, int scale, int interval, int span,
-                                     int rangechoice, int scalechoice, int spanchoice,
-                                     int type, int *blockslice, int *overslice);
-void	       plot_draw            (Plot *m);
-void	       plot_clear           (Plot *m);
-int 	       plot_width           (Plot *m);
-float**        plot_get             (Plot *m);
-void           plot_set_active      (Plot *m, int *);
-void           plot_set_autoscale   (Plot *m, int);
-void           plot_set_bold        (Plot *m, int);
-void           plot_set_sep         (Plot *m, int);
+GtkWidget*     plot_new             (void);
+void	       plot_draw            (Plot *m, fetchdata *f, plotparams *pp);
 int            plot_get_left_pad    (Plot *m);
 
 GdkColor chcolor(int ch);
-
+extern void replot(void);
 G_END_DECLS
 
 #endif

Modified: trunk/spectrum/wave_process.c
===================================================================
--- trunk/spectrum/wave_process.c	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/wave_process.c	2012-06-04 03:26:48 UTC (rev 18353)
@@ -1,57 +1,45 @@
 /*
  *
  *  gtk2 waveform viewer
- *    
+ *
  *      Copyright (C) 2004-2012 Monty
  *
  *  This analyzer 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.
- *   
+ *
  *  The analyzer 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 "waveform.h"
 #include "io.h"
 
-pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
-int feedback_increment=0;
-float **buffercopies;
-float **plot_data=0;
+static pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static int feedback_increment=0;
 
 sig_atomic_t process_active=0;
 sig_atomic_t process_exit=0;
 
-sig_atomic_t acc_clear=0;
 sig_atomic_t acc_rewind=0;
 sig_atomic_t acc_loop=0;
 
+static int metareload = 0;
+
 static void init_process(void){
-  int i;
-  if(plot_data==NULL){
-    plot_data=calloc(total_ch,sizeof(*plot_data));
-    buffercopies=calloc(total_ch,sizeof(*buffercopies));
-    for(i=0;i<total_ch;i++){
-      plot_data[i]=calloc(blocksize,sizeof(**plot_data));
-      buffercopies[i]=calloc(blocksize,sizeof(**buffercopies));
-    }
-  }
 }
 
 /* return 0 on EOF, 1 otherwise */
 static int process(){
-  int fi,ch,i;
-
   if(acc_rewind)
     rewind_files();
   acc_rewind=0;
@@ -59,50 +47,110 @@
   if(input_read(acc_loop,1))
     return 0;
 
-  pthread_mutex_lock(&feedback_mutex);
-  ch=0;
-  for(fi=0;fi<inputs;fi++){
-    for(i=ch;i<ch+channels[fi];i++)
-      memcpy(buffercopies[i],blockbuffer[i],blocksize*sizeof(**buffercopies));
-    ch+=channels[fi];
-  }
-  pthread_mutex_unlock(&feedback_mutex);
-
   feedback_increment++;
   write(eventpipe[1],"",1);
   return 1;
 }
 
 void *process_thread(void *dummy){
+  pthread_mutex_lock(&feedback_mutex);
   init_process();
-  while(!process_exit && process());
+  pthread_mutex_unlock(&feedback_mutex);
+
+  while(1){
+    while(!process_exit && process());
+    pthread_mutex_lock(&feedback_mutex);
+    if(!process_exit && pipe_reload()){
+      /* ah, at least one input was a pipe */
+      init_process();
+      metareload=1;
+      pthread_mutex_unlock(&feedback_mutex);
+      write(eventpipe[1],"",1);
+    }else{
+      pthread_mutex_unlock(&feedback_mutex);
+      break;
+    }
+  }
   process_active=0;
   write(eventpipe[1],"",1);
   return NULL;
 }
 
-float **process_fetch(int *blockslice,int *overslice,int span,
-                      int scale,float range){
-  int fi,i,j,k,ch;
+static fetchdata fetch_ret;
+fetchdata *process_fetch(int span, int scale, float range,
+                         int *process_in){
+  int fi,i,k,ch;
+  int process[total_ch];
 
+  pthread_mutex_lock(&feedback_mutex);
+  if(!blockbuffer){
+    pthread_mutex_unlock(&feedback_mutex);
+    return NULL;
+  }
+
   init_process();
-  if(!blockslice || !overslice || !blockbuffer)return NULL;
 
+  if(metareload){
+    if(fetch_ret.data){
+      for(i=0;i<fetch_ret.total_ch;i++)
+        if(fetch_ret.data[i])free(fetch_ret.data[i]);
+      free(fetch_ret.data);
+      fetch_ret.data=NULL;
+    }
+    if(fetch_ret.active){
+      free(fetch_ret.active);
+      fetch_ret.active=NULL;
+    }
+  }
+
+  if(!fetch_ret.data){
+    fetch_ret.data = calloc(total_ch,sizeof(*fetch_ret.data));
+    for(i=0;i<total_ch;i++)
+      fetch_ret.data[i]=calloc(blocksize,sizeof(**fetch_ret.data));
+  }
+
+  if(!fetch_ret.active)
+    fetch_ret.active = calloc(total_ch,sizeof(*fetch_ret.active));
+
+  /* the passed in process array doesn't necesarily match the
+     current channel structure.  Copy group by group. */
+  {
+    int ch_now=0;
+    int ch_in=0;
+    for(i=0;i<inputs;i++){
+      int ci;
+      for(ci=0;ci<channels[i] && ci<fetch_ret.channels[i];ci++)
+        process[ch_now+ci] = process_in[ch_in+ci];
+      for(;ci<channels[i];ci++)
+        process[ch_now+ci] = 0;
+      ch_now+=channels[i];
+      ch_in+=fetch_ret.channels[i];
+    }
+    memcpy(fetch_ret.active,process,total_ch*sizeof(*process));
+  }
+
+  fetch_ret.groups=inputs;
+  fetch_ret.scale=scale;
+  fetch_ret.span=span;
+  fetch_ret.range=range;
+  fetch_ret.total_ch=total_ch;
+  fetch_ret.increment=feedback_increment;
+
+  memcpy(fetch_ret.bits,bits,sizeof(fetch_ret.bits));
+  memcpy(fetch_ret.channels,channels,sizeof(fetch_ret.channels));
+  memcpy(fetch_ret.rate,rate,sizeof(fetch_ret.rate));
+
+  fetch_ret.reload=metareload;
+  metareload=0;
+
   /* by channel */
   ch=0;
   for(fi=0;fi<inputs;fi++){
-    /* When the blockslice and overslice are equal, the data
-       buffer contains only one copy of the span.  When the
-       overslice is much smaller than the blockslice, we have
-       ceil(blockslice/overslice) back-to-back spans */
-
-    int copies = (int)ceil(blockslice[fi]/overslice[fi]);
     int spann = ceil(rate[fi]/1000000.*span)+1;
-
     for(i=ch;i<ch+channels[fi];i++){
-      int offset=blocksize-spann;
-      float *plotdatap=plot_data[i];
-      for(j=0;j<copies;j++){
+      if(process[i]){
+        int offset=blocksize-spann;
+        float *plotdatap=fetch_ret.data[i];
         float *data=blockbuffer[i]+offset;
         if(scale){
           float drange=todB(range)-scale;
@@ -120,11 +168,11 @@
           for(k=0;k<spann;k++)
             *(plotdatap++)=(data[k]/range);
         }
-        offset-=overslice[fi];
       }
     }
     ch+=channels[fi];
   }
 
-  return plot_data;
+  pthread_mutex_unlock(&feedback_mutex);
+  return &fetch_ret;
 }

Modified: trunk/spectrum/waveform.c
===================================================================
--- trunk/spectrum/waveform.c	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/waveform.c	2012-06-04 03:26:48 UTC (rev 18353)
@@ -1,24 +1,24 @@
 /*
  *
  *  gtk2 waveform viewer
- *    
+ *
  *      Copyright (C) 2004-2012 Monty
  *
  *  This analyzer 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.
- *   
+ *
  *  The analyzer 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 "waveform.h"
@@ -202,6 +202,15 @@
   if(sig==SIGILL)sigill=1;
 }
 
+void blocksize_callback(void){
+  int fi;
+  /* set block size equal to maximum input rate + epsilon*/
+  /* (maximum display width: 1s, maximum update interval 1s) */
+  blocksize=0;
+  for(fi=0;fi<inputs;fi++)
+    if(rate[fi]>blocksize)blocksize=rate[fi]+16;
+}
+
 int main(int argc, char **argv){
   int fi;
 
@@ -237,7 +246,7 @@
   feenableexcept(FE_INEXACT);
   feenableexcept(FE_UNDERFLOW);
   feenableexcept(FE_OVERFLOW);
-#endif 
+#endif
 
   /* Linux Altivec support has a very annoying problem; by default,
      math on denormalized floats will simply crash the program.  FFTW3
@@ -250,12 +259,12 @@
 #ifdef __PPC
 #include <altivec.h>
   signal(SIGILL,sigill_handler);
-  
+
 #if (defined __GNUC__) && (__GNUC__ == 3) && ! (defined __APPLE_CC__)
-  __vector unsigned short noTrap = 
+  __vector unsigned short noTrap =
     (__vector unsigned short){0,0,0,0,0,0,0x1,0};
 #else
-  vector unsigned short noTrap = 
+  vector unsigned short noTrap =
     (vector unsigned short)(0,0,0,0,0,0,0x1,0);
 #endif
 
@@ -267,7 +276,7 @@
   if(pipe(eventpipe)){
     fprintf(stderr,"Unable to open event pipe:\n"
             "  %s\n",strerror(errno));
-    
+
     exit(1);
   }
 
@@ -275,21 +284,15 @@
   if(fcntl(eventpipe[0], F_SETFL, O_NONBLOCK)){
     fprintf(stderr,"Unable to set O_NONBLOCK on event pipe:\n"
             "  %s\n",strerror(errno));
-    
+
     exit(1);
   }
 
   //signal(SIGINT,handler);
   signal(SIGSEGV,handler);
 
-  if(input_load())exit(1);
+  if(input_load(blocksize_callback))exit(1);
 
-  /* set block size equal to maximum input rate */
-  /* (maximum display width: 1s, maximum update interval 1s) */
-  blocksize=0;
-  for(fi=0;fi<inputs;fi++)
-    if(rate[fi]>blocksize)blocksize=rate[fi];
-
   /* begin with a display width of 1s */
   /* begin with an update interval (blockslice) of 100ms */
   for(fi=0;fi<inputs;fi++)

Modified: trunk/spectrum/waveform.h
===================================================================
--- trunk/spectrum/waveform.h	2012-06-03 04:13:23 UTC (rev 18352)
+++ trunk/spectrum/waveform.h	2012-06-04 03:26:48 UTC (rev 18353)
@@ -1,24 +1,24 @@
 /*
  *
  *  gtk2 spectrum analyzer
- *    
+ *
  *      Copyright (C) 2004-2012 Monty
  *
  *  This analyzer 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.
- *   
+ *
  *  The analyzer 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.
  *
- * 
+ *
  */
 
 #ifndef _WAVEFORM_H_
@@ -39,6 +39,7 @@
 #include <math.h>
 #include <signal.h>
 #include <fcntl.h>
+#include "wave_plot.h"
 
 extern int blocksize;
 
@@ -78,14 +79,11 @@
 
 extern void panel_go(int argc,char *argv[]);
 extern void *process_thread(void *dummy);
-extern float **process_fetch(int *, int *, int, int, float);
+extern fetchdata *process_fetch(int span, int scale, float range,
+                                int *process);
 
-extern pthread_mutex_t feedback_mutex;
-extern int feedback_increment;
-extern float **feedback_instant;
 extern sig_atomic_t acc_rewind;
 extern sig_atomic_t acc_loop;
-
 extern sig_atomic_t process_active;
 extern sig_atomic_t process_exit;
 



More information about the commits mailing list