[xiph-commits] r18229 - trunk/spectrum

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Thu Apr 12 07:29:17 PDT 2012


Author: xiphmont
Date: 2012-04-12 07:29:17 -0700 (Thu, 12 Apr 2012)
New Revision: 18229

Added:
   trunk/spectrum/spec_panel.c
   trunk/spectrum/spec_plot.c
   trunk/spectrum/spec_plot.h
   trunk/spectrum/spec_process.c
   trunk/spectrum/spectrum.c
   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
Removed:
   trunk/spectrum/main.c
   trunk/spectrum/panel.c
   trunk/spectrum/plot.h
   trunk/spectrum/process.c
Modified:
   trunk/spectrum/Makefile
   trunk/spectrum/analyzer.h
   trunk/spectrum/io.c
   trunk/spectrum/io.h
   trunk/spectrum/version.h
Log:
Get the beginnings of the simple waveform viewer in



Modified: trunk/spectrum/Makefile
===================================================================
--- trunk/spectrum/Makefile	2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/Makefile	2012-04-12 14:29:17 UTC (rev 18229)
@@ -25,18 +25,23 @@
 ETCDIR=/etc/spectrum
 MANDIR=$(PREFIX)/man
 
-SRC = main.c process.c panel.c plot.c io.c
-OBJ = main.o process.o panel.o plot.o io.o
+SRC = spectrum.c waveform.c spec_process.c wave_process.c spec_panel.c wave_panel.c spec_plot.c wave_plot.c io.c
+SPECTRUM_OBJ = spectrum.o spec_process.o spec_panel.o spec_plot.o io.o
+WAVEFORM_OBJ = waveform.o wave_process.o wave_panel.o wave_plot.o io.o
+OBJ = $(SPECTRUM_OBJ) $(WAVEFORM_OBJ)
+
 GCF = -DETCDIR=\\\"$(ETCDIR)\\\" `pkg-config --cflags gtk+-2.0`
 
+CFLAGS := ${CFLAGS} $(GCF) $(ADD_DEF)
+
 all:	
-	$(MAKE) target CFLAGS="-O3 -ffast-math -fomit-frame-pointer $(GCF) $(ADD_DEF)"
+	$(MAKE) target CFLAGS="${CFLAGS} -O3 -ffast-math -fomit-frame-pointer"
 
 debug:
-	$(MAKE) target CFLAGS="-g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES $(GCF) $(ADD_DEF)"
+	$(MAKE) target CFLAGS="${CFLAGS} -g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES"
 
 profile:
-	$(MAKE) target CFLAGS="-pg -g -O3 -ffast-math $(GCF) $(ADD_DEF)" LIBS="-lgprof-helper "
+	$(MAKE) target CFLAGS="${CFLAGS} -pg -g -O3 -ffast-math" LIBS="-lgprof-helper "
 
 clean:
 	rm -f $(OBJ) *.d *.d.* gmon.out spectrum
@@ -55,15 +60,23 @@
 include $(SRC:.c=.d)
 endif
 
-target:  $(OBJ) spectrum-wisdomrc
+spectrum:  $(SPECTRUM_OBJ) spectrum-wisdomrc
 	./touch-version
-	$(LD) $(OBJ) $(CFLAGS) -o spectrum $(LIBS) `pkg-config --libs gtk+-2.0` -lpthread -lfftw3f -lm 
+	$(LD) $(SPECTRUM_OBJ) -o spectrum $(LIBS) $(CFLAGS) `pkg-config --libs gtk+-2.0` -lpthread -lfftw3f -lm 
 
+waveform:  $(WAVEFORM_OBJ) 
+	./touch-version
+	$(LD) $(WAVEFORM_OBJ) -o waveform $(LIBS) $(CFLAGS) `pkg-config --libs gtk+-2.0` -lpthread -lm 
+
+target:  spectrum waveform
+
 install: target
 	$(INSTALL) -d -m 0755 $(BINDIR)
 	$(INSTALL) -m 0755 spectrum $(BINDIR)
+	$(INSTALL) -m 0755 waveform $(BINDIR)
 	$(INSTALL) -d -m 0755 $(ETCDIR)
 	$(INSTALL) -m 0644 spectrum-gtkrc $(ETCDIR)
+	$(INSTALL) -m 0644 waveform-gtkrc $(ETCDIR)
 	$(INSTALL) -m 0644 spectrum-wisdomrc $(ETCDIR)
 #	$(INSTALL) -d -m 0755 $(MANDIR)
 #	$(INSTALL) -d -m 0755 $(MANDIR)/man1

Modified: trunk/spectrum/analyzer.h
===================================================================
--- trunk/spectrum/analyzer.h	2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/analyzer.h	2012-04-12 14:29:17 UTC (rev 18229)
@@ -91,17 +91,6 @@
 			     float *ymax, float *pmax, float *pmin,
 			     float **floor, int noise);
 
-extern int inputs;
-extern int total_ch;
-extern int bits[MAX_FILES];
-extern int bigendian[MAX_FILES];
-extern int channels[MAX_FILES];
-extern int rate[MAX_FILES];
-extern int signedp[MAX_FILES];
-extern char *inputname[MAX_FILES];
-extern int seekable[MAX_FILES];
-extern int global_seekable;
-
 extern pthread_mutex_t feedback_mutex;
 extern int feedback_increment;
 extern float **feedback_acc;

Modified: trunk/spectrum/io.c
===================================================================
--- trunk/spectrum/io.c	2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/io.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -23,9 +23,15 @@
 
 #include "io.h"
 
+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};
+int rate[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+int signedp[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+
 extern sig_atomic_t acc_loop;
 extern int blocksize;
-static int blockslice[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+int blockslice[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
 
 float **blockbuffer=0;
 int blockbufferfill[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
@@ -371,28 +377,17 @@
 	
       }
 
-      /* select the full-block slice size: ~10fps */
-      blockslice[fi]=rate[fi]/10;
-      while(blockslice[fi]>blocksize/2)blockslice[fi]/=2;
+      if(length[fi]!=-1)
+	bytesleft[fi]=length[fi]*channels[fi]*((bits[fi]+7)/8);
       total_ch += channels[fi];
 
-      if(length[fi]!=-1)
-	bytesleft[fi]=length[fi]*channels[fi]*((bits[fi]+7)/8);
-      
     }else{
       fprintf(stderr,"Unable to open %s: %s\n",inputname[fi],strerror(errno));
       exit(1);
     }
   }
 
-  blockbuffer=malloc(total_ch*sizeof(*blockbuffer));
-  
-  for(i=0;i<total_ch;i++){
-    blockbuffer[i]=calloc(blocksize,sizeof(**blockbuffer));
-  }
-  
   return 0;
-
 }
 
 /* Convert new data from readbuffer into the blockbuffers until the
@@ -506,23 +501,33 @@
      loop unset: return EOF if all streams have hit EOF
    pad individual EOF streams out with zeroes until global EOF is hit  */
 
-int input_read(void){
-  int i,fi,ch=0;
+int input_read(int loop, int partialok){
+  int i,j,fi,ch=0;
   int eof=1;
   int notdone=1;
+  int rewound[total_ch];
+  memset(rewound,0,sizeof(rewound));
 
+  if(blockbuffer==0){
+    blockbuffer=malloc(total_ch*sizeof(*blockbuffer));
+    
+    for(i=0;i<total_ch;i++){
+      blockbuffer[i]=calloc(blocksize,sizeof(**blockbuffer));
+    }
+  }
+
   for(fi=0;fi<inputs;fi++){
-    
     /* shift according to slice */
-    if(blockbufferfill[fi]==blocksize){
-      if(blockslice[fi]<blocksize){
-	for(i=0;i<channels[fi];i++)
-	  memmove(blockbuffer[i+ch],blockbuffer[i+ch]+blockslice[fi],
-		  (blocksize-blockslice[fi])*sizeof(**blockbuffer));
-	blockbufferfill[fi]-=blockslice[fi];
-      }else
-	blockbufferfill[fi]=0;
-    }
+    if(blockslice[fi]<blocksize)
+      for(i=ch;i<channels[fi]+ch;i++)
+        memmove(blockbuffer[i],blockbuffer[i]+blockslice[fi],
+                (blocksize-blockslice[fi])*sizeof(**blockbuffer));
+    blockbufferfill[fi]-=blockslice[fi];
+    if(blockbufferfill[fi]<0)
+      blockbufferfill[fi]=0;
+    for(i=ch;i<channels[fi]+ch;i++)
+      for(j=blockbufferfill[fi];j<blocksize;j++)
+        blockbuffer[i][j]=NAN;
     ch+=channels[fi];
   }
 
@@ -531,11 +536,11 @@
 
     /* if there's data left to be pulled out of a readbuffer, do that */
     LBEconvert();
-    
+
     ch=0;
     for(fi=0;fi<inputs;fi++){
       if(blockbufferfill[fi]!=blocksize){
-	
+
 	/* shift the read buffer before fill if there's a fractional
 	   frame in it */
 	if(readbufferptr[fi]!=readbufferfill[fi] && readbufferptr[fi]>0){
@@ -547,39 +552,44 @@
 	  readbufferfill[fi]=0;
 	  readbufferptr[fi]=0;
 	}
-	
+
 	/* attempt to top off the readbuffer */
 	{
 	  long actually_readbytes=0,readbytes=readbuffersize-readbufferfill[fi];
 
 	  if(readbytes>0)
 	    actually_readbytes=fread(readbuffer[fi]+readbufferfill[fi],1,readbytes,f[fi]);
-	    
-	  if(actually_readbytes<0){
-	    fprintf(stderr,"Input read error from %s: %s\n",inputname[fi],strerror(errno));
+
+	  if(actually_readbytes<=0 && ferror(f[fi])){
+	    fprintf(stderr,"Input read error from %s: %s\n",
+                    inputname[fi],strerror(ferror(f[fi])));
 	  }else if (actually_readbytes==0){
-	    /* don't process any partially-filled blocks; the
-	       stairstep at the end could pollute results badly */
-	    
-	    memset(readbuffer[fi],0,readbuffersize);
-	    bytesleft[fi]=0;
-	    readbufferfill[fi]=0;
-	    readbufferptr[fi]=0;
-	    blockbufferfill[fi]=0;
-	  
+            /* real, hard EOF/error in a partially filled block */
+            if(!partialok){
+              /* partial frame is *not* ok.  zero it out. */
+              memset(readbuffer[fi],0,readbuffersize);
+              bytesleft[fi]=0;
+              readbufferfill[fi]=0;
+              readbufferptr[fi]=0;
+              blockbufferfill[fi]=0;
+            }
+            if(loop && (!rewound[fi] || (partialok && blockbufferfill[fi]))){
+              /* rewind this file and continue */
+              fseek(f[fi],offset[fi],SEEK_SET);
+              if(length[fi]!=-1)
+                bytesleft[fi]=length[fi]*channels[fi]*((bits[fi]+7)/8);
+              notdone=1;
+              rewound[fi]=1;
+            }
 	  }else{
+            /* got a read */
 	    bytesleft[fi]-=actually_readbytes;
 	    readbufferfill[fi]+=actually_readbytes;
-	    
-	    /* conditionally clear global EOF */
-	    if(acc_loop){
-	      if(seekable[fi])eof=0;
-	    }else{
-	      eof=0;
-	    }
-	    notdone=1;
+            notdone=1;
 	  }
 	}
+      }else{
+        eof=0;
       }
       ch += channels[fi];
     }

Modified: trunk/spectrum/io.h
===================================================================
--- trunk/spectrum/io.h	2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/io.h	2012-04-12 14:29:17 UTC (rev 18229)
@@ -47,7 +47,7 @@
 #define readbuffersize 8192
 
 extern int input_load(void);
-extern int input_read(void);
+extern int input_read(int loop_p, int partial_p);
 extern int rewind_files(void);
 
 extern int inputs;
@@ -61,6 +61,7 @@
 extern int seekable[MAX_FILES];
 extern int global_seekable;
 
+extern int blockslice[MAX_FILES];
 extern int blockbufferfill[MAX_FILES];
 extern float **blockbuffer;
 #endif

Deleted: trunk/spectrum/main.c
===================================================================
--- trunk/spectrum/main.c	2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/main.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,292 +0,0 @@
-/*
- *
- *  gtk2 spectrum analyzer
- *    
- *      Copyright (C) 2004 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"
-#include <signal.h>
-#include <getopt.h>
-#include <fenv.h>  // Thank you C99!
-#include <fftw3.h>
-#include <gtk/gtk.h>
-#include "version.h"
-
-int eventpipe[2];
-char *version;
-char *inputname[MAX_FILES];
-int inputs=0;
-int blocksize = 131072;
-
-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};
-int rate[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
-int signedp[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
-
-void handler(int sig){
-  signal(sig,SIG_IGN);
-  if(sig!=SIGINT){
-    fprintf(stderr,
-	    "\nTrapped signal %d; exiting!\n"
-	    "This signal almost certainly indicates a bug in the analyzer;\n"
-	    "If this version of the analyzer 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 I can fix the bug as\n"
-	    "quickly as possible.\n\n"
-	    "-- monty at xiph.org, spectrum revision %s\n\n",sig,version);
-  }
-  
-  gtk_main_quit();
-}
-
-const char *optstring = "-r:c:EeBlb:suhF:";
-
-struct option options [] = {
-        {"rate",required_argument,NULL,'r'},
-        {"channels",required_argument,NULL,'c'},
-        {"big-endian",no_argument,NULL,'E'},
-        {"little-endian",no_argument,NULL,'e'},
-        {"bits",required_argument,NULL,'b'},
-        {"signed",no_argument,NULL,'s'},
-        {"unsigned",no_argument,NULL,'u'},
-        {"help",no_argument,NULL,'h'},
-        {"fft-size",required_argument,NULL,'h'},
-
-        {NULL,0,NULL,0}
-};
-
-static void usage(FILE *f){
-  fprintf( f,
-"\ngtk2 spectrum analyzer, revision %s\n\n"
-
-"USAGE:\n\n"
-"  spectrum [options] [file]\n\n"
-
-"OPTIONS:\n\n"
-"  -b --bits <bits>           : Force input to be read as 8, 16, 24 or 32 bit\n"
-"                               PCM. Default bit depth is normally read from\n"
-"                               the file/stream header or set to 16 bits\n"
-"                               for raw input.\n"
-"  -B -E --big-endian         : Force input to be read as big endian.\n"
-"                               Default endianness is normally read from the\n"
-"                               file/stream header or set to host"
-"                               endianness for raw input.\n"
-"  -c --channels <channels>   : Input channel number override; use to\n"
-"                               specify the number of channels in a raw\n"
-"                               input.  default: 1\n"
-"  -e -l --little-endian      : Force input to be read as little endian.\n"
-"                               Default endianness is normally read from the\n"
-"                               file/stream header or set to host"
-"                               endianness for raw input.\n"
-"  -F --fft-size              : Set the size of the fft transform used. Valid\n"
-"                               range 8192 to 262144, 131072 default.\n"
-"  -h --help                  : print this help\n"
-"  -r --rate <Hz>             : Input sample rate override in Hz; use to\n"
-"                               specify the rate of a raw input.\n"
-"                               default: 44100\n"
-"  -s --signed                : Force input to be read as signed PCM.\n"
-"                               Signedness is normally read from the \n"
-"                               file/stream header or set to signed for raw\n"
-"                               input.\n"
-"  -u --unsigned              : Force input to be read as unsigned PCM.\n"
-"                               Signedness is normally read from the \n"
-"                               file/stream header or set to signed for raw\n"
-"                               input.\n\n"
-
-"INPUT:\n\n"
-
-" Spectrum takes raw, WAV or AIFF input either from stdin or from \n"
-" file[s]/stream[s] specified on the command line.\n\n",version);
-
-}
-
-void parse_command_line(int argc, char **argv){
-  int c,long_option_index;
-
-  while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
-    switch(c){
-    case 1:
-      /* file name that belongs to current group */
-      if(inputs>=MAX_FILES){
-	fprintf(stderr,"Maximum of MAX_FILES input files exceeded.  Oops.  Programmer was lazy.\n\n");
-	exit(1);
-      }
-      inputname[inputs++]=strdup(optarg);
-      break;
-    case 'b':
-      /* force bit width */
-      {
-	int a=atoi(optarg);
-	bits[inputs]=a;
-	if(a!=8 && a!=16 && a!=24 && a!=32){
-	  usage(stderr);
-	  exit(1);
-	}
-      }
-      break;
-    case 'F':
-      blocksize = atoi(optarg);
-      if(blocksize<8192 || blocksize>262144){
-	usage(stderr);
-	exit(1);
-      }
-      break;
-    case 'B':case 'E':
-      /* force big endian */
-      bigendian[inputs]=1;
-      break;
-    case 'c':
-      /* force channels */
-      {
-	int a=atoi(optarg);
-	channels[inputs]=a;
-	if(a<1 || a>32){
-	  usage(stderr);
-	  exit(1);
-	}
-      }
-      break;
-    case 'l':case 'e':
-      /* force little endian */
-      bigendian[inputs]=0;
-      break;
-    case 'h':
-      usage(stdout);
-      exit(0);
-    case 'r':
-      /* force rate */
-      {
-	int a=atoi(optarg);
-	rate[inputs]=a;
-	if(a<4000 || a>200000){
-	  usage(stderr);
-	  exit(1);
-	}
-      }
-      break;
-    case 's':
-      /* force signed */
-      signedp[inputs]=1;
-      break;
-    case 'u':
-      /* force unsigned */
-      signedp[inputs]=0;
-      break;
-    default:
-      usage(stderr);
-      exit(0);
-    }
-  }
-}
-
-static int sigill=0;
-void sigill_handler(int sig){
-  /* make sure */
-  if(sig==SIGILL)sigill=1;
-}
-
-int main(int argc, char **argv){
-
-  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. */
-
-#ifndef DEBUG
-  fedisableexcept(FE_INVALID);
-  fedisableexcept(FE_INEXACT);
-  fedisableexcept(FE_UNDERFLOW);
-  fedisableexcept(FE_OVERFLOW);
-#else
-  feenableexcept(FE_INVALID);
-  feenableexcept(FE_INEXACT);
-  feenableexcept(FE_UNDERFLOW);
-  feenableexcept(FE_OVERFLOW);
-#endif 
-
-  /* Linux Altivec support has a very annoying problem; by default,
-     math on denormalized floats will simply crash the program.  FFTW3
-     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 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};
-#else
-  vector unsigned short noTrap = 
-    (vector unsigned short)(0,0,0,0,0,0,0x1,0);
-#endif
-
-  vec_mtvscr(noTrap);
-#endif
-
-  /* easiest way to inform gtk of changes and not deal with locking
-     issues around the UI */
-  if(pipe(eventpipe)){
-    fprintf(stderr,"Unable to open event pipe:\n"
-            "  %s\n",strerror(errno));
-    
-    exit(1);
-  }
-
-  /* Allows event compression on the read side */
-  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);
-  panel_go(argc,argv);
-
-  return(0);
-}

