#import "HyperupicGen.h"


@implementation HyperupicGen

+ create {

    id 		newInstance;

    newInstance = [ self new ];
    return newInstance;
}

- init {

    [super init];
    [self setDefaults];
    status = IDLE;	

    openReq = [OpenPanel new];
    openMapReq = [OpenPanel new];
    openTableReq = [OpenPanel new];
    saveReq = [SavePanel new];

    return self;
}

- setDefaults {

  interp = 256;
  srate = 44100.;
  pi = 4 * atan(1.);
  twopi = 8 * atan(1.);
  ampref = 1.;
  synt = 0.;
  freqcon = 100.;
  freqdiff = 50.;
  tablesize = Default_Osc_Size;
  x = -1;
  y = -1;
  xstart = -1;
  xend = -1;
  ystart = -1;
  yend = -1;
  srrand(76914020);
  gotImage = NO;
  gotMapFile = NO;
  gotTableFile = NO;
  oscilTable = NO;
  savedSoundFile = YES;
  gotSoundFileName = NO;
  oscInit = NO;
  adjusted = NO;
  strcpy( harmsText, ".7 0 .2 0 .1\0" );
  mapper = @selector(mapRGB);
  dimension = @selector(genNorm);
  display = @selector(displayMinMax);
  coordinate = @selector(setYcoor);
  spread = @selector(genHarmSeries);
  minmax = @selector(mmHarmSeries);
  osctable = @selector(setOscCos);

  return self;
}

- (float) howFar {

  return howfar;
}

- (int) xval {
  return x;
}

- (int) yval {
  return y;
}

- (int) xstartVal {
  return xstart;
}

- (int) ystartVal {
  return ystart;
}

- (int) xendVal {
  return xend;
}

- (int) yendVal {
  return yend;
}


- setHarmsText: sender {

  strcpy( harmsText, [sender stringValue] );
  return self;
}

- setxVal: sender {

  int grabber;

  if ( (grabber = [sender intValue]) > 0 )

  xend = x = grabber;
  xstart = 0;
  return self;
}

- setyVal: sender {

  int grabber;

  if ( (grabber = [sender intValue]) > 0 )

  yend = y = grabber;
  ystart = 0;
  return self;
}

- setxstartVal: sender {

  int	grabber;

  if ( (grabber = [sender intValue]) > 0 && grabber < x )
  xstart = grabber;
  return self;
}

- setystartVal: sender {

  int	grabber;

  if ( (grabber = [sender intValue]) > 0 && grabber < y )
  ystart = grabber;
  return self;
}

- setxendVal: sender {

  int	grabber;

  if ( (grabber = [sender intValue]) > 0 && grabber < x )
  xend = grabber;
  return self;
}

- setyendVal: sender {

  int	grabber;

  if ( (grabber = [sender intValue]) > 0 && grabber < y )
  yend = grabber;
  return self;
}

- (int) getStatus {

  return status;
}

- (float) getBigee {

  return bigee;
}

- (float) getDuration {

  return duration;
}

- (float) adjustFrequency {

  if (adjusted == YES) {
    adjusted = NO;
    return (freqcon /= srate / (float) tablesize);
  }
  else {
    adjusted = YES;
    return (freqcon *= srate / (float) tablesize);
  }
}

- pause {

  status = PAUSED;
  return self;
}

- snuff {

  status = DYING;
  [self resume];
  return self;
}

- resume	//  Gets called from the other thread

{
  kern_return_t		ec;
  msg_header_t 	msg =   {0,
                         TRUE,
                         sizeof(msg_header_t),
                         MSG_TYPE_NORMAL,
                         0};

    if (status == IDLE && appToObjPort != PORT_NULL)
      return nil;

    msg.msg_local_port = PORT_NULL;
    msg.msg_remote_port = appToObjPort;
    ec = msg_send(&msg, SEND_TIMEOUT, 0);

// huh? (look below)
    if (ec == SEND_TIMED_OUT); 

    return self;
}


- _waitForMessage {	// David Jaffe's pause thread gift

  struct {
  	msg_header_t header;
  	char data[MSG_SIZE_MAX];
  } msg;

  status = PAUSED;

  (void)port_allocate(task_self(), &appToObjPort);
  msg.header.msg_size = MSG_SIZE_MAX;
  msg.header.msg_local_port = appToObjPort;
  (void)msg_receive(&msg.header, MSG_OPTION_NONE,0);
  (void)port_deallocate(task_self(),appToObjPort);
  appToObjPort = PORT_NULL;
  
  if (status != DYING)
    status = RUNNING;

  return self;
}

