/**************************************************
 * SynthBuilder
 * Copyright 1993 Nick Porcaro All Rights Reserved
 **************************************************/

/*
 * MKCodeGen.m
 * Music Kit Code generator object (split off from FakeSynthPatch)
 */

#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <ctype.h>
#import <appkit/Application.h>
#import <dpsclient/event.h>
#import <appkit/Matrix.h>
#import <appkit/OpenPanel.h>
#import <appkit/SavePanel.h>
#import "NodeView.h"
#import "FakeSynthPatch.h"
#import "FakePatchPoint.h"
#import "FakeUG.h"
#import "UGDef.h"
#import "MKCodeGen.h"
#import "Controller.h"
#import "Utilities.h"
#define EOS '\0'

static char TempString[1024];

extern id savePanel;
extern id openPanel;
extern char *getSavePath(char *banner);
extern char *getOpenPath(char *banner);
extern char *getAppName();

double atod(STR s)
{
  double x;
  sscanf(s, "%lf", &x);
  return x;
}

@implementation MKCodeGen

- init
{
  [super init];
  [NXApp loadNibSection:"MKCodeGen.nib" owner:self withNames:YES];
  return self;
}

- displayInspector:sender
{
  theSP = [theMainController theSP];
  fakeUGs = [theSP getFakeUGs];
  fakePatchPoints = [theSP getFakePatchPoints];

  if (theSP && [fakeUGs count] && [fakePatchPoints count])
    {
      [theInspector makeKeyAndOrderFront:self];
    }
  else
    {
      NXRunAlertPanel(getAppName(),
		     "Nothing to generate code for", NULL, NULL, NULL);
    }
  return self;
}

- emitCode:sender
{
  char *file_name;
  char *path_leaf;
  int comments;
  int dest;
  int len;
  

  [theInspector performClose:self];
  comments = [generateComments intValue];
  dest = [[patchDest selectedCell] tag];

  /* Get the file name from the user and make sure it is 
   * a cool name for the code generator
   */
  sprintf(TempString, "%s Generate Code", getAppName());
  if (! (file_name = getSavePath(TempString)))
    {
      return self;
    }

  path_leaf = [utilities getPathLeaf:file_name];
  len = strlen(path_leaf);
  if (len > 2 && path_leaf[len-2] == '.')
    {
      path_leaf[len-2] = EOS;
    }

  if (! [utilities checkName:path_leaf])
    {
      return self;
    }

  [self emitCodeTo:file_name genComments:comments ToDest:dest];
  return self;
}

- emitCodeTo:(char *) file genComments:(int) comments ToDest: (int) aDest
{
  int len;
  static char header_file[512], code_file[512];
  char *newFileName;

 /* Turn off the sound in case the user generates code
  * to an app that
  * needs the dsp
  */
  theSP = [theMainController theSP];
  fakeUGs = [theSP getFakeUGs];
  fakePatchPoints = [theSP getFakePatchPoints];
  strcpy(code_file, file);
  [theSP deallocatePatch:self];
  newFileName = [self makeObjCCode:code_file:comments];
  if (! newFileName)
    {
      NXRunAlertPanel(getAppName(),
		      "Could not process file: %s", NULL, NULL, NULL, code_file);
      return self;
    }
  strcpy(code_file, newFileName);
  

  /* Get the header file name from the
   * code file name -- assume code file ends in .m and
   * header file ends in .h
   */
  strcpy(header_file, code_file);
  len = strlen(header_file);
  header_file[len-2] = '\0';
  strcat(header_file, ".h");
  
  newFileName = [self makeHeaderFile:header_file];
  if (! newFileName)
    {
      NXRunAlertPanel(getAppName(),
		      "Could not process file: %s", NULL, NULL, NULL, header_file);
    }
  strcpy(header_file, newFileName);
  
  /* Now do the right thing with the file */
  switch (aDest)
    {
      case(0):
	{
	  sprintf(TempString, "open %s %s", code_file, header_file);
	  system(TempString);
	  break;
	}
      case(1):     /* Standard test script */
	{
	  sprintf(TempString, "%s/mk-tester -mfile %s -sb_dir %s&",
		  [utilities getAppDir], code_file, [utilities getAppDir]);
	  printf("Shell command: %s\n", TempString);
	  system(TempString);
	  break;
	}
      case(2):     /* Custom test script */
	{
	  sprintf(TempString, "%s -mfile %s -sb_dir %s &",
		  [testerScript stringValue], code_file, [utilities getAppDir]);

	  printf("Shell command: %s\n", TempString);
	  system(TempString);
	  break;
	}
      case(3):   /* Future -- make it go to the paste board */
	{
	  break;
	}
      case(4):   /* Just create the file silently */
	{
	  break;
	}
    }

  return self;
}

