[xiph-commits] r14578 - trunk/spectrum

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Tue Mar 11 18:14:29 PDT 2008


Author: xiphmont
Date: 2008-03-11 18:14:29 -0700 (Tue, 11 Mar 2008)
New Revision: 14578

Added:
   trunk/spectrum/COPYING
   trunk/spectrum/Makefile
   trunk/spectrum/analyzer.h
   trunk/spectrum/fisharray.h
   trunk/spectrum/main.c
   trunk/spectrum/panel.c
   trunk/spectrum/plot.c
   trunk/spectrum/plot.h
   trunk/spectrum/process.c
   trunk/spectrum/spectrum-gtkrc
   trunk/spectrum/touch-version
   trunk/spectrum/version.h
Log:
Just get current working state of app as it previously existed back into 
SVN



Added: trunk/spectrum/COPYING
===================================================================
--- trunk/spectrum/COPYING	                        (rev 0)
+++ trunk/spectrum/COPYING	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program 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 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Added: trunk/spectrum/Makefile
===================================================================
--- trunk/spectrum/Makefile	                        (rev 0)
+++ trunk/spectrum/Makefile	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,71 @@
+# Fuck Automake
+# Fuck the horse it rode in on
+# and Fuck its little dog Libtool too
+
+
+# Use the below line to build for PowerPC
+# The PPC build *must* use -maltivec, even if the target is a non-altivec machine
+
+#ADD_DEF= -DUGLY_IEEE754_FLOAT32_HACK=1 -maltivec -mcpu=7400
+
+# use the below for x86 and most other platforms where 'float' is 32 bit IEEE754
+
+#ADD_DEF= -DUGLY_IEEE754_FLOAT32_HACK=1
+
+# use the below for anything without IEE754 floats (eg, VAX)
+
+# ADD_DEF=
+
+
+CC=gcc 
+LD=gcc
+INSTALL=install
+PREFIX=/usr/local
+BINDIR=$(PREFIX)/bin
+ETCDIR=/etc/spectrum
+MANDIR=$(PREFIX)/man
+
+SRC = main.c process.c panel.c plot.c
+OBJ = main.o process.o panel.o plot.o
+GCF = -DETCDIR=\\\"$(ETCDIR)\\\" `pkg-config --cflags gtk+-2.0` -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED
+
+all:	
+	$(MAKE) target CFLAGS="-O3 -ffast-math -fomit-frame-pointer $(GCF) $(ADD_DEF)"
+
+debug:
+	$(MAKE) target CFLAGS="-g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES $(GCF) $(ADD_DEF)"
+
+profile:
+	$(MAKE) target CFLAGS="-pg -g -O3 -ffast-math $(GCF) $(ADD_DEF)" LIBS="-lgprof-helper "
+
+clean:
+	rm -f $(OBJ) *.d *.d.* gmon.out spectrum
+
+distclean: clean
+	rm -f spectrum-wisdomrc
+
+%.d: %.c
+	$(CC) -M $(CFLAGS) $< > $@.$$$$; sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; rm -f $@.$$$$
+
+spectrum-wisdomrc:
+	fftwf-wisdom -v -o spectrum-wisdomrc \
+	rif8192 rib8192
+
+ifeq ($(MAKECMDGOALS),target)
+include $(SRC:.c=.d)
+endif
+
+target:  $(OBJ) spectrum-wisdomrc
+	./touch-version
+	$(LD) $(OBJ) $(CFLAGS) -o spectrum $(LIBS) `pkg-config --libs gtk+-2.0` -lpthread -lfftw3f -lm 
+
+install: target
+	$(INSTALL) -d -m 0755 $(BINDIR)
+	$(INSTALL) -m 0755 spectrum $(BINDIR)
+	$(INSTALL) -d -m 0755 $(ETCDIR)
+	$(INSTALL) -m 0644 spectrum-gtkrc $(ETCDIR)
+	$(INSTALL) -m 0644 spectrum-wisdomrc $(ETCDIR)
+#	$(INSTALL) -d -m 0755 $(MANDIR)
+#	$(INSTALL) -d -m 0755 $(MANDIR)/man1
+#	$(INSTALL) -m 0644 spectrum.1 $(MANDIR)/man1
+

Added: trunk/spectrum/analyzer.h
===================================================================
--- trunk/spectrum/analyzer.h	                        (rev 0)
+++ trunk/spectrum/analyzer.h	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,128 @@
+/*
+ *
+ *  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 _ANALYZER_H_
+#define _ANALYZER_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>
+#include <fftw3.h>
+
+#define MAX_FILES 16
+#define readbuffersize 8192
+/* blocksize for the FFT */
+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 int input_load();
+extern void *process_thread(void *dummy);
+extern void process_dump(int mode);
+extern void rundata_clear();
+extern float **process_fetch(int scale, int mode,int link, int *active, int width, 
+			     float *ymax, float *pmax, float *pmin);
+
+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;
+extern float **feedback_max;
+extern float **feedback_instant;
+extern float **feedback_rp;
+extern sig_atomic_t acc_clear;
+extern sig_atomic_t acc_rewind;
+extern sig_atomic_t acc_loop;
+
+extern sig_atomic_t process_active;
+extern sig_atomic_t process_exit;
+
+#define LINKS 8
+static char *link_entries[LINKS]={"independent","summed",".1\xCE\xA9 shunt","1\xCE\xA9 shunt","10\xCE\xA9 shunt",
+			   "resp/phase","THD","THD+N"};
+#define LINK_INDEPENDENT  0
+#define LINK_SUMMED       1
+#define LINK_IMPEDENCE_p1 2
+#define LINK_IMPEDENCE_1  3
+#define LINK_IMPEDENCE_10 4
+#define LINK_PHASE        5
+#define LINK_THD          6
+#define LINK_THDN         7
+
+#endif
+

