|  | @@ -29,6 +29,7 @@ laPropContainer* LA_PC_CHANNEL;
 | 
	
		
			
				|  |  |  laNodeCategory* LA_NODE_CATEGORY_SYNTHESIZER;
 | 
	
		
			
				|  |  |  laNodeCategory* LA_NODE_CATEGORY_SYSTEM_SOUND;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +laBaseNodeType LA_IDN_INPUT;
 | 
	
		
			
				|  |  |  laBaseNodeType LA_IDN_FM;
 | 
	
		
			
				|  |  |  laBaseNodeType LA_IDN_VCA;
 | 
	
		
			
				|  |  |  laBaseNodeType LA_IDN_NOISE;
 | 
	
	
		
			
				|  | @@ -37,6 +38,7 @@ laBaseNodeType LA_IDN_SCOPE;
 | 
	
		
			
				|  |  |  laBaseNodeType LA_IDN_ENVELOPE;
 | 
	
		
			
				|  |  |  laBaseNodeType LA_IDN_QUANTIZE;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +laPropContainer* LA_PC_IDN_INPUT;
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_FM;
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_VCA;
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_NOISE;
 | 
	
	
		
			
				|  | @@ -69,6 +71,47 @@ laPropContainer* LA_PC_IDN_QUANTIZE;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define SAMPLE_RATE (MAIN.Audio->AudioSampleRate)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +void IDN_InputInit(laSynthNodeInput* n, int NoCreate){
 | 
	
		
			
				|  |  | +    INITPACKET(n->rTime); INITPACKET(n->rTrigger);
 | 
	
		
			
				|  |  | +    if(NoCreate){ return; }
 | 
	
		
			
				|  |  | +    n->OutTime=laCreateOutSocket(n,"TIME",LA_PROP_FLOAT|LA_PROP_ARRAY);
 | 
	
		
			
				|  |  | +    n->OutTrigger=laCreateOutSocket(n,"TRIGGER",LA_PROP_FLOAT|LA_PROP_ARRAY);
 | 
	
		
			
				|  |  | +    strSafeSet(&n->Base.Name,"Input");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void IDN_InputDestroy(laSynthNodeInput* n){
 | 
	
		
			
				|  |  | +    laDestroyOutSocket(n->OutTime); laDestroyOutSocket(n->OutTrigger); strSafeDestroy(&n->Base.Name); memFree(n->rTime); memFree(n->rTrigger);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +int IDN_InputVisit(laSynthNodeInput* n, laNodeVisitInfo* vi){
 | 
	
		
			
				|  |  | +    LA_GUARD_THIS_NODE(n,vi);
 | 
	
		
			
				|  |  | +    LA_ADD_THIS_NODE(n,vi);
 | 
	
		
			
				|  |  | +    return LA_DAG_FLAG_PERM;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +int IDN_InputEval(laSynthNodeInput* n){
 | 
	
		
			
				|  |  | +    laSynth*ss=MAIN.Audio->AudioEvalSynth;
 | 
	
		
			
				|  |  | +    real time=ss->EvalTime,target_time=time+(real)LA_SYNTH_PLEN/(real)MAIN.Audio->AudioSampleRate;
 | 
	
		
			
				|  |  | +    for(int i=0;i<LA_SYNTH_PLEN;i++){
 | 
	
		
			
				|  |  | +        real fac=(real)i/(real)LA_SYNTH_PLEN;
 | 
	
		
			
				|  |  | +        n->rTime[i] = tnsLinearItp(time,target_time,fac);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if(ss->EvalSamples==0){ n->rTrigger[0] = 10; }else{ n->rTrigger[0]=0; }
 | 
	
		
			
				|  |  | +    n->OutTime->Data=n->rTime; n->OutTrigger->Data=n->rTrigger; n->OutTime->ArrLen=LA_SYNTH_PLEN; n->OutTrigger->ArrLen=LA_SYNTH_PLEN;
 | 
	
		
			
				|  |  | +    return 1;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void IDN_InputCopy(laSynthNodeInput* new, laSynthNodeInput* old, int DoRematch){
 | 
	
		
			
				|  |  | +    if(DoRematch){ return; }
 | 
	
		
			
				|  |  | +    LA_IDN_OLD_DUPL(OutTime); LA_IDN_OLD_DUPL(OutTrigger);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void laui_InputNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
 | 
	
		
			
				|  |  | +    laColumn* c=laFirstColumn(uil); laSynthNodeInput*n=This->EndInstance;
 | 
	
		
			
				|  |  | +    LA_BASE_NODE_HEADER(uil,c,This);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    laUiItem* b=laBeginRow(uil,c,0,0);
 | 
	
		
			
				|  |  | +    laUiItem* ui=laShowLabel(uil,c,"Synth",0,0); ui->Expand=1; ui->Flags|=LA_TEXT_ALIGN_RIGHT;
 | 
	
		
			
				|  |  | +    laShowNodeSocket(uil,c,This,"out_time",0)->Flags|=LA_UI_SOCKET_LABEL_E;
 | 
	
		
			
				|  |  | +    laShowNodeSocket(uil,c,This,"out_trigger",0)->Flags|=LA_UI_SOCKET_LABEL_E;
 | 
	
		
			
				|  |  | +    laEndRow(uil,b);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void IDN_FMInit(laSynthNodeFM* n, int NoCreate){
 | 
	
		
			
				|  |  |      INITPACKET(n->OutSamples);
 | 
	
		
			
				|  |  |      if(NoCreate){ return; }
 | 
	
	
		
			
				|  | @@ -138,7 +181,7 @@ int IDN_VCAEval(laSynthNodeVCA* n){
 | 
	
		
			
				|  |  |      n->Out->Data=n->OutSamples; n->Out->ArrLen=LA_SYNTH_PLEN;
 | 
	
		
			
				|  |  |      if(!input){ memset(n->OutSamples,0,sizeof(real)*LA_SYNTH_PLEN); return 0; }
 | 
	
		
			
				|  |  |      for(int i=0;i<LA_SYNTH_PLEN;i++){
 | 
	
		
			
				|  |  | -        real useA=(inputamp?(inputamp[i]*n->rInfluence/10):1)*constamp;
 | 
	
		
			
				|  |  | +        real useA= inputamp?tnsInterpolate(1.0f,inputamp[i]/10.0f,n->rInfluence)*constamp:constamp;
 | 
	
		
			
				|  |  |          n->OutSamples[i]= input[i]*useA;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      return 1;
 | 
	
	
		
			
				|  | @@ -340,6 +383,7 @@ void IDN_EnvelopeInit(laSynthNodeEnvelope* n, int NoCreate){
 | 
	
		
			
				|  |  |          n->Time=20; n->rAttack=1; n->rDelay=5; n->rSustain=5; n->rRelease=7; n->rGate=5;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if(!n->Gate){ n->Gate=laCreateInSocket("GATE",0); }
 | 
	
		
			
				|  |  | +    if(!n->Restart){ n->Restart=laCreateInSocket("RESTART",0); }
 | 
	
		
			
				|  |  |      if(!n->OutSamples){ n->OutSamples=memAcquireSimple(sizeof(real)*LA_SYNTH_PLEN); }
 | 
	
		
			
				|  |  |      n->Out->Data = n->OutSamples; n->Out->DataType = LA_PROP_FLOAT|LA_PROP_ARRAY; n->Out->ArrLen=LA_SYNTH_PLEN;
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -357,12 +401,14 @@ int IDN_EnvelopeVisit(laSynthNodeEnvelope* n, laNodeVisitInfo* vi){
 | 
	
		
			
				|  |  |      if(LA_SRC_AND_PARENT(n->Sustain)){ laBaseNode*bn=n->Sustain->Source->Parent; LA_VISIT_NODE(bn,vi); }
 | 
	
		
			
				|  |  |      if(LA_SRC_AND_PARENT(n->Release)){ laBaseNode*bn=n->Release->Source->Parent; LA_VISIT_NODE(bn,vi); }
 | 
	
		
			
				|  |  |      if(LA_SRC_AND_PARENT(n->Trigger)){ laBaseNode*bn=n->Trigger->Source->Parent; LA_VISIT_NODE(bn,vi); }
 | 
	
		
			
				|  |  | +    if(LA_SRC_AND_PARENT(n->Restart)){ laBaseNode*bn=n->Restart->Source->Parent; LA_VISIT_NODE(bn,vi); }
 | 
	
		
			
				|  |  |      LA_ADD_THIS_NODE(n,vi);
 | 
	
		
			
				|  |  |      return LA_DAG_FLAG_PERM;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  int IDN_EnvelopeEval(laSynthNodeEnvelope* n){
 | 
	
		
			
				|  |  | -    int trigger_at=-1; real trig=n->bTrigger?n->bTrigger*100:-100;
 | 
	
		
			
				|  |  | +    int trigger_at=-1; real trig=n->bTrigger?n->bTrigger*100:-100; real res=0;
 | 
	
		
			
				|  |  |      real* trigger; INPUTPACKET(trigger,n->Trigger); if(!trigger){ LA_GET_SRC_AS_VALUE(trig,n->Trigger); }
 | 
	
		
			
				|  |  | +    real* restart; INPUTPACKET(restart,n->Restart); if(!restart){ LA_GET_SRC_AS_VALUE(res,n->Restart); }
 | 
	
		
			
				|  |  |      real *att,*del,*sus,*rel,*gat;
 | 
	
		
			
				|  |  |      real attack=n->rAttack,delay=n->rDelay,sustain=n->rSustain,release=n->rRelease;
 | 
	
		
			
				|  |  |      real gate = n->rGate; INPUTPACKET(gat,n->Gate); if(!gat) LA_GET_SRC_AS_VALUE(gate,n->Gate);
 | 
	
	
		
			
				|  | @@ -372,11 +418,12 @@ int IDN_EnvelopeEval(laSynthNodeEnvelope* n){
 | 
	
		
			
				|  |  |      INPUTPACKET(rel,n->Release); if(!rel) LA_GET_SRC_AS_VALUE(release,n->Release);
 | 
	
		
			
				|  |  |      attack=attack?pow(attack/10,2):0; delay=delay?pow(delay/10,2):0; release=release?pow(release/10,2):0;
 | 
	
		
			
				|  |  |      for(int i=0;i<LA_SYNTH_PLEN;i++){
 | 
	
		
			
				|  |  | -        if(trigger){ trig=trigger[i]; } if(gat){ gate=gat[i]; }
 | 
	
		
			
				|  |  | +        if(trigger){ trig=trigger[i]; } if(gat){ gate=gat[i]; } if(restart){ res=restart[i]; }
 | 
	
		
			
				|  |  |          if(att){ attack=att[i]*n->iAttack+n->rAttack; }
 | 
	
		
			
				|  |  |          if(del){ delay=del[i]*n->iDelay+n->rDelay; }
 | 
	
		
			
				|  |  |          if(sus){ sustain=sus[i]*n->iSustain+n->rSustain; }
 | 
	
		
			
				|  |  |          if(rel){ release=rel[i]*n->iRelease+n->rRelease; }
 | 
	
		
			
				|  |  | +        if(res){ n->Time=0; n->ReleaseTime=-10000; n->Triggered=0;}
 | 
	
		
			
				|  |  |          if(!n->Triggered){ if(trig > gate){ n->Triggered = 1; n->Time = 0; n->ReleaseTime=10000; n->AtLevel=0; } }
 | 
	
		
			
				|  |  |          else{ if(trig < gate-0.0001f){ n->Triggered = 0; n->ReleaseTime = n->Time; } }
 | 
	
		
			
				|  |  |          if(n->Time < n->ReleaseTime){
 | 
	
	
		
			
				|  | @@ -430,6 +477,7 @@ void laui_EnvelopeNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laCol
 | 
	
		
			
				|  |  |      laEndRow(uil,b);
 | 
	
		
			
				|  |  |      b=laBeginRow(uil,c,0,0);
 | 
	
		
			
				|  |  |      laShowSeparator(uil,c)->Expand=1; 
 | 
	
		
			
				|  |  | +    laShowItem(uil,c,This,"in_restart")->Flags|=LA_UI_SOCKET_LABEL_E;
 | 
	
		
			
				|  |  |      laShowItem(uil,c,This,"trigger")->Flags|=LA_UI_FLAGS_MOMENTARY|LA_UI_FLAGS_HIGHLIGHT;
 | 
	
		
			
				|  |  |      laShowItem(uil,c,This,"out")->Flags|=LA_UI_SOCKET_LABEL_W;
 | 
	
		
			
				|  |  |      laEndRow(uil,b);
 | 
	
	
		
			
				|  | @@ -521,8 +569,10 @@ int laEvalSynthGraphs(){
 | 
	
		
			
				|  |  |      for(ss=MAIN.Audio->Synths.pFirst;ss;ss=ss->Item.pNext){
 | 
	
		
			
				|  |  |          laSpinLock(&ss->Lock);
 | 
	
		
			
				|  |  |          if(!ss->Playing){ laSpinUnlock(&ss->Lock); continue; }
 | 
	
		
			
				|  |  | -        MAIN.Audio->AudioEvalSynth=ss; MAIN.Audio->AudioEvalTime=0;
 | 
	
		
			
				|  |  | +        MAIN.Audio->AudioEvalSynth=ss;
 | 
	
		
			
				|  |  |          laRunPage(ss->Page,1);
 | 
	
		
			
				|  |  | +        ss->EvalSamples += LA_SYNTH_PLEN;
 | 
	
		
			
				|  |  | +        ss->EvalTime = (real)ss->EvalSamples/MAIN.Audio->AudioSampleRate;
 | 
	
		
			
				|  |  |          laSpinUnlock(&ss->Lock);
 | 
	
		
			
				|  |  |          any=1;
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -677,7 +727,9 @@ int OPINV_laSynthPlay(laOperator* a, laEvent* e){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      laRebuildPageEval(ss->Page);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    laSpinLock(&ss->Lock); ss->Playing=play; laSpinUnlock(&ss->Lock);
 | 
	
		
			
				|  |  | +    laSpinLock(&ss->Lock);
 | 
	
		
			
				|  |  | +    ss->Playing=play; if(!play){ ss->EvalSamples=0; ss->EvalTime=0; }
 | 
	
		
			
				|  |  | +    laSpinUnlock(&ss->Lock);
 | 
	
		
			
				|  |  |      
 | 
	
		
			
				|  |  |      ma_device_start(&MAIN.Audio->AudioDevice); laNotifyInstanceUsers(ss);
 | 
	
		
			
				|  |  |      return LA_FINISHED;
 | 
	
	
		
			
				|  | @@ -890,6 +942,12 @@ void laInitAudio(){
 | 
	
		
			
				|  |  |          laAddIntProperty(pc,"__move","Move Slider","Move Slider",LA_WIDGET_HEIGHT_ADJUSTER,0,0,0,0,0,0,0,0,0,laset_AudioChannelMove,0,0,0,0,0,0,0,0,LA_UDF_IGNORE);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    pc=laAddPropertyContainer("la_node_synth_input", "Synth Input Node", "Synthesizer time and trigger input",0,laui_InputNode,sizeof(laSynthNodeInput),lapost_Node,0,1);
 | 
	
		
			
				|  |  | +    LA_PC_IDN_INPUT=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"out_time", "Out Time","Time output","la_out_socket",0,0,0,offsetof(laSynthNodeInput,OutTime),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"out_trigger", "Out Trigger","Trigger output","la_out_socket",0,0,0,offsetof(laSynthNodeInput,OutTrigger),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  |      pc=laAddPropertyContainer("la_node_synth_fm", "FM OSC Node", "Osilliator node with frequency modulation",0,laui_FMNode,sizeof(laSynthNodeFM),lapost_Node,0,1);
 | 
	
		
			
				|  |  |      LA_PC_IDN_FM=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
 | 
	
	
		
			
				|  | @@ -957,6 +1015,7 @@ void laInitAudio(){
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"in_sustain", "Sustain","Sustain input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Sustain),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"in_release", "Release","Release input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Release),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"in_trigger", "Trigger","Trigger input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Trigger),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | +    laAddSubGroup(pc,"in_restart", "Restart","Restart input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Restart),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"out", "Output","Envelope output","la_out_socket",0,0,0,offsetof(laSynthNodeEnvelope,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  |      laAddFloatProperty(pc,"gate","Gate","Gate for the trigger",0,0,0,10,-10,0.01,0,0,offsetof(laSynthNodeEnvelope,rGate),0,0,0,0,0,0,0,0,0,0,0);
 | 
	
		
			
				|  |  |      laAddFloatProperty(pc,"attack","Attack","Attack value",0,0,0,10,0,0.01,0,0,offsetof(laSynthNodeEnvelope,rAttack),0,0,0,0,0,0,0,0,0,0,0);
 | 
	
	
		
			
				|  | @@ -981,6 +1040,7 @@ void laInitAudio(){
 | 
	
		
			
				|  |  |      laAddEnumItemAs(p,"ENABLED","Enabled","Key is enabled",1,' ');
 | 
	
		
			
				|  |  |      laAddEnumItemAs(p,"OUTPUT","Outputting","Key is Outputting",3,U'🌑');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    LA_IDN_REGISTER("Inputs",U'🔌',LA_IDN_INPUT, LA_PC_IDN_INPUT, IDN_Input, laSynthNodeInput);
 | 
	
		
			
				|  |  |      LA_IDN_REGISTER("VCO",'f',LA_IDN_FM, LA_PC_IDN_FM, IDN_FM, laSynthNodeFM);
 | 
	
		
			
				|  |  |      LA_IDN_REGISTER("VCA",'a',LA_IDN_VCA, LA_PC_IDN_VCA, IDN_VCA, laSynthNodeVCA);
 | 
	
		
			
				|  |  |      LA_IDN_REGISTER("Noise",'~',LA_IDN_NOISE, LA_PC_IDN_NOISE, IDN_Noise, laSynthNodeNoise);
 | 
	
	
		
			
				|  | @@ -992,7 +1052,7 @@ void laInitAudio(){
 | 
	
		
			
				|  |  |      LA_NODE_CATEGORY_SYNTHESIZER=laAddNodeCategory("OSC",0,LA_RACK_TYPE_AUDIO);
 | 
	
		
			
				|  |  |      LA_NODE_CATEGORY_SYSTEM_SOUND=laAddNodeCategory("System",0,LA_RACK_TYPE_AUDIO);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_SYNTHESIZER, &LA_IDN_FM,&LA_IDN_NOISE,&LA_IDN_VCA,&LA_IDN_ENVELOPE,&LA_IDN_QUANTIZE,&LA_IDN_SCOPE,0);
 | 
	
		
			
				|  |  | +    laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_SYNTHESIZER, &LA_IDN_INPUT,&LA_IDN_FM,&LA_IDN_NOISE,&LA_IDN_VCA,&LA_IDN_ENVELOPE,&LA_IDN_QUANTIZE,&LA_IDN_SCOPE,0);
 | 
	
		
			
				|  |  |      laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_SYSTEM_SOUND, &LA_IDN_OUTPUT,0);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 |