- setXcoor {

  N = xend - xstart;
  return self;
}

- setYcoor {

  N = yend - ystart;
  return self;
}

- displayFreqs: sender {

  if ( x < 0 || y < 0 ) {
    NXRunAlertPanel("The Beach Boys","Heavens to Murgatroyd! Need coordinates!",
			"okie",NULL,NULL,NULL);
    return nil;
  }

  [self perform:display];
  return self;
}

- displayAll {

  int		i,
		length;
  char		bonk[1024];
  static NXRect wRect = {{120.0, 100.0},{150.0,325.0}};

  [self allocateDataspace];

  [self perform:coordinate];
  [self perform:spread];

  freqDisplay = [[TextView alloc] initFrame:&wRect];
  [[freqDisplay window] setDelegate:self];
  [freqDisplay setTitle:"Frequencies"];

  if ( (wRect.origin.x < 20.0) || (wRect.origin.y < 20.0) ) {
    wRect.origin.x = 120.0;
    wRect.origin.y = 100.0;
  }
  else
    NXOffsetRect(&wRect, 20.0, -20.0);

  for ( i=0; i < N; i++ ) {
    sprintf(bonk," %3f\n", *(freqs+i));
    length = [freqDisplay textLength];
    [freqDisplay setSel:length :length];
    [freqDisplay replaceSel:bonk];
  }

  return self;
}

- displayMinMax {

  int		length;
  char		bonk[1024];
  static NXRect wRect = {{250.0, 300.0},{200.0,100.0}};

  [self perform:coordinate];
  [self perform:minmax];

  freqDisplay = [[TextView alloc] initFrame:&wRect];
  [[freqDisplay window] setDelegate:self];
  [freqDisplay setTitle:"Frequency Extrema"];

  if ( (wRect.origin.x < 20.0) || (wRect.origin.y < 20.0) ) {
    wRect.origin.x = 120.0;
    wRect.origin.y = 100.0;
  }
  else
    NXOffsetRect(&wRect, 20.0, -20.0);


    sprintf(bonk,"minimum:\n%6f\n", mm.min);
    length = [freqDisplay textLength];
    [freqDisplay setSel:length :length];
    [freqDisplay replaceSel:bonk];

    sprintf(bonk,"\nmaximum:\n%6f\n", mm.max);
    length = [freqDisplay textLength];
    [freqDisplay setSel:length :length];
    [freqDisplay replaceSel:bonk];

  return self;
}


- (char *) getImagePath {

  return imageFile;
}

- (char *) getMapPath {

  return mapFile;
}

- (char *) getTablePath {

  return tableFile;
}


- (char *) getSoundPath {

  return soundFile;
}

- (BOOL) getGotImage {

  return gotImage;
}

- (BOOL) getGotMapFile {

  return gotMapFile;
}

- (BOOL) getGotTableFile {

  return gotTableFile;
}

- (BOOL) getOscilTable {

  return oscilTable;
}

- (BOOL) getSavedSoundFile {

  return savedSoundFile;
}

- setImagePath: sender {

  strcpy( imageFile, [sender stringValue]);
  [self openImage];
  return self;
} 


- setMapPath: sender {

  strcpy( mapFile, [sender stringValue]);
  [self openMapFile];
  return self;
} 

- setTablePath: sender {

  strcpy( tableFile, [sender stringValue]);
  [self openTableFile];
  return self;
} 

- ampref: sender {
    ampref = [sender doubleValue];
    return self;
}

- time: sender {
    interp = [sender intValue];
    return self;
}

- freqcon: sender {
    freqcon = (float) [sender doubleValue];
    return self;
}

- freqdiff: sender {
    freqdiff = (float) [sender doubleValue];
    return self;
}

- samplingRate: sender {
    srate = (float) [sender doubleValue];
    return self;
}

- setSeed: sender {
    seed = [sender intValue];
    return self;
}

- setSynt: sender {

  synt = [sender doubleValue];
  return self;
}

- openRequest:sender
{

  static const char *const types[] = {NULL, NULL};

  [openReq setTitle:"looking for an Image file"];
  if ([openReq runModalForTypes:types]) {
    strcpy(imageFile, [openReq filename]);
    [self openImage];
  }
  return self;
}

