diff model/gen.c @ 0:5242703e91d3 tip

Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author tomwalters
date Fri, 20 May 2011 15:19:45 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/model/gen.c	Fri May 20 15:19:45 2011 +0100
@@ -0,0 +1,1548 @@
+/*
+    Copyright (c) Applied Psychology Unit, Medical Research Council. 1988, 1989
+    ===========================================================================
+
+    Permission to use, copy, modify, and distribute this software without fee 
+    is hereby granted for research purposes, provided that this copyright 
+    notice appears in all copies and in all supporting documentation, and that 
+    the software is not redistributed for any fee (except for a nominal shipping 
+    charge). Anyone wanting to incorporate all or part of this software in a
+    commercial product must obtain a license from the Medical Research Council.
+
+    The MRC makes no representations about the suitability of this 
+    software for any purpose.  It is provided "as is" without express or implied 
+    warranty.
+ 
+*/
+
+/*
+	gen.c
+	=====
+
+    APU, ASP model demonstration program.
+
+
+    Copyright (c), 1989  The Medical Research Council, Applied Psychology Unit.
+
+
+    Authors : John Holdsworth (1989), Mike Allerhand (1990)
+
+
+    Edited  : M.Akeroyd 22-1-1993
+              Removed the conditional of `if (centering) Clear (w)' 
+	      (in plot_frame), so that Clear may be called for 
+	      PostScriptWindows that AREN'T centered, so allowing
+	      showpages to be generated.
+              See also edit to ps.c
+
+	    : M Akeroyd 3-8-1993
+              Added new options:
+		erbscale_efb (InputOption) mono_ctn (SilentOption)
+		colour_ctn (SilentOption) planemask_ctn (SilentOption)
+  	      Allowed "bitmap=stdout".
+
+	    : MAA 3rd August 1993
+              Extra options 
+	        framenumber (SilentOption), review (SilentOption)
+             
+            : MAA 22nd July 1994. 
+              Extra postscript options ALl silent
+                xstart_ps xend_ps ystart_ps yend_ps 
+                xtitle_ps ytitle_ps
+              Extra X options
+                fg_col bg_col
+
+            : MAA Summer 1984. Lots of postscript options. All silent
+  	       portraitstr, landscapestr;
+	       fontnamestr, fontsizestr;
+	       xmajorticksstr, xminorticksstr, ymajorticksstr, yminorticksstr;
+	       axistop, axisbottom, axisleft, axisright;
+            
+            : AJD Spring 1995.  
+              Added a new argument 'nwid' to the function SourceDraw, so that genspl
+              can have the necessary information. 
+
+            : AJD May, 1995.
+              Made options overlap, headroom, perspective, bytemax and type silent.
+*/
+
+
+#ifndef  lint
+static char *sccs_id = "@(#)gen.c	1.66 John Holdsworth, Mike Allerhand, Roy Patterson, Paul Manson (MRC-APU) 6/6/91" ;
+#endif
+
+
+/****************************************************************************
+* This module contains:
+*   Display option parameter strings.
+*   Model option parameter strings.
+*   File-format strings.
+*     X windows interface, (included from windows.h).
+*     Options handler routines, (included from options.h).
+*   Option defaults, (application specific).
+*   Strings for generic option types, (OnOption etc).
+*   Display options table, (displayopts[]).
+*     Model option tables, (included from table.c).
+*     Model entry-point functions, (included from model.c).
+*     Display drawing routines, (included from draw.h and fill.h).
+*   Options table construction, (constructOptions(), and mapOptions()).
+*   Main.
+****************************************************************************/
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <math.h>
+
+#if defined(THINK_C) || defined(NeXT)
+#include <stdlib.h>
+#endif
+
+/********************* Display option parameter strings *********************
+* The strings defined here are pointers referenced by the "value" field in
+* the display options table, displayopts. They are assigned to space which
+* initially contains defaults in the "defaultValue" field of the display
+* options table.
+****************************************************************************/
+
+static char *bmaxstr,   *topstr,    *botstr,    *overstr   ;
+static char *headstr,   *downcstr,  *heightstr, *helpstr   ;
+static char *invertstr, *lengthstr, *linstr,    *outstr    ;
+static char *reusestr,  *startstr,  *envstr,    *typestr   ;
+#ifdef X11
+static char *dispstr,   *erasestr,  *hiddenstr, *bitmapstr ;
+static char *kludgestr, *magstr,    *penstr,    *psfilestr ;
+static char *psstr,     *animstr,   *titlestr,  *viewstr   ;
+static char *widthstr,  *xstr,      *ystr                  ; 
+/* All these MAA. */
+char        *monostr,   *colourstr, *planemaskstr, *rotateaxesstr;
+char        *fgcolourstr, *bgcolourstr;                           
+char        *xstartstr, *ystartstr, *xendstr,   *yendstr;         
+char        *xnewtitlestr, *ynewtitlestr;        
+char        *portraitstr, *landscapestr;
+char        *fontnamestr, *fontsizestr, *titlesizestr;
+char        *xticksstr, *yticksstr, *outsidestr;
+char        *axistopstr, *axisbottomstr, *axisleftstr, *axisrightstr;
+char        *xmajorticksstr, *xminorticksstr, *ymajorticksstr, *yminorticksstr;
+char        *axislinewidthstr, *figurelinewidthstr;
+char        *boxstr;
+#endif  /* X11 */
+
+char *framenumberstr,  *reviewstr;            /* MAA: 3-8-1993 */
+
+static char *test1str,  *test2str,  *test3str,  *test4str  ;
+
+char headerstr[64] ;
+
+char *filestr ;
+
+/* application display parameters */
+
+#ifdef X11
+static char  *tiltstr = "0."       ;
+static char  *diststr = "0.5"      ;
+static char  *roomstr = "0%"       ;
+#endif  /* X11 */
+
+/*************** Included from glib (graphics library) directory ***********/
+
+#include "options.h"
+#include "defaults.h"
+
+#ifdef X11
+#include "windows.h"
+#endif  /* X11 */
+
+/******************* Strings for generic option values **********************
+* Generic option values, (ON_OPTION etc), are defined in options.h.
+* Convenience routines used to test or convert option values are:
+*  OptionStringsEqual( str1, str2 )     -(defined in options.c).
+*  isON( str )                          -(defined in options.h).
+*  isOFF( str )                         -(defined in options.c).
+*  isNULL( str )                        -(defined in options.c).
+*  OptionInt( str )                     -(defined in model.c).
+*  OptionDouble( str )                  -(defined in model.c).
+*  Samples( str )                       -(defined in model.c).
+*  Cycles( str, cfreq )                 -(defined in model.c).
+*  Scalar( str )                        -(defined in model.c).
+*  Freq( str )                          -(defined in model.c).
+*
+****************************************************************************/
+
+
+static char helpstring[200] = "no help initially" ;
+static char      OnOption[] =           ON_OPTION ;     /* "on"       */
+static char     OffOption[] =          OFF_OPTION ;     /* "off"      */
+static char    NullOption[] =         NULL_OPTION ;     /* "none"     */
+static char     CenterStr[] =            "center" ;
+static char     LengthStr[] =         "remainder" ;
+static char       LandStr[] =         "landscape" ;
+
+static int isEXCITE( str )
+char *str ;
+{
+    return ( strncmp( str, "excitation", strlen( str ) ) == 0 ) ;
+}
+
+static int isWATERFALL( str )
+char *str ;
+{
+    return ( strncmp( str, "waterfall", strlen( str ) ) == 0 ) ;
+}
+
+static int isGREYSCALE( str )
+char *str ;
+{
+    return ( strncmp( str, "greyscale", strlen( str ) ) == 0 ||
+	     strncmp( str, "grayscale", strlen( str ) ) == 0 ) ;
+}
+
+
+
+/************************* Options table ***********************************
+* The complete options table is built by constructOptions() according to the
+* application. It is composed of the display options, (displayopts[], below),
+* and the model option tables (from table.c) which are appropriate for the
+* application. The sequence of processing stages, (and hence model option
+* tables), for an application is determined by FindStage() in model.c.
+*
+* The complete options table is an array of Option structs.
+* The Option struct is defined in options.h as:
+*
+* typedef struct {
+*   char   *name           ;   The name of the option.
+*   char   *defaultValue   ;   Default value.
+*   char  **value          ;   Address of current value.
+*   char   *comment        ;   Something to print when the user types "-help".
+*   short   classification ;   Type of option it is.
+* } Option;
+*
+* The classification of options uses names defined in options.h:
+*   InputOption
+*   OutputOption
+*   InOutOption
+*   SilentOption
+*
+* Routines which access the option table:
+* Note: Some routines are called to supply arguments to other routines.
+*       Routines which access the option table in some way are denoted (*).
+*       Most of these routines are defined in options.c.
+*
+*                                 main()
+*                                   |
+*     +-----------------+-----------+----+-------+-----------------+
+*     |                 |                |       |                 |
+*     |                 |                | ModeledSource()         |
+*     |                 |                |       |                 |
+*     |                 |                | checkForFile()          |
+* helpopts()            |                |       |                 |
+*     |                 |                +---+---+                 |
+*     |                 |                    |                     |
+*     |              getopts()           readopts()           writeopts()
+*     |                 |                    |                     |
+*     |            getnopts(*)          readnopts()            writenopts(*)
+*     |   [arg countOptions(*)]   [arg countOptions(*)]  [arg countOptions(*)]
+*     |                 |                    |                     |
+*     |         +-------+------+             |            countBytesToWrite(*)
+*     |         |       |      |             |
+*     |         |       |      +------+------+
+*     |         |       |             |
+*     |         |       |      processOptionFile()
+*     |         |       |             |
+*     |         |       +------+------+
+*     |         |              |
+*     |         |        LookUpAndStore(*)
+*     |         |              |
+*     |         |          +---+---+----------+
+*     |         |          |       |          |
+*     |         +-----+----+    LookUp(*) Ambiguity(*)
+*     |         |     |
+*     +----+----+  updateOptionsFile(*)
+*          |
+*   defaultHelpHandler(*)
+*
+*
+* getopts()
+* Operates on the options table given to it as an argument.
+*  a) Each "value" field is initialized to its "defaultValue" field.
+*  b) The "value" fields are re-initialized by any corresponding values found
+*     in the options ("rc") file, (overriding the "defaultValue").
+*  c) The "value" fields are re-initialized again by any corresponding values
+*     found in command-line arguments.
+*  d) Special arguments are dealt with:
+*     "help"      calls the onLineHelpHandler() routine, (see below).
+*     "update"    calls the updateOptionsFile() routine.
+*  e) Return the remainder of the command-line (argc and argv) for use in
+*     main().
+*
+* helpopts()
+* Is called to print the help menu if no input file has been found by
+* getopts(), (in the "rc" file), and none remains on the command-line.
+* An alias for the onLineHelpHandler() function, which is in turn an
+* alias for the defaultHelpHandler() routine, (see options.c).
+*
+* readopts()
+* Read any options header which may be at the start of the input file.
+* Interpret the header like an options ("rc") file, and re-initialize any
+* corresponding "value" fields in the given options table, (as in getopts()).
+* Skip to the start of the input data, allowing for any offset (ie startstr).
+*
+* writeopts()
+* Write the given options table as an options header at the start of an o/p
+* file, if the "header" option is "on".
+*
+****************************************************************************/
+
+static  Option *options;
+
+/******************** Display options table ********************************
+* Options table for application display parameters.
+****************************************************************************/
+
+static  Option displayopts[] = {
+
+  /* Miscellaneous Silent Parameters (hidden from users) */
+
+
+ { "Version",      headerstr, &versionstr,      "Version number and date", OutputOption},
+
+ { "test1",              "0", &test1str,        "Dummy option for test and development", SilentOption},
+ { "test2",              "0", &test2str,        "Dummy option for test and development", SilentOption},
+ { "test3",              "0", &test3str,        "Dummy option for test and development", SilentOption},
+ { "test4",              "0", &test4str,        "Dummy option for test and development", SilentOption},
+
+ { "linear_sgm",   OffOption, &linstr,          "Linear frequency axis for spectrograms",SilentOption},
+ { "useprevious",  OffOption, &reusestr,        "Use previously stored output",          SilentOption},
+
+/* MAA: 21-8-1993 
+ * These next two options are here, because otherwise "gensgm -use ..." (at least) core dumps.
+ */
+ { "framenumber",  OffOption, &framenumberstr,  "Index of current frame (to stderr)\n",     SilentOption},
+ { "review",       OffOption, &reviewstr,       "Wait for keyboard <return> between frames",SilentOption},
+
+#ifdef X11
+ { "pstofile",     OffOption, &psfilestr,       "Send PostScript output to .ps file",    SilentOption},
+ { "invert",       OffOption, &invertstr,       "Invert axis of sai image",              SilentOption},
+ { "fast",         OffOption, &kludgestr,       "Speed up image generation",             SilentOption},
+#endif  /* X11 */
+
+  /* ( The order of the "help" option members is strange due to the way   */
+  /*   it was originally used.  See options.c: defaultHelpHandler() and   */
+  /*   updateOptionsFile() ).                                             */
+
+  /* Input Parameters */
+
+ { "envelope",     OffOption, &envstr,          "Type of model to use required",         SilentOption},
+ { "help",        "not used", &helpstr,         helpstring,                              SilentOption},
+
+#ifdef X11
+
+  /* Screen Output Parameters */
+
+ { "title",       "filename", &titlestr,  "Title of picture, or input filename",       InputOption},
+#ifdef THINK_C 
+ { "x0_win",            "10", &xstr,      "Left edge of window (pixels)",              InOutOption},
+ { "y0_win",            "50", &ystr,      "Lower edge of window (pixels)",             InOutOption},
+#else
+ { "x0_win",       CenterStr, &xstr,      "Left edge of window (pixels)",              InOutOption},
+ { "y0_win",       CenterStr, &ystr,      "Lower edge of window (pixels)",             InOutOption},
+#endif
+#ifdef PC
+ { "width_win",        "400", &widthstr,  "Window width (pixels)",                     InOutOption},
+ { "height_win",       "300", &heightstr, "Window height (pixels)\n",                  InOutOption},
+#else /* UNIX */
+ { "width_win",        "540", &widthstr,  "Window width (pixels)",                     InOutOption},
+ { "height_win",       "400", &heightstr, "Window height (pixels)\n",                  InOutOption},
+#endif
+ { "display",       OnOption, &dispstr,   "Select display of output image",            InOutOption},
+ { "view",           LandStr, &viewstr,   "Select display format",                     InOutOption},
+ { "top",             "1024", &topstr,    "Maximum of plotted range",                  InputOption},
+ { "bottom",         "-1024", &botstr,    "Minimum of plotted range",                  InputOption},
+ { "overlap",          "50%", &overstr,   "Overlap between channels",                  SilentOption}, /* AJD 16-5-95 */
+ { "headroom",          "0%", &roomstr,   "Headroom at top of picture",                SilentOption}, /* AJD 16-5-95 */
+ { "magnification",      "1", &magstr,    "Magnification of image in display",         InputOption},
+ { "pensize",            "1", &penstr,    "Size of plotted lines and dots",            InputOption},
+ { "hiddenline",    OnOption, &hiddenstr, "Hidden line removal\n",                       InputOption},
+ { "perspective",        "0", &tiltstr,   "Perspective view of display (degrees)\n",   SilentOption}, /* AJD 16-5-95 */
+#endif  /* X11 */
+
+ { "downchannel",  OffOption, &downcstr,  "Average adjacent frequency channels\n",     InOutOption},
+
+#ifdef X11
+
+  /* Auditory image cartoon parameters */
+
+ {   "erase_ctn",   OnOption, &erasestr,  "Erase display between cartoon frames",      InOutOption},
+ { "animate_ctn",  OffOption, &animstr,   "Animate cartoon",                           InOutOption},
+ {  "bitmap_ctn",  OffOption, &bitmapstr, "Produce cartoon output (.ctn)\n",           InputOption},
+ {    "mono_ctn",   OnOption, &monostr,   "Force monochrome (single plane) cartoons.", SilentOption},
+ {  "colour_ctn",  OffOption, &colourstr, "Force colour (multi-plane) cartoons.",      SilentOption},
+ {"planemask_ctn", "1", &planemaskstr,    "Planemask for creating cartoons.\n",        SilentOption},
+ {"fg_col",          "black", &fgcolourstr, "Foreground Colour.\n",                    SilentOption},
+ {"bg_col",          "white", &bgcolourstr, "Background Colour.\n",                    SilentOption},
+
+
+ /* Postscript options */
+
+ { "postscript",   OffOption, &psstr,     "Produce postscript output",                 InputOption},
+ { "rotateaxes",   OffOption, &rotateaxesstr,     "Rotate the axes labels in .ps output",  SilentOption},
+ { "xstart_ps",           "", &xstartstr, "Postscript x-axis: start point.",           SilentOption},
+ { "xend_ps",             "", &xendstr,   "Postscript x-axis: end point.",             SilentOption},
+ { "ystart_ps",           "", &ystartstr, "Postscript y-axis: start point.",           SilentOption},
+ { "yend_ps",             "", &yendstr,   "Postscript y-axis: end point.",             SilentOption},
+ { "xtitle_ps",           "", &xnewtitlestr, "Postscript x-axis: title.",              SilentOption},
+ { "ytitle_ps",           "", &ynewtitlestr, "Postscript x-axis: title.",              SilentOption},
+ { "portrait_ps",         "", &portraitstr, "Use 'portrait' page format",              SilentOption},
+ { "landscape_ps",        "", &landscapestr, "Use 'landscape' page format (overrides 'portriat)", SilentOption},
+ { "fontname_ps", "Helvetica", &fontnamestr, "Define font",              SilentOption},
+ { "fontsize_ps",       "12", &fontsizestr, "Define fontsize (points)",                     SilentOption},
+ { "fonttitlesize_ps",   "0", &titlesizestr, "Define fontsize for title (points)",                     SilentOption},
+ { "axislinewidth_ps",   "1", &axislinewidthstr, "Linewidth for axes & tickmarks (points)",                     SilentOption},
+ { "figurelinewidth_ps", "1", &figurelinewidthstr, "Linewidth for figures (points)",                     SilentOption},
+ { "xticks_ps",          "6", &xticksstr,   "Define size of x-axis (big) ticks (points)",   SilentOption},
+ { "yticks_ps",          "6", &yticksstr,   "Define size of y-axis (big) ticks' (points)",   SilentOption},
+ { "outsideticks_ps",OnOption, &outsidestr,   "Print tick-marks 'outside' figure",   SilentOption},
+ { "axistop_ps",    OnOption, &axistopstr,  "Print tick-marks on top side",   SilentOption},
+ { "axisbottom_ps", OnOption, &axisbottomstr, "Print tick-marks on bottom side",   SilentOption},
+ { "axisleft_ps",   OnOption, &axisleftstr, "Print tick-marks on left side",   SilentOption},
+ { "axisright_ps",  OnOption, &axisrightstr, "Print tick-marks on right side",   SilentOption},
+ { "xmajorticks_ps",  "1",   &xmajorticksstr, "Relative *spacing* of ticks",   SilentOption},
+ { "xminorticks_ps",  "1",   &xminorticksstr, "Relative *spacing* of ticks",   SilentOption},
+ { "ymajorticks_ps",  "1",   &ymajorticksstr, "Relative *spacing* of ticks",   SilentOption},
+ { "yminorticks_ps",  "1",   &yminorticksstr, "Relative *spacing* of ticks",   SilentOption},
+ { "box_ps",     OnOption,       &boxstr,                  "Plot a box",   SilentOption},
+#endif  /* X11 */
+
+  /* File Output Parameters */
+ { "output",       OffOption, &outstr,    "Produce output file",                       InputOption},
+ { "header",        OnOption, &headstr,   "Add a header to the output file\n",           InOutOption},
+ { "bytemax",          "255", &bmaxstr,   "Maximum value in byte output",              SilentOption}, /* AJD 16-5-95 */
+ { "type",           "short", &typestr,   "Output data type\n",                        SilentOption}, /* AJD 16-5-95 */
+
+ { "input_wave",  NullOption, &filestr,   "Default input file name",                   InputOption},
+ { "start_wave",       "0ms", &startstr,  "Start point in wave (ms)",                  InputOption},
+ { "length_wave",  LengthStr, &lengthstr, "Length of wave to process (ms)",            InputOption},
+
+ ( char * ) 0 } ;
+
+
+/*********************** Included from stitch directory ********************/
+
+/* interfaces to stitch system */
+
+#include "stitch.h"
+#include "source.h"
+#include "spiral.h"
+#include  "image.h"
+#include  "model.h"
+#include  "units.h"
+#include   "calc.h"
+#include    "ops.h"
+#include     "io.h"
+
+
+#ifdef X11
+#include   "draw.h"
+#include   "fill.h"
+#endif  /* X11 */
+
+/**************************************************************************/
+
+char *AlterSuffix() ;               /* alter suffix of file name. */
+#ifdef X11
+void save_frame(), plot_frame() ;   /* draw callbacks. */
+void fast_frame() ;
+#endif  /* X11 */
+
+extern char *genstate() ;
+static char *gen_state ;
+
+Source DownChannel() ;              /* reduce channels by averaging. */
+#ifdef X11
+Source PackShorts()  ;              /* pack output into one byte. */
+#endif  /* X11 */
+Source checkForFile() ;             /* create source from file if present. */
+
+#define POSTSCRIPT_FILE_SUFFIX ".ps"
+#define CARTOON_FILE_SUFFIX ".ctn"
+
+#ifdef X11
+static WindowObject display = 0 ;
+#endif  /* X11 */
+
+/***************************************************************************/
+
+/* Return length of the file in bytes */
+static long fileLength( fp )
+FILE *fp ;
+{
+    long posn = ftell( fp ) ;
+    long length = 0 ;
+
+    if( fseek( fp, 0l, 2 ) == 0 ) {
+
+	length = ftell( fp ) ;
+
+	(void) fseek( fp, posn, 0 ) ;
+    }
+
+    return ( length ) ;
+}
+
+/* per frame image handling */
+
+static FILE *inputFilePointer = ( FILE * ) 0 ;
+
+#ifdef X11
+
+static FILE *imageFilePointer = ( FILE * ) 0 ;
+static int   images = 0 ;
+static void (*frame_drawer)() ;
+
+/***** axis information *****/
+/*
+  "Axis" is defined in windows.h:143
+  The postscript program for drawing axis stuff is in axis.h
+*/
+
+static int Top()
+{
+    return ( atof( topstr ) / atof( magstr ) + 0.5 ) ;
+}
+
+static int Bot()
+{
+    if( atof( botstr ) < 0 )
+	return ( atof( botstr ) / atof( magstr ) - 0.5 ) ;
+    else
+	return ( atof( botstr ) / atof( magstr ) + 0.5 ) ;
+}
+
+static struct {
+  double min, max ;
+  char *label ;
+} xaxis, yaxis ;
+
+
+
+TimeAxis( min, max, label )
+double *min, *max ;
+char **label ;
+{
+    *min = Samples( startstr, Samplerate() ) / Samplerate()                                         ;
+    *max = Samples( startstr, Samplerate() ) / Samplerate() + Frames() * Framestep() / Samplerate() ;
+
+    if( *max > 1. )
+	*label = "Time [s]" ;
+    else {
+	*label = "Time [ms]" ;
+
+	*min *= 1000. ;
+	*max *= 1000. ;
+    }
+}
+
+MagnitudeAxis( min, max, label )
+double *min, *max ;
+char **label ;
+{
+    *min = Bot() ;
+    *max = Top() / ( 1. - atof( headstr ) / 100. ) ;
+
+    *label = "Magnitude" ;
+}
+
+
+static void DrawAxes( win )
+WindowObject win ;
+{
+    /* frame window with axes */
+
+    Axes( win, titlestr, xaxis.min, xaxis.max, xaxis.label,
+			 yaxis.min, yaxis.max, yaxis.label ) ;
+    return ;
+}
+
+#endif  /* X11 */
+
+/******************************* main **************************************
+* The following tables are used extensively:
+* stage table   Arrays of _stage structs, ("envelope", "fine", etc.), defined
+*               in routine FindStage() in model.c.
+* option table  Array of Option structs built at run-time by
+*               constructOptions() from displayopts, (in gen.c), and tables of
+*               model options included from table.c.
+*
+* Compiler switches for model versions are:
+*     FLOAT     For floating point version, (default uses integer arithmetic).
+*               Defined in calc.h.
+*
+* The model version number is the sccs <release-number>.<level-number> of
+* the file version.c. Refer to that file for incrementing the version number.
+* The version number is stored in versionstr in routine getversion().
+****************************************************************************/
+
+int    argc_save ;
+char **argv_save ;
+
+main( argc, argv )
+int argc ;
+char *argv[] ;
+#ifdef THINK_C
+{
+	static char *realargv[] = { "prog", "", 0 };
+	static char *progs = { "\pgenwav;-;\
+genfbm;genfbr;genfbc;genfbt;genfbd;gensai;genspl;gensas;-;\
+genbmm;gennap;gensai;-;gensgm;gencgm;gensas;-;genasa;genepn;gensep;-;Quit/Q" } ;
+	
+	extern char *MacMenu(), *MacFile() ;
+	
+	MacInit() ;
+
+	realargv[0] = MacMenu(progs) ;
+	realargv[1] = MacFile() ;
+
+	return ( real_main( sizeof( realargv ) / sizeof (*realargv) - 1, realargv ) ) ;
+}
+
+real_main( argc, argv )
+int argc ;
+char *argv[] ;
+#endif
+{
+    int    i ;
+    FILE *ofp = 0 ;
+    Source source ;
+    char *which, *psfilename ;
+    char title[256] ;
+    char *programName ;
+    int x, y, pixels, image, time ;
+    long headerSize, fileFramebytes ;
+    double *frequency_scale ;
+#ifdef X11
+    WindowObject psw = 0 ;
+    char *imageFileName ;
+#endif  /* X11 */
+
+/**************************************************************************
+* Determine which version of the model is required.
+* Set "which" to the last three chars of the program name, (allowing for
+* a ".exe" extension in the case of a PC version).
+**************************************************************************/
+
+
+    programName = argv[0] ;
+
+    which = programName + strlen( programName ) - strlen( "xxx" ) ;
+
+#ifdef PC
+    lowerArgs( argv, argc ) ;
+
+    which -= strlen( ".exe" ) ;
+#endif
+
+
+/***************************************************************************
+* Process arguments, (the model options):
+* Set option defaults (from option table in model.c).
+* Then override these by any options in the options file,
+* (the resource control file, eg .gensairc).
+* Finally, override these by any options given as command-line arguments.
+*
+* offsetOptions() finds the first option, (a ptr to one line of the "res"
+* array), required by the program. This gives a place mark in the options
+* table. (Note that the bottom of the options table, a null, corresponds with
+* the top of the help menu).
+* This first option appropriate to the program is found by looking it up in
+* the stage table (in model.c), and then cross-referencing the option name
+* with the names in the options table.
+*
+* Here is an example of an option, showing the members of the Options struct:
+*   name:         defaultValue: value:       comment:         classification:
+* {"length_wave", LengthStr,    &lengthstr, "Length of wave", InputOption},
+*
+* getopts() operates on the options between the given place mark and the
+* bottom of the table.
+* This is the only call to routine getopts(), which is defined in options.c.
+* First, each "value" is set to its corresponding "defaultvalue".
+* Then the options file (the "rc" file) is opened, and any options found there
+* are assigned to the corresponding "value", (overriding the "defaultvalue").
+* Then the command-line arguments are examined, and any options found there
+* are assigned to the corresponding "value", (overriding again).
+* Special arguments are dealt with:
+*   "help"      calls the onLineHelpHandler() routine, (see below).
+*   "update"    calls the updateOptionsFile() routine.
+*
+* Finally, leave the remainder of the command line (argc and argv).
+* Anything left on the command line is interpreted as follows:
+*   "filestr"   is the input data filename,
+*   "startstr"  is the start point in the data,
+*   "lengthstr" is the amount of data to process,
+*   "whichstr"  is the last 3 chars of the program name (the program version).
+*
+* Note that if no input file has been found by getopts(), (in the "rc" file),
+* and none remains on the command line, then helpopts() is called.
+* This is an alias for the onLineHelpHandler() function, which is in turn an
+* alias for the defaultHelpHandler() routine, (see options.c).
+*
+*
+***************************************************************************/
+
+    getversion( programName ) ;
+
+    /* The helpstring, defined in model.c, is stored as the comment field  */
+    /* of the help option (see table.c). It is accessed in options.c in    */
+    /* defaultHelpHandler(), using options[help].comment.                  */
+
+    (void) sprintf( helpstring, " [file_name]\n       Generates %s", modelHelp( which ) ) ;
+
+    options = constructOptions( which, displayopts ) ;
+
+
+    /* Hack to save argc and argv so they don't get mangled by getopts    */
+    /* This is so the command line args can override a reused file header */
+
+    argc_save = argc ;
+    argv_save = (char **)malloc( ( argc + 1 ) * sizeof( char *) ) ;
+    for ( i = 0 ; i < argc ; i++ ) {
+	argv_save[i] = (char *)malloc( ( strlen( argv[i] ) + 1 ) * sizeof( char ) ) ;
+	strcpy( argv_save[i], argv[i] ) ;
+    }
+
+    getopts( options, &argc, &argv ) ;
+
+    if( argc == 0 && isNULL( filestr ) ) {
+	(void) helpopts( options, programName ) ;
+	stitch_exit( -1 ) ;
+    }
+
+    if( argc > 0 && strcmp( *argv, "+" ) != 0 && argc-- > 0 )
+	filestr   = *argv++ ;
+
+    if( argc > 0 && strcmp( *argv, "+" ) != 0 && argc-- > 0 )
+	startstr  = *argv++ ;
+
+    if( argc > 0 && strcmp( *argv, "+" ) != 0 && argc-- > 0 )
+	lengthstr = *argv++ ;
+
+    if( isNULL( whichstr ) )
+	whichstr = which ;
+
+
+/***************************************************************************
+* horrrrrrrrible hack for now to try out -envelope option
+***************************************************************************/
+
+    if( isON( envstr ) )
+	if( whichstr[1] == 'b' )
+	    whichstr[1] =  'e' ;
+	else {
+	    /* map sai to sie and sas to sse */
+
+	    whichstr[1] = whichstr[2] ;
+	    whichstr[2] = 'e' ;
+	}
+
+
+/***************************************************************************
+* hack test arguments
+***************************************************************************/
+#if 00
+    test1 = atof( test1str ) ;
+    test2 = atof( test2str ) ;
+    test3 = atof( test3str ) ;
+    test4 = atof( test4str ) ;
+#endif
+
+/***************************************************************************
+* Open input data file, (use stdin if filename "-" is given).
+***************************************************************************/
+
+    if( strcmp( filestr, "-" ) == 0 )
+	inputFilePointer = stdin ;
+    else
+	inputFilePointer = fopen( filestr, readBinary ) ;
+
+/***************************************************************************
+* Position input data file pointer correctly:
+* Read any options header at the start of the input file. Set any options.
+* Skip to the start of the input data, allowing for any offset (ie startstr).
+* Set "framesstr" to the number of frames in the input data file, (here this
+* means the number of shorts remaining in the input file).
+* Create a source for the input data file.
+* Re-set "framesstr" to allow for a given "lengthstr", (ie not the whole file).
+***************************************************************************/
+
+    if( inputFilePointer != (FILE *) 0 ) {
+
+	readopts( options, inputFilePointer ) ;
+
+	headerSize = ftell( inputFilePointer ) ;
+
+	setFrames( fileLength( inputFilePointer ) / sizeof ( short ) - (long) Samples( startstr, Samplerate() ) ) ;
+
+	(void) fseek( inputFilePointer, (long)( headerSize + (long) Samples( startstr, Samplerate() ) * sizeof ( short ) ), 0 ) ;
+
+	source = FileSource( inputFilePointer ) ;
+    }
+
+    /* If a specific processing amount is specifed (other than "remainder") */
+    /* and this amount is less than the remainder of the input file, then   */
+    /* override the amount of data to process, (in framesstr).              */
+
+    if ( strncmp( lengthstr, LengthStr, strlen(lengthstr) ) != 0 &&
+			    Samples( lengthstr, Samplerate() ) < Frames() )
+	setFrames( (long) ( Samples( lengthstr, Samplerate() ) ) / Framestep() ) ;
+
+
+/***************************************************************************
+* Create source with data derived from processing file through model.
+* This may involve several stages of callback, and will initialize a chain of
+* objects which will ultimately execute the program, (see SinkSource() below).
+* If reusestr in "on", the "useprevious" option will use a previously
+* generated file, (see checkForFile() routine below).
+* If no input data file is found, report error: "could not open file".
+*
+* The routine ModeledSource() first uses Findstage() to find the appropriate
+* place in the stage table called for by the program, (see model.c).
+* Then it finds the end of the stage table, and works back up the table to
+* the stage called for by the program. Each stage in the table is a process
+* on the way to the program as a whole. The order of the stage table sets the
+* order of processing.
+* Immediately below are the only two calls to routine ModeledSource().
+***************************************************************************/
+
+    if( isON( reusestr ) )
+	source = ModeledSource( source, checkForFile ) ;
+    else
+	if( inputFilePointer != (FILE *) 0 )
+	    source = ModeledSource( source, (Source ( * )()) 0 ) ;
+
+    if( inputFilePointer == (FILE *) 0 )
+	stitch_error( "Could not open file \"%s\" for input\n", filestr ) ;
+
+/***************************************************************************
+* If the "downchannel" option is on, reduce channels by averaging.
+* (See DownChannel() routine below).
+* Then updateFramebytes sets  "framebytesstr" to the total number of bytes
+* in each frame, (ie the new width * height).
+***************************************************************************/
+
+
+    for( time=0 ; time<OptionInt( downcstr ) ; time++ ) {
+	source = DownChannel( source, Frameheight() ) ;
+	setFrameheight( Frameheight() / 2 ) ;
+    }
+    updateFramebytes() ;
+
+#if 0  /* for debugging */
+fprintf( stderr, "%d %d %d %d %ld\n",
+	Framewidth(),
+	Frameheight(),
+	Framebytes(),
+	Framestep(),
+	Frames() ) ;
+#endif
+
+/***************************************************************************
+* From here on is concerned with setting up for output, then starting the
+* processing, and finally cleaning up.
+***************************************************************************/
+
+#ifdef X11
+/* First sort out window placement. */
+
+    if( strcmp( xstr, CenterStr ) == 0 )
+	x = -1 ;
+    else
+	x = atoi( xstr ) ;
+
+    if( strcmp( ystr, CenterStr ) == 0 )
+	y = -1 ;
+    else
+	y = atoi( ystr ) ;
+
+    if( isGREYSCALE( viewstr ) )
+	pixels = 0 ;
+    else {
+	pixels = atoi( penstr ) ;
+
+	if( isON( hiddenstr ) && Frameheight() > 1 )
+
+	    pixels = -abs( pixels ) ;
+
+	/* Convert arguments for line-drawing. See draw.ch */
+	drawTilt     = atof( tiltstr ) ;
+	drawDistance = atof( diststr ) ;
+	drawHeadroom = atof( roomstr ) ;
+	drawOverlap  = atof( overstr ) / 100. ;
+#ifdef mips
+	if( pixels == 0 )
+	    pixels = 1 ;
+#endif
+    }
+#endif  /* X11 */
+
+    /* work out title of output */
+
+    (void) strcpy(  title, filestr ) ;
+    (void) strcat(  title, "." ) ;
+    (void) strncat( title, whichstr, strlen( "xxx" )+1 ) ;
+
+#ifdef X11
+
+    if( strcmp(titlestr,"filename") == 0)   /* was: if(isNULL(titlestr)) */
+	titlestr = title ;
+
+    /* calculate axis information */
+
+    if( isEXCITE( viewstr ) ) {
+	  ChannelAxis( &xaxis.min, &xaxis.max, &xaxis.label ) ;
+	MagnitudeAxis( &yaxis.min, &yaxis.max, &yaxis.label ) ;
+    }
+    else if( isWATERFALL( viewstr ) ) {
+	  ChannelAxis( &xaxis.min, &xaxis.max, &xaxis.label ) ;
+	     TimeAxis( &yaxis.min, &yaxis.max, &yaxis.label ) ;
+    }    /* landscape ones */
+    else if( Framewidth() > 1 ) {
+	    DelayAxis( &xaxis.min, &xaxis.max, &xaxis.label ) ;
+	  ChannelAxis( &yaxis.min, &yaxis.max, &yaxis.label ) ;
+    }
+    else {
+	     TimeAxis( &xaxis.min, &xaxis.max, &xaxis.label ) ;
+
+	if( Frameheight() > 1 )
+	  ChannelAxis( &yaxis.min, &yaxis.max, &yaxis.label ) ;
+	else
+	MagnitudeAxis( &yaxis.min, &yaxis.max, &yaxis.label ) ;
+
+    }
+
+
+    /* select outputs... */
+
+
+    /* select sai drawing method */
+
+    /* Hack so spiral parameters are independent of stage and option order */
+    /* (Spiral parameters appear late in options list, so are not known    */
+    /*  when an earlier stage is specified. So don't process unless sai).  */
+
+    if( strncmp(whichstr,"spl",strlen("spl")) == 0 ) {
+
+	frame_drawer = draw_spiral;
+
+	dotthresh_spl = Top() ;
+	dotsize_spl   = OptionInt(  penstr ) ;
+    }
+    else
+	frame_drawer = draw_frame;
+
+    /* screen output first         */
+    /* (set by option: display=on) */
+
+    if( isON( dispstr ) ) {
+
+	display = newDisplayWindow( titlestr, x, y, atoi( widthstr ), atoi( heightstr ), pixels ) ;
+
+	if( !isGREYSCALE( viewstr ) /* && OptionInt( penstr ) != 0 */ ) {
+
+		if( Top() == Bot() )
+		    stitch_error( "error: display top and bottom parameters the same value\n" );
+
+		if( isEXCITE( viewstr ) )
+		    source = SourceDraw( source, Bot(), Top(), display, Frameheight(), 1, Nwidth(), Frames(), save_frame, frame_drawer ) ;
+		else if( isWATERFALL( viewstr ) ) 
+		    source = SourceDraw( source, Bot(), Top(), display, Frameheight(),  Frames(), Nwidth(), 1, save_frame, frame_drawer ) ;
+		else if( !isON( kludgestr ) ) 
+		    source = SourceDraw( source, Bot(), Top(), display, Framewidth(), Frameheight(), Nwidth(), Frames(), save_frame, frame_drawer ) ; 
+		else {        /* speed-up for sai */
+		    source  = SourceDraw( source,    Bot(), Top(), display, Framewidth(), Frameheight(), Nwidth(), Frames(), save_frame, fast_frame  ) ;
+		    gen_state = genstate( display,   Bot(), Top(),          Framewidth(), Frameheight(), Nwidth(), Frames(), pixels ) ;
+		    frame_drawer=fast_frame ;
+		}
+	}
+	else {
+	    if( isON( erasestr ) )
+		Clear(   display ) ;
+
+	    DrawAxes( display ) ;
+
+	    if( isON( linstr ) )
+		frequency_scale = frequencies ;
+	    else
+		frequency_scale = (double *) 0 ;
+
+	    if( Framewidth() > 1 )
+		source = FillDown(   display, source, Top(), Bot(), Framewidth(), Frameheight() ) ;
+	    else
+		source = fillAcross( display, source, Top(), Bot(), Frameheight(),(int)Frames(), frequency_scale ) ;
+	}
+    }
+
+    /* postscript output next         */
+    /* (set by option: postscript=on) */
+
+    if( isON( psstr ) ) {
+
+	if( isON( psfilestr ) )
+	    psfilename = AlterSuffix( filestr, POSTSCRIPT_FILE_SUFFIX ) ;
+	else
+	    psfilename = (char *) 0 ;
+
+/* the extra argument to newPSWindow. MAA> 27-1-1993. */
+	psw = newPSWindow( psfilename, x, y, atoi(widthstr), atoi(heightstr), atoi( penstr ), isON( hiddenstr ) );
+
+	if( !isGREYSCALE( viewstr ) && OptionInt( penstr ) != 0 ) {
+
+		if( isEXCITE( viewstr ) )
+		    source = SourceDraw( source, Bot(), Top(), psw,     Frameheight(),            1, Nwidth(), Frames(), plot_frame, frame_drawer ) ;
+		else if( isWATERFALL( viewstr ) )
+		    source = SourceDraw( source, Bot(), Top(), psw,     Frameheight(),     Frames(),  Nwidth(),      1, plot_frame, frame_drawer ) ;
+		else
+		    source = SourceDraw( source, Bot(), Top(), psw,     Framewidth(), Frameheight(), Nwidth(), Frames(), plot_frame, frame_drawer ) ;
+	}
+	else { /* greyscale displays */
+
+	    if( isON( erasestr ) )
+		Clear(   psw ) ;
+
+	    DrawAxes( psw ) ;
+
+	    if( isON( linstr ) )
+		frequency_scale = frequencies ;
+	    else
+		frequency_scale = (double *) 0 ;
+
+	    if( Framewidth() > 1 )
+		source = FillDown(   psw, source, Top(), Bot(), Framewidth(), Frameheight() ) ;
+	    else
+		source = fillAcross( psw, source, Top(), Bot(), Frameheight(),(int)Frames(), frequency_scale ) ;
+	}
+    }
+
+
+    if( strcmp( typestr, "char" ) == 0 ) {
+	source = PackShorts( source ) ;
+	fileFramebytes = Framebytes() / sizeof ( short ) * sizeof ( char ) ;
+    }
+    else
+
+#endif  /* X11 */
+
+	fileFramebytes = Framebytes() ;
+
+    /* output to file */
+
+    if( !isOFF( outstr ) ) {
+
+	(void) unlink( title ) ;
+
+	if( OptionStringsEqual( outstr, OnOption ) )
+	    ofp = fopen( title,  writeBinary ) ;
+	else {
+	    if( strcmp( outstr, "stdout" ) == 0 )
+		ofp = stdout;
+	    else
+		ofp = fopen( outstr, writeBinary ) ;
+	}
+
+	if( ofp == (FILE *) 0 )
+	    stitch_error( "Unable to open file %s for output\n", title ) ;
+	else {
+	    if( isON( headstr ) )
+		writeopts( options, ofp ) ;
+
+	    source = FileTap( source, ofp ) ;
+	}
+    }
+
+#ifdef X11
+
+    /* output of screen "image" files in display format */
+    if( !isOFF( bitmapstr ) ) {
+      imageFileName = AlterSuffix(filestr, CARTOON_FILE_SUFFIX ) ;
+
+      if( (strcmp( bitmapstr, "stdout" ) == 0 )){
+           imageFilePointer = stdout;
+	   writeopts( options, imageFilePointer ) ;}
+      else 
+	if ( ( imageFilePointer = fopen(imageFileName, writeBinary ) ) == (FILE *) 0)
+	  stitch_error( "Unable to open file %s for image output\n", imageFileName ) ;
+	else
+	  writeopts( options, imageFilePointer ) ;}
+
+    if (( ofp == stdout ) && ( imageFilePointer == stdout ))
+        stitch_error( "Both 'output' and 'bitmap' set to stdout.\n", title);
+
+#endif  /* X11 */
+
+
+/***************************************************************************
+* Execute the program by pulling data from the source.
+* This is the only call to routine SinkSource(), which is defined in
+* stitch/source.c as sinkSource().
+* (SinkSource is an alias, defined in stitch/source.h, to cast the arguments).
+* Routine sinkSource() executes:  "(void) Pull( source, fileFramebytes ) ;"
+* for "Frames()" times in succession.
+* A chain of source objects, linked by callback-function pointers, was setup
+* earlier, (see call to ModeledSource() above).
+***************************************************************************/
+
+
+    SinkSource( source, fileFramebytes,   Frames() ) ;
+
+
+/***************************************************************************
+* Clean up and close
+****************************************************************************/
+
+    CloseSource( source ) ;
+
+    if( ofp != (FILE *) 0 )
+	(void) fclose( ofp ) ;
+
+#ifdef X11
+
+    if( psw != (WindowObject) 0 )
+	Close( psw ) ;
+
+    if( display != (WindowObject) 0 ) {
+
+	/* animate sai's if selected */
+
+	do
+	{
+	    for( image=1 ; image <= images ; image++ )
+		for( time = OptionInt( animstr ) ; time>0 ; time-- )
+		    Recall( display, image ) ;
+
+	    if( Pause( display ) == 'q' )
+		break;
+
+	} while( isON( animstr ) ) ;
+
+	Close( display ) ;
+    }
+
+#endif  /* X11 */
+
+    stitch_exit( 0 ) ;
+}
+
+/*************************** End main *************************************/
+
+/***************************************************************************
+* checkForFile()
+* Create source from file if present, to be used if reusestr in "on".
+* This is the "useprevious" option to use previously generated files.
+***************************************************************************/
+
+Source checkForFile( which )
+char *which ;
+{
+    static Source noSource = { (struct _source *) 0 } ;
+    long headerSize, fileFramebytes ;
+    char file_name[200] ;
+    FILE *tmp ;
+
+    (void) strcpy( file_name, filestr ) ;
+    (void) strcat( file_name, "."  ) ;
+    (void) strcat( file_name, which ) ;
+
+    if( ( tmp = fopen( file_name, readBinary ) ) != ( FILE * ) 0 ) {
+
+	inputFilePointer = tmp ;
+
+	(void) fprintf( stderr, "Using existing file \"%s\"\n", file_name ) ;
+
+	readopts( options, inputFilePointer ) ;
+
+	/* the saved command line overrides options from file header */
+
+	if( isON( reusestr ) )
+	    cmd_line_opts( options, &argc_save, &argv_save ) ;
+
+	headerSize = ftell( inputFilePointer ) ;
+
+	if( strcmp( typestr, "char" ) == 0 )
+	    fileFramebytes = Framebytes() / 2 ;
+	else
+	    fileFramebytes = Framebytes() ;
+
+	/*
+	If length=remainder then use the whole file.
+	Otherwise the length of the reused file is that given on the command
+	line, or the default length (if no length option is given).
+	*/
+
+	if ( strncmp( lengthstr, LengthStr, strlen(lengthstr) ) == 0 )
+	    setFrames( ( fileLength( inputFilePointer ) - headerSize ) / fileFramebytes - (long) Samples( startstr, Samplerate() ) / Framestep() ) ;
+	else
+	    setFrames( (long) ( Samples( lengthstr, Samplerate() ) ) / Framestep() ) ;
+
+
+	(void) fseek( inputFilePointer, headerSize + (long) Samples( startstr, Samplerate() ) / Framestep() * fileFramebytes, 0 ) ;
+
+#ifndef PC
+	if( strcmp( typestr, "char" ) == 0 )
+	    return( CharShortSource( FileSource( inputFilePointer ) ) ) ;
+	else
+#endif
+	    return(                  FileSource( inputFilePointer )   ) ;
+    }
+    else
+	return( noSource ) ;
+}
+
+/***************************************************************************
+* AlterSuffix().
+* Returns its argument fileName with a newSuffix appended in place of
+* any previous suffix it may have had. It should be noted that this
+* suffix must include any DOT it wishes to have appended to the name.
+***************************************************************************/
+
+/* File Name Suffix Conversion Parameters */
+
+#define BACKSLASH_CHAR  '\\'
+#define SLASH_CHARACTER '/'
+#define NULL_CHARACTER  '\000'
+#define DOT_CHARACTER '.'
+
+char *AlterSuffix(fileName, newSuffix)
+     char *fileName, *newSuffix;
+{
+  char *temp, *temp2, *lastPart;
+#if defined( PC )
+  int i;
+#endif
+
+  temp = stitch_malloc((unsigned) (strlen(fileName) + strlen(newSuffix) + 1), "AlterSuffix" );
+
+  temp = strcpy(temp, fileName);
+
+#if defined(PC)
+  /* Change all backslashes to forward slashes */
+  for (i = 0; temp[i] != NULL_CHARACTER; i++)
+    if (temp[i] == BACKSLASH_CHAR)
+      temp[i] = SLASH_CHARACTER;
+#endif
+  
+  if ((lastPart = strrchr(temp, SLASH_CHARACTER)) == NULL)
+    lastPart = temp;
+  else
+    lastPart++; /* Skip over the actual "/" */
+
+  /* lastPart points to the tail name of the path */
+
+  if ((temp2 = strchr(lastPart, DOT_CHARACTER)) == NULL)
+    temp = strcat(temp, newSuffix);
+  else
+    temp2 = strcpy(temp2, newSuffix);
+
+  return (temp);
+}
+
+#ifdef X11
+
+/***************************************************************************
+* Interceptions for various combinations of drawing.
+* An "interception function" is a function inserted into a chain a functions
+* linked by function pointers. The inserted function "intercepts" the
+* processing, to perform some transformation.
+****************************************************************************/
+
+/* intercept draw to store images for animation */
+
+void save_frame( state, frame, first )
+struct _draw_state *state ;
+short *frame ;
+int first ;
+{
+    if( first ) {
+	if( isON( erasestr ) )
+	    Clear( state->window ) ;
+
+	if( frame_drawer != (void (*)()) draw_spiral && state->framenumber == 1 )
+	    DrawAxes( state->window ) ;
+	}
+
+    frame_drawer( state, frame ) ;
+
+    if( isON( animstr ) && isOFF( kludgestr ) )
+	if( Store( state->window ) )
+	    ++images ;
+
+    if( isON( bitmapstr ) )
+	Write( state->window, imageFilePointer ) ;
+
+    return ;
+}
+
+/* hacky but effective faster frame drawing routine - for sai only! */
+
+void fast_frame( state, frame, first )
+struct _draw_state *state ;
+short *frame ;
+int first ;
+{
+    if( isON( animstr ) || images == 0 )
+	if( Store( state->window ) )
+	    ++images ;
+
+    /* generate image in current cleared image */
+
+    generate( gen_state, frame ) ;
+
+    /* then recal the window using the modified image */
+
+    Recall( state->window, images );
+
+    return ;
+}
+
+/* intercept plot to check if page should be output for printing */
+
+static int plot_wanted( state )
+struct _draw_state *state ;
+{
+#if !defined(PC)
+    if( display != ( WindowObject ) 0 && state->framewidth > 1 && state->frames > 1 ) {
+	(void) fprintf(stderr, "Hit \"y\" to include this image in the PostScript file: ");
+
+	switch ( Pause( display ) ) {
+
+	    case 'y' : case 'Y' :
+	    case 'p' : case 'P' :
+		return 1 ;
+
+	    default :
+		return 0 ;
+	}
+    }
+#endif
+    return 1 ;
+}
+
+void plot_frame( state, frame, first )
+struct _draw_state *state ;
+short *frame ;
+int first ;
+{
+    int centering = state->window->entries->x( state->window ) < 0 &&
+		    state->window->entries->y( state->window ) < 0 ;
+
+    if( isOFF( erasestr ) || state->framewidth == 1 || plot_wanted( state ) ) {
+
+	if( first && ( state->framenumber == 1 || isON( erasestr ) ) ) {
+/*  ***	    if( centering )   *** removed the conditional. M.Akeroyd. 22-1-1993.*/
+		Clear(   state->window ) ;
+
+	    if( frame_drawer != (void (*)()) draw_spiral )
+		DrawAxes( state->window ) ;
+	}
+
+	frame_drawer( state, frame ) ;
+    }
+
+    return ;
+}
+
+#endif  /* X11 */
+
+/***************************************************************************
+* downchannel: average across channels for output  (with special sai version)
+***************************************************************************/
+
+struct _down_channel_state {
+    struct _fillable_source parent ;
+    int channels ;
+    Source input ;
+} ;
+
+
+static Pointer down_channel_callback( state, bytes, buffer )
+struct _down_channel_state *state ;
+ByteCount *bytes ;
+short *buffer ;
+{
+    register int last = *bytes == 0 ;
+    register int chan, outchans = state->channels >> 1 ;
+    long frame, frames = ToPoints( short, *bytes ) / outchans ;
+    register short *iptr = PullItems( state->input, frames * state->channels, short ) ;
+    register short *bptr = buffer ;
+
+    for( frame=0 ; frame<frames ; frame++ ) {
+	for( chan=0 ; chan<outchans ; chan++ )
+	    *bptr++ = ( *iptr++ + *iptr++ ) / 2 ;
+
+	if( ( outchans << 1 ) != state->channels )
+	    iptr++ ;
+    }
+
+    if( !last )
+	return ( (Pointer) buffer ) ;
+    else
+	return ( DeleteFillableSource( state ) ) ;
+}
+
+static Pointer down_channel_callback_sai( state, bytes, buffer )
+struct _down_channel_state *state ;
+ByteCount *bytes ;
+short *buffer ;
+{
+    register int last = *bytes == 0 ;
+    register int chan, outchans = state->channels >> 1 ;
+    long frame, frames = ToPoints( short, *bytes ) / outchans ;
+    register short *iptr = PullItems( state->input, frames * state->channels, short ) ;
+    register short *bptr = buffer ;
+    register short *iptr2 ;
+
+    iptr2 = iptr + frames ;
+    for( chan=0 ; chan<outchans ; chan++ ) {
+	for( frame=0 ; frame<frames ; frame++ )
+	    *bptr++ = ( *iptr++ + *iptr2++ ) / 2 ;
+    }
+
+    if( !last )
+	return ( (Pointer) buffer ) ;
+    else
+	return ( DeleteFillableSource( state ) ) ;
+}
+
+
+Source DownChannel( source, channels )
+Source source ;
+{
+    DeclareNew( struct _down_channel_state *, state ) ;
+
+    state->channels = channels ;
+    state->input = source ;
+
+
+    /* Hack for special-case downchannel routine to handle format of sai and spl frames (mha: 22/6/93) */
+
+    if( strncmp(whichstr,"sai",strlen("sai")) == 0 ||
+	strncmp(whichstr,"spl",strlen("spl")) == 0    )
+
+	return ( SetFillableSource( state, down_channel_callback_sai, "down channeling sai frames" ) ) ;
+
+    return ( SetFillableSource( state, down_channel_callback, "down channeling" ) ) ;
+}
+
+
+#ifdef X11
+
+/***************************************************************************
+* Pack model output onto a single byte if required,
+* using greyscale Top() from display.
+***************************************************************************/
+
+typedef struct { struct _fillable_source parent ; Source input ; } *PackSource ;
+
+static Pointer pack_callback( state, bytes, buffer )
+PackSource state ;
+ByteCount *bytes ;
+Pointer buffer ;
+{
+    register int last = *bytes == 0 ;
+    register short  *iptr = PullShorts( state->input, *bytes ) ;
+    register Pointer optr = buffer ;
+    register Pointer eptr = buffer + *bytes ;
+    register int bytemax  = atoi( bmaxstr  ) ;
+    register int max      = Top() ;
+    register int min      = Bot() ;
+
+    while( optr < eptr )
+	if( *iptr > max ) {
+	    *optr++ = bytemax ;
+	     iptr++ ;
+	}
+	else if( *iptr < min ) {
+	    *optr++ = 0 ;
+	     iptr++ ;
+	}
+	else
+	    *optr++ = ( ( ( *iptr++ - min ) * bytemax + ( bytemax >> 1 ) ) / ( max - min ) ) & 0xff ;
+
+    if( !last )
+	return ( buffer ) ;
+    else
+	return ( DeleteFillableSource( state ) ) ;
+}
+
+
+Source PackShorts( input )
+Source input ;
+{
+    DeclareNew( PackSource, source ) ;
+
+    source->input = input ;
+
+    return ( SetFillableSource( source, pack_callback, "gen.c packing" ) ) ;
+}
+
+#endif  /* X11 */
+
+/***************************************************************************/
+
+#ifdef PC
+static lowerArgs( argv, argc )
+char **argv ;
+int argc ;
+{
+    register char *ptr ;
+    register int arg ;
+
+    for( arg=0 ; arg<=argc ; arg++ )
+	for( ptr=argv[arg] ; *ptr != '\000' ; ptr++ )
+	    if( isupper( *ptr ) )
+		*ptr = tolower( (int) *ptr ) ;
+    return ;
+}
+#endif
+
+
+/***************************************************************************
+* Set the value of versionstr to the model version number and current time.
+* The version number is the sccs <release-number>.<level-number> of the file
+* version.c. The current time is returned by ctime(time(0)).
+* This routine initializes the default version-string (headerstr), which is
+* later copied into the version-string (versionstr) during getopts().
+* In this way, the version-string is available for output at the head of
+* the help and the options (rc) files, and also output as part of the header,
+* (when output=on).
+* Note, the versionstr has to be quoted, otherwise the routine
+* processOptionFile in options.c will report an error (Detected Trailing...)
+* when it finds blanks within a line. This happens when trying to read an
+* options file, for example when reviewing. The addition of quotes enables
+* a line containing blanks to be read complete by the subroutine getName.
+***************************************************************************/
+
+#include "version.c"
+
+getversion( name )
+char *name ;
+{
+  char  releasestr[8] ;
+  char  levelstr[8]   ;
+  char  timestr[32]   ;
+  char *namestr       ;
+  long int  timeval   ;
+
+  sprintf(releasestr, "%d", atoi( version_Id ) ) ;
+  sprintf(levelstr, "%d", atoi( version_Id + strlen(releasestr) + 1 ) ) ;
+
+  if ( ( namestr = strrchr( name, '/' ) ) == (char *)0 )  namestr = name ;
+  else namestr++ ;
+
+#if defined( PC ) || defined( THINK_C )
+  sprintf(headerstr, "\"AIM MRC-APU Release R%s.%s [%s]\"", releasestr, levelstr, namestr ) ;
+#else
+  timeval = time(0);
+  sprintf(timestr,"%s", ctime(&timeval) ) ;
+  timestr[strlen(timestr) - 1] = '\0';  /* remove newline */
+  sprintf(headerstr, "\"AIM MRC-APU Release R%s.%s [%s] %s\"", releasestr, levelstr, namestr, timestr ) ;
+#endif
+
+}