/* The following files must be imported. */
#import <musickit/musickit.h>    
#import <musickit/unitgenerators/unitgenerators.h> 

#import "FluteIns.h"

@implementation FluteIns;

/* Statically declare the synthElement indices. */
static int		ampAsymp,   /* amplitude envelope UG */
			inputNoise,
			inputMult,
			inputAdd,      /* adder for envelope and noise */
			inputAdd2,      /* adder to get exitation */
			jetSquarer,      /* multiplier to fake reed table */
			jetCuber,      /* multiplier to fake reed table */
			sigAdder,     /* adder to approximate sigmoid */
			jetAdder,      /* adder to get net jet pressure */
			reflFilter,      /* reflection filter */
			delayLine,      /* delay UG */
			delayLine2,      /* delay UG */
			gainElement,
			stereoOut,   /* output UG */
    			
			inputNoisePp,
			ampPp,			 /* amplitude patchpoint */
			inputMultPp,
			inputAddPp,
			inputAdd2Pp,
    			jetSquarerPp,
    			jetCuberPp,
    			sigAddPp,
			jetAddPp,
    			filterPp,
    			delayPp,
    			delay2Pp,
			gainPp; /* output patchpoint */

+patchTemplateFor:aNote
{
    /* Step 1: Create (or return) the PatchTemplate. */
    static id theTemplate = nil;
    if (theTemplate)
      return theTemplate;
    theTemplate = [PatchTemplate new];

    /* Step 2:  Add the SynthElement specifications. */	
    ampAsymp = [theTemplate addUnitGenerator:[AsympUGx class]];
    inputNoise = [theTemplate addUnitGenerator:[UnoiseUGy class]];
    inputMult = [theTemplate addUnitGenerator:[Mul2UGxyx class]];
    inputAdd = [theTemplate addUnitGenerator:[Scl2add2UGyxx class]];
    inputAdd2 = [theTemplate addUnitGenerator:[Scl2add2UGxyx class]];
    jetSquarer = [theTemplate addUnitGenerator:[Mul2UGxyy class]];
    jetCuber = [theTemplate addUnitGenerator:[Mul2UGxyx class]];
    sigAdder = [theTemplate addUnitGenerator:[Scl2add2UGxyx class]];
    jetAdder = [theTemplate addUnitGenerator:[Scl2add2UGyxx class]];
    reflFilter = [theTemplate addUnitGenerator:[OnepoleUGyy class]];
    delayLine = [theTemplate addUnitGenerator:[DelayUGxyy class]];
    delayLine2 = [theTemplate addUnitGenerator:[DelayUGyxy class]];
    gainElement = [theTemplate addUnitGenerator:[ScaleUGyy class]];

    stereoOut = [theTemplate addUnitGenerator:[Out2sumUGy class]];

    ampPp = [theTemplate addPatchpoint:MK_xPatch];
    inputNoisePp = [theTemplate addPatchpoint:MK_yPatch];
    inputMultPp = [theTemplate addPatchpoint:MK_xPatch];
    inputAddPp = [theTemplate addPatchpoint:MK_yPatch];
    inputAdd2Pp = [theTemplate addPatchpoint:MK_xPatch];
    jetSquarerPp = [theTemplate addPatchpoint:MK_xPatch];
    jetCuberPp = [theTemplate addPatchpoint:MK_xPatch];
    sigAddPp = [theTemplate addPatchpoint:MK_xPatch];
    jetAddPp = [theTemplate addPatchpoint:MK_yPatch];
    filterPp = [theTemplate addPatchpoint:MK_yPatch];
    delayPp = [theTemplate addPatchpoint:MK_xPatch];
    delay2Pp = [theTemplate addPatchpoint:MK_yPatch];
    gainPp = [theTemplate addPatchpoint:MK_yPatch];

    /* Step 3:  Specify the connections. */
    [theTemplate to:ampAsymp sel:@selector(setOutput:) arg:ampPp];

    [theTemplate to:inputNoise sel:@selector(setOutput:) arg:inputNoisePp];
    
    [theTemplate to:inputMult sel:@selector(setOutput:) arg:inputMultPp];
    [theTemplate to:inputMult sel:@selector(setInput1:) arg:inputNoisePp];
    [theTemplate to:inputMult sel:@selector(setInput2:) arg:ampPp];

    [theTemplate to:inputAdd sel:@selector(setOutput:) arg:inputAddPp];
    [theTemplate to:inputAdd sel:@selector(setInput1:) arg:inputMultPp];
    [theTemplate to:inputAdd sel:@selector(setInput2:) arg:ampPp];

    [theTemplate to:inputAdd2 sel:@selector(setOutput:) arg:inputAdd2Pp];
    [theTemplate to:inputAdd2 sel:@selector(setInput1:) arg:inputAddPp];
    [theTemplate to:inputAdd2 sel:@selector(setInput2:) arg:delayPp];

    [theTemplate to:jetSquarer sel:@selector(setOutput:) arg:jetSquarerPp];
    [theTemplate to:jetSquarer sel:@selector(setInput1:) arg:delay2Pp];
    [theTemplate to:jetSquarer sel:@selector(setInput2:) arg:delay2Pp];

    [theTemplate to:jetCuber sel:@selector(setOutput:) arg:jetCuberPp];
    [theTemplate to:jetCuber sel:@selector(setInput1:) arg:delay2Pp];
    [theTemplate to:jetCuber sel:@selector(setInput2:) arg:jetSquarerPp];

    [theTemplate to:sigAdder sel:@selector(setOutput:) arg:sigAddPp];
    [theTemplate to:sigAdder sel:@selector(setInput1:) arg:delay2Pp];
    [theTemplate to:sigAdder sel:@selector(setInput2:) arg:jetCuberPp];

    [theTemplate to:jetAdder sel:@selector(setOutput:) arg:jetAddPp];
    [theTemplate to:jetAdder sel:@selector(setInput1:) arg:sigAddPp];
    [theTemplate to:jetAdder sel:@selector(setInput2:) arg:delayPp];

    [theTemplate to:reflFilter sel:@selector(setOutput:) arg:filterPp];
    [theTemplate to:reflFilter sel:@selector(setInput:) arg:jetAddPp];

    [theTemplate to:delayLine sel:@selector(setOutput:) arg:delayPp];
    [theTemplate to:delayLine sel:@selector(setInput:) arg:filterPp];

    [theTemplate to:delayLine2 sel:@selector(setOutput:) arg:delay2Pp];
    [theTemplate to:delayLine2 sel:@selector(setInput:) arg:inputAdd2Pp];

    [theTemplate to:gainElement sel:@selector(setOutput:) arg:gainPp];
    [theTemplate to:gainElement sel:@selector(setInput:) arg: jetAddPp];

    /* Return the PatchTemplate. */	
    return theTemplate;
}