- openMapRequest:sender
{
  static const char *const types[] = {NULL, NULL};

  [openMapReq setTitle:"looking for a Mapping file"];
  if ([openReq runModalForTypes:types]) {
    strcpy(mapFile, [openReq filename]);
    if ( ![self openMapFile] )
      return nil;
  }
  return self;
}

- openTableRequest: sender
{
  static const char *const types[] = {NULL, NULL};

  [openTableReq setTitle:"looking for an oscillator Table file"];
  if ([openReq runModalForTypes:types]) {
    strcpy(tableFile, [openReq filename]);
    if ( ![self openTableFile] )
      return nil;
  }
  return self;
}


- saveRequest:sender
{

  if (savedSoundFile == YES)
    return self;

    if (gotSoundFileName == NO)
      [self saveInRequest:sender];
    else
      [self writeSound];

    return self;
}

- saveInRequest:sender
{
    
  [saveReq setTitle:"looking to be Saved!"];
  [saveReq setRequiredFileType:""];
  if ( ![saveReq runModal] )
    return nil;

  strcpy(soundFile, [saveReq filename]);
  gotSoundFileName = YES;

  [self writeSound];

  return self;
}


- openImage {

  NXColorSpace	cs;

  if (pict != nil) {
    [pict free];
    gotImage = NO;
  }

  pict = [[NXBitmapImageRep alloc] initFromFile:imageFile];

  if (pict == nil) {
    NXRunAlertPanel("Oral Hygiene",
		    "Lame user error.  Couldn't open your imagined file.",
		    "okie",NULL,NULL,NULL);
    return nil;
  }

  if ( (cs = [pict colorSpace]) != NX_RGBColorSpace ) {
    NXRunAlertPanel("Roman Polanski","Retreat! Your image is not RGB approved!",
                        "okie",NULL,NULL,NULL);
    return nil;
  }

  if ( ( [pict bitsPerPixel]) != 24 ) {
    NXRunAlertPanel("Doris Day","Heavens to Murgatroyd!  Use a 24-bit image!",
                       "dokie",NULL,NULL,NULL);
    return nil;
  }
  
  pictdata = [pict data];
  xend = x = [pict pixelsWide];
  yend = y = [pict pixelsHigh];
  xstart = ystart = 0;

  gotImage = YES;  

  return self;
}


- openMapFile {

  int	i;
  FILE	*fp;

  if ( mapFunk.data != NULL ) {
    free( mapFunk.data );
    gotMapFile = NO;
  }

  mapFunk.elms = Max_Table_Size;
  mapFunk.data = (float *) space( Max_Table_Size, sizeof(float) );
  
  if ( (fp = fopen( mapFile, "r" )) == NULL ) {
    NXRunAlertPanel("Hamburger Helper","Lame user error. Couldn't open map file.",
		    "okie",NULL,NULL,NULL);
    return nil;
  }
  i = 0;
  while ( (fread(mapFunk.data+i, sizeof(float), 1, fp)) != 0 && 
	 i < Max_Table_Size )
    ++i;
  mapFunk.elms = (i-1);

  gotMapFile = YES;

  return self;
}


- openTableFile {

  int	i;
  FILE	*fp;

  if ( oscTable.data != NULL ) {
    free( oscTable.data );
    gotTableFile = NO;
  }

  oscTable.elms = Max_Osc_Size;
  oscTable.data = (float *) space( Max_Osc_Size, sizeof(float) );
  
  if ( (fp = fopen( tableFile, "r" )) == NULL ) {
    NXRunAlertPanel("The Free Market is Fucked",
		    "Egad! Couldn't open oscillator table!",
		    "dokie",NULL,NULL,NULL);
    return nil;
  }
  i = 0;
  while ( (fread(oscTable.data+i, sizeof(float), 1, fp)) != 0 && 
	 i < Max_Osc_Size )
    ++i;
  oscTable.elms = (i-1);
  oscTable.data = (float *) realloc( oscTable.data, i * sizeof(float) );

  if (oscilTable == YES)
    tablesize = i - 1;

  gotTableFile = YES;

  return self;
}


- allocSound {

  int	width = 2,
	size;

  size = interp * maxtime;
  soundPoint = 0;
  bigee = 0.;
  duration = 0.;

  if ( sfh != NULL )
    SNDFree(sfh);

  SNDAlloc( &sfh, (size*width), SND_FORMAT_LINEAR_16, (int) srate, 1, 0);
  SNDGetDataPointer(sfh, (char **)&soundData, &size, &width );

  return self;          
}