Deleted: trunk/spectrum/panel.c
===================================================================
--- trunk/spectrum/panel.c	2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/panel.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,840 +0,0 @@
-/*
- *
- *  gtk2 spectrum analyzer
- *    
- *      Copyright (C) 2004 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"
-#include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
-#include "fisharray.h"
-#include "plot.h"
-
-sig_atomic_t increment_fish=0;
-
-static struct panel {
-  GtkWidget *twirlimage;
-  GdkPixmap *ff[19];
-  GdkBitmap *fb[19];
-
-  GtkAccelGroup *group;
-  GtkWidget *toplevel;
-
-
-  guint fishframe_timer;
-  int fishframe_init;
-  int fishframe;
-
-  GtkWidget *plot;
-  GtkWidget *run;
-  GtkWidget **chbuttons;
-  
-} p;
-
-int plot_res=0;
-int plot_scale=0;
-int plot_mode=0;
-int plot_link=0;
-int plot_hold=0;
-int plot_depth=90;
-int plot_noise=0;
-int plot_last_update=0;
-int *active;
-
-static void replot(struct panel *p){
-  int i,lactive[total_ch];
-  for(i=0;i<total_ch;i++)
-    lactive[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
-
-  /* update the spectral display; send new data */
-  if(!plot_hold){
-    pthread_mutex_lock(&feedback_mutex);
-    plot_refresh(PLOT(p->plot),lactive);
-    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){
-      /* reschedule to blink */
-      p->fishframe_timer=
-	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
-      return FALSE;
-    }
-
-  }else{
-    p->fishframe++;
-    if(p->fishframe<=1)p->fishframe=12;
-    if(p->fishframe>=19)p->fishframe=0;
-
-    gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
-			      p->ff[p->fishframe],
-			      p->fb[p->fishframe]);
-
-
-    if(p->fishframe==12){
-      /* reschedule to animate */
-      p->fishframe_timer=
-	g_timeout_add(10,(GSourceFunc)reanimate_fish,p);
-      return FALSE;
-    }
-    if(p->fishframe==0){
-      /* reschedule to blink */
-      p->fishframe_timer=
-	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
-      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);
-  }else{
-    p->fishframe_init=1;
-    p->fishframe_timer=
-      g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
-  }
-}
-
-static void dump(GtkWidget *widget,struct panel *p){
-  process_dump(plot_mode);
-}
-
-static void noise(GtkWidget *widget,struct panel *p){
-  if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))){
-    if(plot_noise){
-      plot_noise=0;
-      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),0);
-      clear_noise_floor();
-      plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,0);
-    }else{
-      plot_noise=1;
-      plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,0);
-    }
-  }else{
-    if(plot_noise){
-      gtk_button_set_label(GTK_BUTTON(widget),"clear _noise floor");
-      plot_noise=2;
-      plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,1);
-    }else
-      gtk_button_set_label(GTK_BUTTON(widget),"sample _noise floor");
-  }
-}
-
-
-static void depthchange(GtkWidget *widget,struct panel *p){
-  int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
-  switch(choice){
-  case 0: /* 10dB */
-    plot_depth=10;
-    break;
-  case 1: /* 20dB */
-    plot_depth=20;
-    break;
-  case 2: /* 45dB */
-    plot_depth=45;
-    break;
-  case 3: /* 90dB */
-    plot_depth=90;
-    break;
-  case 4: /*140dB */
-    plot_depth=140;
-    break;
-  }
-  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-}
-
-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);
-
-  /* buttons usually have internal labels */
-  if(GTK_IS_CONTAINER(c))
-    gtk_container_forall (GTK_CONTAINER(c),set_fg,in);
-}
-
-static void set_via_active(struct panel *p, int *active, int *bactive){
-  int fi,i;
-  int ch=0;
-  for(fi=0;fi<inputs;fi++){
-    for(i=ch;i<ch+channels[fi];i++)
-      gtk_widget_set_sensitive(p->chbuttons[i],1);
-    ch+=channels[fi];
-  }
-  plot_set_active(PLOT(p->plot),active,bactive);  
-}
-
-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];
-  int bactive[total_ch];
-
-  for(i=0;i<total_ch;i++)
-    bactive[i]=active[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
-
-  /* set sensitivity */
-  switch(plot_link){
-  case LINK_IMPEDENCE_p1:
-  case LINK_IMPEDENCE_1:
-  case LINK_IMPEDENCE_10:
-  case LINK_THD:
-  case LINK_THDN:
-  case LINK_THD2:
-  case LINK_THDN2:
-  case LINK_SUB_REF:
-
-    /*  first channel in each group insensitive/inactive, used as a reference */
-    ch=0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=ch;i<ch+channels[fi];i++){
-	if(i==ch){
-	  gtk_widget_set_sensitive(p->chbuttons[i],0);
-	  active[i]=0; /* do not frob widget, only plot settings */
-	}else{
-	  gtk_widget_set_sensitive(p->chbuttons[i],1);
-	}
-      }
-      ch+=channels[fi];
-    }
- 
-    plot_set_active(PLOT(p->plot),active,bactive);
-    break;    
-
-  case LINK_SUMMED: /* summing mode */
-  case LINK_SUB_FROM: /* subtract channels from reference */
-    ch=0;
-    for(fi=0;fi<inputs;fi++){
-      int any=0;
-      for(i=ch;i<ch+channels[fi];i++){
-	if(active[i])any=1;
-	active[i]=0;
-      }
-      active[ch]=any;
-      ch+=channels[fi];
-    }
-
-    set_via_active(p,active,bactive);
-    break;
-
-  case LINK_INDEPENDENT: /* normal/independent mode */
-    set_via_active(p,active,bactive);
-    break;    
-
-  case LINK_PHASE: /* response/phase */
-    ch=0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=ch;i<ch+channels[fi];i++)
-	if(channels[fi]<2){
-	  gtk_widget_set_sensitive(p->chbuttons[i],0);
-	  active[i]=0;
-	}else{
-	  if(i<ch+2){
-	    gtk_widget_set_sensitive(p->chbuttons[i],1);
-	  }else{
-	    gtk_widget_set_sensitive(p->chbuttons[i],0);
-	    active[i]=0;
-	  }
-	}
-      ch+=channels[fi];
-    }
-    plot_set_active(PLOT(p->plot),active,bactive);
-    break;    
-  }
-
-  /* set labels */
-  switch(plot_link){
-  case LINK_THD:
-  case LINK_THDN:
-  case LINK_THD2:
-  case LINK_THDN2:
-  case LINK_IMPEDENCE_p1:
-  case LINK_IMPEDENCE_1:
-  case LINK_IMPEDENCE_10:
-  case LINK_SUB_REF:
-    ch=0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=ch;i<ch+channels[fi];i++){
-	if(i==ch){
-	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"reference");
-	}else{
-	  sprintf(buf,"channel %d", i-ch);
-	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
-	}
-      }
-      ch+=channels[fi];
-    }
-    break;    
-  case LINK_SUB_FROM:
-    ch=0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=ch;i<ch+channels[fi];i++){
-	if(i==ch){
-	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"output");
-	}else{
-	  sprintf(buf,"channel %d", i-ch);
-	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
-	}
-      }
-      ch+=channels[fi];
-    }
-    break;    
-
-  case LINK_INDEPENDENT:
-  case LINK_SUMMED:
-
-    ch=0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=ch;i<ch+channels[fi];i++){
-	sprintf(buf,"channel %d", i-ch);
-	gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
-      }
-      ch+=channels[fi];
-    }
-    break;    
-
-  case LINK_PHASE:
-
-    ch=0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=ch;i<ch+channels[fi];i++){
-	if(channels[fi]<2){
-	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"unused");
-	}else if(i==ch){
-	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"response");
-	}else if(i==ch+1){
-	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"phase");
-	}else{
-	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"unused");
-	}
-      }
-      ch+=channels[fi];
-    }
-    break;    
-  }
-
-  /* set colors */
-  switch(plot_link){
-  case LINK_SUMMED:
-  case LINK_SUB_FROM:
-    ch=0;
-    for(fi=0;fi<inputs;fi++){
-      GdkColor rgb = chcolor(ch);
-
-      for(i=ch;i<ch+channels[fi];i++){
-	GtkWidget *button=p->chbuttons[i];	
-	set_fg(button,&rgb);
-      }
-      ch+=channels[fi];
-    }
-    break;
-
-  default:
-    ch=0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=ch;i<ch+channels[fi];i++){
-	GdkColor rgb = chcolor(i);
-	GtkWidget *button=p->chbuttons[i];
-	set_fg(button,&rgb);
-      }
-      ch+=channels[fi];
-    }
-    break;
-  }
-
-}
-
-static void reschange(GtkWidget *widget,struct panel *p){
-  plot_res=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
-  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-}
-
-static void scalechange(GtkWidget *widget,struct panel *p){
-  plot_scale=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
-  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-}
-
-static void modechange(GtkWidget *widget,struct panel *p){
-  plot_mode=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
-  replot(p);
-  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-}
-
-static void linkchange(GtkWidget *widget,struct panel *p){
-  plot_link=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
-  replot(p);
-  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-  chlabels(widget,p);
-}
-
-static void runchange(GtkWidget *widget,struct panel *p){
-  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);
-      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){
-  plot_hold=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
-  replot(p);
-  plot_draw(PLOT(p->plot));
-}
-
-static void loopchange(GtkWidget *widget,struct panel *p){
-  acc_loop=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
-}
-
-static void clearchange(GtkWidget *widget,struct panel *p){
-  acc_clear=1;
-  plot_clear(PLOT(p->plot));
-  if(!process_active){
-    rundata_clear();
-  }
-}
-
-static void rewindchange(GtkWidget *widget,struct panel *p){
-  acc_rewind=1;
-}
-
-extern char *version;
-void panel_create(struct panel *panel){
-  int i;
-
-  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 *mainbox=gtk_hbox_new(0,6);
-  GdkWindow *root=gdk_get_default_root_window();
-  GtkWidget *rightbox=gtk_vbox_new(0,0);
-  GtkWidget *leftbox=gtk_vbox_new(0,6);
-
-  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);
-
-  char versionmarkup[240];
-  snprintf(versionmarkup,240," <span size=\"large\" weight=\"bold\" "
-	   "style=\"italic\" foreground=\"dark blue\">"
-	   "Spectrum Analyzer</span>  <span size=\"small\" foreground=\"#606060\">"
-	   "revision %s</span> ",
-	   version);
-
-  /* the Fucking Fish */
-  for(i=0;i<19;i++)
-    panel->ff[i]=gdk_pixmap_create_from_xpm_d(root,
-					      panel->fb+i,NULL,ff_xpm[i]);
-  panel->twirlimage=gtk_image_new_from_pixmap(panel->ff[0],panel->fb[0]);
-
-  active = calloc(total_ch,sizeof(*active));
-
-  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_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);
-  gtk_table_attach_defaults(GTK_TABLE(topplace),
-			    topframe,0,1,0,1);
-    
-  gtk_container_add (GTK_CONTAINER (panel->toplevel), topplace);
-  gtk_container_set_border_width (GTK_CONTAINER (quitbutton), 3);
-
-  g_signal_connect (G_OBJECT (quitbutton), "clicked",
-		    G_CALLBACK (shutdown), NULL);
-  gtk_widget_add_accelerator (quitbutton, "activate", panel->group, GDK_q, 0, 0);
-
-  gtk_container_set_border_width (GTK_CONTAINER (topframe), 3);
-  gtk_container_set_border_width (GTK_CONTAINER (mainbox), 3);
-  gtk_frame_set_shadow_type(GTK_FRAME(topframe),GTK_SHADOW_ETCHED_IN);
-  gtk_frame_set_label_widget(GTK_FRAME(topframe),toplabel);
-  gtk_label_set_markup(GTK_LABEL(toplabel),versionmarkup);
-
-  gtk_container_add (GTK_CONTAINER(topframe), mainbox);
-
-  g_signal_connect (G_OBJECT (panel->toplevel), "delete_event",
-		    G_CALLBACK (shutdown), NULL);
-
-  /* add the spectrum plot box */
-  panel->plot=plot_new(blocksize/2+1,inputs,channels,rate);
-  gtk_box_pack_end(GTK_BOX(leftbox),panel->plot,1,1,0);
-  gtk_box_pack_start(GTK_BOX(mainbox),leftbox,1,1,0);
-  
-  /*fish */
-  {
-    GtkWidget *box=gtk_hbox_new(1,1);
-    GtkWidget *fishbox=gtk_hbox_new(0,0);
-    gtk_box_pack_end(GTK_BOX(fishbox),panel->twirlimage,0,0,0);
-    gtk_container_set_border_width (GTK_CONTAINER (fishbox), 3);
-
-    gtk_box_pack_start(GTK_BOX(box),fishbox,0,0,0);
-    gtk_box_pack_start(GTK_BOX(rightbox),box,0,0,0);
-  }
-
-  /* rate */
-  /* channels */
-  /* bits */
-  {
-    int fi;
-    int ch=0;
-    char buffer[160];
-    GtkWidget *label;
-    //GtkWidget *vbox=gtk_vbox_new(1,1);
-
-    GtkWidget *sep=gtk_hseparator_new();
-    gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
-
-    panel->chbuttons = calloc(total_ch,sizeof(*panel->chbuttons));
-    for(fi=0;fi<inputs;fi++){
-      
-      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(rightbox),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(rightbox),label,0,0,0);
-
-      for(i=ch;i<ch+channels[fi];i++){
-	GtkWidget *button=panel->chbuttons[i]=gtk_toggle_button_new();
-
-	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(rightbox),button,0,0,0);
-      }
-
-      GtkWidget *sep=gtk_hseparator_new();
-      gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
-      
-      ch+=channels[fi];
-
-    }
-    chlabels(NULL,panel);
-  }
-  
-  GtkWidget *bbox=gtk_vbox_new(0,0);
-
-  /* add the action buttons */
-  /* scale */
-  {
-    GtkWidget *menu=gtk_combo_box_new_text();
-    char *entries[]={"single-pixel","1/24th octave","1/12th octave","1/3 octave"};
-    for(i=0;i<4;i++)
-      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
-    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_res);
-    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-    g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (reschange), panel);
-  }
-
-  {
-    GtkWidget *menu=gtk_combo_box_new_text();
-    char *entries[]={"log scale","ISO log scale","linear scale"};
-    for(i=0;i<3;i++)
-      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
-    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_scale);
-    plot_setting(PLOT(panel->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-    
-    g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (scalechange), panel);
-  }
-  
-  /* depth */
-  {
-    GtkWidget *menu=gtk_combo_box_new_text();
-    char *entries[]={"10dB","20dB","45dB","90dB","140dB"};
-    for(i=0;i<5;i++)
-      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
-    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),3);
-    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-    
-    g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (depthchange), panel);
-  }
-  
-  /* mode */
-  {
-    GtkWidget *menu=gtk_combo_box_new_text();
-    char *entries[]={"realtime","maximum","accumulate"};
-    for(i=0;i<3;i++)
-      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
-    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_mode);
-    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-    
-    g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (modechange), panel);
-  }
-  
-  /* link */
-  {
-    GtkWidget *menu=gtk_combo_box_new_text();
-    for(i=0;i<LINKS;i++)
-      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), link_entries[i]);
-    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
-    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-    
-    g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (linkchange), panel);
-  }
-  
-  {
-    GtkWidget *sep=gtk_hseparator_new();
-    gtk_box_pack_start(GTK_BOX(bbox),sep,0,0,4);
-  }
-  
-  /* 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_box_pack_start(GTK_BOX(bbox),button,0,0,0);
-    panel->run=button;
-  }
-  
-  /* hold */
-  /* loop */
-  {
-    GtkWidget *box=gtk_hbox_new(1,1);
-    GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_hold");
-    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_h, 0, 0);
-    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (holdchange), panel);
-    gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
-    
-    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_box_pack_start(GTK_BOX(box),button,1,1,0);
-    gtk_widget_set_sensitive(button,global_seekable);
-    
-    gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
-  }
-  
-  /* clear */
-  /* rewind */
-  {
-    GtkWidget *box=gtk_hbox_new(1,1);
-    GtkWidget *button=gtk_button_new_with_mnemonic("_clear");
-    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_c, 0, 0);
-    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (clearchange), panel);
-    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_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);
-  }
-  
-  /* dump */
-  {
-    GtkWidget *button=gtk_button_new_with_mnemonic("_dump data");
-    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_d, 0, 0);
-    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (dump), panel);
-    gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
-  }
-
-  /* noise floor */
-  {
-    GtkWidget *button=gtk_toggle_button_new_with_mnemonic("sample _noise floor");
-    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_n, 0, 0);
-    g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (noise), panel);
-    gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
-  }
-
-  gtk_box_pack_end(GTK_BOX(rightbox),bbox,0,0,0);
-  gtk_box_pack_start(GTK_BOX(mainbox),rightbox,0,0,0);
-
-    
-  gtk_widget_show_all(panel->toplevel);
-  //gtk_window_set_resizable(GTK_WINDOW(panel->toplevel),0);
-
-}
-
-static gboolean async_event_handle(GIOChannel *channel,
-				   GIOCondition condition,
-				   gpointer data){
-  struct panel *panel=data;
-  char buf[1];
-
-  /* read all pending */
-  while(read(eventpipe[0],buf,1)>0);
-
-  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);
-
-  /* update the spectral 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));
-
-    while (gtk_events_pending())
-      gtk_main_iteration();
-  }else
-    pthread_mutex_unlock(&feedback_mutex);
-
-  return TRUE;
-}
-
-static int look_for_gtkrc(char *filename){
-  FILE *f=fopen(filename,"r");
-  if(!f)return 0;
-  fprintf(stderr,"Loading spectrum-gtkrc file found at %s\n",filename);
-  gtk_rc_add_default_file(filename);
-  return 1;
-}
-
-void panel_go(int argc,char *argv[]){
-  char *homedir=getenv("HOME");
-  int found=0;
-  memset(&p,0,sizeof(p));
-
-  found|=look_for_gtkrc(ETCDIR"/spectrum-gtkrc");
-  {
-    char *rcdir=getenv("HOME");
-    if(rcdir){
-      char *rcfile="/.spectrum/spectrum-gtkrc";
-      char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
-      strcat(homerc,homedir);
-      strcat(homerc,rcfile);
-      found|=look_for_gtkrc(homerc);
-    }
-  }
-  {
-    char *rcdir=getenv("SPECTRUM_RCDIR");
-    if(rcdir){
-      char *rcfile="/spectrum-gtkrc";
-      char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
-      strcat(homerc,homedir);
-      strcat(homerc,rcfile);
-      found|=look_for_gtkrc(homerc);
-    }
-  }
-  found|=look_for_gtkrc("./spectrum-gtkrc");
-
-  if(!found){
-  
-    fprintf(stderr,"Could not find the spectrum-gtkrc configuration file normally\n"
-	    "installed in one of the following places:\n"
-
-	    "\t./spectrum-gtkrc\n"
-	    "\t$(SPECTRUM_RCDIR)/spectrum-gtkrc\n"
-	    "\t~/.spectrum/spectrum-gtkrc\n\t"
-	    ETCDIR"/spectrum-gtkrc\n"
-	    "This configuration file is used to tune the color, font and other detail aspects\n"
-	    "of the user interface.  Although the analyzer will work without it, the UI\n"
-	    "appearence will likely make the application harder to use due to missing visual\n"
-	    "cues.\n");
-  }
-
-  gtk_rc_add_default_file(ETCDIR"/spectrum-gtkrc");
-  if(homedir){
-    char *rcfile="/.spectrum-gtkrc";
-    char *homerc=calloc(1,strlen(homedir)+strlen(rcfile)+1);
-    strcat(homerc,homedir);
-    strcat(homerc,rcfile);
-    gtk_rc_add_default_file(homerc);
-  }
-  gtk_rc_add_default_file(".spectrum-gtkrc");
-  gtk_rc_add_default_file("spectrum-gtkrc");
-  gtk_init (&argc, &argv);
-
-  panel_create(&p);
-  animate_fish(&p);
-
-  /* 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_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_main ();
-
-}
-

Deleted: trunk/spectrum/plot.h
===================================================================
--- trunk/spectrum/plot.h	2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/plot.h	2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,132 +0,0 @@
-/*
- *
- *  gtk2 spectrum analyzer
- *    
- *      Copyright (C) 2004 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__
-#define __PLOT_H__
-
-#include <glib.h>
-#include <glib-object.h>
-#include <gtk/gtkcontainer.h>
-#include <gtk/gtksignal.h>
-#include <gtk/gtkdrawingarea.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;
-
-struct _Plot{
-
-  GtkDrawingArea canvas;  
-  GdkPixmap *backing;
-  GdkGC     *drawgc;
-  GdkGC     *dashes;
-
-  PangoLayout **lin_layout;
-  PangoLayout **log_layout;
-  PangoLayout **iso_layout;
-  PangoLayout **db_layout;
-  PangoLayout **imp_layout;
-  PangoLayout **phase_layout;
-
-  int configured;
-  float **ydata;
-  float *floor;
-
-  int groups;
-  int *ch;
-  int *ch_active;
-  int *ch_process;
-  int total_ch;
-  int datasize;
-  int mode;
-  int link;
-  int scale;
-  int res;
-  int noise;
-  int *rate;
-  int maxrate;
-
-  float lin_major;
-  float lin_minor;
-  int lin_mult;
-
-  int xgrid[20];
-  int xgrids;
-  int xtic[200];
-  int xtics;
-
-  int ygrid[11];
-  int ygrids;
-  int ytic[200];
-  int ytics;
-
-  float depth;
-  float ymax;
-  float pmax;
-  float pmin;
-
-  float disp_depth;
-  float disp_ymax;
-  float disp_pmax;
-  float disp_pmin;
-
-  float padx;
-  float phax;
-  float pady;
-
-  int ymaxtimer;
-  int phtimer;
-};
-
-struct _PlotClass{
-
-  GtkDrawingAreaClass parent_class;
-  void (* plot) (Plot *m);
-};
-
-GType          plot_get_type        (void);
-GtkWidget*     plot_new             (int n, int inputs, int *channels, int *rate);
-void	       plot_refresh         (Plot *m, int *process);
-void	       plot_setting         (Plot *m, int res, int scale, int mode, int link, int depth, int noise);
-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 *, int *);
-
-GdkColor chcolor(int ch);
-
-G_END_DECLS
-
-#endif
-
-
-
-

Deleted: trunk/spectrum/process.c
===================================================================
--- trunk/spectrum/process.c	2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/process.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,923 +0,0 @@
-/*
- *
- *  gtk2 spectrum analyzer
- *    
- *      Copyright (C) 2004 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"
-#include "io.h"
-
-static float *window=NULL;
-static float *freqbuffer=0;
-static fftwf_plan plan;
-
-pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
-int feedback_increment=0;
-
-float *feedback_count;
-float **plot_data;
-float *plot_floor=NULL;
-float **work_floor=NULL;
-float *process_work;
-
-float **feedback_acc;
-float **feedback_max;
-float **feedback_instant;
-
-/* Gentlemen, power up the Variance hammer */
-float **floor_y;
-float **floor_yy;
-int floor_count;
-
-float **ph_acc;
-float **ph_max;
-float **ph_instant;
-
-float **xmappingL;
-float **xmappingH;
-int metascale = -1;
-int metawidth = -1;
-int metares = -1;
-int metanoise = 0;
-
-sig_atomic_t acc_clear=0;
-sig_atomic_t acc_rewind=0;
-sig_atomic_t acc_loop=0;
-
-sig_atomic_t process_active=0;
-sig_atomic_t process_exit=0;
-
-static void init_process(void){
-  int i;
-  if(window==NULL){
-    process_work=calloc(blocksize+2,sizeof(*process_work));
-    feedback_count=calloc(total_ch,sizeof(*feedback_count));
-    plot_data=calloc(total_ch,sizeof(*plot_data));
-
-    feedback_acc=malloc(total_ch*sizeof(*feedback_acc));
-    feedback_max=malloc(total_ch*sizeof(*feedback_max));
-    feedback_instant=malloc(total_ch*sizeof(*feedback_instant));
-    floor_y=malloc(total_ch*sizeof(*floor_y));
-    floor_yy=malloc(total_ch*sizeof(*floor_yy));
-
-    ph_acc=malloc(total_ch*sizeof(*ph_acc));
-    ph_max=malloc(total_ch*sizeof(*ph_max));
-    ph_instant=malloc(total_ch*sizeof(*ph_instant));
-
-    freqbuffer=fftwf_malloc((blocksize+2)*sizeof(*freqbuffer));
-    for(i=0;i<total_ch;i++){
-
-      floor_y[i]=calloc(blocksize/2+1,sizeof(**floor_y));
-      floor_yy[i]=calloc(blocksize/2+1,sizeof(**floor_yy));
-      feedback_acc[i]=calloc(blocksize/2+1,sizeof(**feedback_acc));
-      feedback_max[i]=calloc(blocksize/2+1,sizeof(**feedback_max));
-      feedback_instant[i]=calloc(blocksize/2+1,sizeof(**feedback_instant));
-
-      ph_acc[i]=calloc(blocksize+2,sizeof(**ph_acc));
-      ph_max[i]=calloc(blocksize+2,sizeof(**ph_max));
-      ph_instant[i]=calloc(blocksize+2,sizeof(**ph_instant));
-    }
-
-    plan=fftwf_plan_dft_r2c_1d(blocksize,freqbuffer,
-                               (fftwf_complex *)freqbuffer,
-                               FFTW_ESTIMATE);
-
-    /* construct proper window (sin^4 I'd think) */
-    window = calloc(blocksize,sizeof(*window));
-    for(i=0;i<blocksize;i++)window[i]=sin(M_PIl*i/blocksize);
-    for(i=0;i<blocksize;i++)window[i]*=window[i];
-    for(i=0;i<blocksize;i++)window[i]=sin(window[i]*M_PIl*.5);
-    for(i=0;i<blocksize;i++)window[i]*=window[i]/(blocksize/4)*.778;
-  }
-}
-
-void rundata_clear(){
-  int i,j;
-  for(i=0;i<total_ch;i++){
-    feedback_count[i]=0;
-    memset(feedback_acc[i],0,(blocksize/2+1)*sizeof(**feedback_acc));
-    memset(feedback_max[i],0,(blocksize/2+1)*sizeof(**feedback_max));
-    memset(feedback_instant[i],0,(blocksize/2+1)*sizeof(**feedback_instant));
-
-    for(j=0;j<blocksize+2;j++){
-      ph_acc[i][j]=0;
-      ph_max[i][j]=0;
-      ph_instant[i][j]=0;
-    }
-  }
-  acc_clear=0;
-}
-
-extern int plot_noise;
-
-/* return 0 on EOF, 1 otherwise */
-static int process(){
-  int fi,i,j,ch;
-  int eof_all;
-  int noise=plot_noise;  
-
-  if(acc_rewind)
-    rewind_files();
-
-  eof_all=input_read();
-
-  if(eof_all){
-    if(acc_loop && !acc_rewind){
-      acc_rewind=1;
-      return process();
-    } else {
-      acc_rewind=0;
-      return 0;
-    }
-  }
-  acc_rewind=0;
-
-  if(acc_clear)
-    rundata_clear();
-
-  /* by channel */
-  ch=0;
-  for(fi=0;fi<inputs;fi++){
-    if(blockbufferfill[fi]){
-      for(i=ch;i<ch+channels[fi];i++){
-	
-	float *data=blockbuffer[i];
-
-	/* window the blockbuffer into the FFT buffer */
-	for(j=0;j<blocksize;j++){
-	  freqbuffer[j]=data[j]*window[j];
-	}
-	
-	/* transform */
-	fftwf_execute(plan);
-	
-	pthread_mutex_lock(&feedback_mutex);
-
-	/* perform desired accumulations */
-	for(j=0;j<blocksize+2;j+=2){
-	  float R = freqbuffer[j];
-	  float I = freqbuffer[j+1];
-	  float sqR = R*R;
-	  float sqI = I*I;
-	  float sqM = sqR+sqI;
-
-	  if(noise==1){
-	    floor_yy[i][j>>1]+=sqM*sqM;
-	    floor_y[i][j>>1]+=sqM;
-	  }
-	  
-	  /* deal with phase accumulate/rotate */
-	  if(i==ch){
-	    /* normalize/store ref for later rotation */
-	    process_work[j] = R;
-	    process_work[j+1] = -I;
-
-	  }else{
-	    /* rotate signed square phase according to ref for phase calculation */
-	    float pR;
-	    float pI;
-	    float rR = process_work[j];
-	    float rI = process_work[j+1];
-	    pR = (rR*R - rI*I);
-	    pI = (rR*I + rI*R);
-
-	    ph_instant[i][j]=pR;
-	    ph_instant[i][j+1]=pI;
-
-	    ph_acc[i][j]+=pR;
-	    ph_acc[i][j+1]+=pI;
-	    
-	    if(feedback_max[i][j>>1]<sqM){
-	      ph_max[i][j]=pR;
-	      ph_max[i][j+1]=pI;
-	    }
-	  }
-	  
-	  feedback_instant[i][j>>1]=sqM;
-	  feedback_acc[i][j>>1]+=sqM;
-	  
-	  if(feedback_max[i][j>>1]<sqM)
-	    feedback_max[i][j>>1]=sqM;
-	  
-	}
-	feedback_count[i]++;
-	
-	pthread_mutex_unlock(&feedback_mutex);
-      }
-    }
-    ch+=channels[fi];
-  }
-  if(noise==1)
-    floor_count++;
-  feedback_increment++;
-  write(eventpipe[1],"",1);
-  return 1;
-}
-
-void *process_thread(void *dummy){
-  while(!process_exit && process());
-  process_active=0;
-  write(eventpipe[1],"",1);
-  return NULL;
-}
-
-void process_dump(int mode){
-  int fi,i,j,ch;
-  FILE *out;
-
-  {   
-    out=fopen("accumulate.m","w");
-    ch = 0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=0;i<blocksize/2+1;i++){
-	fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
-	
-	for(j=ch;j<ch+channels[fi];j++)
-	  fprintf(out,"%f ",todB(feedback_acc[j][i])*.5);
-	fprintf(out,"\n");
-      }
-      fprintf(out,"\n");
-      ch+=channels[fi];
-    }
-    fclose(out);
-  }
-
-  {   
-    out=fopen("max.m","w");
-    ch = 0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=0;i<blocksize/2+1;i++){
-	fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
-	
-	for(j=ch;j<ch+channels[fi];j++)
-	  fprintf(out,"%f ",todB(feedback_max[j][i])*.5);
-	fprintf(out,"\n");
-      }
-      fprintf(out,"\n");
-      ch+=channels[fi];
-    }
-    fclose(out);
-  }
-
-  {   
-    out=fopen("instant.m","w");
-    ch = 0;
-    for(fi=0;fi<inputs;fi++){
-      for(i=0;i<blocksize/2+1;i++){
-	fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
-	
-	for(j=ch;j<ch+channels[fi];j++)
-	  fprintf(out,"%f ",todB(feedback_instant[j][i])*.5);
-	fprintf(out,"\n");
-      }
-      fprintf(out,"\n");
-      ch+=channels[fi];
-    }
-    fclose(out);
-  }
-
-  {   
-    out=fopen("accphase.m","w");
-    ch = 0;
-    for(fi=0;fi<inputs;fi++){
-
-      /* phase */ 
-      for(i=0;i<blocksize+2;i+=2){
-	fprintf(out,"%f ",(double)i*.5*rate[fi]/blocksize);
-	fprintf(out,"%f ",atan2(ph_acc[ch+1][i+1],ph_acc[ch+1][i])*57.29);
-	fprintf(out,"\n");
-      }
-      fprintf(out,"\n");
-      ch+=channels[fi];
-    }
-    fclose(out);
-  }
-
-}
-
-void clear_noise_floor(){
-  int i;
-  for(i=0;i<total_ch;i++){
-    memset(floor_y[i],0,(blocksize/2+1)*sizeof(**floor_y));
-    memset(floor_yy[i],0,(blocksize/2+1)*sizeof(**floor_yy));
-  }
-  floor_count=0;
-}
-
-/* how many bins to 'trim' off the edge of calculated data when we
-   know we've hit a boundary of marginal measurement */
-#define binspan 5
-
-float **process_fetch(int res, int scale, int mode, int link, 
-		      int *active, int width, 
-		      float *ymax, float *pmax, float *pmin,
-		      float **yfloor,int noise){
-  int ch,ci,i,j,fi;
-  float **data;
-  float **ph;
-  float maxrate=-1.;
-  float nyq;
-
-  init_process();
-
-  for(fi=0;fi<inputs;fi++)
-    if(rate[fi]>maxrate)maxrate=rate[fi];
-  nyq=maxrate/2.;
-
-  *yfloor=NULL;
-
-  /* are our scale mappings up to date? */
-  if(res != metares || scale != metascale || width != metawidth){
-    if(!xmappingL) xmappingL = calloc(inputs, sizeof(*xmappingL));
-    if(!xmappingH) xmappingH = calloc(inputs, sizeof(*xmappingH));
-    metanoise=-1;
-
-    if(!work_floor)
-      work_floor = calloc(total_ch,sizeof(*work_floor));
-    for(i=0;i<total_ch;i++){
-      if(work_floor[i])
-	work_floor[i] = realloc(work_floor[i],(width+1)*sizeof(**work_floor));
-      else
-	work_floor[i] = calloc((width+1),sizeof(**work_floor));
-    }
-
-    for(fi=0;fi<inputs;fi++){
-
-      /* if mapping preexists, resize it */
-      if(xmappingL[fi]){
-	xmappingL[fi] = realloc(xmappingL[fi],(width+1)*sizeof(**xmappingL));
-      }else{
-	xmappingL[fi] = malloc((width+1)*sizeof(**xmappingL));
-      }
-      if(xmappingH[fi]){
-	xmappingH[fi] = realloc(xmappingH[fi],(width+1)*sizeof(**xmappingH));
-      }else{
-	xmappingH[fi] = malloc((width+1)*sizeof(**xmappingH));
-      }
-
-      metascale = scale;
-      metawidth = width;
-      metares = res;
-
-      
-      /* generate new numbers */
-      for(i=0;i<width;i++){
-	float off=0;
-	float loff=1.;
-	float hoff=1.;
-	float lfreq,hfreq;
-
-	switch(res){
-	case 0: /* screen-resolution */
-	  off=1.;
-	  break;
-	case 1: /* 1/24th octave */
-	  loff = .95918945710913818816;
-	  hoff = 1.04254690518999138632;
-	  break;
-	case 2: /* 1/12th octave */
-	  loff = .94387431268169349664;
-	  hoff = 1.05946309435929526455;
-	  break;
-	case 3: /* 1/3th octave */
-	  loff = .79370052598409973738;
-	  hoff = 1.25992104989487316475;
-	  break;
-	}
-
-	switch(scale){
-	case 0: /* log */
-	  lfreq= pow(10.,(i-off)/(width-1)
-		     * (log10(nyq)-log10(5.))
-		     + log10(5.)) * loff;
-	  hfreq= pow(10.,(i+off)/(width-1)
-		     * (log10(nyq)-log10(5.))
-		     + log10(5.)) * hoff;
-	  break;
-	case 1: /* ISO */
-	  lfreq= pow(2.,(i-off)/(width-1)
-		     * (log2(nyq)-log2(25.))
-		     + log2(25.)) * loff;
-	  hfreq= pow(2.,(i+off)/(width-1)
-		     * (log2(nyq)-log2(25.))
-		     + log2(25.)) *hoff;
-	  break;
-	case 2: /* screen-resolution linear */
-	  lfreq=(i-off)*nyq/(width-1)*loff;
-	  hfreq=(i+off)*nyq/(width-1)*hoff;
-	  break;
-	}
-
-	xmappingL[fi][i]=lfreq/(rate[fi]*.5)*(blocksize/2);
-	xmappingH[fi][i]=hfreq/(rate[fi]*.5)*(blocksize/2);
-
-      }
-      
-      for(i=0;i<width;i++){
-	if(xmappingL[fi][i]<0.)xmappingL[fi][i]=0.;
-	if(xmappingL[fi][i]>blocksize/2.)xmappingL[fi][i]=blocksize/2.;
-	if(xmappingH[fi][i]<0.)xmappingH[fi][i]=0.;
-	if(xmappingH[fi][i]>blocksize/2.)xmappingH[fi][i]=blocksize/2.;
-      }
-    }
-
-    for(i=0;i<total_ch;i++)
-      if(plot_data[i]){
-	plot_data[i] = realloc(plot_data[i],(width+1)*sizeof(**plot_data));
-      }else{
-	plot_data[i] = malloc((width+1)*sizeof(**plot_data));
-      }
-  }
-
-  /* 'illustrate' the noise floor */
-  if(noise){
-    if(plot_floor)
-      plot_floor=realloc(plot_floor,(width+1)*sizeof(*plot_floor));
-    else
-      plot_floor=calloc((width+1),sizeof(*plot_floor));
-    
-    if(metanoise!=link){
-      float *y = plot_floor;
-      int ch=0;
-      metanoise=link;
-      for(i=0;i<width;i++)
-	y[i]=-300;
-      
-      for(fi=0;fi<inputs;fi++){
-	float *L = xmappingL[fi];
-	float *H = xmappingH[fi];
-	float d = 1./floor_count;
-	
-	for(ci=0;ci<channels[fi];ci++){
-	  float *fy = floor_y[ci+ch];
-	  float *fyy = floor_yy[ci+ch];
-	  float *w = work_floor[ci+ch];
-	  
-	  for(i=0;i<width;i++){
-	    int first=floor(L[i]);
-	    int last=floor(H[i]);
-	    float esum;
-	    float vsum;
-	    float v = fyy[first]*floor_count - fy[first]*fy[first];
-	    
-	    if(first==last){
-	      float del=H[i]-L[i];
-	      esum=fy[first]*del;
-	      vsum=v*del;
-	    }else{
-	      float del=1.-(L[i]-first);
-	      esum=fy[first]*del;
-	      vsum=v*del;
-	      
-	      for(j=first+1;j<last;j++){
-		v = fyy[j]*floor_count - fy[j]*fy[j];
-		esum+=fy[j];
-		vsum+=v;
-	      }
-	      
-	      v = fyy[last]*floor_count - fy[last]*fy[last];
-	      del=(H[i]-last);
-	      esum+=fy[last]*del;
-	      vsum+=v*del;
-	    }
-	    vsum = 10*sqrt(vsum)*d;
-	    esum*=d;
-	    w[i] = esum+vsum*10;
-	    esum = todB_a(w+i)*.5;
-	    
-	    if(esum>y[i])y[i]=esum;
-	  }
-	}
-	ch+=channels[fi];
-      }
-    }
-    if(link == LINK_INDEPENDENT && mode==0)
-      *yfloor=plot_floor;
-  }else{
-    for(i=0;i<total_ch;i++)
-      memset(work_floor[i],0,width*sizeof(**work_floor));
-    metanoise=-1;
-  }
-  
-  /* mode selects the base data set */
-  switch(mode){    
-  case 0: /* independent / instant */
-    data=feedback_instant;
-    ph=ph_instant;
-    break;
-  case 1: /* independent / max */
-    data=feedback_max;
-    ph=ph_max;
-    break;
-  case 2:
-    data=feedback_acc;
-    ph=ph_acc;
-    break;
-  }
-  
-  ch=0;
-  *ymax = -150.;
-  *pmax = -180.;
-  *pmin = 180.;
-  for(fi=0;fi<inputs;fi++){
-    float *L = xmappingL[fi];
-    float *H = xmappingH[fi];
-
-    switch(link){
-    case LINK_INDEPENDENT:
-      
-      for(ci=0;ci<channels[fi];ci++){
-	float *y = plot_data[ci+ch];
-	float *m = data[ci+ch];
-	if(active[ch+ci]){
-	  for(i=0;i<width;i++){
-	    int first=floor(L[i]);
-	    int last=floor(H[i]);
-	    float sum;
-	    
-	    if(first==last){
-	      float del=H[i]-L[i];
-	      sum=m[first]*del;
-	    }else{
-	      float del=1.-(L[i]-first);
-	      sum=m[first]*del;
-	      
-	      for(j=first+1;j<last;j++)
-		sum+=m[j];
-	      
-	      del=(H[i]-last);
-	      sum+=m[last]*del;
-	    }
-
-	    sum=todB_a(&sum)*.5;
-	    if(sum>*ymax)*ymax=sum;
-	    y[i]=sum;	  
-	  }
-	}
-      }
-      break;
-
-    case LINK_SUMMED:
-      {
-	float *y = plot_data[ch];
-	memset(y,0,(width+1)*sizeof(*y));
-      
-	for(ci=0;ci<channels[fi];ci++){
-	  float *m = data[ci+ch];
-	  if(active[ch+ci]){
-	    for(i=0;i<width;i++){
-	      int first=floor(L[i]);
-	      int last=floor(H[i]);
-	      
-	      if(first==last){
-		float del=H[i]-L[i];
-		y[i]+=m[first]*del;
-	      }else{
-		float del=1.-(L[i]-first);
-		y[i]+=m[first]*del;
-		
-		for(j=first+1;j<last;j++)
-		  y[i]+=m[j];
-		
-		del=(H[i]-last);
-		y[i]+=m[last]*del;
-	      }
-	    }
-	  }
-	}
-      
-	for(i=0;i<width;i++){
-	  float sum=todB_a(y+i)*.5;
-	  if(sum>*ymax)*ymax=sum;
-	  y[i]=sum;	  
-	}
-      }
-      break;
-      
-    case LINK_SUB_FROM:
-      {
-	float *y = plot_data[ch];
-	if(active[ch]==0){
-	  for(i=0;i<width;i++)
-	    y[i]=-300;
-	}else{
-	  for(ci=0;ci<channels[fi];ci++){
-	    float *m = data[ci+ch];
-	    if(ci==0 || active[ch+ci]){
-	      for(i=0;i<width;i++){
-		int first=floor(L[i]);
-		int last=floor(H[i]);
-		float sum;
-		
-		if(first==last){
-		  float del=H[i]-L[i];
-		  sum=m[first]*del;
-		}else{
-		  float del=1.-(L[i]-first);
-		  sum=m[first]*del;
-		  
-		  for(j=first+1;j<last;j++)
-		    sum+=m[j];
-		  
-		  del=(H[i]-last);
-		  sum+=m[last]*del;
-		}
-		
-		if(ci==0){
-		  y[i]=sum;
-		}else{
-		  y[i]-=sum;
-		}
-	      }
-	    }
-	  }
-	  
-	  for(i=0;i<width;i++){
-	    float v = (y[i]>0?y[i]:0);
-	    float sum=todB_a(&v)*.5;
-	    if(sum>*ymax)*ymax=sum;
-	    y[i]=sum;	  
-	  }
-	}
-      }
-      break;
-    case LINK_SUB_REF:
-      {
-	float *r = plot_data[ch];
-	for(ci=0;ci<channels[fi];ci++){
-	  float *y = plot_data[ch+ci];
-	  float *m = data[ci+ch];
-	  if(ci==0 || active[ch+ci]){
-	    for(i=0;i<width;i++){
-	      int first=floor(L[i]);
-	      int last=floor(H[i]);
-	      float sum;
-	      
-	      if(first==last){
-		float del=H[i]-L[i];
-		sum=m[first]*del;
-	      }else{
-		float del=1.-(L[i]-first);
-		sum=m[first]*del;
-		
-		for(j=first+1;j<last;j++)
-		  sum+=m[j];
-		
-		del=(H[i]-last);
-		sum+=m[last]*del;
-	      }
-	      
-	      if(ci==0){
-		r[i]=sum;
-	      }else{
-		sum=(r[i]>sum?0.f:sum-r[i]);
-		y[i]=todB_a(&sum)*.5;
-		if(y[i]>*ymax)*ymax=y[i];
-	      }
-	    }
-	  }
-	}
-      }
-      break;
-      
-    case LINK_IMPEDENCE_p1:
-    case LINK_IMPEDENCE_1:
-    case LINK_IMPEDENCE_10:
-      {
-	float shunt = (link == LINK_IMPEDENCE_p1?.1:(link == LINK_IMPEDENCE_1?1:10));
-	float *r = plot_data[ch];
-
-	for(ci=0;ci<channels[fi];ci++){
-	  float *y = plot_data[ci+ch];
-	  float *m = data[ch+ci];
-	  
-	  if(ci==0 || active[ch+ci]){
-	    for(i=0;i<width;i++){
-	      int first=floor(L[i]);
-	      int last=floor(H[i]);
-	      float sum;
-	      
-	      if(first==last){
-		float del=H[i]-L[i];
-		sum=m[first]*del;
-	      }else{
-		float del=1.-(L[i]-first);
-		sum=m[first]*del;
-		
-		for(j=first+1;j<last;j++)
-		  sum+=m[j];
-		
-		del=(H[i]-last);
-		sum+=m[last]*del;
-	      }
-
-	      if(ci==0){
-		/* stash the reference in the work vector */
-		r[i]=sum;
-	      }else{
-		/* the shunt */
-		/* 'r' collected at source, 'sum' across the shunt */
-		float V=sqrt(r[i]);
-		float S=sqrt(sum);
-		
-		if(S>(1e-5) && V>S){
-		  y[i] = shunt*(V-S)/S;
-		}else{
-		  y[i] = NAN;
-		}
-	      }
-	    }
-	  }
-	}
-	    
-	/* scan the resulting buffers for marginal data that would
-	   produce spurious output. Specifically we look for sharp
-	   falloffs of > 40dB or an original test magnitude under
-	   -70dB. */
-	{
-	  float max = -140;
-	  for(i=0;i<width;i++){
-	    float v = r[i] = todB_a(r+i)*.5;
-	    if(v>max)max=v;
-	  }
-
-	  for(ci=1;ci<channels[fi];ci++){
-	    if(active[ch+ci]){
-	      float *y = plot_data[ci+ch];	      
-	      for(i=0;i<width;i++){
-		if(r[i]<max-40 || r[i]<-70){
-		  int j=i-binspan;
-		  if(j<0)j=0;
-		  for(;j<i;j++)
-		    y[j]=NAN;
-		  for(;j<width;j++){
-		    if(r[j]>max-40 && r[j]>-70)break;
-		    y[j]=NAN;
-		  }
-		  i=j+3;
-		  for(;j<i && j<width;j++){
-		    y[j]=NAN;
-		  }
-		}
-		if(!isnan(y[i]) && y[i]>*ymax)*ymax = y[i];
-	      }
-	    }
- 	  }
-	}
-      }
-      break;
-
-    case LINK_PHASE: /* response/phase */
-
-      if(channels[fi]>=2){
-	float *om = plot_data[ch];
-	float *op = plot_data[ch+1];
-
-	float *r = data[ch];
-	float *rn = work_floor[ch];
-	float *m = data[ch+1];
-	float *mn = work_floor[ch+1];
-	float *p = ph[ch+1];
-	float mag[width];
-
-	if(feedback_count[ch]==0){
-	  memset(om,0,width*sizeof(*om));
-	  memset(op,0,width*sizeof(*op));
-	}else{
-	  /* two vectors only; response and phase */
-	  /* response */
-	  if(active[ch] || active[ch+1]){
-	    for(i=0;i<width;i++){
-	      int first=floor(L[i]);
-	      int last=floor(H[i]);
-	      float sumR,sumM;
-	      
-	      if(first==last){
-		float del=H[i]-L[i];
-		sumR=r[first]*del;
-		sumM=m[first]*del;
-	      }else{
-		float del=1.-(L[i]-first);
-		sumR=r[first]*del;
-		sumM=m[first]*del;
-		
-		for(j=first+1;j<last;j++){
-		  sumR+=r[j];
-		  sumM+=m[j];
-		}
-
-		del=(H[i]-last);
-		sumR+=r[last]*del;
-		sumM+=m[last]*del;
-	      }
-	      
-	      if(sumR>rn[i] && sumM>mn[i]){
-		mag[i] = todB_a(&sumR)*.5;
-		sumM /= sumR;
-		om[i] = todB_a(&sumM)*.5;
-	      }else{
-		om[i] = NAN;
-	      }
-	    }
-	  }
-	  
-	  /* phase */
-	  if(active[ch+1]){
-	    for(i=0;i<width;i++){
-	      int first=floor(L[i]);
-	      int last=floor(H[i]);
-	      float sumR,sumI;
-	      
-	      if(first==last){
-		float del=H[i]-L[i];
-		sumR=p[(first<<1)]*del;
-		sumI=p[(first<<1)+1]*del;
-	      }else{
-		float del=1.-(L[i]-first);
-		sumR=p[(first<<1)]*del;
-		sumI=p[(first<<1)+1]*del;
-		
-		for(j=first+1;j<last;j++){
-		  sumR+=p[(j<<1)];
-		  sumI+=p[(j<<1)+1];
-		}
-
-		del=(H[i]-last);
-		sumR+=p[(last<<1)]*del;
-		sumI+=p[(last<<1)+1]*del;
-	      }
-
-	      if(!isnan(om[i])){
-		op[i] = atan2(sumI,sumR)*57.29;
-	      }else{
-		op[i]=NAN;
-	      }
-	    }
-	  }
-	  
-	  /* scan the resulting buffers for marginal data that would
-	     produce spurious output. Specifically we look for sharp
-	     falloffs of > 40dB or an original test magnitude under
-	     -70dB. */
-	  if(active[ch] || active[ch+1]){
-	    for(i=0;i<width;i++){
-	      if(isnan(om[i])){
-		int j=i-binspan;
-		if(j<0)j=0;
-		for(;j<i;j++){
-		  om[j]=NAN;
-		  op[j]=NAN;
-		}
-		for(;j<width;j++){
-		  if(!isnan(om[j]))break;
-		  om[j]=NAN;
-		  op[j]=NAN;
-		}
-		i=j+3;
-		for(;j<i && j<width;j++){
-		  om[j]=NAN;
-		  op[j]=NAN;
-		}
-	      }
-	      if(om[i]>*ymax)*ymax = om[i];
-	      if(op[i]>*pmax)*pmax = op[i];
-	      if(op[i]<*pmin)*pmin = op[i];
-	      
-	    }
-	  }
-	}
-      }
-      break;
-      
-    case LINK_THD: /* THD */
-    case LINK_THD2: /* THD-2 */
-    case LINK_THDN: /* THD+N */
-    case LINK_THDN2: /* THD+N-2 */
-      
-      
-      break;
-      
-    }
-    ch+=channels[fi];
-  }
-  
-  return plot_data;
-}
-

