/**************************************************************************/
/* This is RokNRoleApp.m, which builds and controls the instrument */
/* Copyright (c)1992 - Rick A. Vander Kam - All rights reserved         */
/* Please refer to the README file for additional documentation        */
/**************************************************************************/

#import "RokNRoleApp.h"
#import <math.h>

#define SRATE 22050.00  /* sampling rate */
#define DLL	256  /* max delay line length */
#define TWOPI 6.283185307
#define MAGIC 1073741824.0  /* used for scaling noise */
#define GROW 0.0001  /* for use as FracdelayUG flag */
#define SHRINK -0.0001  /* for use as FracdelayUG flag */
#define E2LENGTH 101.35  /* String lengths for various pitches */
#define E1LENGTH 234.80
#define B1LENGTH 146.63
#define B2LENGTH 57.030
#define A1LENGTH 168.31
#define A2LENGTH 68.593
#define D1LENGTH 119.66
#define D2LENGTH 44.023
#define G1LENGTH 193.84

	static int i,FBPathLength;
	static double bb01,presLength1,presFrac1;
	static double theta1,newLength1,phi1,fracLength1,intLength1,c01,newBb01;
	static double bb02,presLength2,presFrac2;
	static double theta2,newLength2,phi2,fracLength2,intLength2,c02,newBb02;
	static double GlissSpeed,DistoThresh,FBGain;
	static double DistoLevel,XCoeff,X2Coeff,X3Coeff;
	static double FIR1A0,FIR1A1,FIR2A0,FIR2A1;

	DSPDatum rndArray[DLL];
	id theOrch;
	id theDelay,delayMemObj,firCell;
	id theDelay2,delayMemObj2,firCell2;
	id theDelay3,delayMemObj3;
	id theDCBlock,theFIR,theDisto;
	id theDCBlock2, theFIR2;
	id theAdd, theAdd2, theAdd3;
	id theMult;
	id theMixer;
	id theOut;
	id fbpp,firoutpp,fir2outpp,addoutpp,add2outpp;
	id mulin1pp,mulin2pp;
	id firinpp,fir2inpp,dcboutpp,dcb2outpp,distinpp,distoutpp,outpp;
	
	
	
@implementation RokNRoleApp

	static void handleMKError(char *msg) {
   	   if (!NXRunAlertPanel("RokNRole",msg,"OK","Quit",NULL,NULL))
	      [NXApp terminate:NXApp];
	}