- writeSound {

  int err;

  err = SNDWriteSoundfile( soundFile, sfh );

  if (err != SND_ERR_NONE) {
    NXRunAlertPanel("Oedipa Maas","Gadzooks! Couldn't write your sound file!",
                        "okie",NULL,NULL,NULL);
    return nil;
  }
  
  if (status == IDLE)
    savedSoundFile = YES;

  return self;
}

- playSound: sender {

  SNDStartPlaying(sfh, 1, 0, 1, NULL, NULL);
  return self;
}

- stopPlaying: sender {

  SNDStop(1);
  return self;
}

- allocateDataspace {

    if ( ozone != NULL )
      NXDestroyZone(ozone);

    ozone = NXCreateZone( 3 * (N + interp) * 
		sizeof(float), vm_page_size, 0 );

    freqs = (float *) zspace( ozone, N+1, sizeof(float) );
    channel = (float *) zspace( ozone, N<<1+1, sizeof(float) );
    output = (float *) zspace( ozone, interp+1, sizeof(float) );

    return self;
}

- reverseMapping: sender {

  int	i,
	elms;
  float	safety;

  if (gotMapFile == NO) {
    NXRunAlertPanel("Encephalitis","Lordy Lordy! Load amplitude map file first!",
                        "dokie",NULL,NULL,NULL);
    return nil;
  }
  
  elms = mapFunk.elms;
  for ( i=0; i < (elms>>1); i++ ) {
    safety = *(mapFunk.data+i);
    *(mapFunk.data+i) = *(mapFunk.data+(elms-1)-i);
    *(mapFunk.data+(elms-1)-i) = safety;
  }

  return self;
}

- runGen {

  status = RUNNING;
  oscInit = NO;
  srrand(seed);
  howfar = 0.;

// call selected generation method

  if ( [self perform:dimension] ) {
    SNDStop(1);
    NXBeep();
  }
  status = IDLE;

  return self;
}

- genNorm {

  int	cnt;

  N = yend - ystart;		// set number of frequency amplitude pairs
  ampscale = ampref / (float) N;
  maxtime = xend;
  [self allocSound];
  savedSoundFile = NO;
  [self allocateDataspace];
  [self perform:spread];	// create frequency axis values

// main loop (iterate for each value of axis)
  
  for ( timedim = xstart; timedim < maxtime; timedim++ ) {

    timeinc = timedim * Bytes_Per_Pixel;
    // check for death or pause
    if (status > 2)  {
      if (status == PAUSED)
	[self _waitForMessage];
      else {
	if (status == DYING)
	  return nil;
      }
    }

// increment along time axis

    cnt = (N-1);
    for ( freqdim = ystart; freqdim < yend; freqdim++ ) {
      datainc = cnt * x * Bytes_Per_Pixel;
      *(channel+(freqdim<<1)+1) = *(freqs+freqdim);
      [self perform:mapper];
      cnt--;
    }

    if ( ![self oscbank] )
      return nil;

    [self writeoutput];
    howfar = ((float) timedim - xstart) / ((float) maxtime - xstart);
  }
  return self;
}

- genRotNorm {

  int	cnt;

  N = xend - xstart;		// set number of frequency amplitude pairs  
  ampscale = ampref / (float) N;
  maxtime = yend;
  [self allocSound];
  savedSoundFile = NO;
  [self allocateDataspace];
  [self perform:spread];	// create frequency axis values

// main loop (iterate for each value of axis)
  
  for ( timedim = (ystart * x * Bytes_Per_Pixel); 
	timedim < (maxtime * x * Bytes_Per_Pixel); 
	timedim += (x * Bytes_Per_Pixel) ) {

    timeinc = timedim;
    // check for death or pause
    if (status > 2)  {
      if (status == PAUSED)
	[self _waitForMessage];
      else {
	if (status == DYING)
	  return nil;
      }
    }

// increment along time axis

    cnt = 0;
    for ( freqdim = xstart; freqdim < xend; freqdim++ ) {
      datainc = freqdim * Bytes_Per_Pixel;
      *(channel+(freqdim<<1)+1) = *(freqs+freqdim);
      [self perform:mapper];
      cnt++;
    }

    if ( ![self oscbank] )
      return nil;

    [self writeoutput];
    howfar = ( (float) (timedim / (x * Bytes_Per_Pixel)) - ystart ) 
		/ ((float) maxtime - ystart);
  }

  return self;
}