Copied: trunk/spectrum/spec_panel.c (from rev 14614, trunk/spectrum/panel.c)
===================================================================
--- trunk/spectrum/spec_panel.c	                        (rev 0)
+++ trunk/spectrum/spec_panel.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,841 @@
+/*
+ *
+ *  gtk2 spectrum analyzer
+ *    
+ *      Copyright (C) 2004 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"
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "fisharray.h"
+#include "spec_plot.h"
+#include "io.h"
+
+sig_atomic_t increment_fish=0;
+
+static struct panel {
+  GtkWidget *twirlimage;
+  GdkPixmap *ff[19];
+  GdkBitmap *fb[19];
+
+  GtkAccelGroup *group;
+  GtkWidget *toplevel;
+
+
+  guint fishframe_timer;
+  int fishframe_init;
+  int fishframe;
+
+  GtkWidget *plot;
+  GtkWidget *run;
+  GtkWidget **chbuttons;
+  
+} p;
+
+int plot_res=0;
+int plot_scale=0;
+int plot_mode=0;
+int plot_link=0;
+int plot_hold=0;
+int plot_depth=90;
+int plot_noise=0;
+int plot_last_update=0;
+int *active;
+
+static void replot(struct panel *p){
+  int i,lactive[total_ch];
+  for(i=0;i<total_ch;i++)
+    lactive[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
+
+  /* update the spectral display; send new data */
+  if(!plot_hold){
+    pthread_mutex_lock(&feedback_mutex);
+    plot_refresh(PLOT(p->plot),lactive);
+    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){
+      /* reschedule to blink */
+      p->fishframe_timer=
+	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+      return FALSE;
+    }
+
+  }else{
+    p->fishframe++;
+    if(p->fishframe<=1)p->fishframe=12;
+    if(p->fishframe>=19)p->fishframe=0;
+
+    gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
+			      p->ff[p->fishframe],
+			      p->fb[p->fishframe]);
+
+
+    if(p->fishframe==12){
+      /* reschedule to animate */
+      p->fishframe_timer=
+	g_timeout_add(10,(GSourceFunc)reanimate_fish,p);
+      return FALSE;
+    }
+    if(p->fishframe==0){
+      /* reschedule to blink */
+      p->fishframe_timer=
+	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+      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);
+  }else{
+    p->fishframe_init=1;
+    p->fishframe_timer=
+      g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+  }
+}
+
+static void dump(GtkWidget *widget,struct panel *p){
+  process_dump(plot_mode);
+}
+
+static void noise(GtkWidget *widget,struct panel *p){
+  if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))){
+    if(plot_noise){
+      plot_noise=0;
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),0);
+      clear_noise_floor();
+      plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,0);
+    }else{
+      plot_noise=1;
+      plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,0);
+    }
+  }else{
+    if(plot_noise){
+      gtk_button_set_label(GTK_BUTTON(widget),"clear _noise floor");
+      plot_noise=2;
+      plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,1);
+    }else
+      gtk_button_set_label(GTK_BUTTON(widget),"sample _noise floor");
+  }
+}
+
+
+static void depthchange(GtkWidget *widget,struct panel *p){
+  int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  switch(choice){
+  case 0: /* 10dB */
+    plot_depth=10;
+    break;
+  case 1: /* 20dB */
+    plot_depth=20;
+    break;
+  case 2: /* 45dB */
+    plot_depth=45;
+    break;
+  case 3: /* 90dB */
+    plot_depth=90;
+    break;
+  case 4: /*140dB */
+    plot_depth=140;
+    break;
+  }
+  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+}
+
+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);
+
+  /* buttons usually have internal labels */
+  if(GTK_IS_CONTAINER(c))
+    gtk_container_forall (GTK_CONTAINER(c),set_fg,in);
+}
+
+static void set_via_active(struct panel *p, int *active, int *bactive){
+  int fi,i;
+  int ch=0;
+  for(fi=0;fi<inputs;fi++){
+    for(i=ch;i<ch+channels[fi];i++)
+      gtk_widget_set_sensitive(p->chbuttons[i],1);
+    ch+=channels[fi];
+  }
+  plot_set_active(PLOT(p->plot),active,bactive);  
+}
+
+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];
+  int bactive[total_ch];
+
+  for(i=0;i<total_ch;i++)
+    bactive[i]=active[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
+
+  /* set sensitivity */
+  switch(plot_link){
+  case LINK_IMPEDENCE_p1:
+  case LINK_IMPEDENCE_1:
+  case LINK_IMPEDENCE_10:
+  case LINK_THD:
+  case LINK_THDN:
+  case LINK_THD2:
+  case LINK_THDN2:
+  case LINK_SUB_REF:
+
+    /*  first channel in each group insensitive/inactive, used as a reference */
+    ch=0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=ch;i<ch+channels[fi];i++){
+	if(i==ch){
+	  gtk_widget_set_sensitive(p->chbuttons[i],0);
+	  active[i]=0; /* do not frob widget, only plot settings */
+	}else{
+	  gtk_widget_set_sensitive(p->chbuttons[i],1);
+	}
+      }
+      ch+=channels[fi];
+    }
+ 
+    plot_set_active(PLOT(p->plot),active,bactive);
+    break;    
+
+  case LINK_SUMMED: /* summing mode */
+  case LINK_SUB_FROM: /* subtract channels from reference */
+    ch=0;
+    for(fi=0;fi<inputs;fi++){
+      int any=0;
+      for(i=ch;i<ch+channels[fi];i++){
+	if(active[i])any=1;
+	active[i]=0;
+      }
+      active[ch]=any;
+      ch+=channels[fi];
+    }
+
+    set_via_active(p,active,bactive);
+    break;
+
+  case LINK_INDEPENDENT: /* normal/independent mode */
+    set_via_active(p,active,bactive);
+    break;    
+
+  case LINK_PHASE: /* response/phase */
+    ch=0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=ch;i<ch+channels[fi];i++)
+	if(channels[fi]<2){
+	  gtk_widget_set_sensitive(p->chbuttons[i],0);
+	  active[i]=0;
+	}else{
+	  if(i<ch+2){
+	    gtk_widget_set_sensitive(p->chbuttons[i],1);
+	  }else{
+	    gtk_widget_set_sensitive(p->chbuttons[i],0);
+	    active[i]=0;
+	  }
+	}
+      ch+=channels[fi];
+    }
+    plot_set_active(PLOT(p->plot),active,bactive);
+    break;    
+  }
+
+  /* set labels */
+  switch(plot_link){
+  case LINK_THD:
+  case LINK_THDN:
+  case LINK_THD2:
+  case LINK_THDN2:
+  case LINK_IMPEDENCE_p1:
+  case LINK_IMPEDENCE_1:
+  case LINK_IMPEDENCE_10:
+  case LINK_SUB_REF:
+    ch=0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=ch;i<ch+channels[fi];i++){
+	if(i==ch){
+	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"reference");
+	}else{
+	  sprintf(buf,"channel %d", i-ch);
+	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
+	}
+      }
+      ch+=channels[fi];
+    }
+    break;    
+  case LINK_SUB_FROM:
+    ch=0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=ch;i<ch+channels[fi];i++){
+	if(i==ch){
+	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"output");
+	}else{
+	  sprintf(buf,"channel %d", i-ch);
+	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
+	}
+      }
+      ch+=channels[fi];
+    }
+    break;    
+
+  case LINK_INDEPENDENT:
+  case LINK_SUMMED:
+
+    ch=0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=ch;i<ch+channels[fi];i++){
+	sprintf(buf,"channel %d", i-ch);
+	gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
+      }
+      ch+=channels[fi];
+    }
+    break;    
+
+  case LINK_PHASE:
+
+    ch=0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=ch;i<ch+channels[fi];i++){
+	if(channels[fi]<2){
+	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"unused");
+	}else if(i==ch){
+	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"response");
+	}else if(i==ch+1){
+	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"phase");
+	}else{
+	  gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"unused");
+	}
+      }
+      ch+=channels[fi];
+    }
+    break;    
+  }
+
+  /* set colors */
+  switch(plot_link){
+  case LINK_SUMMED:
+  case LINK_SUB_FROM:
+    ch=0;
+    for(fi=0;fi<inputs;fi++){
+      GdkColor rgb = chcolor(ch);
+
+      for(i=ch;i<ch+channels[fi];i++){
+	GtkWidget *button=p->chbuttons[i];	
+	set_fg(button,&rgb);
+      }
+      ch+=channels[fi];
+    }
+    break;
+
+  default:
+    ch=0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=ch;i<ch+channels[fi];i++){
+	GdkColor rgb = chcolor(i);
+	GtkWidget *button=p->chbuttons[i];
+	set_fg(button,&rgb);
+      }
+      ch+=channels[fi];
+    }
+    break;
+  }
+
+}
+
+static void reschange(GtkWidget *widget,struct panel *p){
+  plot_res=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+}
+
+static void scalechange(GtkWidget *widget,struct panel *p){
+  plot_scale=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+}
+
+static void modechange(GtkWidget *widget,struct panel *p){
+  plot_mode=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  replot(p);
+  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+}
+
+static void linkchange(GtkWidget *widget,struct panel *p){
+  plot_link=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  replot(p);
+  plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+  chlabels(widget,p);
+}
+
+static void runchange(GtkWidget *widget,struct panel *p){
+  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);
+      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){
+  plot_hold=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+  replot(p);
+  plot_draw(PLOT(p->plot));
+}
+
+static void loopchange(GtkWidget *widget,struct panel *p){
+  acc_loop=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+}
+
+static void clearchange(GtkWidget *widget,struct panel *p){
+  acc_clear=1;
+  plot_clear(PLOT(p->plot));
+  if(!process_active){
+    rundata_clear();
+  }
+}
+
+static void rewindchange(GtkWidget *widget,struct panel *p){
+  acc_rewind=1;
+}
+
+extern char *version;
+void panel_create(struct panel *panel){
+  int i;
+
+  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 *mainbox=gtk_hbox_new(0,6);
+  GdkWindow *root=gdk_get_default_root_window();
+  GtkWidget *rightbox=gtk_vbox_new(0,0);
+  GtkWidget *leftbox=gtk_vbox_new(0,6);
+
+  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);
+
+  char versionmarkup[240];
+  snprintf(versionmarkup,240," <span size=\"large\" weight=\"bold\" "
+	   "style=\"italic\" foreground=\"dark blue\">"
+	   "Spectrum Analyzer</span>  <span size=\"small\" foreground=\"#606060\">"
+	   "revision %s</span> ",
+	   version);
+
+  /* the Fucking Fish */
+  for(i=0;i<19;i++)
+    panel->ff[i]=gdk_pixmap_create_from_xpm_d(root,
+					      panel->fb+i,NULL,ff_xpm[i]);
+  panel->twirlimage=gtk_image_new_from_pixmap(panel->ff[0],panel->fb[0]);
+
+  active = calloc(total_ch,sizeof(*active));
+
+  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_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);
+  gtk_table_attach_defaults(GTK_TABLE(topplace),
+			    topframe,0,1,0,1);
+    
+  gtk_container_add (GTK_CONTAINER (panel->toplevel), topplace);
+  gtk_container_set_border_width (GTK_CONTAINER (quitbutton), 3);
+
+  g_signal_connect (G_OBJECT (quitbutton), "clicked",
+		    G_CALLBACK (shutdown), NULL);
+  gtk_widget_add_accelerator (quitbutton, "activate", panel->group, GDK_q, 0, 0);
+
+  gtk_container_set_border_width (GTK_CONTAINER (topframe), 3);
+  gtk_container_set_border_width (GTK_CONTAINER (mainbox), 3);
+  gtk_frame_set_shadow_type(GTK_FRAME(topframe),GTK_SHADOW_ETCHED_IN);
+  gtk_frame_set_label_widget(GTK_FRAME(topframe),toplabel);
+  gtk_label_set_markup(GTK_LABEL(toplabel),versionmarkup);
+
+  gtk_container_add (GTK_CONTAINER(topframe), mainbox);
+
+  g_signal_connect (G_OBJECT (panel->toplevel), "delete_event",
+		    G_CALLBACK (shutdown), NULL);
+
+  /* add the spectrum plot box */
+  panel->plot=plot_new(blocksize/2+1,inputs,channels,rate);
+  gtk_box_pack_end(GTK_BOX(leftbox),panel->plot,1,1,0);
+  gtk_box_pack_start(GTK_BOX(mainbox),leftbox,1,1,0);
+  
+  /*fish */
+  {
+    GtkWidget *box=gtk_hbox_new(1,1);
+    GtkWidget *fishbox=gtk_hbox_new(0,0);
+    gtk_box_pack_end(GTK_BOX(fishbox),panel->twirlimage,0,0,0);
+    gtk_container_set_border_width (GTK_CONTAINER (fishbox), 3);
+
+    gtk_box_pack_start(GTK_BOX(box),fishbox,0,0,0);
+    gtk_box_pack_start(GTK_BOX(rightbox),box,0,0,0);
+  }
+
+  /* rate */
+  /* channels */
+  /* bits */
+  {
+    int fi;
+    int ch=0;
+    char buffer[160];
+    GtkWidget *label;
+    //GtkWidget *vbox=gtk_vbox_new(1,1);
+
+    GtkWidget *sep=gtk_hseparator_new();
+    gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
+
+    panel->chbuttons = calloc(total_ch,sizeof(*panel->chbuttons));
+    for(fi=0;fi<inputs;fi++){
+      
+      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(rightbox),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(rightbox),label,0,0,0);
+
+      for(i=ch;i<ch+channels[fi];i++){
+	GtkWidget *button=panel->chbuttons[i]=gtk_toggle_button_new();
+
+	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(rightbox),button,0,0,0);
+      }
+
+      GtkWidget *sep=gtk_hseparator_new();
+      gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
+      
+      ch+=channels[fi];
+
+    }
+    chlabels(NULL,panel);
+  }
+  
+  GtkWidget *bbox=gtk_vbox_new(0,0);
+
+  /* add the action buttons */
+  /* scale */
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"single-pixel","1/24th octave","1/12th octave","1/3 octave"};
+    for(i=0;i<4;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_res);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (reschange), panel);
+  }
+
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"log scale","ISO log scale","linear scale"};
+    for(i=0;i<3;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_scale);
+    plot_setting(PLOT(panel->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (scalechange), panel);
+  }
+  
+  /* depth */
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"10dB","20dB","45dB","90dB","140dB"};
+    for(i=0;i<5;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),3);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (depthchange), panel);
+  }
+  
+  /* mode */
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"realtime","maximum","accumulate"};
+    for(i=0;i<3;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_mode);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (modechange), panel);
+  }
+  
+  /* link */
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    for(i=0;i<LINKS;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), link_entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (linkchange), panel);
+  }
+  
+  {
+    GtkWidget *sep=gtk_hseparator_new();
+    gtk_box_pack_start(GTK_BOX(bbox),sep,0,0,4);
+  }
+  
+  /* 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_box_pack_start(GTK_BOX(bbox),button,0,0,0);
+    panel->run=button;
+  }
+  
+  /* hold */
+  /* loop */
+  {
+    GtkWidget *box=gtk_hbox_new(1,1);
+    GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_hold");
+    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_h, 0, 0);
+    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (holdchange), panel);
+    gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
+    
+    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_box_pack_start(GTK_BOX(box),button,1,1,0);
+    gtk_widget_set_sensitive(button,global_seekable);
+    
+    gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
+  }
+  
+  /* clear */
+  /* rewind */
+  {
+    GtkWidget *box=gtk_hbox_new(1,1);
+    GtkWidget *button=gtk_button_new_with_mnemonic("_clear");
+    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_c, 0, 0);
+    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (clearchange), panel);
+    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_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);
+  }
+  
+  /* dump */
+  {
+    GtkWidget *button=gtk_button_new_with_mnemonic("_dump data");
+    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_d, 0, 0);
+    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (dump), panel);
+    gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
+  }
+
+  /* noise floor */
+  {
+    GtkWidget *button=gtk_toggle_button_new_with_mnemonic("sample _noise floor");
+    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_n, 0, 0);
+    g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (noise), panel);
+    gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
+  }
+
+  gtk_box_pack_end(GTK_BOX(rightbox),bbox,0,0,0);
+  gtk_box_pack_start(GTK_BOX(mainbox),rightbox,0,0,0);
+
+    
+  gtk_widget_show_all(panel->toplevel);
+  //gtk_window_set_resizable(GTK_WINDOW(panel->toplevel),0);
+
+}
+
+static gboolean async_event_handle(GIOChannel *channel,
+				   GIOCondition condition,
+				   gpointer data){
+  struct panel *panel=data;
+  char buf[1];
+
+  /* read all pending */
+  while(read(eventpipe[0],buf,1)>0);
+
+  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);
+
+  /* update the spectral 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));
+
+    while (gtk_events_pending())
+      gtk_main_iteration();
+  }else
+    pthread_mutex_unlock(&feedback_mutex);
+
+  return TRUE;
+}
+
+static int look_for_gtkrc(char *filename){
+  FILE *f=fopen(filename,"r");
+  if(!f)return 0;
+  fprintf(stderr,"Loading spectrum-gtkrc file found at %s\n",filename);
+  gtk_rc_add_default_file(filename);
+  return 1;
+}
+
+void panel_go(int argc,char *argv[]){
+  char *homedir=getenv("HOME");
+  int found=0;
+  memset(&p,0,sizeof(p));
+
+  found|=look_for_gtkrc(ETCDIR"/spectrum-gtkrc");
+  {
+    char *rcdir=getenv("HOME");
+    if(rcdir){
+      char *rcfile="/.spectrum/spectrum-gtkrc";
+      char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
+      strcat(homerc,homedir);
+      strcat(homerc,rcfile);
+      found|=look_for_gtkrc(homerc);
+    }
+  }
+  {
+    char *rcdir=getenv("SPECTRUM_RCDIR");
+    if(rcdir){
+      char *rcfile="/spectrum-gtkrc";
+      char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
+      strcat(homerc,homedir);
+      strcat(homerc,rcfile);
+      found|=look_for_gtkrc(homerc);
+    }
+  }
+  found|=look_for_gtkrc("./spectrum-gtkrc");
+
+  if(!found){
+  
+    fprintf(stderr,"Could not find the spectrum-gtkrc configuration file normally\n"
+	    "installed in one of the following places:\n"
+
+	    "\t./spectrum-gtkrc\n"
+	    "\t$(SPECTRUM_RCDIR)/spectrum-gtkrc\n"
+	    "\t~/.spectrum/spectrum-gtkrc\n\t"
+	    ETCDIR"/spectrum-gtkrc\n"
+	    "This configuration file is used to tune the color, font and other detail aspects\n"
+	    "of the user interface.  Although the analyzer will work without it, the UI\n"
+	    "appearence will likely make the application harder to use due to missing visual\n"
+	    "cues.\n");
+  }
+
+  gtk_rc_add_default_file(ETCDIR"/spectrum-gtkrc");
+  if(homedir){
+    char *rcfile="/.spectrum-gtkrc";
+    char *homerc=calloc(1,strlen(homedir)+strlen(rcfile)+1);
+    strcat(homerc,homedir);
+    strcat(homerc,rcfile);
+    gtk_rc_add_default_file(homerc);
+  }
+  gtk_rc_add_default_file(".spectrum-gtkrc");
+  gtk_rc_add_default_file("spectrum-gtkrc");
+  gtk_init (&argc, &argv);
+
+  panel_create(&p);
+  animate_fish(&p);
+
+  /* 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_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_main ();
+
+}
+

Added: trunk/spectrum/spec_plot.c
===================================================================
--- trunk/spectrum/spec_plot.c	                        (rev 0)
+++ trunk/spectrum/spec_plot.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,1065 @@
+/*
+ *
+ *  gtk2 spectrum analyzer
+ *    
+ *      Copyright (C) 2004 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"
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include "spec_plot.h"
+
+static double log_lfreqs[6]={1.,10.,100.,1000.,10000.,100000};
+static double log_tfreqs[37]={5.,6.,7.,8.,9.,20.,30.,40.,50.,60.,70.,80.,90.
+			 ,200.,300.,400.,500.,600.,700.,800.,900.,
+			 2000.,3000.,4000.,5000.,6000.,7000.,8000.,9000.,
+			 20000.,30000,40000,50000,60000,70000,80000,90000};
+
+static double iso_lfreqs[12]={31.,63.,125.,250.,500.,1000.,2000.,4000.,8000.,
+                              16000.,32000., 64000.};
+static double iso_tfreqs[24]={25.,40.,50.,80.,100.,160.,200.,315.,400.,630.,
+                              800.,1250.,1600.,2500.,3150.,5000.,6300.,10000.,
+                              12500.,20000.,25000.,40000.,50000.,80000.};
+
+static GtkDrawingAreaClass *parent_class = NULL;
+
+static void compute_imp_scale(GtkWidget *widget){
+  Plot *p=PLOT(widget);
+  int height=widget->allocation.height-p->pady;
+  int i;
+  double lfreqs[9]={10000000.,1000000.,100000.,10000.,1000.,100.,10.,1.,.1};
+  double tfreqs[64]={9000000.,8000000.,7000000.,6000000.,
+		     5000000.,4000000.,3000000.,2000000.,
+		     900000.,800000.,700000.,600000.,
+		     500000.,400000.,300000.,200000.,
+		     90000.,80000.,70000.,60000.,
+		     50000.,40000.,30000.,20000.,
+		     9000.,8000.,7000.,6000.,
+		     5000.,4000.,3000.,2000.,
+		     900.,800.,700.,600.,
+		     500.,400.,300.,200.,
+		     90.,80.,70.,60.,
+		     50.,40.,30.,20,
+		     9.,8.,7.,6.,
+		     5.,4.,3.,2.,
+		     .9,.8,.7,.6,
+		     .5,.4,.3,.2};
+
+  for(i=0;i<9;i++)
+    p->ygrid[i]=rint( (log10(p->disp_ymax)-log10(lfreqs[i]))/(log10(p->disp_ymax)-log10(.1)) * (height-1));
+  for(i=0;i<64;i++)
+    p->ytic[i]=rint( (log10(p->disp_ymax)-log10(tfreqs[i]))/(log10(p->disp_ymax)-log10(.1)) * (height-1));
+  p->ygrids=9;
+  p->ytics=64;
+
+}
+
+static void compute_metadata(GtkWidget *widget){
+  Plot *p=PLOT(widget);
+  int width=widget->allocation.width-p->padx;
+  int rate=p->maxrate;
+  int nyq=p->maxrate/2.;
+  int i;
+
+  p->xgrids=0;
+  p->xtics=0;
+
+  /* find the places to plot the x grid lines according to scale */
+  switch(p->scale){
+  case 0: /* log */
+    {
+      for(i=0;i<6;i++){
+        if(log_lfreqs[i]<(nyq-.1))
+          p->xgrids=i+1;
+      }
+      for(i=0;i<37;i++){
+        if(log_tfreqs[i]<(nyq-.1))
+          p->xtics=i+1;
+      }
+
+      for(i=0;i<p->xgrids;i++)
+	p->xgrid[i]=rint( (log10(log_lfreqs[i])-log10(5.))/(log10(nyq)-log10(5.)) * (width-1))+p->padx;
+      for(i=0;i<p->xtics;i++)
+	p->xtic[i]=rint( (log10(log_tfreqs[i])-log10(5.))/(log10(nyq)-log10(5.)) * (width-1))+p->padx;
+    }
+
+    break;
+  case 1: /* ISO log */
+    {
+      for(i=0;i<12;i++){
+        if(iso_lfreqs[i]<(nyq-.1))
+          p->xgrids=i+1;
+      }
+      for(i=0;i<24;i++){
+        if(iso_tfreqs[i]<(nyq-.1))
+          p->xtics=i+1;
+      }
+
+      for(i=0;i<p->xgrids;i++)
+	p->xgrid[i]=rint( (log2(iso_lfreqs[i])-log2(25.))/(log2(nyq)-log2(25.)) * (width-1))+p->padx;
+      for(i=0;i<p->xtics;i++)
+	p->xtic[i]=rint( (log2(iso_tfreqs[i])-log2(25.))/(log2(nyq)-log2(25.)) * (width-1))+p->padx;
+    }
+
+    break;
+  case 2: /* linear spacing */
+    {
+      int j;
+      for(i=0;;i++){
+        if(i*p->lin_major >= nyq-.1 || i*p->lin_major>=100000-.1)
+          break;
+        p->xgrids=i+1;
+      }
+      for(i=0;;i++){
+        if(i*p->lin_minor >= nyq-.1 || i*p->lin_minor>=100000-.1)
+          break;
+        if(i%p->lin_mult!=0)
+          p->xtics++;
+      }
+
+      for(i=0;i<p->xgrids;i++){
+        double lfreq=i*p->lin_major;
+        p->xgrid[i]=rint(lfreq/nyq * (width-1))+p->padx;
+      }
+      j=0;
+      for(i=0;i<p->xtics;i++,j++){
+        double lfreq;
+        if(j%p->lin_mult==0)j++;
+        lfreq=j*p->lin_minor;
+        p->xtic[i]=rint(lfreq/nyq * (width-1))+p->padx;
+      }
+    }
+    break;
+  }
+}
+
+GdkColor chcolor(int ch){
+  GdkColor rgb={0,0,0,0};
+
+  switch(ch%7){
+  case 0:
+    rgb.red=0x4000;
+    rgb.green=0x4000;
+    rgb.blue=0x4000;
+    break;
+  case 1:
+    rgb.red=0xd000;
+    rgb.green=0x0000;
+    rgb.blue=0x0000;
+    break;
+  case 2:
+    rgb.red=0x0000;
+    rgb.green=0xb000;
+    rgb.blue=0x0000;
+    break;
+  case 3:
+    rgb.red=0x0000;
+    rgb.green=0x0000;
+    rgb.blue=0xf000;
+    break;
+  case 4:
+    rgb.red=0xc000;
+    rgb.green=0xc000;
+    rgb.blue=0x0000;
+    break;
+  case 5:
+    rgb.red=0x0000;
+    rgb.green=0xc000;
+    rgb.blue=0xc000;
+    break;
+  case 6:
+    rgb.red=0xc000;
+    rgb.green=0x0000;
+    rgb.blue=0xe000;
+    break;
+  }
+  
+  return rgb;
+}
+
+static void draw(GtkWidget *widget){
+  int i;
+  Plot *p=PLOT(widget);
+  int height=widget->allocation.height;
+  int width=widget->allocation.width;
+  GtkWidget *parent=gtk_widget_get_parent(widget);
+  int impedence = (p->link == LINK_IMPEDENCE_p1 ||
+		   p->link == LINK_IMPEDENCE_1 ||
+		   p->link == LINK_IMPEDENCE_10);
+  int phase = (p->link == LINK_PHASE);
+  int padx = p->padx;
+
+  if(phase){
+    /* are any of the phase channels actually active? */
+    int gi;
+    int ch=0;
+
+    phase = 0;
+    for(gi=0;gi<p->groups && !phase;gi++){
+      if(p->ch_active[ch+1]){
+	phase=1;
+	break;
+      }
+
+      ch+=p->ch[gi];
+    }
+  }
+
+  if(!p->drawgc){
+    p->drawgc=gdk_gc_new(p->backing);
+    gdk_gc_copy(p->drawgc,widget->style->black_gc);
+  }
+  if(!p->dashes){
+    p->dashes=gdk_gc_new(p->backing);
+    gdk_gc_copy(p->dashes, p->drawgc);
+    gdk_gc_set_line_attributes(p->dashes, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
+    gdk_gc_set_dashes(p->dashes,0,"\002\002",2);
+  }
+
+  /* clear the old rectangle out */
+  {
+    GdkGC *gc=parent->style->bg_gc[0];
+    gdk_draw_rectangle(p->backing,gc,1,0,0,padx,height);
+    gdk_draw_rectangle(p->backing,gc,1,0,height-p->pady,width,p->pady);
+
+    gc=parent->style->white_gc;
+    gdk_draw_rectangle(p->backing,gc,1,padx,0,width-padx,height-p->pady+1);
+  }
+
+  /* draw the noise floor if active */
+  if(p->floor){
+    GdkColor rgb = {0,0xd000,0xd000,0xd000};
+    gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+    
+    for(i=0;i<width-padx;i++){
+      float val=p->floor[i];
+      int y;
+
+      /* No noise floor is passed back for display in the modes where it's irrelevant */
+      y= rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-val));
+      if(y<height-p->pady)
+	gdk_draw_line(p->backing,p->drawgc,padx+i,y,padx+i,height-p->pady-1);
+    }
+  }
+
+  /* draw the light x grid */
+  {
+    int i;
+    GdkColor rgb={0,0,0,0};
+
+    rgb.red=0xc000;
+    rgb.green=0xff00;
+    rgb.blue=0xff00;
+    gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+    for(i=0;i<p->xtics;i++)
+      gdk_draw_line(p->backing,p->drawgc,p->xtic[i],0,p->xtic[i],height-p->pady);
+  }
+
+    /* draw the x labels */
+  {
+    PangoLayout **proper;
+    switch(p->scale){
+    case 0:
+      proper=p->log_layout;
+      break;
+    case 1:
+      proper=p->iso_layout;
+      break;
+    case 2:
+      proper=p->lin_layout;
+      break;
+    }
+    for(i=0;i<p->xgrids;i++){
+      int px,py;
+      pango_layout_get_pixel_size(proper[i],&px,&py);
+      
+      gdk_draw_layout (p->backing,
+		       widget->style->black_gc,
+		       p->xgrid[i]-(px/2), height-py+2,
+		       proper[i]);
+    }
+  }
+
+  /* draw the light y grid */
+  if(impedence){ /* impedence mode */
+
+    GdkColor rgb={0,0,0,0};
+    rgb.red=0xc000;
+    rgb.green=0xff00;
+    rgb.blue=0xff00;
+    gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+    compute_imp_scale(widget);
+
+    for(i=0;i<p->ytics;i++)
+      gdk_draw_line(p->backing,p->drawgc,padx,p->ytic[i],width,p->ytic[i]);
+
+  }else{
+    float del=(height-p->pady-1)/(float)p->disp_depth,off;
+    int i,half=0;
+    int max,mul;
+    GdkColor rgb={0,0,0,0};
+
+    {
+      if(del>16){
+	half=1;
+	max=303;
+	mul=1;
+	off=(p->disp_ymax-ceil(p->disp_ymax))*2;
+	del*=.5;
+      }else if(del>8){
+	max=151;
+	mul=1;
+	off=p->disp_ymax-ceil(p->disp_ymax);
+      }else if(del*2>8){
+	max=76;
+	mul=2;
+	off=p->disp_ymax-ceil(p->disp_ymax*.5)*2;
+      }else{
+	max=31;
+	mul=5;
+	off=p->disp_ymax-ceil(p->disp_ymax*.2)*5;
+      }
+
+      rgb.red=0xc000;
+      rgb.green=0xff00;
+      rgb.blue=0xff00;
+      gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+      gdk_gc_set_rgb_fg_color(p->dashes,&rgb);
+
+      for(i=0;i<max;i+=2){
+	int ymid=rint(del * (i * mul + off));
+	if(ymid>=0 && ymid<height-p->pady)
+	  gdk_draw_line(p->backing,p->drawgc,padx,ymid,width,ymid);
+      }
+
+      for(i=1;i<max;i+=2){
+	int ymid=rint(del * (i * mul + off));
+	if(ymid>=0 && ymid<height-p->pady)
+	  gdk_draw_line(p->backing,(half?p->dashes:p->drawgc),padx,ymid,width,ymid);
+      }
+    }
+  }
+
+  /* dark x grid */
+  {
+    int i;
+    GdkColor rgb={0,0,0,0};
+
+    rgb.red=0x0000;
+    rgb.green=0xc000;
+    rgb.blue=0xc000;
+    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);
+  }
+
+  /* dark y grid */
+  if(impedence){
+    GdkColor rgb={0,0,0,0};
+    rgb.red=0x0000;
+    rgb.green=0xc000;
+    rgb.blue=0xc000;
+
+    gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+    for(i=0;i<p->ygrids;i++){
+      int px,py;
+      pango_layout_get_pixel_size(p->imp_layout[i],&px,&py);
+
+      gdk_draw_layout (p->backing,
+		       widget->style->black_gc,
+		       padx-px-2, p->ygrid[i]-py/2,
+		       p->imp_layout[i]);
+
+      gdk_draw_line(p->backing,p->drawgc,padx,p->ygrid[i],width,p->ygrid[i]);
+    }
+    
+  }else{
+    GdkColor rgb={0,0,0,0};
+    int label=ceil(p->disp_ymax/5+28),i;
+    float del=(height-p->pady-1)/(float)p->disp_depth,step;
+    float off=p->disp_ymax-ceil(p->disp_ymax*.2)*5;
+    step=2;
+    if(del>8)step=1;
+
+    for(i=0;i<32;i++){
+      if(((label-i)&1)==0 || step==1){
+	int ymid=rint(del * (i*5+off));
+	int px,py;
+
+	if(((label-i)&1)==0){
+	  rgb.red=0x0000;
+	  rgb.green=0xc000;
+	  rgb.blue=0xc000;
+	}else{
+	  rgb.red=0xa000;
+	  rgb.green=0xe000;
+	  rgb.blue=0xe000;
+	}
+	gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+	if(label-i>=0 && label-i<57 && ymid>=0 && ymid<height-p->pady){
+	  pango_layout_get_pixel_size(p->db_layout[label-i],&px,&py);
+	  
+	  gdk_draw_layout (p->backing,
+			   widget->style->black_gc,
+			   padx-px-2, ymid-py/2,
+			   p->db_layout[label-i]);
+	  gdk_draw_line(p->backing,p->drawgc,padx,ymid,width,ymid);
+	}
+      }
+    }
+  }
+
+  /* phase?  draw in phase and tics on right axis */
+  if(phase){
+    GdkColor rgb={0,0xd000,0x0000,0x0000};
+    float depth = p->disp_pmax-p->disp_pmin;
+    int label=ceil(p->disp_pmax/10+18),i;
+    float del=(height-p->pady-1)/depth,step;
+    float off=p->disp_pmax-ceil(p->disp_pmax*.1)*10;
+    step=2;
+    if(del>8)step=1;
+    
+    gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+    for(i=0;i<37;i++){
+      if(((label-i)&1)==0 || step==1){
+	int ymid=rint(del * (i*10+off));
+	int px,py;
+
+	if(label-i>=0 && label-i<37 && ymid>=0 && ymid<height-p->pady){
+	  pango_layout_get_pixel_size(p->phase_layout[label-i],&px,&py);
+	  
+	  gdk_draw_layout (p->backing,p->drawgc,
+			   width-p->phax, ymid-py/2,
+			   p->phase_layout[label-i]);
+	}
+      }
+    }
+
+    if(del>10){
+      for(i=0;;i++){
+	int ymid=rint(del * (i+off));
+	if(ymid>=height-p->pady)break;
+	if(ymid>=0)
+	  gdk_draw_line(p->backing,p->drawgc,width-p->phax-(i%5==0?15:10),ymid,width-p->phax-(i%5==0?5:7),ymid);
+      }
+    }else if(del>5){
+      for(i=0;;i++){
+	int ymid=rint(del * (i*2+off));
+	if(ymid>=height-p->pady)break;
+	if(ymid>=0)
+	  gdk_draw_line(p->backing,p->drawgc,width-p->phax-12,ymid,width-p->phax-7,ymid);
+      }
+    } else if(del>2){
+      for(i=0;;i++){
+	int ymid=rint(del * (i*5+off));
+	if(ymid>=height-p->pady)break;
+	if(ymid>=0)
+	  gdk_draw_line(p->backing,p->drawgc,width-p->phax-15,ymid,width-p->phax-5,ymid);
+      }
+    }
+    
+    for(i=0;;i++){
+      int ymid=rint(del * (i*10+off));
+      if(ymid>=height-p->pady)break;
+      if(ymid>=0){
+	gdk_draw_line(p->backing,p->drawgc,width-p->phax-5,ymid-1,width-p->phax-2,ymid-1);
+	gdk_draw_line(p->backing,p->drawgc,width-p->phax-25,ymid,width-p->phax-2,ymid);
+	gdk_draw_line(p->backing,p->drawgc,width-p->phax-5,ymid+1,width-p->phax-2,ymid+1);
+      }
+    }
+
+  }
+  
+
+  {
+    GdkGCValues values;
+    //gdk_gc_get_values(p->drawgc,&values);
+    values.line_width=2;
+    gdk_gc_set_values(p->drawgc,&values,GDK_GC_LINE_WIDTH);
+  }
+
+  /* draw actual data */
+  if(p->ydata){
+    int cho=0;
+    int gi;
+    for(gi=0;gi<p->groups;gi++){
+      int ch;
+      GdkColor rgb;
+      
+      for(ch=cho;ch<cho+p->ch[gi];ch++){
+	if(p->ch_active[ch]){
+	  int prev;
+	  int first=0;
+	  float yprev=NAN;
+
+	  rgb = chcolor(ch);
+	  gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+	  
+	  for(i=0;i<width-padx;i++){
+	    float val=p->ydata[ch][i];
+	    int y;
+	    
+	    if(isnan(yprev) || isnan(val)){
+	      yprev = val;
+	    }else{
+	      yprev = val;
+
+	      if(impedence){ /* log scale for impedence */
+		y =rint( (log10(p->disp_ymax)-log10(val))/(log10(p->disp_ymax)-log10(.1)) * 
+			 (height-p->pady-1));
+	      }else if(phase && ch==cho+1){
+		y= rint((height-p->pady-1)/(p->disp_pmax-p->disp_pmin)*(p->disp_pmax-val));
+	      }else{
+		y= rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-val));
+	      }
+	    
+	      if(first && (y<height-p->pady || prev<height-p->pady)){
+		int ly = y;
+		int lp = prev;
+		
+		if(ly>=height-p->pady)ly=height-p->pady;
+		if(lp>=height-p->pady)lp=height-p->pady;
+		
+		gdk_draw_line(p->backing,p->drawgc,padx+i-1,lp,padx+i,ly);
+		
+		ly++;
+		lp++;
+		
+		if(ly>=height-p->pady)ly=height-p->pady;
+		if(lp>=height-p->pady)lp=height-p->pady;
+		
+		gdk_draw_line(p->backing,p->drawgc,padx+i-1,lp,padx+i,ly);
+	      }
+	      first=1;
+	      prev=y;
+	    }
+	    
+	   
+	  }
+	}
+      }
+      cho+=p->ch[gi];
+    }
+  }
+
+  {
+    GdkGCValues values;
+    //gdk_gc_get_values(p->drawgc,&values);
+    values.line_width=1;
+    gdk_gc_set_values(p->drawgc,&values,GDK_GC_LINE_WIDTH);
+  }
+
+
+}
+
+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);
+}
+
+static gboolean expose( GtkWidget *widget, GdkEventExpose *event ){
+  Plot *p=PLOT(widget);
+  gdk_draw_drawable(widget->window,
+		    widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+		    p->backing,
+		    event->area.x, event->area.y,
+		    event->area.x, event->area.y,
+		    event->area.width, event->area.height);
+  
+  return FALSE;
+}
+
+static void size_request (GtkWidget *widget,GtkRequisition *requisition){
+  Plot *p=PLOT(widget);
+  requisition->width = 400;
+  requisition->height = 400;
+  int axisy=0,axisx=0,pady=0,padx=0,phax=0,px,py,i;
+
+  /* find max lin layout */
+  {
+    int max=0;
+    int maxy=0;
+    for(i=0;p->lin_layout[i];i++){
+      if(p->lin_major*i >= p->maxrate/2-.1)break;
+      pango_layout_get_pixel_size(p->lin_layout[i],&px,&py);
+      if(px>max)max=px;
+      if(py>pady)pady=py;
+      if(py>maxy)maxy=py;
+    }
+    max+=maxy*1.5;
+    max*=i+1;
+    if(axisx<max)axisx=max;
+  }
+  /* find max log layout */
+  {
+    int max=0;
+    int maxy=0;
+    for(i=0;p->log_layout[i];i++){
+      if(log_lfreqs[i] >= p->maxrate/2-.1)break;
+      pango_layout_get_pixel_size(p->log_layout[i],&px,&py);
+      if(px>max)max=px;
+      if(py>pady)pady=py;
+      if(py>maxy)maxy=py;
+    }
+    max+=maxy*1.5;
+    max*=i+1;
+    if(axisx<max)axisx=max;
+  }
+  /* find max iso layout */
+  {
+    int max=0;
+    int maxy=0;
+    for(i=0;p->iso_layout[i];i++){
+      if(iso_lfreqs[i] >= p->maxrate/2-.1)break;
+      pango_layout_get_pixel_size(p->iso_layout[i],&px,&py);
+      if(px>max)max=px;
+      if(py>pady)pady=py;
+      if(py>maxy)maxy=py;
+    }
+    max+=maxy*1.5;
+    max*=i+1;
+    if(axisx<max)axisx=max;
+  }
+  /* find max db layout */
+  {
+    int max=0;
+    for(i=0;p->db_layout[i];i++){
+      pango_layout_get_pixel_size(p->db_layout[i],&px,&py);
+      if(py>max)max=py;
+      if(px>padx)padx=px;
+    }
+    axisy=(max)*8;
+    if(axisy<max)axisy=max;
+  }
+  /* find max imped layout */
+  {
+    int max=0;
+    for(i=0;p->imp_layout[i];i++){
+      pango_layout_get_pixel_size(p->imp_layout[i],&px,&py);
+      if(py>max)max=py;
+      if(px>padx)padx=px;
+    }
+    axisy=(max)*8;
+    if(axisy<max)axisy=max;
+  }
+  /* find max phase layout */
+  {
+    int max=0;
+    for(i=0;p->phase_layout[i];i++){
+      pango_layout_get_pixel_size(p->phase_layout[i],&px,&py);
+      if(py>max)max=py;
+      if(px>phax)phax=px;
+    }
+    axisy=(max)*8;
+    if(axisy<max)axisy=max;
+  }
+  
+  if(requisition->width<axisx+padx)requisition->width=axisx+padx;
+  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);
+  
+  p->backing = gdk_pixmap_new(widget->window,
+			      widget->allocation.width,
+			      widget->allocation.height,
+			      -1);
+
+  p->ydata=NULL;
+  p->configured=1;
+  
+  compute_metadata(widget);
+  plot_refresh(p,NULL);
+  draw_and_expose(widget);
+  
+  return TRUE;
+}
+
+static void plot_class_init (PlotClass *class){
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  parent_class = g_type_class_peek_parent (class);
+
+  widget_class->expose_event = expose;
+  widget_class->configure_event = configure;
+  widget_class->size_request = size_request;
+}
+
+static void plot_init (Plot *p){
+  p->mode=0;
+  p->scale=0;
+  p->depth=45.;
+}
+
+GType plot_get_type (void){
+  static GType m_type = 0;
+  if (!m_type){
+    static const GTypeInfo m_info={
+      sizeof (PlotClass),
+      NULL, /* base_init */
+      NULL, /* base_finalize */
+      (GClassInitFunc) plot_class_init,
+      NULL, /* class_finalize */
+      NULL, /* class_data */
+      sizeof (Plot),
+      0,
+      (GInstanceInitFunc) plot_init,
+      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 *ret= GTK_WIDGET (g_object_new (plot_get_type (), NULL));
+  Plot *p=PLOT(ret);
+  int g,i;
+  int ch=0;
+  int maxrate=-1;
+  p->groups = groups;
+  for(g=0;g<groups;g++){
+    ch+=channels[g];
+    if(rate[g]>maxrate)maxrate=rate[g];
+  }
+
+  p->total_ch = ch;
+
+  p->ch=channels;
+  p->rate=rate;
+  p->maxrate=maxrate;
+
+  if(maxrate > 100000){
+    p->lin_major = 10000.;
+    p->lin_minor = 2000.;
+    p->lin_mult = 5;
+  }else if(maxrate > 50000){
+    p->lin_major = 5000.;
+    p->lin_minor = 1000.;
+    p->lin_mult = 5;
+  }else{
+    p->lin_major=2000.;
+    p->lin_minor=500.;
+    p->lin_mult=4;
+  }
+
+  /* generate all the text layouts we'll need */
+  /* linear X scale */
+  {
+    if(maxrate>100000){
+      char *labels[11]={"DC","10kHz","20kHz","30kHz","40kHz","50kHz","60kHz",
+                        "70kHz","80kHz","90kHz",""};
+      p->lin_layout=calloc(12,sizeof(*p->lin_layout));
+      for(i=0;i<11;i++)
+        p->lin_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+    }else if(maxrate > 50000){
+      char *labels[11]={"DC","5kHz","10kHz","15kHz","20kHz","25kHz","30kHz",
+                        "35kHz","40kHz","45kHz",""};
+      p->lin_layout=calloc(12,sizeof(*p->lin_layout));
+      for(i=0;i<11;i++)
+        p->lin_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+    }else{
+      char *labels[14]={"DC","2kHz","4kHz","6kHz","8kHz","10kHz","12kHz",
+                        "14kHz","16kHz","18kHz","20kHz","22kHz","24kHz",""};
+      p->lin_layout=calloc(15,sizeof(*p->lin_layout));
+      for(i=0;i<14;i++)
+        p->lin_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+    }
+  }
+  /* phase Y scale */
+  {
+    char *labels[37]={"-180\xC2\xB0","-170\xC2\xB0","-160\xC2\xB0",
+		      "-150\xC2\xB0","-140\xC2\xB0","-130\xC2\xB0",
+		      "-120\xC2\xB0","-110\xC2\xB0","-100\xC2\xB0",
+		      "-90\xC2\xB0","-80\xC2\xB0","-70\xC2\xB0",
+		      "-60\xC2\xB0","-50\xC2\xB0","-40\xC2\xB0",
+		      "-30\xC2\xB0","-20\xC2\xB0","-10\xC2\xB0",
+		      "-0\xC2\xB0","+10\xC2\xB0","+20\xC2\xB0",
+		      "+30\xC2\xB0","+40\xC2\xB0","+50\xC2\xB0",
+		      "+60\xC2\xB0","+70\xC2\xB0","+80\xC2\xB0",
+		      "+90\xC2\xB0","+100\xC2\xB0","+110\xC2\xB0",
+		      "+120\xC2\xB0","+130\xC2\xB0","+140\xC2\xB0",
+		      "+150\xC2\xB0","+160\xC2\xB0","+170\xC2\xB0",
+		      "+180\xC2\xB0"};
+    p->phase_layout=calloc(38,sizeof(*p->phase_layout));
+    for(i=0;i<37;i++)
+      p->phase_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+  }
+  /* log X scale */
+  {
+    char *labels[6]={"1Hz","10Hz","100Hz","1kHz","10kHz",""};
+    p->log_layout=calloc(7,sizeof(*p->log_layout));
+    for(i=0;i<6;i++)
+      p->log_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+  }
+  /* Impedence Y scale */
+  {
+    char *labels[9]={"10M\xCE\xA9","1M\xCE\xA9","100k\xCE\xA9","10k\xCE\xA9",
+		     "1k\xCE\xA9","100\xCE\xA9","10\xCE\xA9","1\xCE\xA9",".1\xCE\xA9"};
+    p->imp_layout=calloc(10,sizeof(*p->imp_layout));
+    for(i=0;i<9;i++)
+      p->imp_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+  }
+  /* ISO log X scale */
+  {
+    char *labels[12]={"31Hz","63Hz","125Hz","250Hz","500Hz","1kHz","2kHz",
+		      "4kHz","8kHz","16kHz","32kHz","64kHz"};
+    p->iso_layout=calloc(13,sizeof(*p->iso_layout));
+    for(i=0;i<12;i++)
+      p->iso_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+  }
+  /* dB Y scale */
+  {
+    char *labels[57]={"-140dB","-135dB","-130dB","-125dB","-120dB","-115dB",
+		      "-110dB","-105dB","-100dB","-95dB","-90dB","-85dB",
+		      "-80dB","-75dB","-70dB","-65dB","-60dB","-55dB","-50dB",
+		      "-45dB","-40dB","-35dB","-30dB","-25dB","-20dB",
+		      "-15dB","-10dB","-5dB","0dB","+5dB","+10dB","+15dB",
+		      "+20dB","+25dB","+30dB","+35dB","+40dB","+45dB","+50dB",
+		      "+55dB","+60dB","+65dB","+70dB","+75dB","+80dB","+85dB",
+		      "+90dB","+95dB","+100dB","+105dB","+110dB","+115dB",
+		      "+120dB","+125dB","+130dB","+135dB","+140dB"};
+    p->db_layout=calloc(58,sizeof(*p->db_layout));
+    for(i=0;i<57;i++)
+      p->db_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+  }
+
+  p->ch_active=calloc(ch,sizeof(*p->ch_active));
+  p->ch_process=calloc(ch,sizeof(*p->ch_process));
+
+  plot_clear(p);
+  return ret;
+}
+
+void plot_refresh (Plot *p, int *process){
+  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;
+
+  if(process)
+    memcpy(p->ch_process,process,p->total_ch*sizeof(*process));
+  
+  data = process_fetch(p->res, p->scale, p->mode, p->link, 
+		       p->ch_process,width,&ymax,&pmax,&pmin,&floor,p->noise);
+  
+  p->ydata=data;
+  if(floor)
+    p->floor=floor;
+  else
+    p->floor=NULL;
+
+  /* graph limit updates are conditional depending on mode/link */
+  pmax+=5;
+  pmin-=5;
+  if(pmax<5)pmax=5;
+  if(pmin>-30)pmin=-30;
+
+  switch(p->link){
+  case LINK_INDEPENDENT:
+  case LINK_SUMMED:
+  case LINK_PHASE:
+  case LINK_THD:
+  case LINK_THDN:
+    {
+      float dBpp = p->depth/height;
+      ymax += dBpp*10;
+    }
+    break;
+  case LINK_IMPEDENCE_p1:
+  case LINK_IMPEDENCE_1:
+  case LINK_IMPEDENCE_10:
+    if(ymax<12)
+      ymax=12;
+    else
+      ymax *=1.8;
+    break;
+  }
+
+  if(p->mode == 0){
+    /* "Instantaneous' mode scale regression is conditional and
+       damped. Start the timer/run the timer while any one scale measure
+       should be dropping by more than 50px. If any peaks occur above,
+       reset timer.  Once timer runs out, drop 5px per frame */
+#define PXTHRESH 25
+#define PXDEL 10.
+#define TIMERFRAMES 20
+    if(p->ymax>ymax){
+      float oldzero = (height-1)/p->depth*p->ymax;
+      float newzero = (height-1)/p->depth*ymax;
+      
+      if(newzero+PXTHRESH<oldzero){
+	if(p->ymaxtimer){
+	  p->ymaxtimer--;
+	}else{
+	  p->ymax = (oldzero-PXDEL)*p->depth/(height-1);
+	}
+      }else{
+	p->ymaxtimer = TIMERFRAMES;
+      }
+    }else
+      p->ymaxtimer = TIMERFRAMES;
+    
+    if(p->pmax>pmax || p->pmin<pmin){
+      float newmax = (height-1)/(p->pmax-p->pmin)*(p->pmax-pmax);
+      float newmin = (height-1)/(p->pmax-p->pmin)*(pmin-p->pmin);
+      
+      if(newmax>PXTHRESH || newmin>PXTHRESH){
+	if(p->phtimer){
+	  p->phtimer--;
+	}else{
+	  if(newmax>PXTHRESH)
+	    p->pmax -= PXDEL/(height-1)*(p->pmax-p->pmin);
+	  if(newmin>PXTHRESH)
+	    p->pmin += PXDEL/(height-1)*(p->pmax-p->pmin);
+	}
+      }else{
+	p->phtimer = TIMERFRAMES;
+      }
+    }else
+      p->phtimer = TIMERFRAMES;
+  }
+    
+  if(ymax<p->depth-140.)ymax=p->depth-140.;
+  if(ymax>140.)ymax=140.;
+  if(pmax>180)pmax=180;
+  if(pmin<-180)pmin=-180;  
+
+  if(p->mode == 0){
+    if(ymax>p->ymax)p->ymax=ymax;
+    if(pmax>p->pmax)p->pmax=pmax;
+    if(pmin<p->pmin)p->pmin=pmin;
+  }else{
+    p->ymax=ymax;
+    p->pmax=pmax;
+    p->pmin=pmin;
+  }
+
+  p->disp_depth = p->depth;
+  p->disp_ymax = p->ymax;
+  p->disp_pmax = p->pmax;
+  p->disp_pmin = p->pmin;
+
+  /* finally, align phase/response zeros on phase graphs */
+  if(p->disp_ymax>-140){
+    if(p->link == LINK_PHASE){
+      /* In a phase/response graph, 0dB/0degrees are bound and always on-screen. */
+      float mzero = (height-1)/p->disp_depth*p->disp_ymax;
+      float pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
+
+      if(mzero<pzero){
+	/* straightforward; move the dB range down */
+	p->disp_ymax = pzero*p->disp_depth/(height-1);
+      }else{
+	/* a little harder as phase has a min and a max.
+	   First increase the pmax to match the dB zero. */
+	p->disp_pmax = p->disp_pmin/(1-(height-1)/mzero);
+	pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
+
+	/* That worked, but might have run p->max overrange */
+	if(p->disp_pmax>180.){
+	  /* only way to reconcile this one is to increase the pdepth */
+	  p->disp_pmax = 180.;
+	  pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
+	  p->disp_depth = (height-1)/pzero*p->disp_ymax;
+	}
+      }
+    }
+  }
+}
+
+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<width;j++)
+	p->ydata[i][j]=NAN;
+  p->ymax=p->depth-140;
+  p->pmax=0;
+  p->pmin=0;
+  draw_and_expose(widget);
+}
+
+float **plot_get (Plot *p){
+  return(p->ydata);
+}
+
+void plot_setting (Plot *p, int res, int scale, int mode, int link, int depth, int noise){
+  GtkWidget *widget=GTK_WIDGET(p);
+  p->res=res;
+  p->scale=scale;
+  p->mode=mode;
+  p->depth=depth;
+  p->link=link;
+  p->noise=noise;
+
+  p->ymax=-140;
+  p->pmax=0;
+  p->pmin=0;
+
+  compute_metadata(widget);
+  plot_refresh(p,NULL);
+  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, int *b){
+  GtkWidget *widget=GTK_WIDGET(p);
+  memcpy(p->ch_active,a,p->total_ch*sizeof(*a));
+  memcpy(p->ch_process,b,p->total_ch*sizeof(*b));
+  plot_refresh(p,NULL);
+  draw_and_expose(widget);
+}
+