- appDidInit:sender
{

/* Take care of any MusicKit errors */
	MKSetErrorProc(handleMKError);

   	 theOrch=[Orchestra new];
/* Check DSP availability */
	if(![theOrch open]) [theOrch error:"DSP unavailable."];

/* Allocate and hook up */

     /* Patchpoints -- names are mnemonics for unit generator connections */
	fbpp=[theOrch allocPatchpoint:MK_yPatch];
	firoutpp=[theOrch allocPatchpoint:MK_yPatch];
	fir2outpp=[theOrch allocPatchpoint:MK_xPatch];
	addoutpp=[theOrch allocPatchpoint:MK_yPatch];
	add2outpp=[theOrch allocPatchpoint:MK_xPatch];
	firinpp=[theOrch allocPatchpoint:MK_yPatch];
	fir2inpp=[theOrch allocPatchpoint:MK_xPatch];
	dcboutpp=[theOrch allocPatchpoint:MK_yPatch];
	dcb2outpp=[theOrch allocPatchpoint:MK_xPatch];
	mulin1pp=[theOrch allocPatchpoint:MK_xPatch];
	mulin2pp=[theOrch allocPatchpoint:MK_yPatch];
	distinpp=[theOrch allocPatchpoint:MK_yPatch];
	distoutpp=[theOrch allocPatchpoint:MK_yPatch];
	outpp=[theOrch allocPatchpoint:MK_yPatch];

     /* Delay memory objects for DelayUG and FracdelayUG unit generators */
	firCell=[theOrch allocSynthData:MK_xData length:1];
	firCell2=[theOrch allocSynthData:MK_yData length:1];
	delayMemObj=[theOrch allocSynthData:MK_xData length:DLL];
	delayMemObj2=[theOrch allocSynthData:MK_yData length:DLL];
	delayMemObj3=[theOrch allocSynthData:MK_xData length:DLL];

     /* Unit generators */
	theAdd=[theOrch allocUnitGenerator:[Add2UGyyy class]];  /* string loop adder */
	theAdd2=[theOrch allocUnitGenerator:[Add2UGxyx class]];  /* string loop adder */
	theAdd3=[theOrch allocUnitGenerator:[Add2UGyyx class]];  /* string outputs adder */
	theMult=[theOrch allocUnitGenerator:[Mul2UGyxy class]];  /* for scaling feedback signal */
	theDelay=[theOrch allocUnitGenerator:[FracdelayUGyyx class]];  /* string loop delay */
	theFIR=[theOrch allocUnitGenerator:[Fir3UGyy class]];  /* string loop timbre filter */
	theDCBlock=[theOrch allocUnitGenerator:[DcblockUGyy class]];  /* dc-blocking */
	theFIR2=[theOrch allocUnitGenerator:[Fir3UGxx class]];  /* string loop timbre filter */
	theDCBlock2=[theOrch allocUnitGenerator:[DcblockUGxx class]];  /* dc-blocking */
	theDelay2=[theOrch allocUnitGenerator:[FracdelayUGxxy class]];  /* string loop delay */
	theDisto=[theOrch allocUnitGenerator:[CubicnlUGyy class]];  /* distortion unit */
	theMixer=[theOrch allocUnitGenerator:[Scl2add2UGyyy class]];  /* signal mixer */
	theDelay3=[theOrch allocUnitGenerator:[DelayUGyyx class]];  /* feedback path */
	theOut=[theOrch allocUnitGenerator:[Out2sumUGy class]];  /* stereo output */
	
     /* Make the connections */
	[theOut idle];        /* Idle until power on */                                           
	[theDisto setOutput:distoutpp];
	[theDisto setInput:distinpp];
	[theAdd3 setOutput:distinpp];
	[theAdd3 setInput1:dcboutpp];
	[theAdd3 setInput2:dcb2outpp];
	[theDelay setInput:dcboutpp];
	[theDelay setOutput:firinpp];
	[theDelay setDelayMemory:delayMemObj];
	[theDelay setFIRCell:firCell];
	[theDelay2 setInput:dcb2outpp];
	[theDelay2 setOutput:fir2inpp];
	[theDelay2 setDelayMemory:delayMemObj2];
	[theDelay2 setFIRCell:firCell2];
	[theDelay3 setInput:outpp];
	[theDelay3 setOutput:mulin2pp];
	[theDelay3 setDelayMemory:delayMemObj3];
	[theFIR setInput:firinpp];
	[theFIR setOutput:firoutpp];
	[theFIR2 setInput:fir2inpp];
	[theFIR2 setOutput:fir2outpp];
	[theDCBlock setInput:addoutpp];
	[theDCBlock setOutput:dcboutpp];
	[theDCBlock2 setInput:add2outpp];
	[theDCBlock2 setOutput:dcb2outpp];
	[theAdd2 setOutput:add2outpp];
	[theAdd2 setInput1:fbpp];
	[theAdd2 setInput2:fir2outpp];
	[theAdd setOutput:addoutpp];
	[theAdd setInput1:fbpp];
	[theAdd setInput2:firoutpp];
	[theMult setOutput:fbpp];
	[theMult setInput1:mulin1pp];
	[theMult setInput2:mulin2pp];
	[theMixer setOutput:outpp];
	[theMixer setInput1:distinpp];
	[theMixer setInput2:distoutpp];

/* Initialize (default) everything */

	[theOrch setTimed:NO];
	[theOrch setSamplingRate:SRATE];
	[theDelay setBb0:0.99999];  
	[theDelay setBb1:0.0];
	[theDelay setChg:0.0];
	[theDelay adjustLength:128];
	presLength1=128.0;
	bb01=0.99999;
	[theDelay resetPointer];
	[theDelay2 setBb0:0.99999];
	[theDelay2 setBb1:0.0];
	[theDelay2 setChg:0.0];
	[theDelay2 adjustLength:128];
	presLength2=128.0;
	bb02=0.99999;
	[theDelay2 resetPointer];
	[theDelay3 adjustLength:200];
	[theDelay3 resetPointer];
	[delayMemObj setToConstant:0];
	[delayMemObj2 setToConstant:0];
	[delayMemObj3 setToConstant:0];
	[theFIR setBb0:0.25];
	[theFIR setBb1:0.50];
	[theFIR setBb2:0.25];
	[theFIR clear];
	[theFIR2 setBb0:0.25];
	[theFIR2 setBb1:0.50];
	[theFIR2 setBb2:0.25];
	[theFIR2 clear];
	[theDCBlock setBb0:0.9988615];
	[theDCBlock setBb1:-0.9988615];
	[theDCBlock setAa1:0.997723];
	[theDCBlock clear];
	[theDCBlock2 setBb0:0.9988615];
	[theDCBlock2 setBb1:-0.9988615];
	[theDCBlock2 setAa1:0.997723];
	[theDCBlock2 clear];
	[theDisto setAa1:0.99999];
	[theDisto setAa2:0.0];
	[theDisto setAa3:-0.9999];
	[theDisto setThr:0.01];
	[mulin1pp setToConstant:DSPDoubleToFix24(0.03)];
	[theMixer setScale1:0.0];
	[theMixer setScale2:0.99999];
	[theOut setBearing:0.0];
	GlissSpeed=0.1;
	DistoThresh=0.01;
	DistoLevel=0.99999;

/* Start it up */

	[theFIR run];
	[theDCBlock run];
	[theDelay run];
	[theFIR2 run];
	[theDCBlock2 run];
	[theDelay2 run];
	[theDelay3 run];
	[theAdd run];
	[theAdd2 run];
	[theAdd3 run];
	[theDisto run];
	[theMult run];
	[theMixer run];
	[theOut run];
	
	[theOrch run];

	[theOrch flushTimedMessages];

	return self;
}
- powerSwitch:sender
{
/* Patches the output to the "Sink" location when power switch is off */

 	if ([sender state]==1) {
		[theOut run];
		[theOut setInput:outpp];
	}
	else {
		[theOut idle];
	}
	[theOrch flushTimedMessages];
    	return self;
}