- init
{
    /* Sent once when the patch is created. */
    return self;
}

- setDefaults
{
    ampEnv  = nil;	
    amp0    = 0.0;
    amp1    = MK_DEFAULTAMP;  /* 0.1 */
    ampAtt  = MK_NODVAL;      /* parameter not present */
    ampRel  = MK_NODVAL;      /* parameter not present */

    portamento = MK_DEFAULTPORTAMENTO; 	/* 0.1 */
    bearing = MK_DEFAULTBEARING;		/* 0.0 (centered) */
    
    return self;
}

- preemptFor:aNote
{
    [[self synthElementAt:ampAsymp] preemptEnvelope]; 
    [self setDefaults];
    return self;
}

- applyParameters:aNote
  /* This is a private method to the InsTwoWaves class. It is used internally only.
     */
{
    /* Retrieve and store the parameters. */
    id		myAmpEnv = [aNote parAsEnvelope:MK_ampEnv];
    double	myAmp0   = [aNote parAsDouble:MK_amp0];
    double	myAmp1   = [aNote parAsDouble:MK_amp1];
    double	myAmpAtt = [aNote parAsDouble:MK_ampAtt];
    double	myAmpRel = [aNote parAsDouble:MK_ampAtt];


    int		MY_envelopeSlew = [[Note class] parName: "MY_envelopeSlew"];
    double	myEnvelopeSlew   = [aNote parAsDouble:MY_envelopeSlew];

    int		MY_noiseVolume = [[Note class] parName: "MY_noiseVolume"];
    double	myNoiseVolume   = [aNote parAsDouble:MY_noiseVolume];

    int		MY_delay2Length = [[Note class] parName: "MY_delay2Length"];
    double	myDelay2Length   = [aNote parAsDouble:MY_delay2Length];

    int		MY_outAmp = [[Note class] parName: "MY_outAmp"];
    int		MY_dLineLength = [[Note class] parName: "MY_dLineLength"];
    double	myOutAmp   = [aNote parAsDouble:MY_outAmp];
    double	myDLineLength   = [aNote parAsDouble:MY_dLineLength];

    double	myPortamento = [aNote parAsDouble:MK_portamento];
    double	myBearing    = [aNote parAsDouble:MK_bearing];

    /* Store the phrase status. */	
    MKPhraseStatus phraseStatus = [self phraseStatus];

    /* Is aNote a noteOn? */
//    BOOL isNoteOn = [aNote noteType] == MK_noteOn;

    /* Is aNote the beginning of a new phrase? */
    BOOL isNewPhrase = (phraseStatus == MK_phraseOn) || 
                       (phraseStatus == MK_phraseOnPreempt);

    /* Used in the parameter checks. */
    BOOL shouldApplyAmp = NO;
    BOOL shouldApplyBearing = NO;	

    /* The same portamento is used in both frequency and amplitude. */
    if (!MKIsNoDVal(myPortamento)) {
        portamento = myPortamento;
        shouldApplyAmp = YES;}	
    
    /* Check the amplitude parameters and set the instance variables. */
    if (myAmpEnv != nil) {
       ampEnv = myAmpEnv;
        shouldApplyAmp = YES; }
    if (!MKIsNoDVal(myAmp0)) {
        amp0 = myAmp0;
        shouldApplyAmp = YES; }
    if (!MKIsNoDVal(myAmp1)) {
        amp1 = myAmp1;
//	[[self synthElementAt:ampAsymp] abortEnvelope];
//	[[self synthElementAt:ampAsymp] setT60: 0.01];
	[[self synthElementAt:ampAsymp] setTargetVal:amp1];
	}
    if (!MKIsNoDVal(myEnvelopeSlew)) {
             [[self synthElementAt:ampAsymp] setT60: myEnvelopeSlew];
	     		// Hack this for slew on breath pressure
    }	
    if (!MKIsNoDVal(myAmpAtt)) {
        ampAtt = myAmpAtt;
        shouldApplyAmp = YES; }
    if (!MKIsNoDVal(myAmpRel)) {
        ampRel = myAmpRel;
        shouldApplyAmp = YES; }
    /* Apply the amplitude parameters. */
//    if (shouldApplyAmp || isNoteOn)	{
//      MKUpdateAsymp([self synthElementAt:ampAsymp], 
//                    ampEnv, amp0, amp1, ampAtt, ampRel, 
//                    portamento, phraseStatus);
//    }

   /* Check and set the bearing. */
   if (!MKIsNoDVal(myBearing)) {
        bearing = myBearing;
        shouldApplyBearing = YES; }
   if (shouldApplyBearing || isNewPhrase)
     [[self synthElementAt:stereoOut] setBearing:bearing];

[[self synthElementAt:inputAdd2] setScale1: 1.0];
[[self synthElementAt:inputAdd2] setScale2: -0.35];
[[self synthElementAt:sigAdder] setScale1: -1.0];
[[self synthElementAt:sigAdder] setScale2:  1.0];
[[self synthElementAt:jetAdder] setScale1: 0.4];
[[self synthElementAt:jetAdder] setScale2: 0.90];

[[self synthElementAt:reflFilter] setB0: 0.7];
[[self synthElementAt:reflFilter] setA1: -0.3];

   /* Check and set the embouchure. */
   if (!MKIsNoDVal(myDelay2Length)) {
        delay2Length = myDelay2Length;
             [[self synthElementAt:delayLine2] adjustLength:delay2Length];
   }

   /* Check and set the embouchure. */
   if (!MKIsNoDVal(myNoiseVolume)) {
        noiseVolume = myNoiseVolume;
             [[self synthElementAt:inputAdd] setScale1:myNoiseVolume];
             [[self synthElementAt:inputAdd] setScale2: 1.0];
   }

   /* Check and set the fader level. */
   if (!MKIsNoDVal(myOutAmp)) {
        outAmp = myOutAmp;
        [[self synthElementAt:gainElement] setScale:outAmp];
   }

   if (!MKIsNoDVal(myDLineLength)) {
        dLineLength = myDLineLength;
     [[self synthElementAt:delayLine] adjustLength:(int) dLineLength];
   }

  return self;
}    