- (char *) makeHeaderFile:(char *)fileName
{
  char *theFileName;
  FILE *f;

  theFileName = [self setClassNameFromFileName: 'h' : (char *)fileName];
  if (!theFileName)
    {
      NXRunAlertPanel(getAppName(),
                      "%s could not be processed as a file name.",
		      NULL, NULL, NULL,
		      fileName);
      return NULL;
    }

  f = fopen(theFileName, "w");
  if (!f)
    {
      NXRunAlertPanel(getAppName(),
		      "Could not open: %s",
		      NULL, NULL, NULL,
		      theFileName);
      return NULL;
    }

  fprintf(f, "#import <musickit/SynthPatch.h>\n");
  fprintf(f, "#import <musickit/WaveTable.h>\n");
  fprintf(f, "#import <musickit/Envelope.h>\n\n");
  fprintf(f, "@interface %s:SynthPatch\n{\n",[theSP getName]);
  fprintf(f, "  /*** ADD INSTANCE VARIABLES HERE ***/\n");
  fprintf(f, "}\n\n");
  fprintf(f, "+patchTemplateFor:currentNote;\n");
  fprintf(f, "-init;\n");
  fprintf(f, "-testNote;\n");
  fprintf(f, "-noteOnSelf:aNote;\n");
  fprintf(f, "-preemptFor:aNote;\n");
  fprintf(f, "-noteUpdateSelf:aNote;\n");
  fprintf(f, "-(double)noteOffSelf:aNote;\n");
  fprintf(f, "-noteEndSelf;\n\n");
//  fprintf(f, "-_setDefaults;\n");
//  fprintf(f, "-_updateParameters:aNote;\n\n");
  fprintf(f, "@end\n");
  fclose(f);
  return theFileName;
}