Copied: trunk/spectrum/spec_plot.h (from rev 18227, trunk/spectrum/plot.h)
===================================================================
--- trunk/spectrum/spec_plot.h	                        (rev 0)
+++ trunk/spectrum/spec_plot.h	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,132 @@
+/*
+ *
+ *  gtk2 spectrum analyzer
+ *    
+ *      Copyright (C) 2004 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__
+#define __PLOT_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkdrawingarea.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;
+
+struct _Plot{
+
+  GtkDrawingArea canvas;  
+  GdkPixmap *backing;
+  GdkGC     *drawgc;
+  GdkGC     *dashes;
+
+  PangoLayout **lin_layout;
+  PangoLayout **log_layout;
+  PangoLayout **iso_layout;
+  PangoLayout **db_layout;
+  PangoLayout **imp_layout;
+  PangoLayout **phase_layout;
+
+  int configured;
+  float **ydata;
+  float *floor;
+
+  int groups;
+  int *ch;
+  int *ch_active;
+  int *ch_process;
+  int total_ch;
+  int datasize;
+  int mode;
+  int link;
+  int scale;
+  int res;
+  int noise;
+  int *rate;
+  int maxrate;
+
+  float lin_major;
+  float lin_minor;
+  int lin_mult;
+
+  int xgrid[20];
+  int xgrids;
+  int xtic[200];
+  int xtics;
+
+  int ygrid[11];
+  int ygrids;
+  int ytic[200];
+  int ytics;
+
+  float depth;
+  float ymax;
+  float pmax;
+  float pmin;
+
+  float disp_depth;
+  float disp_ymax;
+  float disp_pmax;
+  float disp_pmin;
+
+  float padx;
+  float phax;
+  float pady;
+
+  int ymaxtimer;
+  int phtimer;
+};
+
+struct _PlotClass{
+
+  GtkDrawingAreaClass parent_class;
+  void (* plot) (Plot *m);
+};
+
+GType          plot_get_type        (void);
+GtkWidget*     plot_new             (int n, int inputs, int *channels, int *rate);
+void	       plot_refresh         (Plot *m, int *process);
+void	       plot_setting         (Plot *m, int res, int scale, int mode, int link, int depth, int noise);
+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 *, int *);
+
+GdkColor chcolor(int ch);
+
+G_END_DECLS
+
+#endif
+
+
+
+

Copied: trunk/spectrum/spec_process.c (from rev 18228, trunk/spectrum/process.c)
===================================================================
--- trunk/spectrum/spec_process.c	                        (rev 0)
+++ trunk/spectrum/spec_process.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,914 @@
+/*
+ *
+ *  gtk2 spectrum analyzer
+ *    
+ *      Copyright (C) 2004 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"
+#include "io.h"
+
+static float *window=NULL;
+static float *freqbuffer=0;
+static fftwf_plan plan;
+
+pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+int feedback_increment=0;
+
+float *feedback_count;
+float **plot_data;
+float *plot_floor=NULL;
+float **work_floor=NULL;
+float *process_work;
+
+float **feedback_acc;
+float **feedback_max;
+float **feedback_instant;
+
+/* Gentlemen, power up the Variance hammer */
+float **floor_y;
+float **floor_yy;
+int floor_count;
+
+float **ph_acc;
+float **ph_max;
+float **ph_instant;
+
+float **xmappingL;
+float **xmappingH;
+int metascale = -1;
+int metawidth = -1;
+int metares = -1;
+int metanoise = 0;
+
+sig_atomic_t acc_clear=0;
+sig_atomic_t acc_rewind=0;
+sig_atomic_t acc_loop=0;
+
+sig_atomic_t process_active=0;
+sig_atomic_t process_exit=0;
+
+static void init_process(void){
+  int i;
+  if(window==NULL){
+    process_work=calloc(blocksize+2,sizeof(*process_work));
+    feedback_count=calloc(total_ch,sizeof(*feedback_count));
+    plot_data=calloc(total_ch,sizeof(*plot_data));
+
+    feedback_acc=malloc(total_ch*sizeof(*feedback_acc));
+    feedback_max=malloc(total_ch*sizeof(*feedback_max));
+    feedback_instant=malloc(total_ch*sizeof(*feedback_instant));
+    floor_y=malloc(total_ch*sizeof(*floor_y));
+    floor_yy=malloc(total_ch*sizeof(*floor_yy));
+
+    ph_acc=malloc(total_ch*sizeof(*ph_acc));
+    ph_max=malloc(total_ch*sizeof(*ph_max));
+    ph_instant=malloc(total_ch*sizeof(*ph_instant));
+
+    freqbuffer=fftwf_malloc((blocksize+2)*sizeof(*freqbuffer));
+    for(i=0;i<total_ch;i++){
+
+      floor_y[i]=calloc(blocksize/2+1,sizeof(**floor_y));
+      floor_yy[i]=calloc(blocksize/2+1,sizeof(**floor_yy));
+      feedback_acc[i]=calloc(blocksize/2+1,sizeof(**feedback_acc));
+      feedback_max[i]=calloc(blocksize/2+1,sizeof(**feedback_max));
+      feedback_instant[i]=calloc(blocksize/2+1,sizeof(**feedback_instant));
+
+      ph_acc[i]=calloc(blocksize+2,sizeof(**ph_acc));
+      ph_max[i]=calloc(blocksize+2,sizeof(**ph_max));
+      ph_instant[i]=calloc(blocksize+2,sizeof(**ph_instant));
+    }
+
+    plan=fftwf_plan_dft_r2c_1d(blocksize,freqbuffer,
+                               (fftwf_complex *)freqbuffer,
+                               FFTW_ESTIMATE);
+
+    /* construct proper window (sin^4 I'd think) */
+    window = calloc(blocksize,sizeof(*window));
+    for(i=0;i<blocksize;i++)window[i]=sin(M_PIl*i/blocksize);
+    for(i=0;i<blocksize;i++)window[i]*=window[i];
+    for(i=0;i<blocksize;i++)window[i]=sin(window[i]*M_PIl*.5);
+    for(i=0;i<blocksize;i++)window[i]*=window[i]/(blocksize/4)*.778;
+  }
+}
+
+void rundata_clear(){
+  int i,j;
+  for(i=0;i<total_ch;i++){
+    feedback_count[i]=0;
+    memset(feedback_acc[i],0,(blocksize/2+1)*sizeof(**feedback_acc));
+    memset(feedback_max[i],0,(blocksize/2+1)*sizeof(**feedback_max));
+    memset(feedback_instant[i],0,(blocksize/2+1)*sizeof(**feedback_instant));
+
+    for(j=0;j<blocksize+2;j++){
+      ph_acc[i][j]=0;
+      ph_max[i][j]=0;
+      ph_instant[i][j]=0;
+    }
+  }
+  acc_clear=0;
+}
+
+extern int plot_noise;
+
+/* return 0 on EOF, 1 otherwise */
+static int process(){
+  int fi,i,j,ch;
+  int eof_all;
+  int noise=plot_noise;  
+
+  if(acc_rewind)
+    rewind_files();
+  acc_rewind=0;
+
+  if(input_read(acc_loop,0))
+    return 0;
+
+  if(acc_clear)
+    rundata_clear();
+
+  /* by channel */
+  ch=0;
+  for(fi=0;fi<inputs;fi++){
+    if(blockbufferfill[fi]){
+      for(i=ch;i<ch+channels[fi];i++){
+	
+	float *data=blockbuffer[i];
+
+	/* window the blockbuffer into the FFT buffer */
+	for(j=0;j<blocksize;j++){
+	  freqbuffer[j]=data[j]*window[j];
+	}
+	
+	/* transform */
+	fftwf_execute(plan);
+	
+	pthread_mutex_lock(&feedback_mutex);
+
+	/* perform desired accumulations */
+	for(j=0;j<blocksize+2;j+=2){
+	  float R = freqbuffer[j];
+	  float I = freqbuffer[j+1];
+	  float sqR = R*R;
+	  float sqI = I*I;
+	  float sqM = sqR+sqI;
+
+	  if(noise==1){
+	    floor_yy[i][j>>1]+=sqM*sqM;
+	    floor_y[i][j>>1]+=sqM;
+	  }
+	  
+	  /* deal with phase accumulate/rotate */
+	  if(i==ch){
+	    /* normalize/store ref for later rotation */
+	    process_work[j] = R;
+	    process_work[j+1] = -I;
+
+	  }else{
+	    /* rotate signed square phase according to ref for phase calculation */
+	    float pR;
+	    float pI;
+	    float rR = process_work[j];
+	    float rI = process_work[j+1];
+	    pR = (rR*R - rI*I);
+	    pI = (rR*I + rI*R);
+
+	    ph_instant[i][j]=pR;
+	    ph_instant[i][j+1]=pI;
+
+	    ph_acc[i][j]+=pR;
+	    ph_acc[i][j+1]+=pI;
+	    
+	    if(feedback_max[i][j>>1]<sqM){
+	      ph_max[i][j]=pR;
+	      ph_max[i][j+1]=pI;
+	    }
+	  }
+	  
+	  feedback_instant[i][j>>1]=sqM;
+	  feedback_acc[i][j>>1]+=sqM;
+	  
+	  if(feedback_max[i][j>>1]<sqM)
+	    feedback_max[i][j>>1]=sqM;
+	  
+	}
+	feedback_count[i]++;
+	
+	pthread_mutex_unlock(&feedback_mutex);
+      }
+    }
+    ch+=channels[fi];
+  }
+  if(noise==1)
+    floor_count++;
+  feedback_increment++;
+  write(eventpipe[1],"",1);
+  return 1;
+}
+
+void *process_thread(void *dummy){
+  while(!process_exit && process());
+  process_active=0;
+  write(eventpipe[1],"",1);
+  return NULL;
+}
+
+void process_dump(int mode){
+  int fi,i,j,ch;
+  FILE *out;
+
+  {   
+    out=fopen("accumulate.m","w");
+    ch = 0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=0;i<blocksize/2+1;i++){
+	fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
+	
+	for(j=ch;j<ch+channels[fi];j++)
+	  fprintf(out,"%f ",todB(feedback_acc[j][i])*.5);
+	fprintf(out,"\n");
+      }
+      fprintf(out,"\n");
+      ch+=channels[fi];
+    }
+    fclose(out);
+  }
+
+  {   
+    out=fopen("max.m","w");
+    ch = 0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=0;i<blocksize/2+1;i++){
+	fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
+	
+	for(j=ch;j<ch+channels[fi];j++)
+	  fprintf(out,"%f ",todB(feedback_max[j][i])*.5);
+	fprintf(out,"\n");
+      }
+      fprintf(out,"\n");
+      ch+=channels[fi];
+    }
+    fclose(out);
+  }
+
+  {   
+    out=fopen("instant.m","w");
+    ch = 0;
+    for(fi=0;fi<inputs;fi++){
+      for(i=0;i<blocksize/2+1;i++){
+	fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
+	
+	for(j=ch;j<ch+channels[fi];j++)
+	  fprintf(out,"%f ",todB(feedback_instant[j][i])*.5);
+	fprintf(out,"\n");
+      }
+      fprintf(out,"\n");
+      ch+=channels[fi];
+    }
+    fclose(out);
+  }
+
+  {   
+    out=fopen("accphase.m","w");
+    ch = 0;
+    for(fi=0;fi<inputs;fi++){
+
+      /* phase */ 
+      for(i=0;i<blocksize+2;i+=2){
+	fprintf(out,"%f ",(double)i*.5*rate[fi]/blocksize);
+	fprintf(out,"%f ",atan2(ph_acc[ch+1][i+1],ph_acc[ch+1][i])*57.29);
+	fprintf(out,"\n");
+      }
+      fprintf(out,"\n");
+      ch+=channels[fi];
+    }
+    fclose(out);
+  }
+
+}
+
+void clear_noise_floor(){
+  int i;
+  for(i=0;i<total_ch;i++){
+    memset(floor_y[i],0,(blocksize/2+1)*sizeof(**floor_y));
+    memset(floor_yy[i],0,(blocksize/2+1)*sizeof(**floor_yy));
+  }
+  floor_count=0;
+}
+
+/* how many bins to 'trim' off the edge of calculated data when we
+   know we've hit a boundary of marginal measurement */
+#define binspan 5
+
+float **process_fetch(int res, int scale, int mode, int link, 
+		      int *active, int width, 
+		      float *ymax, float *pmax, float *pmin,
+		      float **yfloor,int noise){
+  int ch,ci,i,j,fi;
+  float **data;
+  float **ph;
+  float maxrate=-1.;
+  float nyq;
+
+  init_process();
+
+  for(fi=0;fi<inputs;fi++)
+    if(rate[fi]>maxrate)maxrate=rate[fi];
+  nyq=maxrate/2.;
+
+  *yfloor=NULL;
+
+  /* are our scale mappings up to date? */
+  if(res != metares || scale != metascale || width != metawidth){
+    if(!xmappingL) xmappingL = calloc(inputs, sizeof(*xmappingL));
+    if(!xmappingH) xmappingH = calloc(inputs, sizeof(*xmappingH));
+    metanoise=-1;
+
+    if(!work_floor)
+      work_floor = calloc(total_ch,sizeof(*work_floor));
+    for(i=0;i<total_ch;i++){
+      if(work_floor[i])
+	work_floor[i] = realloc(work_floor[i],(width+1)*sizeof(**work_floor));
+      else
+	work_floor[i] = calloc((width+1),sizeof(**work_floor));
+    }
+
+    for(fi=0;fi<inputs;fi++){
+
+      /* if mapping preexists, resize it */
+      if(xmappingL[fi]){
+	xmappingL[fi] = realloc(xmappingL[fi],(width+1)*sizeof(**xmappingL));
+      }else{
+	xmappingL[fi] = malloc((width+1)*sizeof(**xmappingL));
+      }
+      if(xmappingH[fi]){
+	xmappingH[fi] = realloc(xmappingH[fi],(width+1)*sizeof(**xmappingH));
+      }else{
+	xmappingH[fi] = malloc((width+1)*sizeof(**xmappingH));
+      }
+
+      metascale = scale;
+      metawidth = width;
+      metares = res;
+
+      
+      /* generate new numbers */
+      for(i=0;i<width;i++){
+	float off=0;
+	float loff=1.;
+	float hoff=1.;
+	float lfreq,hfreq;
+
+	switch(res){
+	case 0: /* screen-resolution */
+	  off=1.;
+	  break;
+	case 1: /* 1/24th octave */
+	  loff = .95918945710913818816;
+	  hoff = 1.04254690518999138632;
+	  break;
+	case 2: /* 1/12th octave */
+	  loff = .94387431268169349664;
+	  hoff = 1.05946309435929526455;
+	  break;
+	case 3: /* 1/3th octave */
+	  loff = .79370052598409973738;
+	  hoff = 1.25992104989487316475;
+	  break;
+	}
+
+	switch(scale){
+	case 0: /* log */
+	  lfreq= pow(10.,(i-off)/(width-1)
+		     * (log10(nyq)-log10(5.))
+		     + log10(5.)) * loff;
+	  hfreq= pow(10.,(i+off)/(width-1)
+		     * (log10(nyq)-log10(5.))
+		     + log10(5.)) * hoff;
+	  break;
+	case 1: /* ISO */
+	  lfreq= pow(2.,(i-off)/(width-1)
+		     * (log2(nyq)-log2(25.))
+		     + log2(25.)) * loff;
+	  hfreq= pow(2.,(i+off)/(width-1)
+		     * (log2(nyq)-log2(25.))
+		     + log2(25.)) *hoff;
+	  break;
+	case 2: /* screen-resolution linear */
+	  lfreq=(i-off)*nyq/(width-1)*loff;
+	  hfreq=(i+off)*nyq/(width-1)*hoff;
+	  break;
+	}
+
+	xmappingL[fi][i]=lfreq/(rate[fi]*.5)*(blocksize/2);
+	xmappingH[fi][i]=hfreq/(rate[fi]*.5)*(blocksize/2);
+
+      }
+      
+      for(i=0;i<width;i++){
+	if(xmappingL[fi][i]<0.)xmappingL[fi][i]=0.;
+	if(xmappingL[fi][i]>blocksize/2.)xmappingL[fi][i]=blocksize/2.;
+	if(xmappingH[fi][i]<0.)xmappingH[fi][i]=0.;
+	if(xmappingH[fi][i]>blocksize/2.)xmappingH[fi][i]=blocksize/2.;
+      }
+    }
+
+    for(i=0;i<total_ch;i++)
+      if(plot_data[i]){
+	plot_data[i] = realloc(plot_data[i],(width+1)*sizeof(**plot_data));
+      }else{
+	plot_data[i] = malloc((width+1)*sizeof(**plot_data));
+      }
+  }
+
+  /* 'illustrate' the noise floor */
+  if(noise){
+    if(plot_floor)
+      plot_floor=realloc(plot_floor,(width+1)*sizeof(*plot_floor));
+    else
+      plot_floor=calloc((width+1),sizeof(*plot_floor));
+    
+    if(metanoise!=link){
+      float *y = plot_floor;
+      int ch=0;
+      metanoise=link;
+      for(i=0;i<width;i++)
+	y[i]=-300;
+      
+      for(fi=0;fi<inputs;fi++){
+	float *L = xmappingL[fi];
+	float *H = xmappingH[fi];
+	float d = 1./floor_count;
+	
+	for(ci=0;ci<channels[fi];ci++){
+	  float *fy = floor_y[ci+ch];
+	  float *fyy = floor_yy[ci+ch];
+	  float *w = work_floor[ci+ch];
+	  
+	  for(i=0;i<width;i++){
+	    int first=floor(L[i]);
+	    int last=floor(H[i]);
+	    float esum;
+	    float vsum;
+	    float v = fyy[first]*floor_count - fy[first]*fy[first];
+	    
+	    if(first==last){
+	      float del=H[i]-L[i];
+	      esum=fy[first]*del;
+	      vsum=v*del;
+	    }else{
+	      float del=1.-(L[i]-first);
+	      esum=fy[first]*del;
+	      vsum=v*del;
+	      
+	      for(j=first+1;j<last;j++){
+		v = fyy[j]*floor_count - fy[j]*fy[j];
+		esum+=fy[j];
+		vsum+=v;
+	      }
+	      
+	      v = fyy[last]*floor_count - fy[last]*fy[last];
+	      del=(H[i]-last);
+	      esum+=fy[last]*del;
+	      vsum+=v*del;
+	    }
+	    vsum = 10*sqrt(vsum)*d;
+	    esum*=d;
+	    w[i] = esum+vsum*10;
+	    esum = todB_a(w+i)*.5;
+	    
+	    if(esum>y[i])y[i]=esum;
+	  }
+	}
+	ch+=channels[fi];
+      }
+    }
+    if(link == LINK_INDEPENDENT && mode==0)
+      *yfloor=plot_floor;
+  }else{
+    for(i=0;i<total_ch;i++)
+      memset(work_floor[i],0,width*sizeof(**work_floor));
+    metanoise=-1;
+  }
+  
+  /* mode selects the base data set */
+  switch(mode){    
+  case 0: /* independent / instant */
+    data=feedback_instant;
+    ph=ph_instant;
+    break;
+  case 1: /* independent / max */
+    data=feedback_max;
+    ph=ph_max;
+    break;
+  case 2:
+    data=feedback_acc;
+    ph=ph_acc;
+    break;
+  }
+  
+  ch=0;
+  *ymax = -150.;
+  *pmax = -180.;
+  *pmin = 180.;
+  for(fi=0;fi<inputs;fi++){
+    float *L = xmappingL[fi];
+    float *H = xmappingH[fi];
+
+    switch(link){
+    case LINK_INDEPENDENT:
+      
+      for(ci=0;ci<channels[fi];ci++){
+	float *y = plot_data[ci+ch];
+	float *m = data[ci+ch];
+	if(active[ch+ci]){
+	  for(i=0;i<width;i++){
+	    int first=floor(L[i]);
+	    int last=floor(H[i]);
+	    float sum;
+	    
+	    if(first==last){
+	      float del=H[i]-L[i];
+	      sum=m[first]*del;
+	    }else{
+	      float del=1.-(L[i]-first);
+	      sum=m[first]*del;
+	      
+	      for(j=first+1;j<last;j++)
+		sum+=m[j];
+	      
+	      del=(H[i]-last);
+	      sum+=m[last]*del;
+	    }
+
+	    sum=todB_a(&sum)*.5;
+	    if(sum>*ymax)*ymax=sum;
+	    y[i]=sum;	  
+	  }
+	}
+      }
+      break;
+
+    case LINK_SUMMED:
+      {
+	float *y = plot_data[ch];
+	memset(y,0,(width+1)*sizeof(*y));
+      
+	for(ci=0;ci<channels[fi];ci++){
+	  float *m = data[ci+ch];
+	  if(active[ch+ci]){
+	    for(i=0;i<width;i++){
+	      int first=floor(L[i]);
+	      int last=floor(H[i]);
+	      
+	      if(first==last){
+		float del=H[i]-L[i];
+		y[i]+=m[first]*del;
+	      }else{
+		float del=1.-(L[i]-first);
+		y[i]+=m[first]*del;
+		
+		for(j=first+1;j<last;j++)
+		  y[i]+=m[j];
+		
+		del=(H[i]-last);
+		y[i]+=m[last]*del;
+	      }
+	    }
+	  }
+	}
+      
+	for(i=0;i<width;i++){
+	  float sum=todB_a(y+i)*.5;
+	  if(sum>*ymax)*ymax=sum;
+	  y[i]=sum;	  
+	}
+      }
+      break;
+      
+    case LINK_SUB_FROM:
+      {
+	float *y = plot_data[ch];
+	if(active[ch]==0){
+	  for(i=0;i<width;i++)
+	    y[i]=-300;
+	}else{
+	  for(ci=0;ci<channels[fi];ci++){
+	    float *m = data[ci+ch];
+	    if(ci==0 || active[ch+ci]){
+	      for(i=0;i<width;i++){
+		int first=floor(L[i]);
+		int last=floor(H[i]);
+		float sum;
+		
+		if(first==last){
+		  float del=H[i]-L[i];
+		  sum=m[first]*del;
+		}else{
+		  float del=1.-(L[i]-first);
+		  sum=m[first]*del;
+		  
+		  for(j=first+1;j<last;j++)
+		    sum+=m[j];
+		  
+		  del=(H[i]-last);
+		  sum+=m[last]*del;
+		}
+		
+		if(ci==0){
+		  y[i]=sum;
+		}else{
+		  y[i]-=sum;
+		}
+	      }
+	    }
+	  }
+	  
+	  for(i=0;i<width;i++){
+	    float v = (y[i]>0?y[i]:0);
+	    float sum=todB_a(&v)*.5;
+	    if(sum>*ymax)*ymax=sum;
+	    y[i]=sum;	  
+	  }
+	}
+      }
+      break;
+    case LINK_SUB_REF:
+      {
+	float *r = plot_data[ch];
+	for(ci=0;ci<channels[fi];ci++){
+	  float *y = plot_data[ch+ci];
+	  float *m = data[ci+ch];
+	  if(ci==0 || active[ch+ci]){
+	    for(i=0;i<width;i++){
+	      int first=floor(L[i]);
+	      int last=floor(H[i]);
+	      float sum;
+	      
+	      if(first==last){
+		float del=H[i]-L[i];
+		sum=m[first]*del;
+	      }else{
+		float del=1.-(L[i]-first);
+		sum=m[first]*del;
+		
+		for(j=first+1;j<last;j++)
+		  sum+=m[j];
+		
+		del=(H[i]-last);
+		sum+=m[last]*del;
+	      }
+	      
+	      if(ci==0){
+		r[i]=sum;
+	      }else{
+		sum=(r[i]>sum?0.f:sum-r[i]);
+		y[i]=todB_a(&sum)*.5;
+		if(y[i]>*ymax)*ymax=y[i];
+	      }
+	    }
+	  }
+	}
+      }
+      break;
+      
+    case LINK_IMPEDENCE_p1:
+    case LINK_IMPEDENCE_1:
+    case LINK_IMPEDENCE_10:
+      {
+	float shunt = (link == LINK_IMPEDENCE_p1?.1:(link == LINK_IMPEDENCE_1?1:10));
+	float *r = plot_data[ch];
+
+	for(ci=0;ci<channels[fi];ci++){
+	  float *y = plot_data[ci+ch];
+	  float *m = data[ch+ci];
+	  
+	  if(ci==0 || active[ch+ci]){
+	    for(i=0;i<width;i++){
+	      int first=floor(L[i]);
+	      int last=floor(H[i]);
+	      float sum;
+	      
+	      if(first==last){
+		float del=H[i]-L[i];
+		sum=m[first]*del;
+	      }else{
+		float del=1.-(L[i]-first);
+		sum=m[first]*del;
+		
+		for(j=first+1;j<last;j++)
+		  sum+=m[j];
+		
+		del=(H[i]-last);
+		sum+=m[last]*del;
+	      }
+
+	      if(ci==0){
+		/* stash the reference in the work vector */
+		r[i]=sum;
+	      }else{
+		/* the shunt */
+		/* 'r' collected at source, 'sum' across the shunt */
+		float V=sqrt(r[i]);
+		float S=sqrt(sum);
+		
+		if(S>(1e-5) && V>S){
+		  y[i] = shunt*(V-S)/S;
+		}else{
+		  y[i] = NAN;
+		}
+	      }
+	    }
+	  }
+	}
+	    
+	/* scan the resulting buffers for marginal data that would
+	   produce spurious output. Specifically we look for sharp
+	   falloffs of > 40dB or an original test magnitude under
+	   -70dB. */
+	{
+	  float max = -140;
+	  for(i=0;i<width;i++){
+	    float v = r[i] = todB_a(r+i)*.5;
+	    if(v>max)max=v;
+	  }
+
+	  for(ci=1;ci<channels[fi];ci++){
+	    if(active[ch+ci]){
+	      float *y = plot_data[ci+ch];	      
+	      for(i=0;i<width;i++){
+		if(r[i]<max-40 || r[i]<-70){
+		  int j=i-binspan;
+		  if(j<0)j=0;
+		  for(;j<i;j++)
+		    y[j]=NAN;
+		  for(;j<width;j++){
+		    if(r[j]>max-40 && r[j]>-70)break;
+		    y[j]=NAN;
+		  }
+		  i=j+3;
+		  for(;j<i && j<width;j++){
+		    y[j]=NAN;
+		  }
+		}
+		if(!isnan(y[i]) && y[i]>*ymax)*ymax = y[i];
+	      }
+	    }
+ 	  }
+	}
+      }
+      break;
+
+    case LINK_PHASE: /* response/phase */
+
+      if(channels[fi]>=2){
+	float *om = plot_data[ch];
+	float *op = plot_data[ch+1];
+
+	float *r = data[ch];
+	float *rn = work_floor[ch];
+	float *m = data[ch+1];
+	float *mn = work_floor[ch+1];
+	float *p = ph[ch+1];
+	float mag[width];
+
+	if(feedback_count[ch]==0){
+	  memset(om,0,width*sizeof(*om));
+	  memset(op,0,width*sizeof(*op));
+	}else{
+	  /* two vectors only; response and phase */
+	  /* response */
+	  if(active[ch] || active[ch+1]){
+	    for(i=0;i<width;i++){
+	      int first=floor(L[i]);
+	      int last=floor(H[i]);
+	      float sumR,sumM;
+	      
+	      if(first==last){
+		float del=H[i]-L[i];
+		sumR=r[first]*del;
+		sumM=m[first]*del;
+	      }else{
+		float del=1.-(L[i]-first);
+		sumR=r[first]*del;
+		sumM=m[first]*del;
+		
+		for(j=first+1;j<last;j++){
+		  sumR+=r[j];
+		  sumM+=m[j];
+		}
+
+		del=(H[i]-last);
+		sumR+=r[last]*del;
+		sumM+=m[last]*del;
+	      }
+	      
+	      if(sumR>rn[i] && sumM>mn[i]){
+		mag[i] = todB_a(&sumR)*.5;
+		sumM /= sumR;
+		om[i] = todB_a(&sumM)*.5;
+	      }else{
+		om[i] = NAN;
+	      }
+	    }
+	  }
+	  
+	  /* phase */
+	  if(active[ch+1]){
+	    for(i=0;i<width;i++){
+	      int first=floor(L[i]);
+	      int last=floor(H[i]);
+	      float sumR,sumI;
+	      
+	      if(first==last){
+		float del=H[i]-L[i];
+		sumR=p[(first<<1)]*del;
+		sumI=p[(first<<1)+1]*del;
+	      }else{
+		float del=1.-(L[i]-first);
+		sumR=p[(first<<1)]*del;
+		sumI=p[(first<<1)+1]*del;
+		
+		for(j=first+1;j<last;j++){
+		  sumR+=p[(j<<1)];
+		  sumI+=p[(j<<1)+1];
+		}
+
+		del=(H[i]-last);
+		sumR+=p[(last<<1)]*del;
+		sumI+=p[(last<<1)+1]*del;
+	      }
+
+	      if(!isnan(om[i])){
+		op[i] = atan2(sumI,sumR)*57.29;
+	      }else{
+		op[i]=NAN;
+	      }
+	    }
+	  }
+	  
+	  /* scan the resulting buffers for marginal data that would
+	     produce spurious output. Specifically we look for sharp
+	     falloffs of > 40dB or an original test magnitude under
+	     -70dB. */
+	  if(active[ch] || active[ch+1]){
+	    for(i=0;i<width;i++){
+	      if(isnan(om[i])){
+		int j=i-binspan;
+		if(j<0)j=0;
+		for(;j<i;j++){
+		  om[j]=NAN;
+		  op[j]=NAN;
+		}
+		for(;j<width;j++){
+		  if(!isnan(om[j]))break;
+		  om[j]=NAN;
+		  op[j]=NAN;
+		}
+		i=j+3;
+		for(;j<i && j<width;j++){
+		  om[j]=NAN;
+		  op[j]=NAN;
+		}
+	      }
+	      if(om[i]>*ymax)*ymax = om[i];
+	      if(op[i]>*pmax)*pmax = op[i];
+	      if(op[i]<*pmin)*pmin = op[i];
+	      
+	    }
+	  }
+	}
+      }
+      break;
+      
+    case LINK_THD: /* THD */
+    case LINK_THD2: /* THD-2 */
+    case LINK_THDN: /* THD+N */
+    case LINK_THDN2: /* THD+N-2 */
+      
+      
+      break;
+      
+    }
+    ch+=channels[fi];
+  }
+  
+  return plot_data;
+}
+