- noteOnSelf:aNote
{
    delayMemory = [Orchestra allocSynthData:MK_yData length:100];
    [[self synthElementAt: delayLine] setDelayMemory: delayMemory];
    [delayMemory setToConstant:0 length:100 offset:0];  
    delayMemory2 = [Orchestra allocSynthData:MK_yData length:40];
    [[self synthElementAt: delayLine2] setDelayMemory: delayMemory2];
    [delayMemory2 setToConstant:0 length:40 offset:0];  
    /* Apply the parameters to the patch. */	
    [self applyParameters:aNote];

    /* Make the final connection to the output sample stream. */	
    [[self synthElementAt:stereoOut] setInput:[self synthElementAt:gainPp]];

    /* Tell the UnitGenerators to begin running. */	
    [synthElements makeObjectsPerform:@selector(run)];

    return self;
}

- noteUpdateSelf:aNote
{
    /* Apply the parameters to the patch. */	
    [self applyParameters: aNote];
	
    return self;	
}

- (double)noteOffSelf:aNote
{   
    /* Apply the parameters. */
    [self applyParameters: aNote];

    /* Same for amplitude, but also return the release duration. */
    return [[self synthElementAt:ampAsymp] finish];
}

- noteEndSelf
{
    /* Remove the patch's Out2sum from the output sample stream. */
    [[self synthElementAt:stereoOut] idle]; 

    /* Abort the frequency Envelope. */
    [[self synthElementAt:ampAsymp] abortEnvelope];

    /* Set the instance variables to their default values. */ 
    [self setDefaults];

    return self;
}

@end