- (char *) makeObjCCode:(char *)fileName:(int)doComments
{
  int i, j, k, l;
  BOOL first;
  id patchPoint, theUG, aFakeUG, fromFakeUG;
  BOOL isDataElement, isSineRom, isConstantpp, isEnvelope, declareThem, hasEnvelope;
  char *ug_name, *ug_type, *inst_name, *arg_val, *arg_name, *type_str, *cast_str;
  char *thing;
  int arg_type;
  int arg_count = 0;
  int pointCount=0, stickPoint = -1;
  int envelopes_processed = 0;
  double *xArray, *yArray, *smoothingArray;
  char first_part[256], curr_env[256], x_array[20], y_array[20], smooth_array[20];
  FILE *f;
  extern char *getenv();
  char *theFileName;

  theFileName = [self setClassNameFromFileName: 'm' : fileName];
  if (!theFileName)
    {
      NXRunAlertPanel(getAppName(),
                      "%s could not be processed as a file name.",
		      NULL, NULL, NULL,
		      fileName);
      return NULL;
    }

  f = fopen(theFileName, "w");
  if (!f)
    {
      NXRunAlertPanel(getAppName(),
		      "Could not open: %s",
		      NULL, NULL, NULL,
		      theFileName);
      return NULL;
    }

  fprintf(f, "#import <appkit/appkit.h>\n"); /*** Added by Nick ***/
  fprintf(f, "#import <musickit/unitgenerators/unitgenerators.h>\n");
  fprintf(f, "#import <musickit/PatchTemplate.h>\n");

  fprintf(f, "#import \"%s.h\"\n\n", [theSP getName]);
  fprintf(f, "#define SE NX_ADDRESS(synthElements)\n\n");
  fprintf(f, "@implementation %s:SynthPatch\n\n",[theSP getName]);
  fprintf(f, "static int ");
  first=YES;
  for (i=0; i<[fakeUGs count]; i++)
    {
      aFakeUG = [fakeUGs objectAt:i];
      if ([aFakeUG isSoundMaker])
	{
	  if (![aFakeUG isData])
	    {
	      if (first)
		{
		  fprintf(f, "%s", [aFakeUG getName]);
		  first=NO;
		}
	      else
		{
		  fprintf(f, ", %s", [aFakeUG getName]);
		}
	    }
	}
    }

  for (i=0; i<[fakePatchPoints count]; i++)
    {
      fromFakeUG = [[[fakePatchPoints objectAt:i] getFromNode] superview];
      if ([fromFakeUG isSoundMaker])
	{
	  if (![fromFakeUG isSineROM])
	    {
	      if (first)
		{
		  fprintf(f, "%s",[[fakePatchPoints objectAt:i] getName]);
		  first=NO;
		}
	      else
		{
		  fprintf(f, ", %s",[[fakePatchPoints objectAt:i] getName]);
		}
	    }
	}
    }

  fprintf(f, ";\n\n");
  fprintf(f, "+patchTemplateFor:currentNote\n");
  fprintf(f, "{\n  static PatchTemplate *template = nil;\n");
  fprintf(f, "  if (template)\n    return template;\n");
  fprintf(f, "  [UnitGenerator enableErrorChecking:YES]; /*** Comment this out once your patch is working. ***/\n");
  fprintf(f, "  template = [[PatchTemplate alloc] init];\n");


  for (i=0; i<[fakeUGs count]; i++)
    {
      aFakeUG = [fakeUGs objectAt:i];
      if ([aFakeUG isSoundMaker])
	{
	  if (![aFakeUG isData])
	    {
	      ug_name = [self checkForUnconnectedPins: i ];
	      fprintf(f, "  %s = [template addUnitGenerator:[%s class]];\n",
		      [aFakeUG getName], ug_name);
	    }
	}
    }

#define KARPLUS_STRONG_WORKS1 1 /* This causes Karplus Strong to go higher pitched */
#ifdef KARPLUS_STRONG_WORKS1
  for (i=0; i<[fakePatchPoints count]; i++)
    {
      fromFakeUG = [[[fakePatchPoints objectAt:i] getFromNode] superview];
      if ([fromFakeUG isSoundMaker])
	{
	  if ([fromFakeUG isData])
	    {
	      if ([[fakePatchPoints objectAt:i] getXMemory])
		{
		  theUG = [[[fakePatchPoints objectAt:i] getFromNode] superview];
		  if ( [ theUG hasConstant] && ! [theUG isStorage])
		    {
		      fprintf(f, "  %s = [template addPatchpoint:MK_xData];\n",
			      [[fakePatchPoints objectAt:i] getName]);
		    }
		  else
		    {
		      fprintf(f, "  %s = [template addSynthData:MK_xData length:%i];\n",
			      [[fakePatchPoints objectAt:i] getName],
			      [[[[fakePatchPoints objectAt:i] 
				 getFromNode] superview] getLength]);
		    }
		}
	      else
		{
		  if (![[[[fakePatchPoints objectAt:i] getFromNode] superview] 
			isSineROM])
		    {
		      fprintf(f, "  %s = [template addSynthData:MK_yData length:%i];\n",
			      [[fakePatchPoints objectAt:i] getName],
			      [[[[fakePatchPoints objectAt:i]
				 getFromNode] superview] getLength]);
		    }
		}
	    }
	  else
	    {
	      if ([[fakePatchPoints objectAt:i] getXMemory])
		{
		  fprintf(f, "  %s = [template addPatchpoint:MK_xPatch];\n",
			  [[fakePatchPoints objectAt:i] getName]);
		}
	      else
		{
		  fprintf(f, "  %s = [template addPatchpoint:MK_yPatch];\n",
			  [[fakePatchPoints objectAt:i] getName]);
		}
	    }
	}
    }
      
  fprintf(f, "  return template;\n}");
  fprintf(f, "\n\n-_setDefaults\n{\n");
  fprintf(f, "  /*** ADD ANY INITIALIZATION CODE HERE. ***/\n");
  fprintf(f, "  return self;\n}\n\n");
#else
  for (i=0; i<[fakePatchPoints count]; i++)
    {
      fromFakeUG = [[[fakePatchPoints objectAt:i] getFromNode] superview];
      if ([fromFakeUG isSoundMaker])
	{
	  if ([fromFakeUG isData])
	    if ([[fakePatchPoints objectAt:i] getXMemory])
	      fprintf(f, "  %s = [template addSynthData:MK_xData length:%i];\n",
		     [[fakePatchPoints objectAt:i] getName],
		     [[[[fakePatchPoints objectAt:i] 
			getFromNode] superview] getLength]);
	    else {
	      if (![fromFakeUG isSineROM])
		 fprintf(f, "  %s = [template addSynthData:MK_yData length:%i];\n",
		      [[fakePatchPoints objectAt:i] getName],
		      [[[[fakePatchPoints objectAt:i]
			 getFromNode] superview] getLength]);
	  }
	  else
	    if ([[fakePatchPoints objectAt:i] getXMemory])
	      fprintf(f, "  %s = [template addPatchpoint:MK_xPatch];\n",
		     [[fakePatchPoints objectAt:i] getName]);
	    else
	      fprintf(f, "  %s = [template addPatchpoint:MK_yPatch];\n",
		     [[fakePatchPoints objectAt:i] getName]);
	}
    }

  fprintf(f, "  return template;\n}");
  fprintf(f, "\n\n-_setDefaults\n{\n");
  fprintf(f, "  /*** ADD ANY INITIALIZATION CODE HERE. ***/\n");
  fprintf(f, "  return self;\n}\n\n");
#endif KARPLUS_STRONG_WORKS1


  // Declare static id variables for the envelopes (Nick)
  envelopes_processed = 0;
  for (i=0; i<[fakeUGs count]; i++)
    {
      aFakeUG = [fakeUGs objectAt:i];
      if ([aFakeUG isSoundMaker])
	{
	  if ([[fakeUGs objectAt:i] isEnvelopeHandler])
	    {
	      ug_name = [[fakeUGs objectAt:i] getName];
	      envelopes_processed++;
	      fprintf(f, "static id envelope%d = nil;\n", envelopes_processed);
	    }
	}
    }

  fprintf(f, "\n\n");

  // Put the envelope code in _updateParameters (Nick)
  fprintf(f, "-_updateParameters:aNote\n{\n\n");
  fprintf(f, "\n");
  fprintf(f, "  if ([self phraseStatus] <= MK_phraseRearticulate) {\n");

  envelopes_processed = 0;
  for (i=0; i<[fakeUGs count]; i++)
    {
      aFakeUG = [fakeUGs objectAt:i];
      if ([aFakeUG isSoundMaker])
	{
	  if ([[fakeUGs objectAt:i] isEnvelopeHandler])
	    {
	      ug_name = [[fakeUGs objectAt:i] getName];
	      envelopes_processed++;
	      sprintf(curr_env, "envelope%d", envelopes_processed);
	      fprintf(f, "  MKUpdateAsymp(SE[%s], %s, 0.0, 1.0,\n", ug_name, curr_env);
	      fprintf(f, "                MK_NODVAL, MK_NODVAL, MK_NODVAL,\n");
	      fprintf(f, "                [self phraseStatus]);\n");
	    }
	}
    }

  fprintf(f, "  }\n");
  fprintf(f, "  return self;\n}\n\n");
  fprintf(f, "-init\n{\n");

  // Figure out if we need to declare freqs, amps, partials
  declareThem = NO;
  for (i=0; i<[fakePatchPoints count]; i++)
    {
      int k;
      fromFakeUG = [[fakePatchPoints objectAt:i] getFromFakeUG];
      if ([fromFakeUG isSoundMaker])
	{
	  if ([fromFakeUG isData])
	    {
	      if (![fromFakeUG hasConstant])
		{
		  for (k=0; k<MAXNOTEPARAMETERS; k++)
		    {
		      if ([fromFakeUG getArgType:k] == PARTIALS)
			{
			  declareThem = YES;
			  break;
			}
		    }
		}
	    }
	}
    }

  if (declareThem)
    {
      fprintf(f, "  double *freqs, *amps;\n  id partials;\n");
    }

  for (i=0; i<[fakePatchPoints count]; i++)
    {
      int k;
      patchPoint = [fakePatchPoints objectAt:i];
      fromFakeUG = [patchPoint getFromFakeUG];
      if (! [fromFakeUG isSoundMaker])
	{
	  goto end_of_loop0;
	}

      if (![fromFakeUG isData])
	fprintf(f, "  [SE[%s] %sSE[%s]];\n",
	       [fromFakeUG getName],
	       [fromFakeUG 
	      getConnectionName:[patchPoint getFromConnectionNumber]],
	       [patchPoint getName]);
      else
	if ([fromFakeUG hasConstant])
	  {
	    fprintf(f, "  [SE[%s]",[patchPoint getName]);
	    /** Fixed by Nick **/
	    fprintf(f, " setToConstant:DSPDoubleToFix24(%f)];\n",
		    atod([fromFakeUG getConstant]));
	  }
	else
	  {
	    for (k=0; k<MAXNOTEPARAMETERS; k++)
	      {
		if (([fromFakeUG getArgType:k])==PARTIALS)
		  {
		    int l;
		    for (l=0; l<[[patchPoint getToNodes] count]; l++)
		      {
			double *freqs, *amps;
			char *scoreString;
			int p, q, pointCount=0;
			scoreString = [fromFakeUG getArgName:k];
			for (p=0; scoreString[p]!='\0'; p++)
			  if (scoreString[p]=='{')
			    pointCount++;
			freqs = (double *) NXZoneCalloc([self zone], 
							pointCount, sizeof(double));
			amps = (double *) NXZoneCalloc([self zone], 
						       pointCount, sizeof(double));
			q=0;
			for (p=0; p<pointCount; p++)
			  {
			    freqs[p] = amps[p] = 0.0;
			    while (scoreString[q]!='{')
			      q++;
			    sscanf(scoreString+q, "{%lf,%lf}",
				   &freqs[p], &amps[p]);
			    q++;
			  }
			fprintf(f, "  partials=[[Partials alloc]");
			fprintf(f, " init];\n  freqs=(double *)");
			fprintf(f, " calloc(%i, sizeof(*freqs));\n", 
				pointCount);
			fprintf(f, "  amps=(double *) calloc(%i,",
				pointCount);
			fprintf(f, " sizeof(*amps));\n");
			for (p=0; p<pointCount; p++)
			  fprintf(f, "  freqs[%i]=%f;\n", p, freqs[p]);
			for (p=0; p<pointCount; p++)
			  fprintf(f, "  amps[%i]=%f;\n", p, amps[p]);
			fprintf(f, "  [partials setPartialCount:%i",
				pointCount);
			fprintf(f, " freqRatios:freqs ampRatios:amps");
			fprintf(f, " phases:NULL orDefaultPhase:0.0];\n");
			fprintf(f, "  [SE[%s]", [patchPoint getName]);
			fprintf(f, " setData:[partials dataDSPLength:%i]];\n",
				[fromFakeUG getLength]);
		      }
		  }
	      }
	  }
      /* A UG is an output if it has no output nodes. */
      for (k=0; k<[[patchPoint getToNodes] count]; k++)
	{
	  id toFakeUG = [[[patchPoint getToNodes] objectAt:k] superview];
	  if ([toFakeUG getConnectionName:0]!=NULL)
	    {
	      if (([fromFakeUG isData]
		   && ([fromFakeUG isSineROM])
		   && ([toFakeUG isOscg])))
		{
		  fprintf(f, "  [SE[%s]",
			  [toFakeUG getName]);
		  fprintf(f, " setTableToSineROM];\n");
		}
	      else
		fprintf(f, "  [SE[%s] %sSE[%s]];\n",
			[toFakeUG getName],
			[toFakeUG 
		       getConnectionName:[[[patchPoint getToNodes]
					 objectAt:k]
					  getTag]],
			[patchPoint getName]);
	    }
	}
end_of_loop0:
      ;
    }

  fprintf(f, "  [self _setDefaults]; \n");
  fprintf(f, "  return self;\n}\n\n");

// ****************************************
// start testNote method (Nick)
// ****************************************

  fprintf(f, "// ** Added by Nick **\n");
  fprintf(f, "-testNote\n");
  fprintf(f, "{\n\n");

  envelopes_processed = 0;
  for (i=0; i<[fakeUGs count]; i++)
    {
      aFakeUG = [fakeUGs objectAt:i];
      if (! [aFakeUG isSoundMaker])
	{
	  goto end_of_loop;
	}

      isDataElement = [aFakeUG isData];
      isSineRom = [aFakeUG isSineROM];
      isEnvelope = [aFakeUG isEnvelopeHandler];
      ug_name = [aFakeUG getName];
      ug_type = [aFakeUG getTypeString];
      inst_name = [aFakeUG getName];
      arg_count = [aFakeUG getArgCount];

      /* I think this line is flawed */
      isConstantpp = [[[[fakePatchPoints objectAt:i] getFromNode] superview] hasConstant];

      if (doComments)
	{
	  /* Generate the comments  */
  
	  if (isConstantpp || isDataElement)
	    {
	      thing = "Synth Data";
	    }
	  else
	    {
	      thing = "Unit Generator";
	    }
	  
	  fprintf(f, "\n//\n");
	  fprintf(f, "// *** %s (graph instance number %d) ***\n", thing, i);
	  fprintf(f, "//     Instance name: %s\n", inst_name);
	  fprintf(f, "//     Type: %s", ug_type);
	  if ( (isDataElement) && (! isSineRom) && (!isConstantpp))
	    {
	      fprintf(f, " (data element of length: %d)\n",
		      [aFakeUG getLength]);
	    }
	  else if (isEnvelope)
	    {
	      fprintf(f, "\n//    Envelope!\n");
	    }
	  else
	    {
	      fprintf(f, "\n");
	    }
	  
	  fprintf(f, "//     Number of arguments: %d\n", arg_count);
	  
	  for (j=0; j<arg_count; j++)
	    {
	      arg_val = [aFakeUG getArg:j];
	      if (! arg_val || (strcmp(arg_val, "") == 0) )
		{
		  arg_val = "UNSET";
		}
	      arg_type = [aFakeUG getArgType:j];
	      arg_name =  [aFakeUG getArgName:j];
	      if ( (!arg_name) || (strcmp(arg_name, "") == 0) )
		{
		  break;
		}
	      type_str = [aFakeUG getArgTypeStr:arg_type];
	      if (!type_str)
		{
		  cast_str = "";
		}
	      
	      if (arg_type == PARTIALS)
		{
		  fprintf(f, "// *** WARNING cannot generate code for UG of type %s\n",
			  type_str);
		}
	      
	      
	      fprintf(f, "//     %s %s %s\n", 
		      arg_name, type_str, arg_val);
	    }
	  
	  fprintf(f, " \n");
	}
	  
// Generate the code

      if ( (arg_count > 0) && (! isDataElement))
	{
	  for (j=0; j<arg_count; j++)
	    {
	      /* Make sure we can even do it */
	      isEnvelope = NO;
	      arg_type = [aFakeUG getArgType:j];
	      type_str = [aFakeUG getArgTypeStr:arg_type];
	      pointCount = 0;
	      if (arg_type == ENV)
		{
		  isEnvelope = YES;
		  envelopes_processed++;
		  sprintf(curr_env, "envelope%d", envelopes_processed);
		  sprintf(x_array, "xArray%d", envelopes_processed);
		  sprintf(y_array, "yArray%d", envelopes_processed);
		  sprintf(smooth_array, "smoothArray%d", envelopes_processed);

		  arg_val = [aFakeUG getArg:j];
		  for (k=0; arg_val[k] != '\0'; k++)
		    {
		      if (arg_val[k]=='|')
			{
			stickPoint = pointCount-1;
		        }
		      if (arg_val[k]=='(')
			pointCount++;
		    }
		  xArray = (double *) NXZoneCalloc([self zone], 
						   pointCount, sizeof(double));
		  yArray = (double *) NXZoneCalloc([self zone], 
						   pointCount, sizeof(double));
		  smoothingArray =  (double *) NXZoneCalloc([self zone], 
							    pointCount, sizeof(double));
		  
		  l=0;
		  for (k=0; k<pointCount; k++)
		    {
		      xArray[k] = yArray[k] = 0.0;
		      smoothingArray[k] = 1.0;
		      while (arg_val[l]!='(') 
			{
			  l++;
			}
		      sscanf(arg_val+l, "(%lf,%lf,%lf)",
			     &xArray[k], &yArray[k], &smoothingArray[k]);
		      l++;
		    }
		  
		  fprintf(f, "  if (! %s) {\n", curr_env);
		  fprintf(f, "        double %s[] = {", x_array);
		  for (k=0; k<pointCount; k++)
		    {
		      if (k == pointCount -1)
			{
			  fprintf(f, "%f};\n", xArray[k]);
			}
		      else
			{
			  fprintf(f, "%f, ", xArray[k]);
			}
		    }
		  
		  fprintf(f, "        double %s[] = {", y_array);
		  for (k=0; k<pointCount; k++)
		    {
		      if (k == pointCount - 1)
			{
			  fprintf(f, "%f};\n", yArray[k]);
			}
		      else
			{
			  fprintf(f, "%f, ", yArray[k]);
			}
		    }
		  
		  fprintf(f, "        double %s[] = {", smooth_array);
		  for (k=0; k<pointCount; k++)
		    {
		      if (k == pointCount - 1)
			{
			  fprintf(f, "%f};\n", smoothingArray[k]);
			}
		      else
			{
			  fprintf(f, "%f, ", smoothingArray[k]);
			}
		    }
		  
		  fprintf(f, "        %s = [[Envelope alloc] init];\n", curr_env);
		  // I added orSamplingPeriod:0 per DAJ mail of 12/20/92
		  fprintf(f, "        [%s setPointCount: %d xArray:%s orSamplingPeriod:0 yArray:%s smoothingArray:%s orDefaultSmoothing:1.0];\n", 
			  curr_env, pointCount, x_array, y_array, smooth_array);

		  if (stickPoint!=-1)
		    {
			  fprintf(f, "        [%s setStickPoint: %d];\n", curr_env, stickPoint);
		    }

		  fprintf(f, "    [SE[%s] setEnvelope:%s yScale:1.0 yOffset:0.0 xScale:1.0 releaseXScale:1.0 funcPtr:NULL];\n",ug_name, curr_env);
		  fprintf(f, "    }\n");
		  NXZoneFree([self zone], xArray);
		  NXZoneFree([self zone], smoothingArray);
		  NXZoneFree([self zone], yArray);
		  xArray = NULL;
		  smoothingArray = NULL;
		  yArray = NULL;
		}
	      else if (arg_type == PARTIALS)
		{
		  fprintf(f, "// *** ERROR cannot generate code for UG of type %s\n",
			  type_str);
		  break;
		}
	      else  /* All other argument types */
		{
		  /* Write out the first part */
                  sprintf(first_part, "    [SE[%s] ", ug_name);
		  
		  /* Write code for paramter method setting */
		  arg_val = [aFakeUG getArg:j];
		  arg_name =  [aFakeUG getArgName:j];
		  cast_str = [aFakeUG getArgCast:arg_type];
		  if ( (!cast_str) || (strcmp(cast_str, "") == 0))
		    {
		      fprintf(stderr, "Warning: NULL cast on method %s for UG %s\n",
			      arg_name, type_str);
		      cast_str = "";
		    }
		  
		  /* Only generate the code for non-NULL arg_val's
		   * This corresponds to David Jaffe's request not to
		   * generate code for "unset" values
		   */
		  if (arg_val && (strcmp(arg_val, "") != 0) )
		    {
//		      fprintf(f, "%s %s %s %s];\n",  first_part, arg_name, cast_str, arg_val);
// Took out cast per daj request of 3-5-93 (nick)
		      fprintf(f, "%s %s %s];\n",  first_part, arg_name, arg_val);
		    }
		}
	    }
	}
end_of_loop:
      ;
    }

// ****************************************
// end testNote method (Nick)
// ****************************************

  fprintf(f, "  return self;\n}\n");

  fprintf(f, "-noteOnSelf:aNote\n");
  fprintf(f, "{\n");
  fprintf(f, "// This produces the sound designed in %s.\n", getAppName());
  fprintf(f, "// For further control, remove the testNote method and add\n");
  fprintf(f, "// parmeter-setting code to _updateParameters:\n");
  fprintf(f, "  [self testNote];\n");   /** (added by Nick) **/
  fprintf(f, "  [self _updateParameters:aNote];\n");
  for (i=0; i<[fakePatchPoints count]; i++)
    {
      int k;
      id patchPoint = [fakePatchPoints objectAt:i];
      for (k=0; k<[[patchPoint getToNodes] count]; k++)
	{
	  id toFakeUG = [[[patchPoint getToNodes] objectAt:k] superview];
	  if ([toFakeUG isSoundMaker])
	    {
	      if ([toFakeUG getConnectionName:0]==NULL)
		fprintf(f, "  [SE[%s] %sSE[%s]];\n",
			[toFakeUG getName],
			[toFakeUG 
		       getConnectionName:[[[patchPoint getToNodes]
					 objectAt:k]
					  getTag]],
			[patchPoint getName]);
	    }
	}
    }

  fprintf(f, "  [synthElements makeObjectsPerform:@selector(run)];\n");
  fprintf(f, "  return self;\n}\n\n");
  fprintf(f, "-noteUpdateSelf:aNote\n");
  fprintf(f, "{\n  [self _updateParameters:aNote];\n");
  fprintf(f, "  return self;\n}\n\n");
  fprintf(f, "-(double)noteOffSelf:aNote\n");
  fprintf(f, "{\n  double t=0;\n");

  hasEnvelope = NO;
  for (i=0; i<[fakeUGs count]; i++)
    {
      aFakeUG = [fakeUGs objectAt:i];
      if ([aFakeUG isSoundMaker])
	{
	  if ([[fakeUGs objectAt:i] isEnvelopeHandler])
	    {
	      hasEnvelope = YES;
	      break;
	    }
	}
    }

  if (hasEnvelope)
    {
      fprintf(f, "  double u;\n");
    }
      
  fprintf(f, "  [self _updateParameters:aNote];\n");
  for (i=0; i<[fakeUGs count]; i++)
    {
      aFakeUG = [fakeUGs objectAt:i];
      if ([aFakeUG isSoundMaker])
	{
	  if ([aFakeUG isEnvelopeHandler])
	    {
	      fprintf(f, "  u = [SE[%s] finish];\n  t=(u>t)?u:t;\n",
		      [aFakeUG getName]);
	    }
	}
    }

  fprintf(f, "  return t;\n}\n\n");
  fprintf(f, "-noteEndSelf\n{\n");
  for (i=0; i<[fakePatchPoints count]; i++)
    {
      id patchPoint = [fakePatchPoints objectAt:i];
      int k;
      id toFakeUG;

      for (k=0; k<[[patchPoint getToNodes] count]; k++)
	{
	  toFakeUG = [[[patchPoint getToNodes] objectAt:k] superview];
	  if ([toFakeUG isSoundMaker])
	    {
	      if ([toFakeUG getConnectionName:0] == NULL)
		/*** The following change made by DAJ.  July 26, 1992 ***/
		fprintf(f,
			"  [SE[%s] idle];\n",
			[[[[patchPoint getToNodes] objectAt:0] superview]
			 getName]);
	    }
	}
 
    }


  for (i=0; i<[fakeUGs count]; i++)
    if ([[fakeUGs objectAt:i] isEnvelopeHandler])
      fprintf(f,
	      "  [SE[%s] abortEnvelope];\n", [[fakeUGs objectAt:i] getName]);
  fprintf(f, "  return self;\n}\n\n");
  fprintf(f, "-preemptFor:aNote\n{\n");
  for (i=0; i<[fakeUGs count]; i++)
    if ([[fakeUGs objectAt:i] isEnvelopeHandler])
      fprintf(f,
	      "  [SE[%s] preemptEnvelope];\n", [[fakeUGs objectAt:i] getName]);
  fprintf(f, "  return self;\n}\n\n@end\n");
  fclose(f);
  return theFileName;
}