- pluck:sender
{
/* 
Plucking consists of loading up the two string loop delay memory objects with random noise, which is scaled to avoid premature clipping when the outputs are added together.
*/

	for (i=0; i<DLL; i++) {
	   rndArray[i] = DSPDoubleToFix24((double)random()/(2.0*MAGIC)-1.0);
	}
	[delayMemObj setData:rndArray length:DLL offset:0];
	[delayMemObj2 setData:rndArray length:DLL offset:0];
	[theOrch flushTimedMessages];
	return self;
}

- setFBGain:sender
{
/* Set multiplicative constant for scaling the fed-back output signal */

	FBGain=[sender doubleValue];
	[fbgainf setDoubleValue:FBGain];
	[fbgains setDoubleValue:FBGain];
	[mulin1pp setToConstant:DSPDoubleToFix24(FBGain)];
	[theOrch flushTimedMessages];
	return self;
}

- setPitch1:sender
{
/*
Here is where the tuning takes place.  Much of what follows is necessary to handle glissandi.  For the mathematical explanation, refer to Sullivan (reference [2] in the README file).  Basically, the floating point value (length in samples) of the tuning slider is first split into its integer and fractional parts, and the fractional part is used to compute the coefficients for the two-point FIR filter built into the interpolated delay line.  If the change in length (from the previous value) is less than 1.0, a glissando will occur; otherwise, the new length is set immediately.
*/

     /* Get integer and fractional parts */
	newLength1=[sender doubleValue];
	[str1f setDoubleValue:newLength1];
	theta1=TWOPI/newLength1;
	intLength1=(int) rint(newLength1-0.50) - 1; 
	fracLength1=newLength1 - 1.0 - (double) intLength1;
	if(fracLength1>=1.0) {
	   intLength1++;
	   fracLength1-=1.0;
	}
     /* Compute interpolation coefficients */
	phi1= -fracLength1*theta1;
	if(fabs(phi1) > 0.0001) {
	   newBb01=1.0/(1.0-(1.0/(cos(theta1)+sin(theta1)/tan(phi1))));
	}
	else {
	   newBb01=0.99999;
	}

     /* Test whether glissando required */
	if((presLength1-newLength1>=1.0)||(presLength1-newLength1<=-1.0)) {
		/* Here for no gliss */
	   [theDelay adjustLength:intLength1];
	   [theDelay resetPointer];
	   [theDelay setBb0:newBb01];
	   [theDelay setBb1:(0.99999-newBb01)];
	   [theOrch flushTimedMessages];
	}
	else {  /* Need to gliss */
	   if(newLength1>presLength1) {  /* Increasing length if true */
	      if(presFrac1>fracLength1) {  /* Must grow one sample if true */
	         [theDelay setChg:GROW];   /* signal that we need to grow */
		/* Loop for "smooth" pitch transition, up to integer boundary */
		 for(c01=bb01; c01>0.0; c01-=GlissSpeed) {
		 	[theDelay setBb0:c01];
			[theDelay setBb1:(0.99999-c01)];
			[theOrch flushTimedMessages];
		}
		/* Trigger the grow */
		[theDelay setBb0:0.0];
		[theDelay setBb1:0.99999];
		[theOrch flushTimedMessages];
		/* Following the grow, must update the integer part of the length */
		[theDelay adjustLength:intLength1];
		[theDelay setChg:0.0];  /* Disable more growing */
		[theOrch flushTimedMessages];
		/* After growing, switch from pure delay to zero delay & continue "smooth" transition */
		for(c01=0.99999; c01>=newBb01; c01 -= GlissSpeed) {
			[theDelay setBb0:c01];
			[theDelay setBb1:(0.99999-c01)];
			[theOrch flushTimedMessages];
		}
	     }
	     else {  /* Increase but no grow */
	         for (c01=bb01; c01>=newBb01; c01 -= GlissSpeed) {
		  	[theDelay setBb0:c01];
			[theDelay setBb1:(0.99999-c01)];
			[theOrch flushTimedMessages];
	         }
	      }
	   }
	   else {  /* Decreasing length */
	      if(presFrac1<fracLength1) {  /* Must shrink one sample if true */
	         [theDelay setChg:SHRINK];
		 for(c01=bb01; c01<0.99999; c01+=GlissSpeed) {
		 	[theDelay setBb0:c01];
			[theDelay setBb1:(0.99999-c01)];
			[theOrch flushTimedMessages];
		}
		[theDelay setBb0:0.99999];
		[theDelay setBb1:0.0];
		[theOrch flushTimedMessages];
		[theDelay adjustLength:intLength1];
		[theDelay setChg:0.0];
		[theOrch flushTimedMessages];
		for(c01=0.0; c01<=newBb01; c01 += GlissSpeed) {
			[theDelay setBb0:c01];
			[theDelay setBb1:(0.99999-c01)];
			[theOrch flushTimedMessages];
		}
	      }
	      else {   /* Decrease but no shrink */
	         for (c01=bb01; c01<=newBb01; c01 += GlissSpeed) {
		  	[theDelay setBb0:c01];
			[theDelay setBb1:(0.99999-c01)];
			[theOrch flushTimedMessages];
	         }
	      }
	   }
	}
	
	/* Housekeeping */	
	bb01=newBb01;
	[theDelay setBb0:bb01]; /* Finish off -- set "exact" values just to be sure */
	[theDelay setBb1:(1-bb01)]; 
	[theOrch flushTimedMessages];
	presFrac1=fracLength1;
	presLength1=newLength1; 

	return self;
}