Copied: trunk/spectrum/spectrum.c (from rev 18228, trunk/spectrum/main.c)
===================================================================
--- trunk/spectrum/spectrum.c	                        (rev 0)
+++ trunk/spectrum/spectrum.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,296 @@
+/*
+ *
+ *  gtk2 spectrum analyzer
+ *    
+ *      Copyright (C) 2004 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"
+#include "io.h"
+#include <signal.h>
+#include <getopt.h>
+#include <fenv.h>  // Thank you C99!
+#include <fftw3.h>
+#include <gtk/gtk.h>
+#include "version.h"
+
+int eventpipe[2];
+char *version;
+char *inputname[MAX_FILES];
+int inputs=0;
+int blocksize = 131072;
+
+void handler(int sig){
+  signal(sig,SIG_IGN);
+  if(sig!=SIGINT){
+    fprintf(stderr,
+	    "\nTrapped signal %d; exiting!\n"
+	    "This signal almost certainly indicates a bug in the analyzer;\n"
+	    "If this version of the analyzer 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 I can fix the bug as\n"
+	    "quickly as possible.\n\n"
+	    "-- monty at xiph.org, spectrum revision %s\n\n",sig,version);
+  }
+  
+  gtk_main_quit();
+}
+
+const char *optstring = "-r:c:EeBlb:suhF:";
+
+struct option options [] = {
+        {"rate",required_argument,NULL,'r'},
+        {"channels",required_argument,NULL,'c'},
+        {"big-endian",no_argument,NULL,'E'},
+        {"little-endian",no_argument,NULL,'e'},
+        {"bits",required_argument,NULL,'b'},
+        {"signed",no_argument,NULL,'s'},
+        {"unsigned",no_argument,NULL,'u'},
+        {"help",no_argument,NULL,'h'},
+        {"fft-size",required_argument,NULL,'F'},
+
+        {NULL,0,NULL,0}
+};
+
+static void usage(FILE *f){
+  fprintf( f,
+"\ngtk2 spectrum analyzer, revision %s\n\n"
+
+"USAGE:\n\n"
+"  spectrum [options] [file]\n\n"
+
+"OPTIONS:\n\n"
+"  -b --bits <bits>           : Force input to be read as 8, 16, 24 or 32 bit\n"
+"                               PCM. Default bit depth is normally read from\n"
+"                               the file/stream header or set to 16 bits\n"
+"                               for raw input.\n"
+"  -B -E --big-endian         : Force input to be read as big endian.\n"
+"                               Default endianness is normally read from the\n"
+"                               file/stream header or set to host"
+"                               endianness for raw input.\n"
+"  -c --channels <channels>   : Input channel number override; use to\n"
+"                               specify the number of channels in a raw\n"
+"                               input.  default: 1\n"
+"  -e -l --little-endian      : Force input to be read as little endian.\n"
+"                               Default endianness is normally read from the\n"
+"                               file/stream header or set to host"
+"                               endianness for raw input.\n"
+"  -F --fft-size              : Set the size of the fft transform used. Valid\n"
+"                               range 8192 to 262144, 131072 default.\n"
+"  -h --help                  : print this help\n"
+"  -r --rate <Hz>             : Input sample rate override in Hz; use to\n"
+"                               specify the rate of a raw input.\n"
+"                               default: 44100\n"
+"  -s --signed                : Force input to be read as signed PCM.\n"
+"                               Signedness is normally read from the \n"
+"                               file/stream header or set to signed for raw\n"
+"                               input.\n"
+"  -u --unsigned              : Force input to be read as unsigned PCM.\n"
+"                               Signedness is normally read from the \n"
+"                               file/stream header or set to signed for raw\n"
+"                               input.\n\n"
+
+"INPUT:\n\n"
+
+" Spectrum takes raw, WAV or AIFF input either from stdin or from \n"
+" file[s]/stream[s] specified on the command line.\n\n",version);
+
+}
+
+void parse_command_line(int argc, char **argv){
+  int c,long_option_index;
+
+  while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
+    switch(c){
+    case 1:
+      /* file name that belongs to current group */
+      if(inputs>=MAX_FILES){
+	fprintf(stderr,"Maximum of MAX_FILES input files exceeded.  Oops.  Programmer was lazy.\n\n");
+	exit(1);
+      }
+      inputname[inputs++]=strdup(optarg);
+      break;
+    case 'b':
+      /* force bit width */
+      {
+	int a=atoi(optarg);
+	bits[inputs]=a;
+	if(a!=8 && a!=16 && a!=24 && a!=32){
+	  usage(stderr);
+	  exit(1);
+	}
+      }
+      break;
+    case 'F':
+      blocksize = atoi(optarg);
+      if(blocksize<8192 || blocksize>262144){
+	usage(stderr);
+	exit(1);
+      }
+      break;
+    case 'B':case 'E':
+      /* force big endian */
+      bigendian[inputs]=1;
+      break;
+    case 'c':
+      /* force channels */
+      {
+	int a=atoi(optarg);
+	channels[inputs]=a;
+	if(a<1 || a>32){
+	  usage(stderr);
+	  exit(1);
+	}
+      }
+      break;
+    case 'l':case 'e':
+      /* force little endian */
+      bigendian[inputs]=0;
+      break;
+    case 'h':
+      usage(stdout);
+      exit(0);
+    case 'r':
+      /* force rate */
+      {
+	int a=atoi(optarg);
+	rate[inputs]=a;
+	if(a<4000 || a>200000){
+	  usage(stderr);
+	  exit(1);
+	}
+      }
+      break;
+    case 's':
+      /* force signed */
+      signedp[inputs]=1;
+      break;
+    case 'u':
+      /* force unsigned */
+      signedp[inputs]=0;
+      break;
+    default:
+      usage(stderr);
+      exit(0);
+    }
+  }
+}
+
+static int sigill=0;
+void sigill_handler(int sig){
+  /* make sure */
+  if(sig==SIGILL)sigill=1;
+}
+
+int main(int argc, char **argv){
+  int fi;
+
+  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. */
+
+#ifndef DEBUG
+  fedisableexcept(FE_INVALID);
+  fedisableexcept(FE_INEXACT);
+  fedisableexcept(FE_UNDERFLOW);
+  fedisableexcept(FE_OVERFLOW);
+#else
+  feenableexcept(FE_INVALID);
+  feenableexcept(FE_INEXACT);
+  feenableexcept(FE_UNDERFLOW);
+  feenableexcept(FE_OVERFLOW);
+#endif 
+
+  /* Linux Altivec support has a very annoying problem; by default,
+     math on denormalized floats will simply crash the program.  FFTW3
+     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 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};
+#else
+  vector unsigned short noTrap = 
+    (vector unsigned short)(0,0,0,0,0,0,0x1,0);
+#endif
+
+  vec_mtvscr(noTrap);
+#endif
+
+  /* easiest way to inform gtk of changes and not deal with locking
+     issues around the UI */
+  if(pipe(eventpipe)){
+    fprintf(stderr,"Unable to open event pipe:\n"
+            "  %s\n",strerror(errno));
+    
+    exit(1);
+  }
+
+  /* Allows event compression on the read side */
+  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);
+
+  /* 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);
+
+  return(0);
+}

Modified: trunk/spectrum/version.h
===================================================================
--- trunk/spectrum/version.h	2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/version.h	2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,2 +1,2 @@
 #define VERSION "$Id$ "
-/* DO NOT EDIT: Automated versioning hack [Wed Apr 11 21:38:57 EDT 2012] */
+/* DO NOT EDIT: Automated versioning hack [Thu Apr 12 10:26:00 EDT 2012] */