- genRetro {

  int	cnt;

  N = yend - ystart;		// set number of frequency amplitude pairs
  ampscale = ampref / (float) N;
  maxtime = xend;
  [self allocSound];
  savedSoundFile = NO;
  [self allocateDataspace];
  [self perform:spread];	// create frequency axis values

// main loop (iterate for each value of axis)
  
  for ( timedim=(maxtime-1); timedim >= xstart; timedim-- ) {

    timeinc = timedim * Bytes_Per_Pixel;	
    // check for death or pause
    if (status > 2)  {
      if (status == PAUSED)
	[self _waitForMessage];
      else {
	if (status == DYING)
	  return nil;
      }
    }

// increment along time axis

    cnt = (N-1);
    for ( freqdim = ystart; freqdim < yend; freqdim++ ) {
      datainc = cnt * x * Bytes_Per_Pixel;
      *(channel+(freqdim<<1)+1) = *(freqs+freqdim);
      [self perform:mapper];
      cnt--;
    }

    if ( ![self oscbank] )
      return nil;

    [self writeoutput];

    howfar = ( (float) (maxtime-1) - timedim ) / 
      		((float) (maxtime-1) - xstart);
  }
  return self;
}

- genRotRetro {

  int	cnt;

  N = xend - xstart;		// set number of frequency amplitude pairs  
  ampscale = ampref / (float) N;
  maxtime = yend;
  [self allocSound];
  savedSoundFile = NO;
  [self allocateDataspace];
  [self perform:spread];	// create frequency axis values

// main loop (iterate for each value of axis)
  
  for ( timedim = ( (maxtime-1) * x * Bytes_Per_Pixel); 
	timedim >= ystart * x * Bytes_Per_Pixel;
	timedim -= (x * Bytes_Per_Pixel) ) {

    timeinc = timedim;
    // check for death or pause
    if (status > 2)  {
      if (status == PAUSED)
	[self _waitForMessage];
      else {
	if (status == DYING)
	  return nil;
      }
    }

// increment along time axis

    cnt = 0;
    for ( freqdim = xstart; freqdim < xend; freqdim++ ) {
      datainc = freqdim * Bytes_Per_Pixel;
      *(channel+(freqdim<<1)+1) = *(freqs+freqdim);
      [self perform:mapper];
      cnt++;
    }

    if ( ![self oscbank] )
      return nil;

    [self writeoutput];
    howfar = ( (float) (maxtime-1) - (timedim / (x * Bytes_Per_Pixel)) )
		      / ((float) (maxtime-1) - ystart);
  }

  return self;
}


- genNormInv {

  int	cnt;

  N = yend - ystart; 		// set number of frequency amplitude pairs
  ampscale = ampref / (float) N;
  maxtime = xend;
  [self allocSound];
  savedSoundFile = NO;
  [self allocateDataspace];
  [self perform:spread];	// create frequency axis values

// main loop (iterate for each value of axis)
  
  for ( timedim=xstart; timedim < maxtime; timedim++ ) {

    timeinc = timedim * Bytes_Per_Pixel;
    // check for death or pause
    if (status > 2)  {
      if (status == PAUSED)
	[self _waitForMessage];
      else {
	if (status == DYING)
	  return nil;
      }
    }

// increment along time axis

    cnt = (N-1);
    for ( freqdim = ystart; freqdim < yend; freqdim++ ) {
      datainc = cnt * x * Bytes_Per_Pixel;
      *(channel+(freqdim<<1)+1) = *(freqs+cnt);
      [self perform:mapper];
      cnt--;
    }

    if ( ![self oscbank] )
      return nil;

    [self writeoutput];
    howfar = ( (float) timedim - xstart ) / ((float) maxtime - xstart);
  }
  return self;
}

- genRotNormInv {

  int	cnt;

  N = xend - xstart;		// set number of frequency amplitude pairs  
  ampscale = ampref / (float) N;
  maxtime = yend;
  [self allocSound];
  savedSoundFile = NO;
  [self allocateDataspace];
  [self perform:spread];	// create frequency axis values

// main loop (iterate for each value of axis)
  
  for ( timedim = (ystart * x * Bytes_Per_Pixel);
       timedim < (maxtime * x * Bytes_Per_Pixel); 
       timedim += (x * Bytes_Per_Pixel) ) {

    timeinc = timedim;
    // check for death or pause
    if (status > 2)  {
      if (status == PAUSED)
	[self _waitForMessage];
      else {
	if (status == DYING)
	  return nil;
      }
    }

// increment along time axis

    cnt = 0;
    for ( freqdim = xstart; freqdim < xend; freqdim++ ) {
      datainc = freqdim * Bytes_Per_Pixel;
      *(channel+(freqdim<<1)+1) = *(freqs + ( (x-1) - freqdim ));
      [self perform:mapper];
      cnt++;
    }

    if ( ![self oscbank] )
      return nil;

    [self writeoutput];
    howfar = ( ((float) timedim / (x * Bytes_Per_Pixel)) - ystart ) /
      		((float) maxtime - ystart);
  }

  return self;
}