- setFIR2A1:sender  /* Adjust coefficient "a1" for string 2 */
{
	FIR2A1=[sender doubleValue];
	[fir2a1f setDoubleValue:FIR2A1];
	[fir2a1s setDoubleValue:FIR2A1];
	[theFIR2 setBb1:FIR2A1];
	[theOrch flushTimedMessages];
	return self;
}

- setFIR2A0:sender   /* Adjust coefficient "a0" for string 2 */
{
	FIR2A0=[sender doubleValue];
	[fir2a0f setDoubleValue:FIR2A0];
	[fir2a0s setDoubleValue:FIR2A0];
	[theFIR2 setBb0:FIR2A0];
	[theFIR2 setBb2:FIR2A0];
	[theOrch flushTimedMessages];
	return self;
}

- setPitch2:sender  /* Just like string 1 -- see above */
{
	newLength2=[sender doubleValue];
	[str2f setDoubleValue:newLength2];
	theta2=TWOPI/newLength2;
	intLength2=(int) rint(newLength2-0.50) - 1; 
	fracLength2=newLength2 - 1.0 - (double) intLength2;
	if(fracLength2>=1.0) {
	   intLength2++;
	   fracLength2-=1.0;
	}
	phi2= -fracLength2*theta2;
	if(fabs(phi2) > 0.0001) {
	   newBb02=1.0/(1.0-(1.0/(cos(theta2)+sin(theta2)/tan(phi2))));
	}
	else {
	   newBb02=0.99999;
	}
	if((presLength2-newLength2>=1.0)||(presLength2-newLength2<=-1.0)) {
	   [theDelay2 adjustLength:intLength2];
	   [theDelay2 resetPointer];
	   [theDelay2 setBb0:newBb02];
	   [theDelay2 setBb1:(0.99999-newBb02)];
	   [theOrch flushTimedMessages];
	}
	else {
	   if(newLength2>presLength2) {
	      if(presFrac2>fracLength2) {  /* Must grow one sample if true */
	         [theDelay2 setChg:GROW]; 
		 for(c02=bb02; c02>0.0; c02-=GlissSpeed) {
		 	[theDelay2 setBb0:c02];
			[theDelay2 setBb1:(0.99999-c02)];
			[theOrch flushTimedMessages];
		}
		[theDelay2 setBb0:0.0];
		[theDelay2 setBb1:0.99999];
		[theOrch flushTimedMessages];
		[theDelay2 adjustLength:intLength2];
		[theDelay2 setChg:0.0];
		[theOrch flushTimedMessages];
		for(c02=0.99999; c02>=newBb02; c02 -= GlissSpeed) {
			[theDelay2 setBb0:c02];
			[theDelay2 setBb1:(0.99999-c02)];
			[theOrch flushTimedMessages];
		}
	     }
	     else {  /* Increase but no grow */
	         for (c02=bb02; c02>=newBb02; c02 -= GlissSpeed) {
		  	[theDelay2 setBb0:c02];
			[theDelay2 setBb1:(0.99999-c02)];
			[theOrch flushTimedMessages];
	         }
	      }
	   }
	   else {
	      if(presFrac2<fracLength2) {  /* Must shrink one sample if true */
	         [theDelay2 setChg:SHRINK];
		 for(c02=bb02; c02<0.99999; c02+=GlissSpeed) {
		 	[theDelay2 setBb0:c02];
			[theDelay2 setBb1:(0.99999-c02)];
			[theOrch flushTimedMessages];
		}
		[theDelay2 setBb0:0.99999];
		[theDelay2 setBb1:0.0];
		[theOrch flushTimedMessages];
		[theDelay2 adjustLength:intLength2];
		[theDelay2 setChg:0.0];
		[theOrch flushTimedMessages];
		for(c02=0.0; c02<=newBb02; c02 += GlissSpeed) {
			[theDelay2 setBb0:c02];
			[theDelay2 setBb1:(0.99999-c02)];
			[theOrch flushTimedMessages];
		}
	      }
	      else {   /* Decrease but no shrink */
	         for (c02=bb02; c02<=newBb02; c02 += GlissSpeed) {
		  	[theDelay2 setBb0:c02];
			[theDelay2 setBb1:(0.99999-c02)];
			[theOrch flushTimedMessages];
	         }
	      }
	   }
	}
	
	
	bb02=newBb02;
	[theDelay2 setBb0:bb02]; /* Finish off -- set "exact" values just to be sure */
	[theDelay2 setBb1:(0.99999-bb02)]; 
	[theOrch flushTimedMessages];
	presFrac2=fracLength2;
	presLength2=newLength2; 

	return self;
}