- (char *)setClassNameFromFileName:(char) suff : (char *) fileName
/* Utilitity function for setting class name from a filename.
 * The suffix suff is ignored in setting the class name.
 * The fileName is returned with suff appended if it didn't already exist
 * If something bizzare happens, NULL is returned.
 */
{
  int l;
  static char name_buf[1024];
  char *path_leaf;

  if (! fileName)
    {
      return(NULL);
    }

  /* Change the patch name to reflect the "root" name of the file
   * (that is, the name with no suffix).
   * We assume the name is legit from an earlier check
   */
  strcpy(name_buf, fileName);
  l = strlen(name_buf);
  if ( l > 2 && (name_buf[l-1] == suff) && (name_buf[l-2] == '.'))
    {
      /* Get rid of the suffix */
      name_buf[l-2] = EOS;
    }

  path_leaf = [utilities getPathLeaf:name_buf];
  if (! path_leaf)
    {
      return NULL;
    }

  [theSP changeName:path_leaf];

  /* Add in the suffix again and return it */
  l = strlen(name_buf);
  name_buf[l] = '.';
  name_buf[l+1] = suff;
  name_buf[l+2] = EOS;

  return(name_buf);
}

- (char *)checkForUnconnectedPins: (int) index

/* Function to check connections.  Eventually add code
 * to check all the connections.
 */
{
  static char ug_name_out[256];
  char *ug_name;

  ug_name = [[fakeUGs objectAt:index] getTypeString];
  if (strcmp([[fakeUGs objectAt:index] getTypeString], "Add2UGxx") == 0)
    {
      NXRunAlertPanel(getAppName(),
		      "%s may have unconnected pins.  Naming it Add2UGxxx",
		      NULL, NULL, NULL,
		      ug_name);
      ug_name = "Add2UGxxx";
    }
  
  strcpy(ug_name_out, ug_name);
  return (ug_name_out);
}


@end