Added: trunk/spectrum/fisharray.h
===================================================================
--- trunk/spectrum/fisharray.h	                        (rev 0)
+++ trunk/spectrum/fisharray.h	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,1245 @@
+static char *ff_xpm[19][66] = {
+  /* 0 */
+  {
+    "40 40 23 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #27536C",
+    "$	c #111E1E",
+    "%	c #04423C",
+    "&	c #059B9A",
+    "*	c #06E1E0",
+    "=	c #147A64",
+    "-	c #3C3E04",
+    ";	c #1CC2A4",
+    ">	c #2CFEFC",
+    ",	c #84824C",
+    "'	c #EDEEEC",
+    ")	c #CCCACC",
+    "!	c #5D602C",
+    "~	c #A1A20A",
+    "{	c #E7E809",
+    "]	c #BECD18",
+    "^	c #9CEE4C",
+    "/	c #46E09D",
+    "(	c #76D260",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@@@.####++++            ",
+    "           @@@@@@.#$$$$$$#+++           ",
+    "          @@@@@@.$%&****=$$#++          ",
+    "         @@.. at .-$%;*>>>**;=$$++         ",
+    "        @.#$$#$$&***>>>>***&%$++        ",
+    "       @+$,')!$$$$$&;*****;*;%$++       ",
+    "      @@$,''''$,~~-$%=******;;%%++      ",
+    "     @@+$''-!'.!{{~{~$=;;;;;;&&$$##     ",
+    "    @@.$$''!!',!{{~{{,$=;;&&==$$$$$$$   ",
+    "   @@.$,$,''''$],]~{!{~$%%%%$$=;***&$   ",
+    "  #$$$~{]-#).$,{{{~{]{],$$$%=;******$$  ",
+    " $%&&$-{{]!$-~{{{{{{,{,{~-$%***>>***$#+ ",
+    "#$***&$-{{{{{{{{{{{{]{]]!]~-*******&$#++",
+    "#$$;;*&$-{{{{{!!!!!{,{,{]~!]*****&%$$##+",
+    " +$$$=;&$]{{{^===$-]~]~]!]~]****&%$$### ",
+    "  ##$$=;%~{{{/>*&-]],],]]],(******;#$#  ",
+    "   #%$$;&{{^/**&-]{{{{{]!{]/*******=$   ",
+    "   $$*;=${{]$$$$~{{{]]]]~]];*******=$   ",
+    "   $%%%$$~{{~,,~{{]]!------&*******=$   ",
+    "    $$$$$$,~]]]]~,-$%=*$$$$$$=&&&&=$$   ",
+    "       ++#$$----$$%&;**%$###$$$$$$$$    ",
+    "        +++#$$$$$$$%&&=$$#######        ",
+    "         +++++++++#$$$$$#######         ",
+    "          ++++++++++##########          ",
+    "           +++++++++#########           ",
+    "            ++++++++########            ",
+    "             +++++++#######             ",
+    "              ++++++######              ",
+    "               +++++#####               ",
+    "                ++++####                ",
+    "                 +++###                 ",
+    "                  ++##                  ",
+    "                   ++                   "},
+  /* 1 */
+  {
+    "40 40 22 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #27536C",
+    "$	c #111E1E",
+    "%	c #04423C",
+    "&	c #059B9A",
+    "*	c #06E1E0",
+    "=	c #1CC2A4",
+    "-	c #147A64",
+    ";	c #3C3E04",
+    ">	c #84824C",
+    ",	c #EDEEEC",
+    "'	c #5D602C",
+    ")	c #A1A20A",
+    "!	c #CCCACC",
+    "~	c #E7E809",
+    "{	c #BECD18",
+    "]	c #46E09D",
+    "^	c #9CEE4C",
+    "/	c #76D260",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@@@.####++++            ",
+    "           @@@@@@.#$$$$$$++++           ",
+    "          @@@@@@.$%&*=&-%$++++          ",
+    "         @@....+$%=****=&%$#+++         ",
+    "        @@#$$$$$%=&&=****&%$#+++        ",
+    "       @@+;$>,,+$$$$-&****&%$#+++       ",
+    "      @@.+'',,,,'>)$$%&****&%$++++      ",
+    "     @@@.>;!,';,>;~~);%&=*=&&$$####     ",
+    "    @@@@#>',,+',>;~~~)$%&&&-%$$$$$$+    ",
+    "   @@@@;'~'.,,,,;>~>~){$%%%$$$&=&%$++   ",
+    "  @+$$$$){{;'!!#$~~~~{{{$$$%==**=#$+++  ",
+    " @.$%&&%$;~{'$$${~~~~)))>;$&****&#$++++ ",
+    "..#$=***=$;{~~~~~~~~~{{)){;&**=&&%$+++++",
+    "+++$$--&==$;~~~~~';;;;))){~]*&&%%$#####+",
+    " +++$$$%%==$)~~~^-%%$$))>{{]*&&&%$##### ",
+    "  +++#$$$-*-'~~~]**-${>){{{=****&$####  ",
+    "   +++#$$-*&{~~/=&&%'~~~{{)&***=&$%##   ",
+    "    +#$**&$${~~{$$$$){{))>--&===&$%#    ",
+    "     #$#%$$;{~~{>>>>{;;;$$$$#&&&%$#     ",
+    "      #$$$$'')))))''$$%%$$#$$$$$$$      ",
+    "       +++#$$$;;;;$$#&=&$%###%%%#       ",
+    "        +++++#$$$$%%$%%$$#######        ",
+    "         ++++++++++$$$$$#######         ",
+    "          ++++++++++##########          ",
+    "           +++++++++#########           ",
+    "            ++++++++########            ",
+    "             +++++++#######             ",
+    "              ++++++######              ",
+    "               +++++#####               ",
+    "                ++++####                ",
+    "                 +++###                 ",
+    "                  ++##                  ",
+    "                   ++                   "},
+  /* 2 */
+  {
+    "40 40 22 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #27536C",
+    "$	c #111E1E",
+    "%	c #147A64",
+    "&	c #06E1E0",
+    "*	c #04423C",
+    "=	c #059B9A",
+    "-	c #1CC2A4",
+    ";	c #CCCACC",
+    ">	c #EDEEEC",
+    ",	c #5D602C",
+    "'	c #84824C",
+    ")	c #ACAE6C",
+    "!	c #3C3E04",
+    "~	c #A1A20A",
+    "{	c #FCFE4C",
+    "]	c #E7E809",
+    "^	c #2CFEFC",
+    "/	c #BECD18",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@@@+#+++++++            ",
+    "           @@@@@@.$$$$#++++++           ",
+    "          @@@@@@.$%&&%$#++++++          ",
+    "         @@@@..@#*=-&-=$+++++++         ",
+    "        @@@@#$$#$*$$*-=**+++++++        ",
+    "       @@@@.$;>+$,>>'*=%*#+++++++       ",
+    "      @@@@.$;>>>,>>>>$%=%$++++++++      ",
+    "     @@@@@.$>;$)@>!,>.%==*#++++++++     ",
+    "    @@@@@@.$>;$~.>,,>'$%=%$#++++++++    ",
+    "   @@@@@@@+$''{]!>>>>$!$%*$$#++++++++   ",
+    "  @@@@@@.$$**$$~',;.$,~$$$*%$#++++++++  ",
+    " @@@@@@@#*=^^-%$~~!!']~~$%==*#+++++++++ ",
+    "........$=^^&&&-$~//]]/,,*==*#++++++++++",
+    "++++++++*&-***-==$~],!!!$,==*##########+",
+    " +++++++**%$$$$$*%$~%**$$'=%$########## ",
+    "  +++++++**%$$$$$**~--=*!~==*#########  ",
+    "   +++++++*$*%%%$%=~%*%$~,==*########   ",
+    "    ++++++#$-&&&=%$~~!$$,*=%$#######    ",
+    "     +++++#$$****$,~~~,$$$**$######     ",
+    "      +++++#$$!!!~~~'!$$$$$$######      ",
+    "       ++++++$!!!!!!$$%%$########       ",
+    "        ++++++$$$*$$#**=$#######        ",
+    "         +++++++#*++##$$#######         ",
+    "          ++++++++++##########          ",
+    "           +++++++++#########           ",
+    "            ++++++++########            ",
+    "             +++++++#######             ",
+    "              ++++++######              ",
+    "               +++++#####               ",
+    "                ++++####                ",
+    "                 +++###                 ",
+    "                  ++##                  ",
+    "                   ++                   "},
+  /* 3 */
+  {
+    "40 40 20 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #27536C",
+    "$	c #04423C",
+    "%	c #147A64",
+    "&	c #111E1E",
+    "*	c #06E1E0",
+    "=	c #059B9A",
+    "-	c #5D602C",
+    ";	c #EDEEEC",
+    ">	c #CCCACC",
+    ",	c #3C3E04",
+    "'	c #84824C",
+    ")	c #ACAE6C",
+    "!	c #2CFEFC",
+    "~	c #A1A20A",
+    "{	c #1CC2A4",
+    "]	c #46E09D",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@@@.++++++++            ",
+    "           @@@@@@@@.+++++++++           ",
+    "          @@@@@@@@@#$+++++++++          ",
+    "         @@@@@@@...%%+##+++++++         ",
+    "        @@@@@@.#&&&*=&&&&#++++++        ",
+    "       @@@@@@@+-;;.$$.;;-#+++++++       ",
+    "      @@@@@@@@&>;;;,,;;;>&++++++++      ",
+    "     @@@@@@@@@-;-,;@@;,-;-+++++++++     ",
+    "    @@@@@@@@@@-;'-;));-';-++++++++++    ",
+    "   @@@@@@@@@@@,;;;>,,>;;;,+++++++++++   ",
+    "  @@@@@@@@@@@@&,>,&$$&,>,&++++++++++++  ",
+    " @@@@@@@@@@@@@&-,%*!*=%,,&+++++++++++++ ",
+    "..............&~&*****{&-&++++++++++++++",
+    "+++++++++&&&&&,-%{$$$$=$,,&&&&&########+",
+    " ++++++++&#%%&--{&&&&&&%,,&$$&&######## ",
+    "  +++++++$#!!$--%&&&&&&$,,$=%&&#######  ",
+    "   ++++++#&{*&-]$&%%%%&&%,&%$&#######   ",
+    "    ++++++&&&&,-{*****=%,,&&&&######    ",
+    "     ++++++#++&~-$$$$$$,'&##&######     ",
+    "      ++++++++#&~~,,,,'-&&########      ",
+    "       ++++++++#&,,,,,,&&########       ",
+    "        +++++++${#&&&&&%&#######        ",
+    "         ++++++&#+++###$&######         ",
+    "          ++++++++++##########          ",
+    "           +++++++++#########           ",
+    "            ++++++++########            ",
+    "             +++++++#######             ",
+    "              ++++++######              ",
+    "               +++++#####               ",
+    "                ++++####                ",
+    "                 +++###                 ",
+    "                  ++##                  ",
+    "                   ++                   "},
+  /* 4 */
+  {
+    "40 40 25 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #27536C",
+    "$	c #04423C",
+    "%	c #111E1E",
+    "&	c #1CC2A4",
+    "*	c #2CFEFC",
+    "=	c #06E1E0",
+    "-	c #059B9A",
+    ";	c #147A64",
+    ">	c #84824C",
+    ",	c #EDEEEC",
+    "'	c #5D602C",
+    ")	c #CCCACC",
+    "!	c #3C3E04",
+    "~	c #ACAE6C",
+    "{	c #E7E809",
+    "]	c #FCFE4C",
+    "^	c #A1A20A",
+    "/	c #BECD18",
+    "(	c #479653",
+    "_	c #46E09D",
+    ":	c #CCFA74",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@@@+#+++++++            ",
+    "           @@@@@@.$%%%#++++++           ",
+    "          @@@@@@.%&*=-%#++++++          ",
+    "         @@@@@@.%&**=-$%+++++++         ",
+    "        @@@@@@@#;**;%%$%%%%$++++        ",
+    "       @@@@@@@+$**;>,,'%+,)%#++++       ",
+    "      @@@@@@@@%&*=%,,,,',,,)%+++++      ",
+    "     @@@@@@@@+;**-.,'!,@~%),%#+++++     ",
+    "    @@@@@@@@.%&*=$>,'',.~%),%#++++++    ",
+    "   @@@@@@@@+#%-&$'%,,,,!{]>>%#+++++++   ",
+    "  @@@@@@@@.%&;$%%~>%.)'>^%%$$%%+++++++  ",
+    " @@@@@@@@@.;**&%//]^!!^~%-&**-$$+++++++ ",
+    "..........+;**(^~]]]]]~%=****=;%++++++++",
+    "++++++++++#;==>%'''>]~%=*=#$$&-%#######+",
+    " +++++++++#$==/%%$$(]%&-%%%%$&$%####### ",
+    "  ++++++++#-**]'#==_];$%%%%%-$%#######  ",
+    "   +++++++#;**^{%-$;:*-%;;;$%%#######   ",
+    "    ++++++##==;'%%!{]%-=**=;%#######    ",
+    "     ++++++%$$$%!^{{{>%;$$$%%######     ",
+    "      ++++++%%#$%%'^{{/!!!%%%#####      ",
+    "       ++++++++#&&$%!!!!!!%######       ",
+    "        +++++++%$-##%%$%%%######        ",
+    "         ++++++#%%++##$%#######         ",
+    "          ++++++++++##########          ",
+    "           +++++++++#########           ",
+    "            ++++++++########            ",
+    "             +++++++#######             ",
+    "              ++++++######              ",
+    "               +++++#####               ",
+    "                ++++####                ",
+    "                 +++###                 ",
+    "                  ++##                  ",
+    "                   ++                   "},
+  /* 5 */
+  {
+    "40 40 24 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #27536C",
+    "$	c #111E1E",
+    "%	c #04423C",
+    "&	c #3C3E04",
+    "*	c #1CC2A4",
+    "=	c #2CFEFC",
+    "-	c #06E1E0",
+    ";	c #059B9A",
+    ">	c #147A64",
+    ",	c #EDEEEC",
+    "'	c #84824C",
+    ")	c #A1A20A",
+    "!	c #5D602C",
+    "~	c #E7E809",
+    "{	c #CCCACC",
+    "]	c #BECD18",
+    "^	c #46E09D",
+    "/	c #CCFA74",
+    "(	c #76D260",
+    "_	c #9CEE4C",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@.+++++++++++            ",
+    "           @@@@#$$$$$%+++++++           ",
+    "          @@@@&%*=--;%$#++++++          ",
+    "         @@@+$>-==---*%$####+++         ",
+    "        @@@+$>====----*%$$$$$%++        ",
+    "       @@@+$>-===--;%$$$+,,'$&#++       ",
+    "      @@@@$%-==--->$$))!,,,,!!++++      ",
+    "     ++++&$------>&)~~&',&!,{&'++++     ",
+    "    @$$$$$$>*--->$)~~~&',!+,,!'$++++    ",
+    "   @@$%--;$$$>>%$~)~'~)&,,,,.!~!$++++   ",
+    "  @@@$;---**%$$$~]~~~~~$#{{!$]]'$$$$#+  ",
+    " @@@@$;===--*$&))])~~~~]$$$!]]&$%;;%$#+ ",
+    ".....$%----=*!~]]~~~~~~~~~~~]$$----;$#++",
+    "++++++$%;;--^~~]]]!!!!!~~~~~&$---;;$$##+",
+    " +++++$$%;;-^~~))]&$>>>~~~~)%-->%$$$### ",
+    "  +++#$-----=/]~))~$*--(~~~!>-%$$$$###  ",
+    "   ++#$--===-_~~~~~!%;;;(~~~-*%$$%###   ",
+    "    +#$----*;>))~~~)$$$$]~~]$%--;$##    ",
+    "     #$>;;;#$$$$!!!])''']~~]&$$%%$#     ",
+    "      $$$$$$#+#%%%$$!)]]]))!'$$$$#      ",
+    "       ####+++#%--*%$$&&&&$$$%###       ",
+    "        +++++++$%>;$%#$$$$%#####        ",
+    "         ++++++#$$$$$##########         ",
+    "          ++++++++++##########          ",
+    "           +++++++++#########           ",
+    "            ++++++++########            ",
+    "             +++++++#######             ",
+    "              ++++++######              ",
+    "               +++++#####               ",
+    "                ++++####                ",
+    "                 +++###                 ",
+    "                  ++##                  ",
+    "                   ++                   "},
+  /* 6 */
+  {
+    "40 40 23 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #111E1E",
+    "$	c #04423C",
+    "%	c #059B9A",
+    "&	c #06E1E0",
+    "*	c #27536C",
+    "=	c #2CFEFC",
+    "-	c #1CC2A4",
+    ";	c #147A64",
+    ">	c #5D602C",
+    ",	c #CCCACC",
+    "'	c #EDEEEC",
+    ")	c #84824C",
+    "!	c #3C3E04",
+    "~	c #A1A20A",
+    "{	c #E7E809",
+    "]	c #BECD18",
+    "^	c #9CEE4C",
+    "/	c #46E09D",
+    "(	c #479653",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@++++++++++++            ",
+    "           @@.+######$+++++++           ",
+    "          @@+#$%&&&&%##*++++++          ",
+    "         @@*#%&===&&&%$##+++*++         ",
+    "        @@*$-====&&&&%%;######*+        ",
+    "       @@**&===&&&-%$#####>,')#*+       ",
+    "      @@*$&==&&&&%*##!~~>#'''')#++      ",
+    "     *+*#&&&&&&&%$#~{~{{>.'>!''#*++     ",
+    "   ######$%-&-%%$#){{~{{>)'>>''##+++    ",
+    "  ##%&&&&$##$$$$#~{>{~])]#'''')#>#+++   ",
+    "  ##&==&&&&-$###~]{]{~{{{)#.,*#~{)###*  ",
+    " +##&&=&&&&&$#!~{){){{{{{{)##>~{{!#%;$# ",
+    "..*#%&&&&&&&>~{>]]{]{{{{{{{]]{{]!#-=&%#*",
+    "+++##$%-&-&&{)]{{){){!!!!!{{{{{!#&&-%##*",
+    " +++##;%&&&&{]{>]~{~{!#;;;^{]]~#-&%###* ",
+    "  +#$%&&&=&&/){]{~{){]!%&=/]]]>$&%##**  ",
+    "   #;&==&&&&/{{>]{{{{{]!;-%(~]~&&###*   ",
+    "   #;&&&&----]{]]]]]{{{)####]~~#;&%##   ",
+    "   #$%&&-%%%;!>>!!!>]~]]))>>~~>##$$##   ",
+    "   ##$$$**$######$$##!>~~~~~>>######    ",
+    "    ########*++#$&=-%###!!!!##$**       ",
+    "        +++++++$#%=%$#######$***        ",
+    "         +++++++$#$##**********         ",
+    "          ++++++++++**********          ",
+    "           +++++++++*********           ",
+    "            ++++++++********            ",
+    "             +++++++*******             ",
+    "              ++++++******              ",
+    "               +++++*****               ",
+    "                ++++****                ",
+    "                 +++***                 ",
+    "                  ++**                  ",
+    "                   ++                   "},
+  /* 7 */
+  {
+    "40 40 22 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #111E1E",
+    "$	c #04423C",
+    "%	c #27536C",
+    "&	c #147A64",
+    "*	c #06E1E0",
+    "=	c #059B9A",
+    "-	c #1CC2A4",
+    ";	c #2CFEFC",
+    ">	c #5D602C",
+    ",	c #CCCACC",
+    "'	c #EDEEEC",
+    ")	c #3C3E04",
+    "!	c #84824C",
+    "~	c #E7E809",
+    "{	c #A1A20A",
+    "]	c #BECD18",
+    "^	c #76D260",
+    "/	c #479653",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@.++++++++++            ",
+    "           @@@@@+####$+++++++           ",
+    "          @@@@.%$&**=$#%++++++          ",
+    "         @@@@.#$*****-$#%+%%+++         ",
+    "        @@@@.#-;;***-=&######$++        ",
+    "       @@@@.#=;****===&%#>#,'>#++       ",
+    "      @.%%%#$*****-=&$####>'.'>+++      ",
+    "     @.#$$$$#$=====&#)!~~~>>#,!#+++     ",
+    "    @@%#=***&##&&=&##{]~~~).>'!#++++    ",
+    "   @@@%$*;****$##$#)!~~]~~{>,'))#++++   ",
+    "  @@@@%$*****==&$#)]~~{~~]]>###{)#%+++  ",
+    " @@@@@%$****===$!{~]~{~~~~]~{>{{>$&#+++ ",
+    "......%#=*-====]~]{]{{]~]]]]{{{{)&-$%+++",
+    "+++++++#$$&====]~{~!!~~]>)>{{{{)&-&$%%%+",
+    " ++++++%##$&***]~]{~~]]])$$#){>&*$#$%%% ",
+    "  +++++##=*****^~~~!!]]{{#--={>=$#%%%%  ",
+    "   ++++#=*;***==~]]~]]{]{)$=={/&#$#%%   ",
+    "    +++#=****===/{{{{]{{{{###{>&=&#%    ",
+    "     ++#=***=====)))){{{{{>>!{>#$$%     ",
+    "      +#&=====%$####$##>!{{!>####%      ",
+    "       ##&&&$#####$$*==%#))###$%%       ",
+    "        #######%#&=$$==$$###%%%%        ",
+    "         %%%++++$##%####$%%%%%%         ",
+    "          ++++++++++%##%%%%%%%          ",
+    "           +++++++++%%%%%%%%%           ",
+    "            ++++++++%%%%%%%%            ",
+    "             +++++++%%%%%%%             ",
+    "              ++++++%%%%%%              ",
+    "               +++++%%%%%               ",
+    "                ++++%%%%                ",
+    "                 +++%%%                 ",
+    "                  ++%%                  ",
+    "                   ++                   "},
+  /* 8 */
+  {
+    "40 40 21 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #111E1E",
+    "$	c #04423C",
+    "%	c #059B9A",
+    "&	c #27536C",
+    "*	c #06E1E0",
+    "=	c #1CC2A4",
+    "-	c #5D602C",
+    ";	c #84824C",
+    ">	c #EDEEEC",
+    ",	c #3C3E04",
+    "'	c #147A64",
+    ")	c #BECD18",
+    "!	c #CCCACC",
+    "~	c #A1A20A",
+    "{	c #E7E809",
+    "]	c #479653",
+    "^	c #76D260",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@@.+++++++++            ",
+    "           @@@@@@.###$+++++++           ",
+    "          @@@@@@.#$%%#&+++++++          ",
+    "         @@@@@@@&$**%&#&&&+++++         ",
+    "        @@@@@@@.#%*%%%#####&++++        ",
+    "       @@@@@++.#$==%%%$-;>,#+++++       ",
+    "      @@@@@+####'%%%%$##>>>,&+++++      ",
+    "     @@@@@.#$%'#$%%'#,)#@#!-#++++++     ",
+    "    @@@@@@.#'%%'#''#~){-!-!-#+++++++    ",
+    "   @@@@@@@.#%**%$##~{{)-,!!#&++++++++   ",
+    "  @@@@@@@@.#===%'##{)~))~####&++++++++  ",
+    " @@@@@@@@@.#%%%%%'){~{~)~;,$'#&++++++++ ",
+    "...........#'%%%%){));)~~~;#%#$+++++++++",
+    "+++++++++++$#'%%%]{)~;~~~-,###&&&&&&&&&+",
+    " +++++++++++##'%%'~--~;~~]''##&&&&&&&&& ",
+    "  ++++++++++#$'%%'~~;-~~^**%#&&&&&&&&&  ",
+    "   ++++++++##%=*%]~~~~~~~$'$#&&&&&&&&   ",
+    "    +++++++##%*=%~~~~~~~~,##$$&&&&&&    ",
+    "     ++++++##%%%%'~~~~~~~~-###&&&&&     ",
+    "      +++++##%%%%'#-;--~;-#&&&&&&&      ",
+    "       ++++##%%%'$#,#%###$&&&&&&&       ",
+    "        +++&#$$$##'$#$%'#&&&&&&&        ",
+    "         +++&###$##$&###&&&&&&&         ",
+    "          +++++++&$+&&#&&&&&&&          ",
+    "           +++++++++&&&&&&&&&           ",
+    "            ++++++++&&&&&&&&            ",
+    "             +++++++&&&&&&&             ",
+    "              ++++++&&&&&&              ",
+    "               +++++&&&&&               ",
+    "                ++++&&&&                ",
+    "                 +++&&&                 ",
+    "                  ++&&                  ",
+    "                   ++                   "},
+  /* 9 */
+  {
+    "40 40 20 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #147A64",
+    "$	c #27536C",
+    "%	c #04423C",
+    "&	c #111E1E",
+    "*	c #06E1E0",
+    "=	c #CCCACC",
+    "-	c #84824C",
+    ";	c #3C3E04",
+    ">	c #BECD18",
+    ",	c #A1A20A",
+    "'	c #E7E809",
+    ")	c #5D602C",
+    "!	c #059B9A",
+    "~	c #1CC2A4",
+    "{	c #76D260",
+    "]	c #479653",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@@@.++++++++            ",
+    "           @@@@@@@@##++++++++           ",
+    "          @@@@@@@@@$%+++++++++          ",
+    "         @@@@@@@@..##+$++++++++         ",
+    "        @@@@@@@.$&&*#&&&++++++++        ",
+    "       @@@@@@@@+&==#%==&$++++++++       ",
+    "      @@@@@@@@@$==&%%&=-&+++++++++      ",
+    "     @@@@@@@@@@$=;>$$,&=;++++++++++     ",
+    "    @@@@@@@@@@@$-,'$$>,-;+++++++++++    ",
+    "   @@@@@@@@@@@@&&>>$$,-&&++++++++++++   ",
+    "  @@@@@@@@@@@@@&)>-**),;&+++++++++++++  ",
+    " @@@@@@@@@@@@@@&->)*!;-)&++++++++++++++ ",
+    "...............&)')*!;,;&+++++++++++++++",
+    "+++++++++++$&&&&',)*!;),&&&&%$$$$$$$$$$+",
+    " ++++++++++&##%&,',~~),-&%%%&$$$$$$$$$$ ",
+    "  +++++++++%#*#&',>##,),&%!%&$$$$$$$$$  ",
+    "   +++++++++&#&&'')**;,,&&%&$$$$$$$$$   ",
+    "    ++++++++&&%&>')*!;,,&&&&$$$$$$$$    ",
+    "     ++++++++++&;{)*!;]&&$$$$$$$$$$     ",
+    "      ++++++++++#!&*~&#%$$$$$$$$$$      ",
+    "       ++++++++$*%&##&%#&$$$$$$$$       ",
+    "        +++++++%~$+%%$%!&$$$$$$$        ",
+    "         ++++++&&++$%$$&&$$$$$$         ",
+    "          ++++++$+++$$$$$$$$$$          ",
+    "           +++++++++$$$$$$$$$           ",
+    "            ++++++++$$$$$$$$            ",
+    "             +++++++$$$$$$$             ",
+    "              ++++++$$$$$$              ",
+    "               +++++$$$$$               ",
+    "                ++++$$$$                ",
+    "                 +++$$$                 ",
+    "                  ++$$                  ",
+    "                   ++                   "},
+  /* 10 */
+  {
+    "40 40 24 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #27536C",
+    "$	c #04423C",
+    "%	c #111E1E",
+    "&	c #06E1E0",
+    "*	c #147A64",
+    "=	c #059B9A",
+    "-	c #2CFEFC",
+    ";	c #1CC2A4",
+    ">	c #3C3E04",
+    ",	c #EDEEEC",
+    "'	c #84824C",
+    ")	c #5D602C",
+    "!	c #CCCACC",
+    "~	c #BECD18",
+    "{	c #E7E809",
+    "]	c #A1A20A",
+    "^	c #76D260",
+    "/	c #479653",
+    "(	c #46E09D",
+    "_	c #9CEE4C",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@@@.##++++++            ",
+    "           @@@@@@@#$%%#++++++           ",
+    "          @@@@@@@.$&&*%#++++++          ",
+    "         @@@@....%=&&&$%+++++++         ",
+    "        @@@@.%%%%$&--&;%++++++++        ",
+    "       @@@@@%>,')*&&&&&*%###+++++       ",
+    "      @@@@@+>,,,%%=&&&&=%%%%#+++++      ",
+    "     @@@@@@#)!%@%~>%;&&*%$&*%#+++++     ",
+    "    @@@@@@@#)!)!){{]$;=%;&&;%#++++++    ",
+    "   @@@@@@@@+%!!>'{{{]$%*---&%#+++++++   ",
+    "  @@@@@@@@+%%%>~{~~{{%$&--&;%#++++++++  ",
+    " @@@@@@@@+%$#)~{{]{~{~;&--&;%#+++++++++ ",
+    ".........#$&%~{{{{]{~{~&&&&=%+++++++++++",
+    "++++++++++%$%)]{{{~~~{^&&&*%###########+",
+    " +++++++++%%**/{{~{]]{(&&=%$########### ",
+    "  ++++++++#%&&&_{~]]]{(&&;*%##########  ",
+    "   +++++++#%$&*~{{{{{~^&&&&$%########   ",
+    "    ++++++#$%>>]{~~~~~^&&-&$%#######    ",
+    "     +++++#%%]{~~~]~]];&&&&$%######     ",
+    "      ++++++#%)']))')$;&&&&$%#####      ",
+    "       +++++++#%%$=%>%$;&&;$%####       ",
+    "        +++++++%;&$%==%%$$$%$###        ",
+    "         ++++++#%%%+$%%%%%%$###         ",
+    "          ++++++#$#+#%%#######          ",
+    "           +++++++++#########           ",
+    "            ++++++++########            ",
+    "             +++++++#######             ",
+    "              ++++++######              ",
+    "               +++++#####               ",
+    "                ++++####                ",
+    "                 +++###                 ",
+    "                  ++##                  ",
+    "                   ++                   "},
+  /* 11 */
+  {
+    "40 40 22 1",
+    " 	c None",
+    ".	c #7999B4",
+    "+	c #4B6C99",
+    "@	c #8EB0C8",
+    "#	c #27536C",
+    "$	c #111E1E",
+    "%	c #04423C",
+    "&	c #059B9A",
+    "*	c #06E1E0",
+    "=	c #147A64",
+    "-	c #2CFEFC",
+    ";	c #1CC2A4",
+    ">	c #5D602C",
+    ",	c #EDEEEC",
+    "'	c #CCCACC",
+    ")	c #84824C",
+    "!	c #E7E809",
+    "~	c #3C3E04",
+    "{	c #BECD18",
+    "]	c #A1A20A",
+    "^	c #9CEE4C",
+    "/	c #76D260",
+    "                   .+                   ",
+    "                  @.++                  ",
+    "                 @@.+++                 ",
+    "                @@@.++++                ",
+    "               @@@@.+++++               ",
+    "              @@@@@.++++++              ",
+    "             @@@@@@.+++++++             ",
+    "            @@@@@@@.##++++++            ",
+    "           @@@@@@.#$$$$#+++++           ",
+    "          @@@@@@.$%&**=%%#++++          ",
+    "         @@.....$%**--*;%$+++++         ",
+    "        @@#$$$$$%**----**&$#++++        ",
+    "       @@#>,'$>$&&*----**;=$#++++       ",
+    "      @@.>,.,>$$$$%*-****&&$$$$$#+      ",
+    "     @@@$)'$>>!!!)~$;*;&&&%$%%%%$#+     ",
+    "    @@@@$),>.~!!!{]$$&&==$$=***&$$++    ",
+    "   @@@@~>~,'>]!!{!!)~$%$$=*---**%$+++   ",
+    "  @@.+$>!$$~>!!!!]!!{~$%;*---***%$++++  ",
+    " @@.$=%]!{]{!!!!!!{!{!]]=*--****%$+++++ ",
+    "...+=*=~!!!!!!!!!!{{{]{!^*-****&$#++++++",
+    "++++#**%~!!!!)>>!!!]]!{!^***&&%%$######+",
+    " +++#$%;=)!~$=%>!!!!!{!!^*;&&=$$%###### ",
+    "  ++++$%;)!;*;${!!!]]!!!^*****;$$#####  ",
+    "   ++$%%&/!;&%>!!!!!!!!!;*---**&$####   ",
+    "    +%*;=)!$$$!!!!!!!!!/**--***&$###    ",
+    "     #%%$)!{]]!!!!!>>>>********=$##     ",
+    "      +$$$~]{!!{]$$%$$$$%&;;;;&=$#      ",
+    "       ++#$$$~~$%&&*%&$$$$$%===$$       ",
+    "        ++++%$$#&**=%;=$%$$$$$$$        ",
+    "         ++++++#$$$$%$$$#######         ",
+    "          +++++++#%###########          ",
+    "           +++++++++#########           ",
+    "            ++++++++########            ",
+    "             +++++++#######             ",
+    "              ++++++######              ",
+    "               +++++#####               ",
+    "                ++++####                ",
+    "                 +++###                 ",
+    "                  ++##                  ",
+    "                   ++                   "},
+  /* 12 */
+  {"40 40 23 1",
+   " 	c None",
+   ".	c #7999B4",
+   "+	c #4B6C99",
+   "@	c #8EB0C8",
+   "#	c #27536C",
+   "$	c #111E1E",
+   "%	c #04423C",
+   "&	c #059B9A",
+   "*	c #06E1E0",
+   "=	c #147A64",
+   "-	c #3C3E04",
+   ";	c #1CC2A4",
+   ">	c #2CFEFC",
+   ",	c #84824C",
+   "'	c #EDEEEC",
+   ")	c #CCCACC",
+   "!	c #5D602C",
+   "~	c #A1A20A",
+   "{	c #E7E809",
+   "]	c #BECD18",
+   "^	c #9CEE4C",
+   "/	c #46E09D",
+   "(	c #76D260",
+   "                   .+                   ",
+   "                  @.++                  ",
+   "                 @@.+++                 ",
+   "                @@@.++++                ",
+   "               @@@@.+++++               ",
+   "              @@@@@.++++++              ",
+   "             @@@@@@.+++++++             ",
+   "            @@@@@@@.####++++            ",
+   "           @@@@@@.#$$$$$$#+++           ",
+   "          @@@@@@.$%&****=$$#++          ",
+   "         @@.. at .-$%;*>>>**;=$$++         ",
+   "        @.#$$#$$&***>>>>***&%$++        ",
+   "       @+$,')!$$$$$&;*****;*;%$++       ",
+   "      @@$,$$$!$,~~-$%=******;;%%++      ",
+   "     @@+$''-!'.!{{~{~$=;;;;;;&&$$##     ",
+   "    @@.$$''!!',!{{~{{,$=;;&&==$$$$$$$   ",
+   "   @@.$,$,''''$],]~{!{~$%%%%$$=;***&$   ",
+   "  #$$$~{]-#).$,{{{~{]{],$$$%=;******$$  ",
+   " $%&&$-{{]!$-~{{{{{{,{,{~-$%***>>***$#+ ",
+   "#$***&$-{{{{{{{{{{{{]{]]!]~-*******&$#++",
+   "#$$;;*&$-{{{{{!!!!!{,{,{]~!]*****&%$$##+",
+   " +$$$=;&$]{{{^===$-]~]~]!]~]****&%$$### ",
+   "  ##$$=;%~{{{/>*&-]],],]]],(******;#$#  ",
+   "   #%$$;&{{^/**&-]{{{{{]!{]/*******=$   ",
+   "   $$*;=${{]$$$$~{{{]]]]~]];*******=$   ",
+   "   $%%%$$~{{~,,~{{]]!------&*******=$   ",
+   "    $$$$$$,~]]]]~,-$%=*$$$$$$=&&&&=$$   ",
+   "       ++#$$----$$%&;**%$###$$$$$$$$    ",
+   "        +++#$$$$$$$%&&=$$#######        ",
+   "         +++++++++#$$$$$#######         ",
+   "          ++++++++++##########          ",
+   "           +++++++++#########           ",
+   "            ++++++++########            ",
+   "             +++++++#######             ",
+   "              ++++++######              ",
+   "               +++++#####               ",
+   "                ++++####                ",
+   "                 +++###                 ",
+   "                  ++##                  ",
+   "                   ++                   "},
+  /* 13 */
+  {"40 40 23 1",
+   " 	c None",
+   ".	c #7999B4",
+   "+	c #4B6C99",
+   "@	c #8EB0C8",
+   "#	c #27536C",
+   "$	c #111E1E",
+   "%	c #04423C",
+   "&	c #059B9A",
+   "*	c #06E1E0",
+   "=	c #147A64",
+   "-	c #3C3E04",
+   ";	c #1CC2A4",
+   ">	c #2CFEFC",
+   ",	c #84824C",
+   "'	c #EDEEEC",
+   ")	c #CCCACC",
+   "!	c #5D602C",
+   "~	c #A1A20A",
+   "{	c #E7E809",
+   "]	c #BECD18",
+   "^	c #9CEE4C",
+   "/	c #46E09D",
+   "(	c #76D260",
+   "                   .+                   ",
+   "                  @.++                  ",
+   "                 @@.+++                 ",
+   "                @@@.++++                ",
+   "               @@@@.+++++               ",
+   "              @@@@@.++++++              ",
+   "             @@@@@@.+++++++             ",
+   "            @@@@@@@.####++++            ",
+   "           @@@@@@.#$$$$$$#+++           ",
+   "          @@@@@@.$%&****=$$#++          ",
+   "         @@.. at .-$%;*>>>**;=$$++         ",
+   "        @.#$$#$$&***>>>>***&%$++        ",
+   "       @+$,')!$$$$$&;*****;*;%$++       ",
+   "      @@$$!'!$$,~~-$%=******;;%%++      ",
+   "     @@+$!$$$!.!{{~{~$=;;;;;;&&$$##     ",
+   "    @@.$$''!!',!{{~{{,$=;;&&==$$$$$$$   ",
+   "   @@.$,$,''''$],]~{!{~$%%%%$$=;***&$   ",
+   "  #$$$~{]-#).$,{{{~{]{],$$$%=;******$$  ",
+   " $%&&$-{{]!$-~{{{{{{,{,{~-$%***>>***$#+ ",
+   "#$***&$-{{{{{{{{{{{{]{]]!]~-*******&$#++",
+   "#$$;;*&$-{{{{{!!!!!{,{,{]~!]*****&%$$##+",
+   " +$$$=;&$]{{{^===$-]~]~]!]~]****&%$$### ",
+   "  ##$$=;%~{{{/>*&-]],],]]],(******;#$#  ",
+   "   #%$$;&{{^/**&-]{{{{{]!{]/*******=$   ",
+   "   $$*;=${{]$$$$~{{{]]]]~]];*******=$   ",
+   "   $%%%$$~{{~,,~{{]]!------&*******=$   ",
+   "    $$$$$$,~]]]]~,-$%=*$$$$$$=&&&&=$$   ",
+   "       ++#$$----$$%&;**%$###$$$$$$$$    ",
+   "        +++#$$$$$$$%&&=$$#######        ",
+   "         +++++++++#$$$$$#######         ",
+   "          ++++++++++##########          ",
+   "           +++++++++#########           ",
+   "            ++++++++########            ",
+   "             +++++++#######             ",
+   "              ++++++######              ",
+   "               +++++#####               ",
+   "                ++++####                ",
+   "                 +++###                 ",
+   "                  ++##                  ",
+   "                   ++                   "},
+  /* 14 */
+  {"40 40 23 1",
+   " 	c None",
+   ".	c #7999B4",
+   "+	c #4B6C99",
+   "@	c #8EB0C8",
+   "#	c #27536C",
+   "$	c #111E1E",
+   "%	c #04423C",
+   "&	c #059B9A",
+   "*	c #06E1E0",
+   "=	c #147A64",
+   "-	c #3C3E04",
+   ";	c #1CC2A4",
+   ">	c #2CFEFC",
+   ",	c #84824C",
+   "'	c #EDEEEC",
+   ")	c #CCCACC",
+   "!	c #5D602C",
+   "~	c #A1A20A",
+   "{	c #E7E809",
+   "]	c #BECD18",
+   "^	c #9CEE4C",
+   "/	c #46E09D",
+   "(	c #76D260",
+   "                   .+                   ",
+   "                  @.++                  ",
+   "                 @@.+++                 ",
+   "                @@@.++++                ",
+   "               @@@@.+++++               ",
+   "              @@@@@.++++++              ",
+   "             @@@@@@.+++++++             ",
+   "            @@@@@@@.####++++            ",
+   "           @@@@@@.#$$$$$$#+++           ",
+   "          @@@@@@.$%&****=$$#++          ",
+   "         @@.. at .-$%;*>>>**;=$$++         ",
+   "        @.#$$#$$&***>>>>***&%$++        ",
+   "       @+$,')!$$$$$&;*****;*;%$++       ",
+   "      @@$$''''$,~~-$%=******;;%%++      ",
+   "     @@+$'''''.!{{~{~$=;;;;;;&&$$##     ",
+   "    @@.$$$'''$,!{{~{{,$=;;&&==$$$$$$$   ",
+   "   @@.$,$!$$$!$],]~{!{~$%%%%$$=;***&$   ",
+   "  #$$$~{]-#).$,{{{~{]{],$$$%=;******$$  ",
+   " $%&&$-{{]!$-~{{{{{{,{,{~-$%***>>***$#+ ",
+   "#$***&$-{{{{{{{{{{{{]{]]!]~-*******&$#++",
+   "#$$;;*&$-{{{{{!!!!!{,{,{]~!]*****&%$$##+",
+   " +$$$=;&$]{{{^===$-]~]~]!]~]****&%$$### ",
+   "  ##$$=;%~{{{/>*&-]],],]]],(******;#$#  ",
+   "   #%$$;&{{^/**&-]{{{{{]!{]/*******=$   ",
+   "   $$*;=${{]$$$$~{{{]]]]~]];*******=$   ",
+   "   $%%%$$~{{~,,~{{]]!------&*******=$   ",
+   "    $$$$$$,~]]]]~,-$%=*$$$$$$=&&&&=$$   ",
+   "       ++#$$----$$%&;**%$###$$$$$$$$    ",
+   "        +++#$$$$$$$%&&=$$#######        ",
+   "         +++++++++#$$$$$#######         ",
+   "          ++++++++++##########          ",
+   "           +++++++++#########           ",
+   "            ++++++++########            ",
+   "             +++++++#######             ",
+   "              ++++++######              ",
+   "               +++++#####               ",
+   "                ++++####                ",
+   "                 +++###                 ",
+   "                  ++##                  ",
+   "                   ++                   "},
+  /* 15 */
+  {"40 40 23 1",
+   " 	c None",
+   ".	c #7999B4",
+   "+	c #4B6C99",
+   "@	c #8EB0C8",
+   "#	c #27536C",
+   "$	c #111E1E",
+   "%	c #04423C",
+   "&	c #059B9A",
+   "*	c #06E1E0",
+   "=	c #147A64",
+   "-	c #3C3E04",
+   ";	c #1CC2A4",
+   ">	c #2CFEFC",
+   ",	c #84824C",
+   "'	c #EDEEEC",
+   ")	c #CCCACC",
+   "!	c #5D602C",
+   "~	c #A1A20A",
+   "{	c #E7E809",
+   "]	c #BECD18",
+   "^	c #9CEE4C",
+   "/	c #46E09D",
+   "(	c #76D260",
+   "                   .+                   ",
+   "                  @.++                  ",
+   "                 @@.+++                 ",
+   "                @@@.++++                ",
+   "               @@@@.+++++               ",
+   "              @@@@@.++++++              ",
+   "             @@@@@@.+++++++             ",
+   "            @@@@@@@.####++++            ",
+   "           @@@@@@.#$$$$$$#+++           ",
+   "          @@@@@@.$%&****=$$#++          ",
+   "         @@.. at .-$%;*>>>**;=$$++         ",
+   "        @.#$$#$$&***>>>>***&%$++        ",
+   "       @+$,')!$$$$$&;*****;*;%$++       ",
+   "      @@$,''''$,~~-$%=******;;%%++      ",
+   "     @@+$'''''.!{{~{~$=;;;;;;&&$$##     ",
+   "    @@.$$''''',!{{~{{,$=;;&&==$$$$$$$   ",
+   "   @@.$,$-'''-$],]~{!{~$%%%%$$=;***&$   ",
+   "  #$$$~{]----$,{{{~{]{],$$$%=;******$$  ",
+   " $%&&$-{{]!$-~{{{{{{,{,{~-$%***>>***$#+ ",
+   "#$***&$-{{{{{{{{{{{{]{]]!]~-*******&$#++",
+   "#$$;;*&$-{{{{{!!!!!{,{,{]~!]*****&%$$##+",
+   " +$$$=;&$]{{{^===$-]~]~]!]~]****&%$$### ",
+   "  ##$$=;%~{{{/>*&-]],],]]],(******;#$#  ",
+   "   #%$$;&{{^/**&-]{{{{{]!{]/*******=$   ",
+   "   $$*;=${{]$$$$~{{{]]]]~]];*******=$   ",
+   "   $%%%$$~{{~,,~{{]]!------&*******=$   ",
+   "    $$$$$$,~]]]]~,-$%=*$$$$$$=&&&&=$$   ",
+   "       ++#$$----$$%&;**%$###$$$$$$$$    ",
+   "        +++#$$$$$$$%&&=$$#######        ",
+   "         +++++++++#$$$$$#######         ",
+   "          ++++++++++##########          ",
+   "           +++++++++#########           ",
+   "            ++++++++########            ",
+   "             +++++++#######             ",
+   "              ++++++######              ",
+   "               +++++#####               ",
+   "                ++++####                ",
+   "                 +++###                 ",
+   "                  ++##                  ",
+   "                   ++                   "},
+  /* 16 */
+  {"40 40 23 1",
+   " 	c None",
+   ".	c #7999B4",
+   "+	c #4B6C99",
+   "@	c #8EB0C8",
+   "#	c #27536C",
+   "$	c #111E1E",
+   "%	c #04423C",
+   "&	c #059B9A",
+   "*	c #06E1E0",
+   "=	c #147A64",
+   "-	c #3C3E04",
+   ";	c #1CC2A4",
+   ">	c #2CFEFC",
+   ",	c #84824C",
+   "'	c #EDEEEC",
+   ")	c #CCCACC",
+   "!	c #5D602C",
+   "~	c #A1A20A",
+   "{	c #E7E809",
+   "]	c #BECD18",
+   "^	c #9CEE4C",
+   "/	c #46E09D",
+   "(	c #76D260",
+   "                   .+                   ",
+   "                  @.++                  ",
+   "                 @@.+++                 ",
+   "                @@@.++++                ",
+   "               @@@@.+++++               ",
+   "              @@@@@.++++++              ",
+   "             @@@@@@.+++++++             ",
+   "            @@@@@@@.####++++            ",
+   "           @@@@@@.#$$$$$$#+++           ",
+   "          @@@@@@.$%&****=$$#++          ",
+   "         @@.. at .-$%;*>>>**;=$$++         ",
+   "        @.#$$#$$&***>>>>***&%$++        ",
+   "       @+$,')!$$$$$&;*****;*;%$++       ",
+   "      @@$$''''$,~~-$%=******;;%%++      ",
+   "     @@+$'''''.!{{~{~$=;;;;;;&&$$##     ",
+   "    @@.$$$'''$,!{{~{{,$=;;&&==$$$$$$$   ",
+   "   @@.$,$!$$$!$],]~{!{~$%%%%$$=;***&$   ",
+   "  #$$$~{]-#).$,{{{~{]{],$$$%=;******$$  ",
+   " $%&&$-{{]!$-~{{{{{{,{,{~-$%***>>***$#+ ",
+   "#$***&$-{{{{{{{{{{{{]{]]!]~-*******&$#++",
+   "#$$;;*&$-{{{{{!!!!!{,{,{]~!]*****&%$$##+",
+   " +$$$=;&$]{{{^===$-]~]~]!]~]****&%$$### ",
+   "  ##$$=;%~{{{/>*&-]],],]]],(******;#$#  ",
+   "   #%$$;&{{^/**&-]{{{{{]!{]/*******=$   ",
+   "   $$*;=${{]$$$$~{{{]]]]~]];*******=$   ",
+   "   $%%%$$~{{~,,~{{]]!------&*******=$   ",
+   "    $$$$$$,~]]]]~,-$%=*$$$$$$=&&&&=$$   ",
+   "       ++#$$----$$%&;**%$###$$$$$$$$    ",
+   "        +++#$$$$$$$%&&=$$#######        ",
+   "         +++++++++#$$$$$#######         ",
+   "          ++++++++++##########          ",
+   "           +++++++++#########           ",
+   "            ++++++++########            ",
+   "             +++++++#######             ",
+   "              ++++++######              ",
+   "               +++++#####               ",
+   "                ++++####                ",
+   "                 +++###                 ",
+   "                  ++##                  ",
+   "                   ++                   "},
+  /* 17 */
+  {"40 40 23 1",
+   " 	c None",
+   ".	c #7999B4",
+   "+	c #4B6C99",
+   "@	c #8EB0C8",
+   "#	c #27536C",
+   "$	c #111E1E",
+   "%	c #04423C",
+   "&	c #059B9A",
+   "*	c #06E1E0",
+   "=	c #147A64",
+   "-	c #3C3E04",
+   ";	c #1CC2A4",
+   ">	c #2CFEFC",
+   ",	c #84824C",
+   "'	c #EDEEEC",
+   ")	c #CCCACC",
+   "!	c #5D602C",
+   "~	c #A1A20A",
+   "{	c #E7E809",
+   "]	c #BECD18",
+   "^	c #9CEE4C",
+   "/	c #46E09D",
+   "(	c #76D260",
+   "                   .+                   ",
+   "                  @.++                  ",
+   "                 @@.+++                 ",
+   "                @@@.++++                ",
+   "               @@@@.+++++               ",
+   "              @@@@@.++++++              ",
+   "             @@@@@@.+++++++             ",
+   "            @@@@@@@.####++++            ",
+   "           @@@@@@.#$$$$$$#+++           ",
+   "          @@@@@@.$%&****=$$#++          ",
+   "         @@.. at .-$%;*>>>**;=$$++         ",
+   "        @.#$$#$$&***>>>>***&%$++        ",
+   "       @+$,')!$$$$$&;*****;*;%$++       ",
+   "      @@$$!'!$$,~~-$%=******;;%%++      ",
+   "     @@+$!$$$!.!{{~{~$=;;;;;;&&$$##     ",
+   "    @@.$$''!!',!{{~{{,$=;;&&==$$$$$$$   ",
+   "   @@.$,$,''''$],]~{!{~$%%%%$$=;***&$   ",
+   "  #$$$~{]-#).$,{{{~{]{],$$$%=;******$$  ",
+   " $%&&$-{{]!$-~{{{{{{,{,{~-$%***>>***$#+ ",
+   "#$***&$-{{{{{{{{{{{{]{]]!]~-*******&$#++",
+   "#$$;;*&$-{{{{{!!!!!{,{,{]~!]*****&%$$##+",
+   " +$$$=;&$]{{{^===$-]~]~]!]~]****&%$$### ",
+   "  ##$$=;%~{{{/>*&-]],],]]],(******;#$#  ",
+   "   #%$$;&{{^/**&-]{{{{{]!{]/*******=$   ",
+   "   $$*;=${{]$$$$~{{{]]]]~]];*******=$   ",
+   "   $%%%$$~{{~,,~{{]]!------&*******=$   ",
+   "    $$$$$$,~]]]]~,-$%=*$$$$$$=&&&&=$$   ",
+   "       ++#$$----$$%&;**%$###$$$$$$$$    ",
+   "        +++#$$$$$$$%&&=$$#######        ",
+   "         +++++++++#$$$$$#######         ",
+   "          ++++++++++##########          ",
+   "           +++++++++#########           ",
+   "            ++++++++########            ",
+   "             +++++++#######             ",
+   "              ++++++######              ",
+   "               +++++#####               ",
+   "                ++++####                ",
+   "                 +++###                 ",
+   "                  ++##                  ",
+   "                   ++                   "},
+  /* 18 */
+  {"40 40 23 1",
+   " 	c None",
+   ".	c #7999B4",
+   "+	c #4B6C99",
+   "@	c #8EB0C8",
+   "#	c #27536C",
+   "$	c #111E1E",
+   "%	c #04423C",
+   "&	c #059B9A",
+   "*	c #06E1E0",
+   "=	c #147A64",
+   "-	c #3C3E04",
+   ";	c #1CC2A4",
+   ">	c #2CFEFC",
+   ",	c #84824C",
+   "'	c #EDEEEC",
+   ")	c #CCCACC",
+   "!	c #5D602C",
+   "~	c #A1A20A",
+   "{	c #E7E809",
+   "]	c #BECD18",
+   "^	c #9CEE4C",
+   "/	c #46E09D",
+   "(	c #76D260",
+   "                   .+                   ",
+   "                  @.++                  ",
+   "                 @@.+++                 ",
+   "                @@@.++++                ",
+   "               @@@@.+++++               ",
+   "              @@@@@.++++++              ",
+   "             @@@@@@.+++++++             ",
+   "            @@@@@@@.####++++            ",
+   "           @@@@@@.#$$$$$$#+++           ",
+   "          @@@@@@.$%&****=$$#++          ",
+   "         @@.. at .-$%;*>>>**;=$$++         ",
+   "        @.#$$#$$&***>>>>***&%$++        ",
+   "       @+$,')!$$$$$&;*****;*;%$++       ",
+   "      @@$,$$$!$,~~-$%=******;;%%++      ",
+   "     @@+$''-!'.!{{~{~$=;;;;;;&&$$##     ",
+   "    @@.$$''!!',!{{~{{,$=;;&&==$$$$$$$   ",
+   "   @@.$,$,''''$],]~{!{~$%%%%$$=;***&$   ",
+   "  #$$$~{]-#).$,{{{~{]{],$$$%=;******$$  ",
+   " $%&&$-{{]!$-~{{{{{{,{,{~-$%***>>***$#+ ",
+   "#$***&$-{{{{{{{{{{{{]{]]!]~-*******&$#++",
+   "#$$;;*&$-{{{{{!!!!!{,{,{]~!]*****&%$$##+",
+   " +$$$=;&$]{{{^===$-]~]~]!]~]****&%$$### ",
+   "  ##$$=;%~{{{/>*&-]],],]]],(******;#$#  ",
+   "   #%$$;&{{^/**&-]{{{{{]!{]/*******=$   ",
+   "   $$*;=${{]$$$$~{{{]]]]~]];*******=$   ",
+   "   $%%%$$~{{~,,~{{]]!------&*******=$   ",
+   "    $$$$$$,~]]]]~,-$%=*$$$$$$=&&&&=$$   ",
+   "       ++#$$----$$%&;**%$###$$$$$$$$    ",
+   "        +++#$$$$$$$%&&=$$#######        ",
+   "         +++++++++#$$$$$#######         ",
+   "          ++++++++++##########          ",
+   "           +++++++++#########           ",
+   "            ++++++++########            ",
+   "             +++++++#######             ",
+   "              ++++++######              ",
+   "               +++++#####               ",
+   "                ++++####                ",
+   "                 +++###                 ",
+   "                  ++##                  ",
+   "                   ++                   "},
+
+
+};
+
+