- setFIR1A1:sender  /* Adjust coefficient "a1" for string 1 */
{
	FIR1A1=[sender doubleValue];
	[fir1a1f setDoubleValue:FIR1A1];
	[fir1a1s setDoubleValue:FIR1A1];
	[theFIR setBb1:FIR1A1];
	[theOrch flushTimedMessages];
	return self;
}

- setFIR1A0:sender  /* Adjust coefficient "a0" for string 1 */
{
	FIR1A0=[sender doubleValue];
	[fir1a0f setDoubleValue:FIR1A0];
	[fir1a0s setDoubleValue:FIR1A0];
	[theFIR setBb0:FIR1A0];
	[theFIR setBb2:FIR1A0];
	[theOrch flushTimedMessages];
	return self;
}

- setDistoLevel:sender  /* set the mix of distorted and undistorted signal */
{
	DistoLevel=[sender doubleValue];
	[theMixer setScale1:(0.99999-DistoLevel)*DistoThresh];
	[theMixer setScale2:DistoLevel];
	[theOrch flushTimedMessages];
	return self;
}

- setDistoCoeffX2:sender  /* set the "input squared" distortion coefficient */
{
	X2Coeff=[sender doubleValue];
	[x2f setDoubleValue:X2Coeff];
	[x2s setDoubleValue:X2Coeff];
	[theDisto setAa2:X2Coeff];
	[theOrch flushTimedMessages];
	return self;
}