Added: trunk/spectrum/wave_panel.c
===================================================================
--- trunk/spectrum/wave_panel.c	                        (rev 0)
+++ trunk/spectrum/wave_panel.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,758 @@
+/*
+ *
+ *  gtk2 waveform viewer
+ *    
+ *      Copyright (C) 2004 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;
+
+static struct panel {
+  GtkWidget *twirlimage;
+  GdkPixmap *ff[19];
+  GdkBitmap *fb[19];
+
+  GtkAccelGroup *group;
+  GtkWidget *toplevel;
+
+
+  guint fishframe_timer;
+  int fishframe_init;
+  int fishframe;
+
+  GtkWidget *plot;
+  GtkWidget *run;
+  GtkWidget **chbuttons;
+
+} p;
+
+int plot_range=0;
+int plot_scale=0;
+int plot_span=0;
+int plot_rchoice=0;
+int plot_schoice=0;
+int plot_spanchoice=0;
+int plot_interval=0;
+int plot_trigger=0;
+int plot_hold=0;
+int plot_last_update=0;
+int *active;
+
+int overslice[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+
+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){
+      /* reschedule to blink */
+      p->fishframe_timer=
+	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+      return FALSE;
+    }
+
+  }else{
+    p->fishframe++;
+    if(p->fishframe<=1)p->fishframe=12;
+    if(p->fishframe>=19)p->fishframe=0;
+
+    gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
+			      p->ff[p->fishframe],
+			      p->fb[p->fishframe]);
+
+
+    if(p->fishframe==12){
+      /* reschedule to animate */
+      p->fishframe_timer=
+	g_timeout_add(10,(GSourceFunc)reanimate_fish,p);
+      return FALSE;
+    }
+    if(p->fishframe==0){
+      /* reschedule to blink */
+      p->fishframe_timer=
+	g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+      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);
+  }else{
+    p->fishframe_init=1;
+    p->fishframe_timer=
+      g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+  }
+}
+
+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);
+
+  /* buttons usually have internal labels */
+  if(GTK_IS_CONTAINER(c))
+    gtk_container_forall (GTK_CONTAINER(c),set_fg,in);
+}
+
+static void rangechange(GtkWidget *widget,struct panel *p){
+  int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  plot_rchoice=choice;
+  switch(choice){
+  case 0:
+    plot_range=0;
+    break;
+  case 1:
+    plot_range=-6;
+    break;
+  case 2:
+    plot_range=-14;
+    break;
+  case 3:
+    plot_range=-20;
+    break;
+  case 4:
+    plot_range=-40;
+    break;
+  case 5:
+    plot_range=-60;
+    break;
+  }
+  plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,NULL,NULL);
+}
+
+static void scalechange(GtkWidget *widget,struct panel *p){
+  int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  plot_schoice=choice;
+  switch(choice){
+  case 0:
+    plot_scale=0;
+    break;
+  case 1:
+    plot_scale=-65;
+    break;
+  case 2:
+    plot_scale=-96;
+    break;
+  case 3:
+    plot_scale=-120;
+    break;
+  case 4:
+    plot_scale=-160;
+    break;
+  }
+  plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,NULL,NULL);
+}
+
+static void spanchange(GtkWidget *widget,struct panel *p){
+  int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  plot_spanchoice=choice;
+  switch(choice){
+  case 0:
+    plot_span=1000000;
+    break;
+  case 1:
+    plot_span=500000;
+    break;
+  case 2:
+    plot_span=200000;
+    break;
+  case 3:
+    plot_span=100000;
+    break;
+  case 4:
+    plot_span=50000;
+    break;
+  case 5:
+    plot_span=20000;
+    break;
+  case 6:
+    plot_span=10000;
+    break;
+  case 7:
+    plot_span=5000;
+    break;
+  case 8:
+    plot_span=2000;
+    break;
+  case 9:
+    plot_span=1000;
+    break;
+  case 10:
+    plot_span=500;
+    break;
+  case 11:
+    plot_span=200;
+    break;
+  case 12:
+    plot_span=100;
+    break;
+  }
+
+  /* update interval limited to < 25fps */
+  int temp = (plot_interval < 50000 ? 50000:plot_interval),fi;
+
+  if(temp <= plot_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]=rate[fi]*temp/1000000;
+      overslice[fi]=rate[fi]*temp/1000000;
+    }
+  }else{
+    /* if the limited update interval is longer than the span, we
+       overdraw */
+    for(fi=0;fi<inputs;fi++)
+      blockslice[fi]=rate[fi]*temp/1000000;
+    for(fi=0;fi<inputs;fi++)
+      overslice[fi]=rate[fi]*plot_interval/1000000;
+  }
+  plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,blockslice,overslice);
+}
+
+/* intervals that are >= the span and would result in > 25fps are overdrawn */
+static void intervalchange(GtkWidget *widget,struct panel *p){
+  int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  switch(choice){
+  case 0:
+    plot_interval=1000000;
+    break;
+  case 1:
+    plot_interval=500000;
+    break;
+  case 2:
+    plot_interval=200000;
+    break;
+  case 3:
+    plot_interval=100000;
+    break;
+  case 4:
+    plot_interval=50000; // 20/sec
+    break;
+  case 5:
+    plot_interval=20000; // 50/sec
+    break;
+  case 6:
+    plot_interval=10000;
+    break;
+  case 7:
+    plot_interval=5000;
+    break;
+  case 8:
+    plot_interval=2000;
+    break;
+  case 9:
+    plot_interval=1000;
+    break;
+  case 10:
+    plot_interval=500;
+    break;
+  case 11:
+    plot_interval=200;
+    break;
+  case 12:
+    plot_interval=100;
+    break;
+  }
+
+  /* update interval limited to < 25fps */
+  int temp = (plot_interval < 50000 ? 50000:plot_interval),fi;
+
+  if(temp <= plot_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]=rate[fi]*temp/1000000;
+      overslice[fi]=rate[fi]*temp/1000000;
+    }
+  }else{
+    /* if the limited update interval is longer than the span, we
+       overdraw */
+    for(fi=0;fi<inputs;fi++)
+      blockslice[fi]=rate[fi]*temp/1000000;
+    for(fi=0;fi<inputs;fi++)
+      overslice[fi]=rate[fi]*plot_interval/1000000;
+  }
+  plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,blockslice,overslice);
+}
+
+static void triggerchange(GtkWidget *widget,struct panel *p){
+  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){
+  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);
+      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){
+  plot_hold=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+  replot(p);
+  plot_draw(PLOT(p->plot));
+}
+
+static void loopchange(GtkWidget *widget,struct panel *p){
+  acc_loop=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+}
+
+static void rewindchange(GtkWidget *widget,struct panel *p){
+  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];
+
+  for(i=0;i<total_ch;i++)
+    active[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
+
+  plot_set_active(PLOT(p->plot),active);
+}
+
+extern char *version;
+void panel_create(struct panel *panel){
+  int i;
+
+  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 *mainbox=gtk_hbox_new(0,6);
+  GdkWindow *root=gdk_get_default_root_window();
+  GtkWidget *rightbox=gtk_vbox_new(0,0);
+  GtkWidget *leftbox=gtk_vbox_new(0,6);
+
+  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);
+
+  char versionmarkup[240];
+  snprintf(versionmarkup,240," <span size=\"large\" weight=\"bold\" "
+	   "style=\"italic\" foreground=\"dark blue\">"
+	   "Waveform Viewer</span>  <span size=\"small\" foreground=\"#606060\">"
+	   "revision %s</span> ",
+	   version);
+
+  /* the Fucking Fish */
+  for(i=0;i<19;i++)
+    panel->ff[i]=gdk_pixmap_create_from_xpm_d(root,
+					      panel->fb+i,NULL,ff_xpm[i]);
+  panel->twirlimage=gtk_image_new_from_pixmap(panel->ff[0],panel->fb[0]);
+
+  active = calloc(total_ch,sizeof(*active));
+
+  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_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);
+  gtk_table_attach_defaults(GTK_TABLE(topplace),
+			    topframe,0,1,0,1);
+    
+  gtk_container_add (GTK_CONTAINER (panel->toplevel), topplace);
+  gtk_container_set_border_width (GTK_CONTAINER (quitbutton), 3);
+
+  g_signal_connect (G_OBJECT (quitbutton), "clicked",
+		    G_CALLBACK (shutdown), NULL);
+  gtk_widget_add_accelerator (quitbutton, "activate", panel->group, GDK_q, 0, 0);
+
+  gtk_container_set_border_width (GTK_CONTAINER (topframe), 3);
+  gtk_container_set_border_width (GTK_CONTAINER (mainbox), 3);
+  gtk_frame_set_shadow_type(GTK_FRAME(topframe),GTK_SHADOW_ETCHED_IN);
+  gtk_frame_set_label_widget(GTK_FRAME(topframe),toplabel);
+  gtk_label_set_markup(GTK_LABEL(toplabel),versionmarkup);
+
+  gtk_container_add (GTK_CONTAINER(topframe), mainbox);
+
+  g_signal_connect (G_OBJECT (panel->toplevel), "delete_event",
+		    G_CALLBACK (shutdown), NULL);
+
+  /* add the waveform plot box */
+  panel->plot=plot_new(blocksize,inputs,channels,rate);
+  gtk_box_pack_end(GTK_BOX(leftbox),panel->plot,1,1,0);
+  gtk_box_pack_start(GTK_BOX(mainbox),leftbox,1,1,0);
+  
+  /*fish */
+  {
+    GtkWidget *box=gtk_hbox_new(1,1);
+    GtkWidget *fishbox=gtk_hbox_new(0,0);
+    gtk_box_pack_end(GTK_BOX(fishbox),panel->twirlimage,0,0,0);
+    gtk_container_set_border_width (GTK_CONTAINER (fishbox), 3);
+
+    gtk_box_pack_start(GTK_BOX(box),fishbox,0,0,0);
+    gtk_box_pack_start(GTK_BOX(rightbox),box,0,0,0);
+  }
+
+  /* rate */
+  /* channels */
+  /* bits */
+  {
+    int fi;
+    int ch=0;
+    char buffer[160];
+    GtkWidget *label;
+    //GtkWidget *vbox=gtk_vbox_new(1,1);
+
+    GtkWidget *sep=gtk_hseparator_new();
+    gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
+
+    panel->chbuttons = calloc(total_ch,sizeof(*panel->chbuttons));
+    for(fi=0;fi<inputs;fi++){
+      
+      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(rightbox),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(rightbox),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(rightbox),button,0,0,0);
+      }
+
+      GtkWidget *sep=gtk_hseparator_new();
+      gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
+
+      ch+=channels[fi];
+
+    }
+    chlabels(NULL,panel);
+  }
+  
+  GtkWidget *bbox=gtk_vbox_new(0,0);
+
+  /* add the action buttons */
+  /* range */
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"\xC2\xB1""1.0 / 0dBFS",
+                     "\xC2\xB1""0.5 / -6dBFS",
+                     "\xC2\xB1""0.2 / -14dBFS",
+                     "\xC2\xB1""0.1 / -20dBFS",
+                     "\xC2\xB1""0.01 / -40dBFS",
+                     "\xC2\xB1""0.001 / -60dBFS"};
+    for(i=0;i<6;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (rangechange), panel);
+  }
+
+  /* scale */
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"linear","-65dB+","-96dB+","-120dB+","-160dB+"};
+    for(i=0;i<5;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (scalechange), panel);
+  }
+
+  /* span */
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"1s span",
+                     "500ms span","200ms span","100ms span",
+                     "50ms span","20ms span","10ms span",
+                     "5ms span","2ms span","1ms span",
+                     "500\xCE\xBCs span","200\xCE\xBCs span",
+                     "100\xCE\xBCs span"};
+    for(i=0;i<13;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (spanchange), panel);
+  }
+
+  /* trigger */
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"free run"};
+    for(i=0;i<1;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (triggerchange), panel);
+  }
+
+  /* interval */
+  {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"1s interval",
+                     "500ms interval","200ms interval","100ms interval",
+                     "50ms interval","20ms interval","10ms interval",
+                     "5ms interval","2ms interval","1ms interval",
+                     "500\xCE\xBCs interval","200\xCE\xBCs interval",
+                     "100\xCE\xBCsinterval "};
+    for(i=0;i<13;i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),3);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (intervalchange), panel);
+  }
+
+  {
+    GtkWidget *sep=gtk_hseparator_new();
+    gtk_box_pack_start(GTK_BOX(bbox),sep,0,0,4);
+  }
+
+  /* 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_box_pack_start(GTK_BOX(bbox),button,0,0,0);
+    panel->run=button;
+  }
+
+  /* hold */
+  /* loop */
+  {
+    GtkWidget *box=gtk_hbox_new(1,1);
+    GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_hold");
+    gtk_widget_add_accelerator (button, "activate", panel->group, GDK_h, 0, 0);
+    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (holdchange), panel);
+    gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
+
+    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_box_pack_start(GTK_BOX(box),button,1,1,0);
+    gtk_widget_set_sensitive(button,global_seekable);
+
+    gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
+  }
+
+  /* clear */
+  /* rewind */
+  {
+    GtkWidget *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_set_sensitive(button,global_seekable);
+    gtk_box_pack_start(GTK_BOX(bbox),button,1,1,0);
+  }
+
+  gtk_box_pack_end(GTK_BOX(rightbox),bbox,0,0,0);
+  gtk_box_pack_start(GTK_BOX(mainbox),rightbox,0,0,0);
+
+  gtk_widget_show_all(panel->toplevel);
+  //gtk_window_set_resizable(GTK_WINDOW(panel->toplevel),0);
+
+}
+
+static gboolean async_event_handle(GIOChannel *channel,
+				   GIOCondition condition,
+				   gpointer data){
+  struct panel *panel=data;
+  char buf[1];
+
+  /* read all pending */
+  while(read(eventpipe[0],buf,1)>0);
+
+  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);
+
+  /* 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));
+
+    while (gtk_events_pending())
+      gtk_main_iteration();
+  }else
+    pthread_mutex_unlock(&feedback_mutex);
+
+  return TRUE;
+}
+
+static int look_for_gtkrc(char *filename){
+  FILE *f=fopen(filename,"r");
+  if(!f)return 0;
+  fprintf(stderr,"Loading waveform-gtkrc file found at %s\n",filename);
+  gtk_rc_add_default_file(filename);
+  return 1;
+}
+
+void panel_go(int argc,char *argv[]){
+  char *homedir=getenv("HOME");
+  int found=0;
+  memset(&p,0,sizeof(p));
+
+  found|=look_for_gtkrc(ETCDIR"/waveform-gtkrc");
+  {
+    char *rcdir=getenv("HOME");
+    if(rcdir){
+      char *rcfile="/.spectrum/waveform-gtkrc";
+      char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
+      strcat(homerc,homedir);
+      strcat(homerc,rcfile);
+      found|=look_for_gtkrc(homerc);
+    }
+  }
+  {
+    char *rcdir=getenv("SPECTRUM_RCDIR");
+    if(rcdir){
+      char *rcfile="/waveform-gtkrc";
+      char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
+      strcat(homerc,homedir);
+      strcat(homerc,rcfile);
+      found|=look_for_gtkrc(homerc);
+    }
+  }
+  found|=look_for_gtkrc("./waveform-gtkrc");
+
+  if(!found){
+  
+    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"
+	    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"
+	    "cues.\n");
+  }
+
+  gtk_rc_add_default_file(ETCDIR"/waveform-gtkrc");
+  if(homedir){
+    char *rcfile="/.waveform-gtkrc";
+    char *homerc=calloc(1,strlen(homedir)+strlen(rcfile)+1);
+    strcat(homerc,homedir);
+    strcat(homerc,rcfile);
+    gtk_rc_add_default_file(homerc);
+  }
+  gtk_rc_add_default_file(".waveform-gtkrc");
+  gtk_rc_add_default_file("waveform-gtkrc");
+  gtk_init (&argc, &argv);
+
+  panel_create(&p);
+  animate_fish(&p);
+
+  /* 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_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_main ();
+
+}
+

Added: trunk/spectrum/wave_plot.c
===================================================================
--- trunk/spectrum/wave_plot.c	                        (rev 0)
+++ trunk/spectrum/wave_plot.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,527 @@
+/*
+ *
+ *  gtk2 waveform viewer
+ *    
+ *      Copyright (C) 2004 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 <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include "wave_plot.h"
+
+static GtkDrawingAreaClass *parent_class = NULL;
+
+static void compute_metadata(GtkWidget *widget){
+  Plot *p=PLOT(widget);
+  int width=widget->allocation.width-p->padx;
+  int i,j;
+
+  p->xgrids=10;
+  p->xtics=30;
+
+  for(i=0;i<p->xgrids;i++)
+    p->xgrid[i]=rint(i/(float)(p->xgrids) * (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*4) * (width-1))+p->padx;
+  }
+}
+
+GdkColor chcolor(int ch){
+  GdkColor rgb={0,0,0,0};
+
+  switch(ch%7){
+  case 0:
+    rgb.red=0x4000;
+    rgb.green=0x4000;
+    rgb.blue=0x4000;
+    break;
+  case 1:
+    rgb.red=0xd000;
+    rgb.green=0x0000;
+    rgb.blue=0x0000;
+    break;
+  case 2:
+    rgb.red=0x0000;
+    rgb.green=0xb000;
+    rgb.blue=0x0000;
+    break;
+  case 3:
+    rgb.red=0x0000;
+    rgb.green=0x0000;
+    rgb.blue=0xf000;
+    break;
+  case 4:
+    rgb.red=0xc000;
+    rgb.green=0xc000;
+    rgb.blue=0x0000;
+    break;
+  case 5:
+    rgb.red=0x0000;
+    rgb.green=0xc000;
+    rgb.blue=0xc000;
+    break;
+  case 6:
+    rgb.red=0xc000;
+    rgb.green=0x0000;
+    rgb.blue=0xe000;
+    break;
+  }
+  
+  return rgb;
+}
+
+static void draw(GtkWidget *widget){
+  int i;
+  Plot *p=PLOT(widget);
+  int height=widget->allocation.height;
+  int width=widget->allocation.width;
+  GtkWidget *parent=gtk_widget_get_parent(widget);
+  int padx = p->padx;
+
+  if(!p->drawgc){
+    p->drawgc=gdk_gc_new(p->backing);
+    gdk_gc_copy(p->drawgc,widget->style->black_gc);
+  }
+
+  /* clear the old rectangle out */
+  {
+    GdkGC *gc=parent->style->bg_gc[0];
+    gdk_draw_rectangle(p->backing,gc,1,0,0,padx,height);
+    gdk_draw_rectangle(p->backing,gc,1,0,height-p->pady,width,p->pady);
+
+    gc=parent->style->white_gc;
+    gdk_draw_rectangle(p->backing,gc,1,padx,0,width-padx,height-p->pady+1);
+  }
+
+  /* draw the light x grid */
+  {
+    int i;
+    GdkColor rgb={0,0,0,0};
+
+    rgb.red=0xc000;
+    rgb.green=0xff00;
+    rgb.blue=0xff00;
+    gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+    for(i=0;i<p->xtics;i++)
+      gdk_draw_line(p->backing,p->drawgc,p->xtic[i],0,p->xtic[i],height-p->pady);
+  }
+
+  PangoLayout **proper=p->x_layout[p->spanchoice];
+
+  for(i=0;i<p->xgrids;i++){
+    int px,py;
+    pango_layout_get_pixel_size(proper[i],&px,&py);
+
+    gdk_draw_layout (p->backing,
+                     widget->style->black_gc,
+                     p->xgrid[i]-(px/2), height-py+2,
+                     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;
+    rgb.blue=0xff00;
+    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);
+      gdk_draw_line(p->backing,p->drawgc,padx,y1,width,y1);
+      gdk_draw_line(p->backing,p->drawgc,padx,y2,width,y2);
+    }
+  }
+
+  /* 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);
+
+    rgb.red=0x0000;
+    rgb.green=0xc000;
+    rgb.blue=0xc000;
+    gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+    for(i=-8;i<9;i+=4){
+      int y=rint(center + center*i/9);
+
+      if(i==0)
+        gdk_draw_line(p->backing,widget->style->black_gc,padx,y,width,y);
+      else
+        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);
+
+      if(i<=0){
+        rgb.red=0x0000;
+        rgb.green=0x0000;
+        rgb.blue=0x0000;
+      }else{
+        rgb.red=0xc000;
+        rgb.green=0x0000;
+        rgb.blue=0x0000;
+      }
+      gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+      gdk_draw_layout (p->backing,
+                       p->drawgc,
+                       padx-px-2, y-py/2,
+                       p->y_layout[p->rchoice][p->schoice][i/4+2]);
+
+      rgb.red=0x0000;
+      rgb.green=0xc000;
+      rgb.blue=0xc000;
+      gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+    }
+  }
+
+  /* dark x grid */
+  {
+    int i;
+    GdkColor rgb={0,0,0,0};
+
+    rgb.red=0x0000;
+    rgb.green=0xc000;
+    rgb.blue=0xc000;
+    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);
+  }
+
+
+  {
+    GdkGCValues values;
+    //gdk_gc_get_values(p->drawgc,&values);
+    values.line_width=2;
+    gdk_gc_set_values(p->drawgc,&values,GDK_GC_LINE_WIDTH);
+  }
+
+  /* draw actual data */
+  if(p->ydata){
+  }
+
+  {
+    GdkGCValues values;
+    //gdk_gc_get_values(p->drawgc,&values);
+    values.line_width=1;
+    gdk_gc_set_values(p->drawgc,&values,GDK_GC_LINE_WIDTH);
+  }
+
+
+}
+
+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);
+}
+
+static gboolean expose( GtkWidget *widget, GdkEventExpose *event ){
+  Plot *p=PLOT(widget);
+  gdk_draw_drawable(widget->window,
+		    widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+		    p->backing,
+		    event->area.x, event->area.y,
+		    event->area.x, event->area.y,
+		    event->area.width, event->area.height);
+
+  return FALSE;
+}
+
+static void size_request (GtkWidget *widget,GtkRequisition *requisition){
+  Plot *p=PLOT(widget);
+  requisition->width = 400;
+  requisition->height = 200;
+  int axisy=0,axisx=0,pady=0,padx=0,phax=0,px,py,i;
+
+  /* find max X layout */
+  {
+    int max=0;
+    int maxy=0;
+    for(i=0;p->x_layout[1][i];i++){
+      pango_layout_get_pixel_size(p->x_layout[1][i],&px,&py);
+      if(px>max)max=px;
+      if(py>pady)pady=py;
+      if(py>maxy)maxy=py;
+    }
+    max+=maxy*2.;
+    max*=i+1;
+    if(axisx<max)axisx=max;
+  }
+
+  /* find max Y layout */
+  {
+    int max=0;
+    for(i=0;p->y_layout[5][4][i];i++){
+      pango_layout_get_pixel_size(p->y_layout[5][4][i],&px,&py);
+      if(py>max)max=py;
+      if(px>padx)padx=px;
+    }
+    axisy=(max)*8;
+    if(axisy<max)axisy=max;
+  }
+
+  if(requisition->width<axisx+padx)requisition->width=axisx+padx;
+  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);
+
+  p->backing = gdk_pixmap_new(widget->window,
+			      widget->allocation.width,
+			      widget->allocation.height,
+			      -1);
+
+  p->ydata=NULL;
+  p->configured=1;
+
+  compute_metadata(widget);
+  plot_refresh(p);
+  draw_and_expose(widget);
+
+  return TRUE;
+}
+
+static void plot_class_init (PlotClass *class){
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  parent_class = g_type_class_peek_parent (class);
+
+  widget_class->expose_event = expose;
+  widget_class->configure_event = configure;
+  widget_class->size_request = size_request;
+}
+
+static void plot_init (Plot *p){
+}
+
+GType plot_get_type (void){
+  static GType m_type = 0;
+  if (!m_type){
+    static const GTypeInfo m_info={
+      sizeof (PlotClass),
+      NULL, /* base_init */
+      NULL, /* base_finalize */
+      (GClassInitFunc) plot_class_init,
+      NULL, /* class_finalize */
+      NULL, /* class_data */
+      sizeof (Plot),
+      0,
+      (GInstanceInitFunc) plot_init,
+      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 *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];
+
+  p->total_ch = ch;
+
+  p->ch=channels;
+  p->rate=rate;
+  p->size=size;
+
+  /* generate all the text layouts we'll need */
+  /* linear X scale */
+  {
+    char *labels[13][11]={
+      {"","100ms","200ms","300ms","400ms","500ms","600ms","700ms","800ms","900ms",""},
+      {"","50ms","100ms","150ms","200ms","250ms","300ms","350ms","400ms","450ms",""},
+      {"","20ms","40ms","60ms","80ms","100ms","120ms","140ms","160ms","180ms",""},
+      {"","10ms","20ms","30ms","40ms","50ms","60ms","70ms","80ms","90ms",""},
+      {"","5ms","10ms","15ms","20ms","25ms","30ms","35ms","40ms","45ms",""},
+      {"","2ms","4ms","6ms","8ms","10ms","12ms","14ms","16ms","18ms",""},
+      {"","1ms","2ms","3ms","4ms","5ms","6ms","7ms","8ms","9ms",""},
+      {"","100\xC2\xBCs","200\xC2\xBCs","300\xC2\xBCs","400\xC2\xBCs","500\xC2\xBCs",
+       "600\xC2\xBCs","700\xC2\xBCs","800\xC2\xBCs","900\xC2\xBCs",""},
+      {"","50\xC2\xBCs","100\xC2\xBCs","150\xC2\xBCs","200\xC2\xBCs","250\xC2\xBCs",
+       "300\xC2\xBCs","350\xC2\xBCs","400\xC2\xBCs","450\xC2\xBCs",""},
+      {"","20\xC2\xBCs","40\xC2\xBCs","60\xC2\xBCs","80\xC2\xBCs","100\xC2\xBCs",
+       "120\xC2\xBCs","140\xC2\xBCs","160\xC2\xBCs","180\xC2\xBCs",""},
+      {"","10\xC2\xBCs","20\xC2\xBCs","30\xC2\xBCs","40\xC2\xBCs","50\xC2\xBCs",
+       "60\xC2\xBCs","70\xC2\xBCs","80\xC2\xBCs","90\xC2\xBCs",""},
+      {"","5\xC2\xBCs","10\xC2\xBCs","15\xC2\xBCs","20\xC2\xBCs","25\xC2\xBCs",
+       "30\xC2\xBCs","35\xC2\xBCs","40\xC2\xBCs","45\xC2\xBCs",""},
+      {"","2\xC2\xBCs","4\xC2\xBCs","6\xC2\xBCs","8\xC2\xBCs","10\xC2\xBCs",
+       "12\xC2\xBCs","14\xC2\xBCs","16\xC2\xBCs","18\xC2\xBCs",""}};
+
+    p->x_layout=calloc(13,sizeof(*p->x_layout));
+    for(i=0;i<12;i++){
+      p->x_layout[i]=calloc(11,sizeof(**p->x_layout));
+      for(j=0;j<10;j++)
+        p->x_layout[i][j]=gtk_widget_create_pango_layout(ret,labels[i][j]);
+    }
+  }
+
+  /* phase Y scale */
+  {
+    char *label1[6] = {"1",".5",".2",".1",".01",".001"};
+    char *label1a[6] = {".5",".25",".1",".05",".005",".0005"};
+
+    char *labeln1[6] = {"-1","-.5","-.2","-.1","-.01","-.001"};
+    char *labeln1a[6] = {"-.5","-.25","-.1","-.05","-.005","-.0005"};
+
+    char *label2[6] = {"0dB","-6dB","-14dB","-20dB","-40dB","-60dB"};
+    char *label3[5] = {"0","-65dB","-96dB","-120dB","-160dB"};
+
+    int val2[6] = {0,-6,-14,-20,-40,-60};
+    int val3[5] = {0,-65,-96,-120,-160};
+
+    p->y_layout=calloc(6,sizeof(*p->y_layout));
+    for(i=0;i<6;i++){
+      p->y_layout[i]=calloc(5,sizeof(**p->y_layout));
+      p->y_layout[i][0]=calloc(6,sizeof(***p->y_layout));
+
+      p->y_layout[i][0][0]=gtk_widget_create_pango_layout(ret,label1[i]);
+      p->y_layout[i][0][1]=gtk_widget_create_pango_layout(ret,label1a[i]);
+      p->y_layout[i][0][2]=gtk_widget_create_pango_layout(ret,label3[0]);
+      p->y_layout[i][0][3]=gtk_widget_create_pango_layout(ret,labeln1a[i]);
+      p->y_layout[i][0][4]=gtk_widget_create_pango_layout(ret,labeln1[i]);
+
+      for(j=1;j<5;j++){
+        char buf[10];
+        p->y_layout[i][j]=calloc(6,sizeof(***p->y_layout));
+        p->y_layout[i][j][0]=gtk_widget_create_pango_layout(ret,label2[i]);
+        p->y_layout[i][j][2]=gtk_widget_create_pango_layout(ret,label3[j]);
+        p->y_layout[i][j][4]=gtk_widget_create_pango_layout(ret,label2[i]);
+        sprintf(buf,"%ddB",(val2[i]+val3[j])/2);
+        p->y_layout[i][j][1]=gtk_widget_create_pango_layout(ret,buf);
+        p->y_layout[i][j][3]=gtk_widget_create_pango_layout(ret,buf);
+      }
+    }
+  }
+
+  p->ch_active=calloc(ch,sizeof(*p->ch_active));
+  
+  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->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, int range, int scale, int interval, int span, int rangechoice, int scalechoice, int spanchoice,
+                   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;
+
+  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);
+}
+