- genRetroInv {

  int	cnt;

  N = yend - ystart;		// set number of frequency amplitude pairs
  ampscale = ampref / (float) N;
  maxtime = xend;
  [self allocSound];
  savedSoundFile = NO;
  [self allocateDataspace];
  [self perform:spread];	// create frequency axis values

// main loop (iterate for each value of axis)
  
  for ( timedim=(maxtime-1); timedim >= xstart; timedim-- ) {

    timeinc = timedim * Bytes_Per_Pixel;
    // check for death or pause
    if (status > 2)  {
      if (status == PAUSED)
	[self _waitForMessage];
      else {
	if (status == DYING)
	  return nil;
      }
    }

// increment along time axis

    cnt = (N-1);
    for ( freqdim = ystart; freqdim < yend; freqdim++ ) {
      datainc = cnt * x * Bytes_Per_Pixel;
      *(channel+(freqdim<<1)+1) = *(freqs+cnt);
      [self perform:mapper];
      cnt--;
    }


    if ( ![self oscbank] )
      return nil;

    [self writeoutput];
    howfar = ( (float) (maxtime-1) - timedim ) / 
		((float) (maxtime-1) - xstart);
  }
  return self;
}

- genRotRetroInv {

  int	cnt;

  N = xend - xstart;		// set number of frequency amplitude pairs  
  ampscale = ampref / (float) N;
  maxtime = yend;
  [self allocSound];
  savedSoundFile = NO;
  [self allocateDataspace];
  [self perform:spread];	// create frequency axis values

// main loop (iterate for each value of axis)
  
  for ( timedim = ( (maxtime-1) * x * Bytes_Per_Pixel);
       timedim >= ystart * x * Bytes_Per_Pixel;
       timedim -= (x * Bytes_Per_Pixel) ) {

    timeinc = timedim;
    // check for death or pause
    if (status > 2)  {
      if (status == PAUSED)
	[self _waitForMessage];
      else {
	if (status == DYING)
	  return nil;
      }
    }

// increment along time axis

    cnt = 0;
    for ( freqdim = xstart; freqdim < xend; freqdim++ ) {
      datainc = freqdim * Bytes_Per_Pixel;
      *(channel+(freqdim<<1)+1) = *(freqs + ( (x-1) - freqdim ));
      [self perform:mapper];
      cnt++;
    }

    if ( ![self oscbank] )
      return nil;

    [self writeoutput];
    howfar = ( (float) (maxtime-1) - (timedim / (x * Bytes_Per_Pixel)) )
	      / ((float) maxtime - ystart);
  }

  return self;
}


- genHarmSeries {

  int	i;

  for ( i=0; i<N; i++ )
    *(freqs+i) = freqcon + (freqdiff * i);

  return self;
}

- genScale {

  int	i;

  *freqs = freqcon;
  for ( i=1; i<N; i++ )
    *(freqs+i) = *(freqs+i-1) * freqdiff;

  return self;
}

- mmHarmSeries {

  mm.min = freqcon;
  mm.max = (N-1) * freqdiff + freqcon;
  return self;
}

- mmScale {

  mm.min = freqcon;
  mm.max = pow(freqdiff, (N-1)) * freqcon;
  return self;
}

- mapRGB {

  static float	scaler = 766.0;

  *(channel+(freqdim<<1)) = *(mapFunk.data + (int) ( ( (float) ( 
	(int) ( *(pictdata+datainc+timeinc) ) + 
	(int) ( *(pictdata+datainc+timeinc+1) ) +
	(int) ( *(pictdata+datainc+timeinc+2) )) / scaler ) * 
	(float) mapFunk.elms ));

  return self;
}

- mapR {

  static float	scaler = 256.0;

  *(channel+(freqdim<<1)) = *(mapFunk.data + (int) ( ( (float) ( 
	(int) ( *(pictdata+datainc+timeinc) )) / scaler ) * 
	(float) mapFunk.elms ));

  return self;
}