- setDistoThresh:sender  /* set the hard clipping threshold in the distortion unit */
{	
	DistoThresh=[sender doubleValue];
	[thrf setDoubleValue:DistoThresh];
	[thrs setDoubleValue:DistoThresh];
	[theDisto setThr:DistoThresh];
	[theMixer setScale1:(0.99999-DistoLevel)*DistoThresh];
	[theMixer setScale2:DistoLevel];
	[theOrch flushTimedMessages];
	return self;
}

- setFBPathLength:sender  /* adjust the feedback path length */
{
	FBPathLength=[sender intValue];
	[fbplf setIntValue:FBPathLength];
	[fbpls setIntValue:FBPathLength];
	[theDelay3 adjustLength:FBPathLength];
	[theDelay3 resetPointer];
	[theOrch flushTimedMessages];
	return self;
}

- setDistoCoeffX3:sender  /* set the "input cubed" distortion coefficient */
{
	X3Coeff=[sender doubleValue];
	[x3f setDoubleValue:X3Coeff];
	[x3s setDoubleValue:X3Coeff];
	[theDisto setAa3:X3Coeff];
	[theOrch flushTimedMessages];
	return self;
}

- setGlissSpeed:sender  /* adjust parameter for smoothness vs. speed of glissandi */
{
	GlissSpeed=[sender doubleValue];
	[gsf setDoubleValue:GlissSpeed];
	[gss setDoubleValue:GlissSpeed];
	return self;
}

