[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