Added: trunk/spectrum/main.c
===================================================================
--- trunk/spectrum/main.c	                        (rev 0)
+++ trunk/spectrum/main.c	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,293 @@
+/*
+ *
+ *  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 total_ch=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);
+}

Added: trunk/spectrum/panel.c
===================================================================
--- trunk/spectrum/panel.c	                        (rev 0)
+++ trunk/spectrum/panel.c	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,763 @@
+/*
+ *
+ *  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_scale=1;
+int plot_mode=0;
+int plot_link=0;
+int plot_hold=0;
+int plot_depth=45;
+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 depthchange(GtkWidget *widget,struct panel *p){
+  int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  switch(choice){
+  case 0: /* 20dB */
+    plot_depth=20;
+    break;
+  case 1: /* 45dB */
+    plot_depth=45;
+    break;
+  case 2: /* 90dB */
+    plot_depth=90;
+    break;
+  case 3: /*140dB */
+    plot_depth=140;
+    break;
+  }
+  plot_setting(PLOT(p->plot),plot_scale,plot_mode,plot_link,plot_depth);
+}
+
+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 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:
+
+    /*  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 */
+
+    /* sum mode is a special case; all the data comes on first vector of
+       the group if any elements of the group are active at all */
+    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];
+    }
+
+    /* fall through */
+  case LINK_INDEPENDENT: /* normal/independent mode */
+    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);
+    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_IMPEDENCE_p1:
+  case LINK_IMPEDENCE_1:
+  case LINK_IMPEDENCE_10:
+
+    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_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:
+    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 scalechange(GtkWidget *widget,struct panel *p){
+  plot_scale=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  plot_setting(PLOT(p->plot),plot_scale,plot_mode,plot_link,plot_depth);
+}
+
+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_scale,plot_mode,plot_link,plot_depth);
+}
+
+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_scale,plot_mode,plot_link,plot_depth);
+  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[]={"linear","log","ISO log"};
+    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_scale,plot_mode,plot_link,plot_depth);
+    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[]={"20dB","45dB","90dB","140dB"};
+    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),1);
+    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[]={"instant","slowav","maximum","accumulate"};
+    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_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);
+  }
+
+  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));
+  }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/plot.c
===================================================================
--- trunk/spectrum/plot.c	                        (rev 0)
+++ trunk/spectrum/plot.c	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,906 @@
+/*
+ *
+ *  gt2 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 "plot.h"
+
+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->ymax)-log10(lfreqs[i]))/(log10(p->ymax)-log10(.1)) * (height-1));
+  for(i=0;i<64;i++)
+    p->ytic[i]=rint( (log10(p->ymax)-log10(tfreqs[i]))/(log10(p->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 i;
+
+  /* find the places to plot the x grid lines according to scale */
+  switch(p->scale){
+  case 0: /* linear; 2kHz spacing */
+    {
+      float lfreq;
+      for(i=0;i<11;i++){
+	lfreq=i*2000.;
+	p->xgrid[i]=rint(lfreq/20000. * (width-1))+p->padx;
+      }
+	
+      p->xgrids=11;
+      p->xtics=0;
+      while((lfreq=(p->xtics+1)*500.)<20000.)
+	p->xtic[p->xtics++]=rint(lfreq/20000. * (width-1))+p->padx;
+    }
+    break;
+    
+  case 1: /* log */
+    {
+      double lfreqs[6]={1.,10.,100.,1000.,10000.,100000};
+      double 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};
+      for(i=0;i<6;i++)
+	p->xgrid[i]=rint( (log10(lfreqs[i])-log10(5.))/(log10(100000.)-log10(5.)) * (width-1))+p->padx;
+      for(i=0;i<37;i++)
+	p->xtic[i]=rint( (log10(tfreqs[i])-log10(5.))/(log10(100000.)-log10(5.)) * (width-1))+p->padx;
+      p->xgrids=6;
+      p->xtics=37;
+    }
+    
+    break;
+  case 2: /* ISO log */
+    {
+      double lfreqs[10]={31.,63.,125.,250.,500.,1000.,2000.,4000.,8000.,16000.};
+      double tfreqs[20]={25.,40.,50.,80.,100.,160.,200.,315.,400.,630.,800.,
+			1250.,1600.,2500.,3150.,5000.,6300.,10000.,12500.,20000.};
+      for(i=0;i<10;i++)
+	p->xgrid[i]=rint( (log2(lfreqs[i])-log2(25.))/(log2(20000.)-log2(25.)) * (width-1))+p->padx;
+      for(i=0;i<20;i++)
+	p->xtic[i]=rint( (log2(tfreqs[i])-log2(25.))/(log2(20000.)-log2(25.)) * (width-1))+p->padx;
+      p->xgrids=10;
+      p->xtics=20;
+    }
+
+    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 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->lin_layout;
+      break;
+    case 1:
+      proper=p->log_layout;
+      break;
+    case 2:
+      proper=p->iso_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->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->ymax-ceil(p->ymax))*2;
+	del*=.5;
+      }else if(del>8){
+	max=151;
+	mul=1;
+	off=p->ymax-ceil(p->ymax);
+      }else if(del*2>8){
+	max=76;
+	mul=2;
+	off=p->ymax-ceil(p->ymax*.5)*2;
+      }else{
+	max=31;
+	mul=5;
+	off=p->ymax-ceil(p->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->ymax/5+28),i;
+    float del=(height-p->pady-1)/(float)p->depth,step;
+    float off=p->ymax-ceil(p->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,0,0,0};
+    float depth = p->pmax-p->pmin;
+    int label=ceil(p->pmax/10+18),i;
+    float del=(height-p->pady-1)/depth,step;
+    float off=p->pmax-ceil(p->pmax*.1)*10;
+    step=2;
+    if(del>8)step=1;
+    
+    gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+    gdk_gc_set_rgb_fg_color(p->dashes,&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,
+			   widget->style->black_gc,
+			   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);
+      }
+    }
+
+  }
+  
+  /* draw actual data */
+  {
+    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;	
+	  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(impedence){ /* log scale for impedence */
+	      y =rint( (log10(p->ymax)-log10(val))/(log10(p->ymax)-log10(.1)) * 
+		       (height-p->pady-1));
+	    }else if(phase && ch==cho+1){
+	      y= rint((height-p->pady-1)/(p->pmax-p->pmin)*(p->pmax-val));
+	    }else{
+	      y= rint((height-p->pady-1)/p->depth*(p->ymax-val));
+	    }
+	    
+	    if(isnan(yprev) || isnan(val)){
+	      yprev = val;
+	    }else{
+	      yprev = val;
+
+	      if(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);
+	      }
+	    }
+	    prev=y;
+	   
+	  }
+	}
+      }
+      cho+=p->ch[gi];
+    }
+  }
+}
+
+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);
+  /* no smaller than 800x600 */
+  requisition->width = 800;
+  requisition->height = 600;
+  int axisy=0,axisx=0,pady=0,padx=0,phax=0,px,py,i;
+
+  /* find max lin layout */
+  {
+    int max=0;
+    for(i=0;p->lin_layout[i];i++){
+      pango_layout_get_pixel_size(p->lin_layout[i],&px,&py);
+      if(px>max)max=px;
+      if(py>pady)pady=py;
+    }
+    max*=i;
+    if(axisx<max)axisx=max;
+  }
+  /* find max log layout */
+  {
+    int max=0;
+    for(i=0;p->log_layout[i];i++){
+      pango_layout_get_pixel_size(p->log_layout[i],&px,&py);
+      if(px>max)max=px;
+      if(py>pady)pady=py;
+    }
+    max*=i;
+    if(axisx<max)axisx=max;
+  }
+  /* find max iso layout */
+  {
+    int max=0;
+    for(i=0;p->iso_layout[i];i++){
+      pango_layout_get_pixel_size(p->iso_layout[i],&px,&py);
+      if(px>max)max=px;
+      if(py>pady)pady=py;
+    }
+    max*=i;
+    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+5)*8;
+    if(axisy<max)axisx=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+5)*8;
+    if(axisy<max)axisx=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+5)*8;
+    if(axisy<max)axisx=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);
+  int i;
+
+  if (p->backing)
+    g_object_unref(p->backing);
+  
+  p->backing = gdk_pixmap_new(widget->window,
+			      widget->allocation.width,
+			      widget->allocation.height,
+			      -1);
+
+  if(p->ydata){
+    for(i=0;i<p->total_ch;i++)free(p->ydata[i]);
+    free(p->ydata);
+  }
+
+  p->ydata=malloc(p->total_ch*sizeof(*p->ydata));
+  for(i=0;i<p->total_ch;i++)
+    p->ydata[i]=
+      calloc(widget->allocation.width,sizeof(**p->ydata));
+  
+  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;
+
+  p->groups = groups;
+  for(g=0;g<groups;g++)
+    ch+=channels[g];
+  p->total_ch = ch;
+
+  p->ch=channels;
+  p->rate=rate;
+
+  /* generate all the text layouts we'll need */
+  {
+    char *labels[11]={"DC","2kHz","4kHz","6kHz","8kHz","10kHz","12kHz",
+		      "14kHz","16kHz","18kHz",""};
+    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]);
+  }
+  {
+    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]);
+  }
+  {
+    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]);
+  }
+  {
+    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]);
+  }
+  {
+    char *labels[10]={"31Hz","63Hz","125Hz","250Hz","500Hz","1kHz","2kHz",
+		      "4kHz","8kHz","16kHz"};
+    p->iso_layout=calloc(11,sizeof(*p->iso_layout));
+    for(i=0;i<10;i++)
+      p->iso_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+  }
+  {
+    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));
+
+  /*
+    p->cal=calloc(p->ch,sizeof(*p->cal));
+    for(i=0;i<p->ch;i++)
+      p->cal[i]=calloc(p->datasize,sizeof(**p->cal));
+  */
+  plot_clear(p);
+  return ret;
+}
+
+void plot_refresh (Plot *p, int *process){
+  int i;
+  float ymax,pmax,pmin;
+  int width=GTK_WIDGET(p)->allocation.width-p->padx;
+  int height=GTK_WIDGET(p)->allocation.height-p->pady;
+  float **data;
+  
+  if(process)
+    memcpy(p->ch_process,process,p->total_ch*sizeof(*process));
+  
+  data = process_fetch(p->scale,p->mode,p->link,
+		       p->ch_process,width,&ymax,&pmax,&pmin);
+  
+  if(p->ydata)
+    for(i=0;i<p->total_ch;i++)
+      memcpy(p->ydata[i],data[i],width*sizeof(**p->ydata));
+
+  /* graph limit updates are conditional depending on mode/link */
+  if(pmax<12)pmax=12;
+  if(pmin>-12)pmin=-12;
+
+  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:
+    ymax *=1.8;
+    break;
+  }
+
+  //if(p->ymax>ymax)ymax=p->ymax;
+  //if(pmax<p->pmax)pmax=p->pmax;
+  //if(pmin>p->pmin)pmin=p->pmin;
+
+  /* time damp decay */
+
+
+
+  /* finally, align phase/response zeros on phase graphs */
+  if(ymax>-140){
+    if(p->link == LINK_PHASE){
+      /* align the phase and response zeros by shifting phase */
+      float mzero = (height-1)/p->depth*ymax;
+      float pzero = (height-1)/(pmax-pmin)*pmax;
+      
+      if(mzero<pzero){
+	pmin = pmax-(height-1)/mzero*pmax;
+	pzero = (height-1)/(pmax-pmin)*pmax;
+      }else{
+	pmax = pmin/(1-(height-1)/mzero);
+	pzero = (height-1)/(pmax-pmin)*pmax;
+      }
+      
+      /* If phase shifts beyond +/- 180, shift main scale. */
+      
+      if(pmin<-180.){
+	/* increase ymax, shift main scale zero down */
+	pmin = -180.;
+	pzero = (height-1)/(pmax-pmin)*pmax;
+	ymax = pzero*p->depth/(height-1);
+      }else if(pmax>180.){
+	/* only way to reconcile this one is to increase the pdepth */
+	pmax = 180.;
+	pzero = (height-1)/(pmax-pmin)*pmax;
+	p->depth = (height-1)/pzero*ymax;
+      }
+    }
+  }
+
+  if(ymax<p->depth-140.)ymax=p->depth-140.;
+  if(ymax>140.)ymax=140.;
+  if(pmax>180)pmax=180;
+  if(pmin<-180)pmin=-180;
+  
+  p->ymax=ymax;
+  p->pmax=pmax;
+  p->pmin=pmin;
+}
+
+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=-140;
+  p->pmax=12.;
+  p->pmin=-12.;
+  draw_and_expose(widget);
+}
+
+float **plot_get (Plot *p){
+  return(p->ydata);
+}
+
+void plot_setting (Plot *p, int scale, int mode, int link, int depth){
+  GtkWidget *widget=GTK_WIDGET(p);
+  p->scale=scale;
+  p->mode=mode;
+  p->depth=depth;
+  p->link=link;
+
+  p->ymax=-140;
+  p->pmax=12.;
+  p->pmin=-12.;
+
+  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);
+}
+