- mapG {

  static float	scaler = 256.0;

  *(channel+(freqdim<<1)) = *(mapFunk.data + (int) ( ( (float) ( 
	(int) ( *(pictdata+datainc+timeinc+1) )) / scaler ) * 
	(float) mapFunk.elms ));

  return self;
}

- mapB {

  static float	scaler = 256.0;

  *(channel+(freqdim<<1)) = *(mapFunk.data + (int) ( ( (float) ( 
	(int) ( *(pictdata+datainc+timeinc+2) )) / scaler ) * 
	(float) mapFunk.elms ));

  return self;
}

- mapRG {

  static float	scaler = 511.0;

  *(channel+(freqdim<<1)) = *(mapFunk.data + (int) ( ( (float) ( 
	(int) ( *(pictdata+datainc+timeinc) ) + 
	(int) ( *(pictdata+datainc+timeinc+1) )) / scaler ) * 
	(float) mapFunk.elms ));

  return self;
}

- mapRB {

  static float	scaler = 511.0;

  *(channel+(freqdim<<1)) = *(mapFunk.data + (int) ( ( (float) ( 
	(int) ( *(pictdata+datainc+timeinc) ) + 
	(int) ( *(pictdata+datainc+timeinc+2) )) / scaler ) * 
	(float) mapFunk.elms ));

  return self;
}

- mapGB {

  static float	scaler = 511.0;

  *(channel+(freqdim<<1)) = *(mapFunk.data + (int) ( ( (float) ( 
	(int) ( *(pictdata+datainc+timeinc+1) ) +
	(int) ( *(pictdata+datainc+timeinc+2) )) / scaler ) * 
	(float) mapFunk.elms ));

  return self;
}

- selectGenHarmSeries: sender {

  spread = @selector(genHarmSeries);
  minmax = @selector(mmHarmSeries);
  return self;
}

- selectGenScale: sender {

  spread = @selector(genScale);
  minmax = @selector(mmScale);
  return self;
}

- selectDisplayAll: sender {

  display = @selector(displayAll);
  return self;
}

- selectDisplayMinMax: sender {

  display = @selector(displayMinMax);
  return self;
}

- selectXcoor: sender {
  
  coordinate = @selector(setXcoor);
  return self;
}

- selectYcoor: sender {
  
  coordinate = @selector(setYcoor);
  return self;
}


- selectGenNorm: sender {

  dimension = @selector(genNorm);
  return self;
}

- selectGenRetro: sender {

  dimension = @selector(genRetro);
  return self;
}

- selectGenRotNorm: sender {

  dimension = @selector(genRotNorm);
  return self;
}

- selectGenRotRetro: sender {

  dimension = @selector(genRotRetro);
  return self;
}

- selectGenNormInv: sender {

  dimension = @selector(genNormInv);
  return self;
}

- selectGenRetroInv: sender {

  dimension = @selector(genRetroInv);
  return self;
}

- selectGenRotNormInv: sender {

  dimension = @selector(genRotNormInv);
  return self;
}

- selectGenRotRetroInv: sender {

  dimension = @selector(genRotRetroInv);
  return self;
}

- selectMapR: sender {

  mapper = @selector(mapR);
  return self;
}

- selectMapG: sender {

  mapper = @selector(mapG);
  return self;
}

- selectMapB: sender {

  mapper = @selector(mapB);
  return self;
}

- selectMapRG: sender {

  mapper = @selector(mapRG);
  return self;
}

- selectMapRB: sender {

  mapper = @selector(mapRB);
  return self;
}

- selectMapGB: sender {

  mapper = @selector(mapGB);
  return self;
}

- selectMapRGB: sender {

  mapper = @selector(mapRGB);
  return self;
}

- writeoutput
{
  int   i;

  for ( i=0; i < interp; i++ ) {
    if (fabs( (*(output+i) *= ampscale) ) > bigee)
      bigee = fabs( *(output+i) );
    *(soundData + soundPoint + i) = (short) (32767. * *(output+i));
  }
  soundPoint += interp;
  duration = (float) soundPoint / srate;

  return self;
}

- selectOscCos: sender {

  if (status != IDLE)
    return nil;

  oscilTable = NO;
  tablesize = Default_Osc_Size;
  osctable = @selector(setOscCos);
  return self;
}