- setDistoCoeffX:sender  /* set the "unmodified input" distortion coefficient */
{
	XCoeff=[sender doubleValue];
	[xf setDoubleValue:XCoeff];
	[xs setDoubleValue:XCoeff];
	[theDisto setAa1:XCoeff];
	[theOrch flushTimedMessages];
	return self;
}

- setPresetChord:sender  /* Tuning and plucking for preset chord buttons */
{
	if(sender==eButton) {
		presLength1=E1LENGTH;
		presLength2=B1LENGTH;
	}
	if(sender==aButton) {
		presLength1=A1LENGTH;
		presLength2=E2LENGTH;
	}
	if(sender==dButton) {
		presLength1=D1LENGTH;
		presLength2=A2LENGTH;
	}
	if(sender==gButton) {
		presLength1=G1LENGTH;
		presLength2=D1LENGTH;
	}
	[str1f setDoubleValue:presLength1];
	[str1s setDoubleValue:presLength1];
	[str2f setDoubleValue:presLength2];
	[str2s setDoubleValue:presLength2];
	theta1=TWOPI/presLength1;
	intLength1=(int) rint(presLength1-0.50) - 1; 
	presFrac1=presLength1 - 1.0 - (double) intLength1;
	if(presFrac1>=1.0) {
	   intLength1++;
	   presFrac1-=1.0;
	}
	phi1= -presFrac1*theta1;
	if(fabs(phi1) > 0.0001) {
	   bb01=1.0/(1.0-(1.0/(cos(theta1)+sin(theta1)/tan(phi1))));
	}
	else {
	   bb01=0.99999;
	}
	theta2=TWOPI/presLength2;
	intLength2=(int) rint(presLength2-0.50) - 1; 
	presFrac2=presLength2 - 1.0 - (double) intLength2;
	if(presFrac2>=1.0) {
	   intLength2++;
	   presFrac2-=1.0;
	}
	phi2= -presFrac2*theta2;
	if(fabs(phi2) > 0.0001) {
	   bb02=1.0/(1.0-(1.0/(cos(theta2)+sin(theta2)/tan(phi2))));
	}
	else {
	   bb02=0.99999;
	}
	[theDelay adjustLength:intLength1];
	[theDelay resetPointer];
	[theDelay setBb0:bb01];
	[theDelay setBb1:(0.99999-bb01)];
	[theDelay2 adjustLength:intLength2];
	[theDelay2 resetPointer];
	[theDelay2 setBb0:bb02];
	[theDelay2 setBb1:(0.99999-bb02)];
	[theOrch flushTimedMessages];
	[self pluck:nil];	
	return self;
}

- terminate:sender  /* Shut everything down when exiting the program */
{
	[theOut idle];
	[theOrch flushTimedMessages];
	[theOut dealloc];
	[theDelay dealloc];
	[theDelay2 dealloc];
	[theDelay3 dealloc];
	[theDisto dealloc];
	[theDCBlock dealloc];
	[theFIR dealloc];
	[theDCBlock2 dealloc];
	[theFIR2 dealloc];
	[theAdd dealloc];
	[theAdd2 dealloc];
	[theAdd3 dealloc];
	[theMult dealloc];
	[theMixer dealloc];
	[firCell dealloc];	
	[firCell2 dealloc];	
	[firoutpp dealloc];
	[firinpp dealloc];	
	[fir2outpp dealloc];
	[fir2inpp dealloc];	
	[dcboutpp dealloc];
	[dcb2outpp dealloc];	
	[fbpp dealloc];	
	[addoutpp dealloc];	
	[add2outpp dealloc];	
	[distinpp dealloc];
	[distoutpp dealloc];
	[mulin1pp dealloc];
	[mulin2pp dealloc];	
	[outpp dealloc];	
	[delayMemObj dealloc];	
	[delayMemObj2 dealloc];	
	[delayMemObj3 dealloc];	
	[theOrch flushTimedMessages];
	
	[theOrch close];
	[theOrch free];

	return [super terminate:self];
}
@end