Added: trunk/spectrum/plot.h
===================================================================
--- trunk/spectrum/plot.h	                        (rev 0)
+++ trunk/spectrum/plot.h	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,115 @@
+/*
+ *
+ *  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;
+
+  float **ydata;
+
+  int groups;
+  int *ch;
+  int *ch_active;
+  int *ch_process;
+  int total_ch;
+  int datasize;
+  int mode;
+  int link;
+  int scale;
+  int *rate;
+
+  int xgrid[11];
+  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 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, int *process);
+void	       plot_setting         (Plot *m, int scale, int mode, int link, int depth);
+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
+
+
+
+

Added: trunk/spectrum/process.c
===================================================================
--- trunk/spectrum/process.c	                        (rev 0)
+++ trunk/spectrum/process.c	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,1192 @@
+/*
+ *
+ *  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"
+
+static int blockslice[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+
+static float **blockbuffer=0;
+static int blockbufferfill[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
+static float *window;
+static float *freqbuffer=0;
+static fftwf_plan plan;
+
+static unsigned char readbuffer[MAX_FILES][readbuffersize];
+static int readbufferfill[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
+static int readbufferptr[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
+
+static FILE *f[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
+static off_t offset[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
+static off_t length[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+static off_t bytesleft[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+int seekable[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
+int global_seekable=0;
+
+pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+int feedback_increment=0;
+
+float *feedback_count;
+float **feedback_work;
+float *process_work;
+
+float **feedback_acc;
+float **feedback_av;
+float **feedback_max;
+float **feedback_instant;
+
+float **ph_acc;
+float **ph_av;
+float **ph_max;
+float **ph_instant;
+
+float **xmappingL;
+float **xmappingH;
+int metascale = -1;
+int metawidth = -1;
+
+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 int host_is_big_endian() {
+  int32_t pattern = 0xfeedface; /* deadbeef */
+  unsigned char *bytewise = (unsigned char *)&pattern;
+  if (bytewise[0] == 0xfe) return 1;
+  return 0;
+}
+
+/* Macros to read header data */
+#define READ_U32_LE(buf) \
+        (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U16_LE(buf) \
+        (((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U32_BE(buf) \
+        (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
+
+#define READ_U16_BE(buf) \
+        (((buf)[0]<<8)|((buf)[1]&0xff))
+
+double read_IEEE80(unsigned char *buf){
+  int s=buf[0]&0xff;
+  int e=((buf[0]&0x7f)<<8)|(buf[1]&0xff);
+  double f=((unsigned long)(buf[2]&0xff)<<24)|
+    ((buf[3]&0xff)<<16)|
+    ((buf[4]&0xff)<<8) |
+    (buf[5]&0xff);
+  
+  if(e==32767){
+    if(buf[2]&0x80)
+      return HUGE_VAL; /* Really NaN, but this won't happen in reality */
+    else{
+      if(s)
+	return -HUGE_VAL;
+      else
+	return HUGE_VAL;
+    }
+  }
+  
+  f=ldexp(f,32);
+  f+= ((buf[6]&0xff)<<24)|
+    ((buf[7]&0xff)<<16)|
+    ((buf[8]&0xff)<<8) |
+    (buf[9]&0xff);
+  
+  return ldexp(f, e-16446);
+}
+
+static int find_chunk(FILE *in, char *type, unsigned int *len, int endian){
+  unsigned int i;
+  unsigned char buf[8];
+
+  while(1){
+    if(fread(buf,1,8,in) <8)return 0;
+
+    if(endian)
+      *len = READ_U32_BE(buf+4);
+    else
+      *len = READ_U32_LE(buf+4);
+
+    if(memcmp(buf,type,4)){
+
+      if((*len) & 0x1)(*len)++;
+      
+      for(i=0;i<*len;i++)
+	if(fgetc(in)==EOF)return 0;
+
+    }else return 1;
+  }
+}
+
+int input_load(void){
+
+  int stdinp=0,i,fi;
+  if(inputs==0){
+    /* look at stdin... is it a file, pipe, tty...? */
+    if(isatty(STDIN_FILENO)){
+      fprintf(stderr,
+	      "Spectrum requires either an input file on the command line\n"
+	      "or stream data piped|redirected to stdin. spectrum -h will\n"
+	      "give more details.\n");
+      return 1;
+    }
+    stdinp=1;    /* file coming in via stdin */
+    inputname[0]=strdup("stdin");
+    inputs++;
+  }
+
+  for(fi=0;fi<inputs;fi++){
+
+    if(stdinp && fi==0){
+      int newfd=dup(STDIN_FILENO);
+      f[fi]=fdopen(newfd,"rb");
+    }else{
+      f[fi]=fopen(inputname[fi],"rb");
+    }
+    seekable[fi]=0;
+
+    /* Crappy! Use a lib to do this for pete's sake! */
+    if(f[fi]){
+      char headerid[12];
+      off_t filelength;
+	
+      /* parse header (well, sort of) and get file size */
+      seekable[fi]=(fseek(f[fi],0,SEEK_CUR)?0:1);
+
+      if(!seekable[fi]){
+	filelength=-1;
+      }else{
+	fseek(f[fi],0,SEEK_END);
+	filelength=ftello(f[fi]);
+	fseek(f[fi],0,SEEK_SET);
+	global_seekable=1;
+      }
+      
+      fread(headerid,1,12,f[fi]);
+      if(!strncmp(headerid,"RIFF",4) && !strncmp(headerid+8,"WAVE",4)){
+	unsigned int chunklen;
+      
+	if(find_chunk(f[fi],"fmt ",&chunklen,0)){
+	  int ltype;
+	  int lch;
+	  int lrate;
+	  int lbits;
+	  unsigned char *buf=alloca(chunklen);
+	
+	  fread(buf,1,chunklen,f[fi]);
+	
+	  ltype = READ_U16_LE(buf); 
+	  lch =   READ_U16_LE(buf+2); 
+	  lrate = READ_U32_LE(buf+4);
+	  lbits = READ_U16_LE(buf+14);
+	  
+	  if(ltype!=1){
+	    fprintf(stderr,"%s:\n\tWAVE file not PCM.\n",inputname[fi]);
+	    return 1;
+	  }
+	      
+	  if(bits[fi]==-1)bits[fi]=lbits;
+	  if(channels[fi]==-1)channels[fi]=lch;
+	  if(signedp[fi]==-1){
+	    signedp[fi]=0;
+	    if(bits[fi]>8)signedp[fi]=1;
+	  }
+	  if(bigendian[fi]==-1)bigendian[fi]=0;
+	  if(rate[fi]==-1){
+	    if(lrate<4000 || lrate>192000){
+	      fprintf(stderr,"%s:\n\tSampling rate out of bounds\n",inputname[fi]);
+	      return 1;
+	    }
+	    rate[fi]=lrate;
+	  }
+
+	  if(find_chunk(f[fi],"data",&chunklen,0)){
+	    off_t pos=ftello(f[fi]);
+	    int bytes=(bits[fi]+7)/8;
+	    if(seekable[fi])
+	      filelength=
+		(filelength-pos)/
+		(channels[fi]*bytes)*
+		(channels[fi]*bytes)+pos;
+	    
+	    if(chunklen==0UL ||
+	       chunklen==0x7fffffffUL || 
+	       chunklen==0xffffffffUL){
+	      if(filelength==-1){
+		length[fi]=-1;
+		fprintf(stderr,"%s: Incomplete header; assuming stream.\n",inputname[fi]);
+	      }else{
+		length[fi]=(filelength-pos)/(channels[fi]*bytes);
+		fprintf(stderr,"%s: Incomplete header; using actual file size.\n",inputname[fi]);
+	      }
+	    }else if(filelength==-1 || chunklen+pos<=filelength){
+	      length[fi]=(chunklen/(channels[fi]*bytes));
+	      fprintf(stderr,"%s: Using declared file size (%ld).\n",
+		      inputname[fi],(long)length[fi]*channels[fi]*bytes);
+	      
+	    }else{
+	      
+	      length[fi]=(filelength-pos)/(channels[fi]*bytes);
+	      fprintf(stderr,"%s: File truncated; Using actual file size.\n",inputname[fi]);
+	    }
+	    offset[fi]=ftello(f[fi]);
+	  } else {
+	    fprintf(stderr,"%s: WAVE file has no \"data\" chunk following \"fmt \".\n",inputname[fi]);
+	    return 1;
+	  }
+	}else{
+	  fprintf(stderr,"%s: WAVE file has no \"fmt \" chunk.\n",inputname[fi]);
+	  return 1;
+	}
+      
+      }else if(!strncmp(headerid,"FORM",4) && !strncmp(headerid+8,"AIF",3)){
+	unsigned int len;
+	int aifc=0;
+	if(headerid[11]=='C')aifc=1;
+	unsigned char *buffer;
+	char buf2[8];
+	
+	int lch;
+	int lbits;
+	int lrate;
+	int bytes;
+	
+	/* look for COMM */
+	if(!find_chunk(f[fi], "COMM", &len,1)){
+	  fprintf(stderr,"%s: AIFF file has no \"COMM\" chunk.\n",inputname[fi]);
+	  return 1;
+	}
+	
+	if(len < 18 || (aifc && len<22)) {
+	  fprintf(stderr,"%s: AIFF COMM chunk is truncated.\n",inputname[fi]);
+	  return 1;
+	}
+	
+	buffer = alloca(len);
+	
+	if(fread(buffer,1,len,f[fi]) < len){
+	  fprintf(stderr, "%s: Unexpected EOF in reading AIFF header\n",inputname[fi]);
+	  return 1;
+	}
+	
+	lch = READ_U16_BE(buffer);
+	lbits = READ_U16_BE(buffer+6);
+	lrate = (int)read_IEEE80(buffer+8);
+      
+	if(bits[fi]==-1)bits[fi]=lbits;
+	bytes=(bits[fi]+7)/8;
+	if(signedp[fi]==-1)signedp[fi]=1;
+	if(rate[fi]==-1){
+	  if(lrate<4000 || lrate>192000){
+	    fprintf(stderr,"%s:\n\tSampling rate out of bounds\n",inputname[fi]);
+	    return 1;
+	  }
+	  rate[fi]=lrate;
+	}
+	if(channels[fi]==-1)channels[fi]=lch;
+	
+	if(bigendian[fi]==-1){
+	  if(aifc){
+	    if(!memcmp(buffer+18, "NONE", 4)) {
+	      bigendian[fi] = 1;
+	    }else if(!memcmp(buffer+18, "sowt", 4)) {
+	      bigendian[fi] = 0;
+	    }else{
+	      fprintf(stderr, "%s: Spectrum supports only linear PCM AIFF-C files.\n",inputname[fi]);
+	      return 1;
+	    }
+	  }else
+	    bigendian[fi] = 1;
+	}
+	if(!find_chunk(f[fi], "SSND", &len, 1)){
+	  fprintf(stderr,"%s: AIFF file has no \"SSND\" chunk.\n",inputname[fi]);
+	  return 1;
+	}
+	
+	if(fread(buf2,1,8,f[fi]) < 8){
+	  fprintf(stderr,"%s: Unexpected EOF reading AIFF header\n",inputname[fi]);
+	  return 1;
+	}
+	
+	{
+	  int loffset = READ_U32_BE(buf2);
+	  int lblocksize = READ_U32_BE(buf2+4);
+	  
+	  /* swallow some data */
+	  for(i=0;i<loffset;i++)
+	    if(fgetc(f[fi])==EOF)break;
+	  
+	  if( lblocksize == 0 && (bits[fi] == 24 || bits[fi] == 16 || bits[fi] == 8)){
+	    
+	    off_t pos=ftello(f[fi]);
+	    
+	    if(seekable[fi])
+	      filelength=
+		(filelength-pos)/
+		(channels[fi]*bytes)*
+		(channels[fi]*bytes)+pos;
+	  
+	    if(len==0UL ||
+	       len==0x7fffffffUL || 
+	       len==0xffffffffUL){
+	      if(filelength==-1){
+		length[fi]=-1;
+		fprintf(stderr,"%s: Incomplete header; assuming stream.\n",inputname[fi]);
+	      }else{
+		length[fi]=(filelength-pos)/(channels[fi]*bytes);
+		fprintf(stderr,"%s: Incomplete header; using actual file size.\n",inputname[fi]);
+	      }
+	    }else if(filelength==-1 || (len+pos-loffset-8)<=filelength){
+	      length[fi]=((len-loffset-8)/(channels[fi]*bytes));
+	      fprintf(stderr,"%s: Using declared file size.\n",inputname[fi]);
+	      
+	    }else{
+	      length[fi]=(filelength-pos)/(channels[fi]*bytes);
+	      fprintf(stderr,"%s: File truncated; Using actual file size.\n",inputname[fi]);
+	    }
+	    offset[fi]=pos;
+	  }else{
+	    fprintf(stderr, "%s: Spectrum supports only linear PCM AIFF-C files.\n",inputname[fi]);
+	    return 1;
+	  }
+	}
+      } else {
+	/* must be raw input */
+	fprintf(stderr,"Input has no header; assuming raw stream/file.\n");
+      
+	if(channels[fi]==-1)channels[fi]=1;
+	if(rate[fi]==-1)rate[fi]=44100;
+	if(bits[fi]==-1)bits[fi]=16;
+	if(signedp[fi]==-1)signedp[fi]=1;
+	if(bigendian[fi]==-1)bigendian[fi]=host_is_big_endian();
+      
+	offset[fi]=0;
+	length[fi]=-1;
+	if(seekable[fi])length[fi]=filelength/(channels[fi]*((bits[fi]+7)/8));
+	
+	memcpy(readbuffer[fi],headerid,12);
+	readbufferfill[fi]=12;
+	
+      }
+
+      /* select the full-block slice size: ~10fps */
+      blockslice[fi]=rate[fi]/10;
+      while(blockslice[fi]>blocksize/2)blockslice[fi]/=2;
+      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));
+  process_work=calloc(blocksize+2,sizeof(*process_work));
+  feedback_count=calloc(total_ch,sizeof(*feedback_count));
+  feedback_work=calloc(total_ch,sizeof(*feedback_work));
+
+  feedback_acc=malloc(total_ch*sizeof(*feedback_acc));
+  feedback_av=malloc(total_ch*sizeof(*feedback_av));
+  feedback_max=malloc(total_ch*sizeof(*feedback_max));
+  feedback_instant=malloc(total_ch*sizeof(*feedback_instant));
+
+  ph_acc=malloc(total_ch*sizeof(*ph_acc));
+  ph_av=malloc(total_ch*sizeof(*ph_av));
+  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++){
+    blockbuffer[i]=calloc(blocksize,sizeof(**blockbuffer));
+
+    feedback_acc[i]=calloc(blocksize/2+1,sizeof(**feedback_acc));
+    feedback_av[i]=calloc(blocksize/2+1,sizeof(**feedback_av));
+    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_av[i]=calloc(blocksize+2,sizeof(**ph_av));
+    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;
+    
+  return 0;
+
+}
+
+/* Convert new data from readbuffer into the blockbuffers until the
+   blockbuffer is full */
+static void LBEconvert(void){
+  float scale=1./2147483648.;
+  int ch=0,fi;
+
+  for(fi=0;fi<inputs;fi++){
+    int bytes=(bits[fi]+7)/8;
+    int j;
+    int32_t xor=(signedp[fi]?0:0x80000000UL);
+    
+    int readlimit=(readbufferfill[fi]-readbufferptr[fi])/
+      channels[fi]/bytes*channels[fi]*bytes+readbufferptr[fi];
+
+    int bfill = blockbufferfill[fi];
+    int rptr = readbufferptr[fi];
+    unsigned char *rbuf = readbuffer[fi];
+
+    if(readlimit){
+      
+      switch(bytes){
+      case 1:
+	
+	while(bfill<blocksize && rptr<readlimit){
+	  for(j=ch;j<channels[fi]+ch;j++)
+	    blockbuffer[j][bfill]=((rbuf[rptr++]<<24)^xor)*scale;
+	  bfill++;
+	}
+	break;
+	
+      case 2:
+      
+	if(bigendian[fi]){
+	  while(bfill<blocksize && rptr<readlimit){
+	    for(j=ch;j<channels[fi]+ch;j++){
+	      blockbuffer[j][bfill]=
+		(((rbuf[rptr+1]<<16)| (rbuf[rptr]<<24))^xor)*scale;
+	      rptr+=2;
+	    }
+	    bfill++;
+	  }
+	}else{
+	  while(bfill<blocksize && rptr<readlimit){
+	    for(j=ch;j<channels[fi]+ch;j++){
+	      blockbuffer[j][bfill]=
+		(((rbuf[rptr]<<16)| (rbuf[rptr+1]<<24))^xor)*scale;
+	      rptr+=2;
+	    }
+	    bfill++;
+	  }
+	}
+	break;
+	
+      case 3:
+	
+	if(bigendian[fi]){
+	  while(bfill<blocksize && rptr<readlimit){
+	    for(j=ch;j<channels[fi]+ch;j++){
+	      blockbuffer[j][bfill]=
+		(((rbuf[rptr+2]<<8)|(rbuf[rptr+1]<<16)|(rbuf[rptr]<<24))^xor)*scale;
+	      rptr+=3;
+	    }
+	    bfill++;
+	  }
+	}else{
+	  while(bfill<blocksize && rptr<readlimit){
+	    for(j=ch;j<channels[fi]+ch;j++){
+	      blockbuffer[j][bfill]=
+		(((rbuf[rptr]<<8)|(rbuf[rptr+1]<<16)|(rbuf[rptr+2]<<24))^xor)*scale;
+	      rptr+=3;
+	    }
+	    bfill++;
+	  }
+	}
+	break;
+      case 4:
+	
+	if(bigendian[fi]){
+	  while(bfill<blocksize && rptr<readlimit){
+	    for(j=ch;j<channels[fi]+ch;j++){
+	      blockbuffer[j][bfill]=
+		(((rbuf[rptr+3])|(rbuf[rptr+2]<<8)|(rbuf[rptr+1]<<16)|(rbuf[rptr+3]<<24))^xor)*scale;
+	      rptr+=4;
+	    }
+	    bfill++;
+	  }
+	}else{
+	  while(bfill<blocksize && rptr<readlimit){
+	    for(j=ch;j<channels[fi]+ch;j++){
+	      blockbuffer[j][bfill]=
+		(((rbuf[rptr])|(rbuf[rptr+1]<<8)|(rbuf[rptr+2]<<16)|(rbuf[rptr+3]<<24))^xor)*scale;
+	      rptr+=4;
+	    }
+	    bfill++;
+	  }
+	}
+	break;
+      }
+    }
+    ch+=channels[fi];
+    blockbufferfill[fi]=bfill;
+    readbufferptr[fi]=rptr;    
+  }
+}
+
+/* when we return, the blockbuffer is full or we're at EOF */
+/* EOF cases: 
+     loop set: return EOF if all seekable streams have hit EOF
+     loop unset: return EOF if all streams have hit EOF
+   pad individual EOF streams out with zeroes until global EOF is hit  */
+
+static int input_read(void){
+  int i,fi,ch=0;
+  int eof=1;
+  int notdone=1;
+
+  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;
+    }
+    ch+=channels[fi];
+  }
+
+  while(notdone){
+    notdone=0;
+
+    /* 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){
+	  memmove(readbuffer[fi],readbuffer[fi]+readbufferptr[fi],
+		  (readbufferfill[fi]-readbufferptr[fi])*sizeof(**readbuffer));
+	  readbufferfill[fi]-=readbufferptr[fi];
+	  readbufferptr[fi]=0;
+	}else{
+	  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));
+	  }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;
+	  
+	  }else{
+	    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;
+	  }
+	}
+      }
+      ch += channels[fi];
+    }
+  }
+  return eof;
+}
+
+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_av[i],0,(blocksize/2+1)*sizeof(**feedback_av));
+    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_av[i][j]=0;
+      ph_max[i][j]=0;
+      ph_instant[i][j]=0;
+    }
+  }
+  acc_clear=0;
+}
+
+/* return 0 on EOF, 1 otherwise */
+static int process(){
+  int fi,i,j,ch;
+  int eof_all;
+
+  /* for each file, FOR SCIENCE! */
+  for(fi=0;fi<inputs;fi++){
+    if(acc_rewind && seekable[fi]){
+
+      blockbufferfill[fi]=0;
+      readbufferptr[fi]=0;
+      readbufferfill[fi]=0;
+      fseek(f[fi],offset[fi],SEEK_SET);
+      if(length[fi]!=-1)bytesleft[fi]=length[fi]*channels[fi]*((bits[fi]+7)/8);
+    }
+  }
+
+  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;
+
+	  /* 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;
+	    }
+
+	    if(feedback_count[i]>=100){
+	      ph_av[i][j]*=.99;
+	      ph_av[i][j+1]*=.99;
+	    }
+
+	    ph_av[i][j]+=.1*pR;
+	    ph_av[i][j+1]+=.1*pI;
+	    
+	  }
+	  
+	  feedback_instant[i][j>>1]=sqM;
+	  feedback_acc[i][j>>1]+=sqM;
+	  if(feedback_count[i]>=100)
+	    feedback_av[i][j>>1]*=.99;
+	  feedback_av[i][j>>1]+=.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];
+  }
+  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);
+  }
+
+}
+
+float **process_fetch(int scale, int mode,int link, int *active, int width, 
+		      float *ymax, float *pmax, float *pmin){
+  int ch,ci,i,j,fi;
+  float **data;
+  float **ph;
+
+  /* are our scale mappings up to date? */
+  if(scale != metascale || width != metawidth){
+
+    if(!xmappingL) xmappingL = calloc(inputs, sizeof(*xmappingL));
+    if(!xmappingH) xmappingH = calloc(inputs, sizeof(*xmappingH));
+
+    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;
+
+      /* generate new numbers */
+      switch(scale){
+      case 0: /* linear */
+	for(i=0;i<width;i++){
+	  float lfreq=(i-1.)*20000./(width-1);
+	  float hfreq=(i+1.)*20000./(width-1);
+	  xmappingL[fi][i]=lfreq/(rate[fi]*.5)*(blocksize/2);
+	  xmappingH[fi][i]=hfreq/(rate[fi]*.5)*(blocksize/2);
+	}
+	break;
+      case 1: /* log */
+	for(i=0;i<width;i++){
+	  float lfreq= pow(10.,(i-1.)/(width-1)*(log10(100000.)-log10(5.))+log10(5.));
+	  float hfreq= pow(10.,(i+1.)/(width-1)*(log10(100000.)-log10(5.))+log10(5.));
+	  xmappingL[fi][i]=lfreq/(rate[fi]*.5)*(blocksize/2);
+	  xmappingH[fi][i]=hfreq/(rate[fi]*.5)*(blocksize/2);
+	}
+	break;
+      case 2: /* iso log */
+	for(i=0;i<width;i++){
+	  float lfreq= pow(2.,(i-1.)/(width-1)*(log2(20000.)-log2(25.))+log2(25.));
+	  float hfreq= pow(2.,(i+1.)/(width-1)*(log2(20000.)-log2(25.))+log2(25.));
+	  xmappingL[fi][i]=lfreq/(rate[fi]*.5)*(blocksize/2);
+	  xmappingH[fi][i]=hfreq/(rate[fi]*.5)*(blocksize/2);
+	}
+	break;
+      }
+      
+      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(feedback_work[i]){
+	feedback_work[i] = realloc(feedback_work[i],(width+1)*sizeof(**feedback_work));
+      }else{
+	feedback_work[i] = malloc((width+1)*sizeof(**feedback_work));
+      }
+  }
+      
+  /* mode selects the base data set */
+  switch(mode){    
+  case 0: /* independent / instant */
+    data=feedback_instant;
+    ph=ph_instant;
+    break;
+  case 1: /* independent / average */
+    data=feedback_av;
+    ph=ph_av;
+    break;
+  case 2: /* independent / max */
+    data=feedback_max;
+    ph=ph_max;
+    break;
+  case 3:
+    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 = feedback_work[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 = feedback_work[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_IMPEDENCE_p1:
+    case LINK_IMPEDENCE_1:
+    case LINK_IMPEDENCE_10:
+      {
+	float shunt = (link == LINK_IMPEDENCE_p1?.1:(link == LINK_IMPEDENCE_1?1:10));
+
+	for(ci=0;ci<channels[fi];ci++){
+	  float *y = feedback_work[ci+ch];
+	  float *r = feedback_work[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 */
+		y[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;
+		  if(y[i]>*ymax)*ymax=y[i];
+		}else{
+		  y[i] = NAN;
+		}
+	      }
+	    }
+	  }
+	}
+      }
+      break;
+
+    case LINK_PHASE: /* response/phase */
+
+      if(channels[fi]>=2){
+	float *om = feedback_work[ch];
+	float *op = feedback_work[ch+1];
+
+	float *r = data[ch];
+	float *m = data[ch+1];
+	float *p = ph[ch+1];
+
+	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]){
+	    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 > 1e-8){
+		sumM /= sumR;
+		om[i] = todB_a(&sumM)*.5;
+		if(om[i]>*ymax)*ymax = om[i];
+	      }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(sumR*sumR+sumI*sumI > 1e-16){
+		op[i] = atan2(sumI,sumR)*57.29;
+		if(op[i]>*pmax)*pmax=op[i];
+		if(op[i]<*pmin)*pmin=op[i];
+	      }else{
+		op[i]=NAN;
+	      }
+	    }
+	  }
+	}
+      }
+      break;
+
+    case LINK_THD: /* THD */
+      break;
+      
+    case LINK_THDN: /* THD+N */
+      break;
+      
+    }
+    ch+=channels[fi];
+  }
+
+  return feedback_work;
+
+}
+