- selectOscComplex: sender {

  int	i,
  	grabber;

  float	hoax;
  char	*doink;

  if (status != IDLE)
    return nil;

  oscilTable = NO;

  doink = (char *) space( 8192, sizeof(char) );

  strcpy( doink, harmsText );

  harms.data = (float *) space( Max_Harm, sizeof(float) );
  tablesize = Default_Osc_Size;

  i = 0;
  while ( i < Max_Harm ) {
    if ( (grabber = sscanf(doink, "%f", &hoax)) == 0 || grabber == EOF)
      break;
    *(harms.data+i) = hoax;
    ++i;
    if ( (doink = index( doink, ' ' )) == NULL )
      break;
    while ( isspace(*doink) )
      doink++;
  } 
  harms.elms = i;

  osctable = @selector(setOscComplex);

  return self;
}

- selectOscTable: sender {

  if (status != IDLE)
    return nil;

  tablesize = oscTable.elms - 1;
  oscilTable = YES;
  osctable = @selector(setOscTable);
  return self;
}

- setOscCos {

  int	i;
  float twopioL = twopi / tablesize;

  for ( i = 0; i < tablesize+1; i++ )
    *(table+i) = cos( twopioL*i );

  return self;
}

- setOscComplex {

  int	i,j;
  float twopioL = twopi / tablesize;

  for ( i=0; i < tablesize+1; i++ )
    *(table+i) = cos( twopioL*i );

  for ( j=2; j <= harms.elms; j++ ) {
    for ( i=0; i < tablesize+1; i++ )
      *(table+i) += cos( j*twopioL*i );
  }

  return self;
}

- setOscTable {

  int	i;

  if (gotTableFile == NO) {
    return nil;
  }

  tablesize = oscTable.elms - 1;

  for ( i=0; i < tablesize+1; i++ )
    *(table+i) = *(oscTable.data + i);

  return self;
}


- oscbank
{

  static float 	Iinv,
		*lastamp,
		*lastfreq,
		*index,
  	 	Pinc;
  int 		i,j,
		amp,
		freq;

/* first pass: allocate memory to hold previous values
   of amplitude and frequency for each channel, the table
   index for each oscillator, and the table itself; also
   compute constants */

  if ( oscInit == NO ) {

    if ( funzone != NULL )
      NXDestroyZone(funzone);

    if (oscilTable == YES)
      tablesize = oscTable.elms - 1;
    
    funzone = NXCreateZone( 3 * (N + interp) * 
			   sizeof(float), vm_page_size, 0 );
    
    lastamp = (float *) zspace( funzone, N+1, sizeof(float) );
    lastfreq = (float *) zspace( funzone, N+1, sizeof(float) );
    index = (float *) zspace( funzone, N+1, sizeof(float) );
    table = (float *) zspace( funzone, tablesize+1, sizeof(float) );
    
    for ( i = 0; i < N+1; i++ ) {
      *(lastamp+i) = *(lastfreq+i) = 0.;
      *(index+i) = prand() * (float) tablesize;
    }

    if ( ![self perform:osctable] )
      return nil;

    Iinv = 1. / interp;
    Pinc = ( (float) tablesize ) / srate;

    oscInit = YES;
  }

  for ( i = 0; i < interp; i++ )
    *(output+i) = 0.;

/* for each channel, compute I samples using linear
   interpolation on the amplitude and frequency
   control values */

    for ( i=0; i < N; i++ ) {

      register float 	a,
			ainc,
			f,
			finc,
			address;

      freq = ( amp = ( i << 1 ) ) + 1;
      if ( *(channel+amp) < synt ) 	/* skip the little ones */
	continue;

      *(channel+freq) *= Pinc;
      finc = ( *(channel+freq) - ( f = *(lastfreq+i) ) ) * Iinv;
      
      ainc = ( *(channel+amp) - ( a = *(lastamp+i) ) ) * Iinv;
      address = *(index+i);
      
/* accumulate the I samples from each oscillator into
   output array O (initially assumed to be zero);
   f is frequency in Hz scaled by oscillator increment
   factor and pitch (Pinc); a is amplitude; */

      for ( j=0; j < interp; j++ ) {
	*(output+j) += a * *(table + ( (int) address ));
	address += f;
	
	while ( address >= (float) tablesize )
	  address -= (float) tablesize;
	
	while ( address < 0. )
	  address += (float) tablesize;
	
	a += ainc;
	f += finc;
      } 

/* save current values for next iteration */

      *(lastfreq+i) = *(channel+freq);
      *(lastamp+i) = *(channel+amp);
      *(index+i) = address;
    }

    return self;
}

@end