Added: trunk/spectrum/wave_plot.h
===================================================================
--- trunk/spectrum/wave_plot.h	                        (rev 0)
+++ trunk/spectrum/wave_plot.h	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,118 @@
+/*
+ *
+ *  gtk2 waveform viewer
+ *    
+ *      Copyright (C) 2004 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__
+#define __PLOT_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkdrawingarea.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;
+
+struct _Plot{
+
+  GtkDrawingArea canvas;  
+  GdkPixmap *backing;
+  GdkGC     *drawgc;
+
+  PangoLayout ***x_layout;
+  PangoLayout ****y_layout;
+
+  int configured;
+  float **ydata;
+  int size;
+
+  int groups;
+  int *ch;
+  int *ch_active;
+  int total_ch;
+
+  int range;
+  int scale;
+  int interval;
+  int span;
+
+  int rchoice;
+  int schoice;
+  int spanchoice;
+
+  int *blockslice;
+  int *overslice;
+
+  int *rate;
+
+  int xgrid[20];
+  int xgrids;
+  int xtic[200];
+  int xtics;
+
+  int ygrid[5];
+  int ygrids;
+  int ytic[50];
+  int ytics;
+
+  float padx;
+  float phax;
+  float pady;
+
+};
+
+struct _PlotClass{
+
+  GtkDrawingAreaClass parent_class;
+  void (* plot) (Plot *m);
+};
+
+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, int range, int scale, int interval, int span,
+                                     int rangechoice, int scalechoice, int spanchoice,
+                                     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 *);
+
+GdkColor chcolor(int ch);
+
+G_END_DECLS
+
+#endif
+
+
+
+

Added: trunk/spectrum/wave_process.c
===================================================================
--- trunk/spectrum/wave_process.c	                        (rev 0)
+++ trunk/spectrum/wave_process.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,115 @@
+/*
+ *
+ *  gtk2 waveform viewer
+ *    
+ *      Copyright (C) 2004 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;
+
+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 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;
+
+  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){
+  while(!process_exit && process());
+  process_active=0;
+  write(eventpipe[1],"",1);
+  return NULL;
+}
+
+float **process_fetch(int *blockslice,int *overslice,int span){
+  int fi,i,j,k,ch;
+
+  init_process();
+  if(!blockslice || !overslice)return NULL;
+
+  /* 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 = rate[fi]*span/1000000;
+
+    for(i=ch;i<ch+channels[fi];i++){
+      int offset=0;
+      float *plotdatap=plot_data[i];
+      pthread_mutex_lock(&feedback_mutex);
+      for(j=0;j<copies;j++){
+        float *data=blockbuffer[i]+offset;
+        for(k=0;k<spann;k++)
+          *(plotdatap++)=data[k];
+        offset+=overslice[fi];
+      }
+    }
+    ch+=channels[fi];
+  }
+
+  return plot_data;
+}

Added: trunk/spectrum/waveform.c
===================================================================
--- trunk/spectrum/waveform.c	                        (rev 0)
+++ trunk/spectrum/waveform.c	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,288 @@
+/*
+ *
+ *  gtk2 waveform viewer
+ *    
+ *      Copyright (C) 2004 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 <signal.h>
+#include <getopt.h>
+#include <fenv.h>  // Thank you C99!
+#include <gtk/gtk.h>
+#include "version.h"
+#include "io.h"
+
+int eventpipe[2];
+char *version;
+char *inputname[MAX_FILES];
+int inputs=0;
+int blocksize = 131072; /* starting default */
+
+void handler(int sig){
+  signal(sig,SIG_IGN);
+  if(sig!=SIGINT){
+    fprintf(stderr,
+	    "\nTrapped signal %d; exiting!\n"
+	    "This signal almost certainly indicates a bug in the analyzer;\n"
+	    "If this version of the analyzer is newer than a few months old,\n"
+	    "please email a detailed report of the crash along with\n"
+	    "processor type, OS version, and as much information as possible\n"
+            "about what caused the crash.  The best possible report will\n"
+            "outline the exact steps needed to reproduce the error, ensuring\n"
+            "that I can fix the bug as quickly as possible.\n\n"
+	    "-- monty at xiph.org, spectrum revision %s\n\n",sig,version);
+  }
+
+  gtk_main_quit();
+}
+
+const char *optstring = "-r:c:EeBlb:suh";
+
+struct option options [] = {
+        {"rate",required_argument,NULL,'r'},
+        {"channels",required_argument,NULL,'c'},
+        {"big-endian",no_argument,NULL,'E'},
+        {"little-endian",no_argument,NULL,'e'},
+        {"bits",required_argument,NULL,'b'},
+        {"signed",no_argument,NULL,'s'},
+        {"unsigned",no_argument,NULL,'u'},
+        {"help",no_argument,NULL,'h'},
+
+        {NULL,0,NULL,0}
+};
+
+static void usage(FILE *f){
+  fprintf( f,
+"\ngtk2 waveform viewer, revision %s\n\n"
+
+"USAGE:\n\n"
+"  waveform [options] [file]\n\n"
+
+"OPTIONS:\n\n"
+"  -b --bits <bits>           : Force input to be read as 8, 16, 24 or 32 bit\n"
+"                               PCM. Default bit depth is normally read from\n"
+"                               the file/stream header or set to 16 bits\n"
+"                               for raw input.\n"
+"  -B -E --big-endian         : Force input to be read as big endian.\n"
+"                               Default endianness is normally read from the\n"
+"                               file/stream header or set to host"
+"                               endianness for raw input.\n"
+"  -c --channels <channels>   : Input channel number override; use to\n"
+"                               specify the number of channels in a raw\n"
+"                               input.  default: 1\n"
+"  -e -l --little-endian      : Force input to be read as little endian.\n"
+"                               Default endianness is normally read from the\n"
+"                               file/stream header or set to host"
+"                               endianness for raw input.\n"
+"  -h --help                  : print this help\n"
+"  -r --rate <Hz>             : Input sample rate override in Hz; use to\n"
+"                               specify the rate of a raw input.\n"
+"                               default: 44100\n"
+"  -s --signed                : Force input to be read as signed PCM.\n"
+"                               Signedness is normally read from the \n"
+"                               file/stream header or set to signed for raw\n"
+"                               input.\n"
+"  -u --unsigned              : Force input to be read as unsigned PCM.\n"
+"                               Signedness is normally read from the \n"
+"                               file/stream header or set to signed for raw\n"
+"                               input.\n\n"
+
+"INPUT:\n\n"
+
+" Waveform takes raw, WAV or AIFF input either from stdin or from \n"
+" file[s]/stream[s] specified on the command line.\n\n",version);
+
+}
+
+void parse_command_line(int argc, char **argv){
+  int c,long_option_index;
+
+  while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
+    switch(c){
+    case 1:
+      /* file name that belongs to current group */
+      if(inputs>=MAX_FILES){
+	fprintf(stderr,"Maximum of MAX_FILES input files exceeded.  Oops.  Programmer was lazy.\n\n");
+	exit(1);
+      }
+      inputname[inputs++]=strdup(optarg);
+      break;
+    case 'b':
+      /* force bit width */
+      {
+	int a=atoi(optarg);
+	bits[inputs]=a;
+	if(a!=8 && a!=16 && a!=24 && a!=32){
+	  usage(stderr);
+	  exit(1);
+	}
+      }
+      break;
+    case 'B':case 'E':
+      /* force big endian */
+      bigendian[inputs]=1;
+      break;
+    case 'c':
+      /* force channels */
+      {
+	int a=atoi(optarg);
+	channels[inputs]=a;
+	if(a<1 || a>32){
+	  usage(stderr);
+	  exit(1);
+	}
+      }
+      break;
+    case 'l':case 'e':
+      /* force little endian */
+      bigendian[inputs]=0;
+      break;
+    case 'h':
+      usage(stdout);
+      exit(0);
+    case 'r':
+      /* force rate */
+      {
+	int a=atoi(optarg);
+	rate[inputs]=a;
+	if(a<4000 || a>200000){
+	  usage(stderr);
+	  exit(1);
+	}
+      }
+      break;
+    case 's':
+      /* force signed */
+      signedp[inputs]=1;
+      break;
+    case 'u':
+      /* force unsigned */
+      signedp[inputs]=0;
+      break;
+    default:
+      usage(stderr);
+      exit(0);
+    }
+  }
+}
+
+static int sigill=0;
+void sigill_handler(int sig){
+  /* make sure */
+  if(sig==SIGILL)sigill=1;
+}
+
+int main(int argc, char **argv){
+  int fi;
+
+  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. */
+
+#ifndef DEBUG
+  fedisableexcept(FE_INVALID);
+  fedisableexcept(FE_INEXACT);
+  fedisableexcept(FE_UNDERFLOW);
+  fedisableexcept(FE_OVERFLOW);
+#else
+  feenableexcept(FE_INVALID);
+  feenableexcept(FE_INEXACT);
+  feenableexcept(FE_UNDERFLOW);
+  feenableexcept(FE_OVERFLOW);
+#endif 
+
+  /* Linux Altivec support has a very annoying problem; by default,
+     math on denormalized floats will simply crash the program.  FFTW3
+     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 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};
+#else
+  vector unsigned short noTrap = 
+    (vector unsigned short)(0,0,0,0,0,0,0x1,0);
+#endif
+
+  vec_mtvscr(noTrap);
+#endif
+
+  /* easiest way to inform gtk of changes and not deal with locking
+     issues around the UI */
+  if(pipe(eventpipe)){
+    fprintf(stderr,"Unable to open event pipe:\n"
+            "  %s\n",strerror(errno));
+    
+    exit(1);
+  }
+
+  /* Allows event compression on the read side */
+  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);
+
+  /* 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++)
+    blockslice[fi]=rate[fi]/10;
+
+  panel_go(argc,argv);
+
+  return(0);
+}

Added: trunk/spectrum/waveform.h
===================================================================
--- trunk/spectrum/waveform.h	                        (rev 0)
+++ trunk/spectrum/waveform.h	2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,96 @@
+/*
+ *
+ *  gtk2 spectrum analyzer
+ *    
+ *      Copyright (C) 2004 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_
+#define _WAVEFORM_H_
+
+#define _GNU_SOURCE
+#define _ISOC99_SOURCE
+#define _FILE_OFFSET_BITS 64
+#define _REENTRANT 1
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#define __USE_GNU 1
+#include <pthread.h>
+#include <string.h>
+#include <math.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#define MAX_FILES 16
+#define readbuffersize 8192
+
+extern int blocksize;
+
+static inline float todB(float x){
+  return logf((x)*(x)+1e-30f)*4.34294480f;
+}
+
+#ifdef UGLY_IEEE754_FLOAT32_HACK
+
+static inline float todB_a(const float *x){
+  union {
+    int32_t i;
+    float f;
+  } ix;
+  ix.f = *x;
+  ix.i = ix.i&0x7fffffff;
+  return (float)(ix.i * 7.17711438e-7f -764.6161886f);
+}
+
+#else
+
+static inline float todB_a(const float *x){
+  return todB(*x);
+}
+
+#endif
+
+#ifndef max
+#define max(x,y) ((x)>(y)?(x):(y))
+#endif
+
+
+#define toOC(n)     (log(n)*1.442695f-5.965784f)
+#define fromOC(o)   (exp(((o)+5.965784f)*.693147f))
+
+extern int eventpipe[2];
+
+extern void panel_go(int argc,char *argv[]);
+extern void *process_thread(void *dummy);
+extern float **process_fetch(int *, int *, int);
+
+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;
+
+#endif
+



More information about the commits mailing list