Added: trunk/spectrum/spectrum-gtkrc
===================================================================
--- trunk/spectrum/spectrum-gtkrc	                        (rev 0)
+++ trunk/spectrum/spectrum-gtkrc	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,78 @@
+style "button-poppy" {
+
+	font_name = "sans 8"
+	GtkButton::relief = none
+
+	GtkButton::focus-padding = 0
+	GtkButton::focus-line-width = 1
+	GtkButton::interior-focus = 0
+}
+
+style "panel" {
+	bg[NORMAL]="#c0c0ce" 
+	bg[ACTIVE]="#e0f0ff" 
+	bg[PRELIGHT]="#e0f0ff" 
+
+	text[NORMAL]="#000000"
+	text[ACTIVE]="#000000"
+	text[PRELIGHT]="#000000" 
+
+	fg[NORMAL]="#000000"
+	fg[ACTIVE]="#000000"
+	fg[PRELIGHT]="#000000" 
+
+}
+
+style "panel-text" {
+	font_name = "sans bold 7"
+}
+
+style "scale-text" {
+	font_name = "sans 7"
+}
+
+style "readout" {
+	font_name = "sans 8"
+}
+
+style "frame-label" {
+	font_name = "sans bold 10"
+}
+
+style "small-readout" {
+	base[NORMAL]="#ffffff" 
+	base[ACTIVE]="#ffffff" 
+	bg[NORMAL]="#ffffff" 
+	bg[ACTIVE]="#ffffff" 
+	text[NORMAL]="#606060"
+	font_name = "Fixed, Nimbus Mono L, Courier, Monospace 8"	
+}
+
+style "quitbutton" {
+	bg[NORMAL]="#d0d0d0"
+	bg[PRELIGHT]="#ffc0c0"
+	bg[ACTIVE]="#ffc0c0"
+	font_name = "sans 8"	
+	GtkButton::focus-padding = 0
+	GtkButton::focus-line-width = 1
+	GtkButton::interior-focus = 0
+}
+
+widget "*" style "panel"
+widget "*.GtkLabel" style "panel-text"
+widget "*.Plot" style "scale-text"
+widget "*.framelabel" style "frame-label"
+
+widget "*.GtkEntry" style "readout"
+widget "*.GtkMenu*" style "button-poppy"
+widget "*.GtkComboBox*" style "button-poppy"
+widget "*.GtkToggleButton*" style "button-poppy"
+widget "*.GtkButton*" style "button-poppy"
+widget "*.quitbutton" style "quitbutton"
+widget "*.quitbutton.GtkLabel" style "quitbutton"
+widget "*.readout" style "readout"
+
+
+
+
+

Added: trunk/spectrum/touch-version
===================================================================
--- trunk/spectrum/touch-version	                        (rev 0)
+++ trunk/spectrum/touch-version	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+if(open F,"version.h"){
+	$line=<F>;
+	close F;
+
+	if(open F,">version.h"){
+	
+	    print F "$line";
+	    chomp($line=`date`);
+	    print F "/* DO NOT EDIT: Automated versioning hack [$line] */\n";
+	    close F;
+	    0;
+	}else{
+	    print "touch-version: Failed to write new version.h\n";
+	    1;
+	}
+}else{
+	print "touch-version: Failed to open version.h\n";
+	1;
+}


Property changes on: trunk/spectrum/touch-version
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/spectrum/version.h
===================================================================
--- trunk/spectrum/version.h	                        (rev 0)
+++ trunk/spectrum/version.h	2008-03-12 01:14:29 UTC (rev 14578)
@@ -0,0 +1,2 @@
+#define VERSION "$Id: version.h 6914 2004-06-29 03:05:41Z xiphmont $ "
+/* DO NOT EDIT: Automated versioning hack [Tue Mar 11 11:24:58 EDT 2008] */



More information about the commits mailing list