|
@@ -39,6 +39,7 @@ laBaseNodeType LA_IDN_SCOPE;
|
|
|
laBaseNodeType LA_IDN_ENVELOPE;
|
|
|
laBaseNodeType LA_IDN_QUANTIZE;
|
|
|
laBaseNodeType LA_IDN_SYNTH_DRIVER;
|
|
|
+laBaseNodeType LA_IDN_SYNTH_TRIGGER;
|
|
|
|
|
|
laPropContainer* LA_PC_IDN_INPUT;
|
|
|
laPropContainer* LA_PC_IDN_PULSE;
|
|
@@ -49,7 +50,8 @@ laPropContainer* LA_PC_IDN_OUTPUT;
|
|
|
laPropContainer* LA_PC_IDN_SCOPE;
|
|
|
laPropContainer* LA_PC_IDN_ENVELOPE;
|
|
|
laPropContainer* LA_PC_IDN_QUANTIZE;
|
|
|
-laPropContainer* LA_PC_IDN_DRIVER;
|
|
|
+laPropContainer* LA_PC_IDN_SYNTH_DRIVER;
|
|
|
+laPropContainer* LA_PC_IDN_SYNTH_TRIGGER;
|
|
|
|
|
|
#define _2PI 6.283185307
|
|
|
|
|
@@ -687,6 +689,67 @@ void laui_SynthDriverNode(laUiList *uil, laPropPack *This, laPropPack *Extra, la
|
|
|
laShowItemFull(uil,cl,This,"target",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0);
|
|
|
}
|
|
|
|
|
|
+void IDN_SynthTriggerInit(laSynthNodeTrigger* n, int NoCreate){
|
|
|
+ if(NoCreate){ return; }
|
|
|
+ n->Prev=laCreateInSocket("PREV",0); n->Next=laCreateOutSocket(n,"NEXT",0);
|
|
|
+ n->InTrigger=laCreateInSocket("TRIGGER",LA_PROP_FLOAT|LA_PROP_ARRAY);
|
|
|
+ strSafeSet(&n->Base.Name,"Sound Trigger"); n->TriggerEdge=1;
|
|
|
+}
|
|
|
+void IDN_SynthTriggerDestroy(laSynthNodeTrigger* n){
|
|
|
+ laDestroyInSocket(n->InTrigger); strSafeDestroy(&n->Base.Name);
|
|
|
+ laDestroyInSocket(n->Prev); laDestroyOutSocket(n->Next);
|
|
|
+}
|
|
|
+int IDN_SynthTriggerVisit(laSynthNodeTrigger* n, laNodeVisitInfo* vi){
|
|
|
+ LA_GUARD_THIS_NODE(n,vi);
|
|
|
+ if(LA_SRC_AND_PARENT(n->Prev)){ laBaseNode*bn=n->Prev->Source->Parent; LA_VISIT_NODE(bn,vi); }
|
|
|
+ if(LA_SRC_AND_PARENT(n->InTrigger)){ laBaseNode*bn=n->InTrigger->Source->Parent; LA_VISIT_NODE(bn,vi); }
|
|
|
+ LA_ADD_THIS_NODE(n,vi);
|
|
|
+ return LA_DAG_FLAG_PERM;
|
|
|
+}
|
|
|
+int IDN_SynthTriggerEval(laSynthNodeTrigger* n){
|
|
|
+ laSynth*ss=n->Target; if(!ss){ return 0; }
|
|
|
+ real trig=0; real* trigger=0; INPUTPACKET(trigger,n->InTrigger); LA_GET_SRC_AS_VALUE(trig,n->InTrigger);
|
|
|
+ if(MAIN.Audio->AudioEvalSynth == ss){ return 0; }
|
|
|
+ if(trigger){
|
|
|
+ for(int i=0;i<LA_SYNTH_PLEN;i++){
|
|
|
+ if(trigger[i]>=5){
|
|
|
+ if(n->TriggerEdge){ if(!n->Triggered){ n->Triggered=1; laSynthTriggerLater(ss); } }
|
|
|
+ else{ laSynthTriggerLater(ss); }
|
|
|
+ }else{ n->Triggered=0; }
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ if(trig>=0.5){
|
|
|
+ if(n->TriggerEdge){ if(!n->Triggered){ n->Triggered=1; laSynthTriggerLater(ss); } }
|
|
|
+ else{ laSynthTriggerLater(ss); }
|
|
|
+ }else{ n->Triggered=0; }
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+void IDN_SynthTriggerCopy(laSynthNodeTrigger* new, laSynthNodeTrigger* old, int DoRematch){
|
|
|
+ if(DoRematch){ LA_IDN_NEW_LINK(InTrigger); return; }
|
|
|
+ new->TriggerEdge = old->TriggerEdge;
|
|
|
+ memAssignRef(new,&new->Target,old->Target);
|
|
|
+}
|
|
|
+void laui_SynthTriggerNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
|
|
|
+ laColumn* c=laFirstColumn(uil),*cl,*cr,*cll,*clr;
|
|
|
+ LA_BASE_NODE_HEADER(uil,c,This);
|
|
|
+ laSplitColumn(uil,c,0.6); cl=laLeftColumn(c,0); cr=laRightColumn(c,3);
|
|
|
+
|
|
|
+ laUiItem* b2, *rui;
|
|
|
+
|
|
|
+ laShowNodeSocket(uil,cr,This,"prev",0)->Flags|=LA_UI_SOCKET_LABEL_W;
|
|
|
+ laShowNodeSocket(uil,cr,This,"next",0)->Flags|=LA_UI_SOCKET_LABEL_W;
|
|
|
+
|
|
|
+ laUiItem* b=laBeginRow(uil,cl,0,0);
|
|
|
+ laShowItemFull(uil,cl,This,"trigger_edge",0,0,0,0)->Flags|=LA_UI_FLAGS_ICON;
|
|
|
+ laShowNodeSocket(uil,cl,This,"in_trigger",0);
|
|
|
+ laShowItemFull(uil,cl,This,"trigger",0,"text=Trigger",0,0)->Flags|=LA_UI_FLAGS_MOMENTARY|LA_UI_FLAGS_HIGHLIGHT;
|
|
|
+ laShowSeparator(uil,cl)->Expand=1;
|
|
|
+ laEndRow(uil,b);
|
|
|
+
|
|
|
+ laShowItemFull(uil,cl,This,"target",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0);
|
|
|
+}
|
|
|
+
|
|
|
void laRebuildSynthGraphs(int current_only){
|
|
|
if(MAIN.Audio->CurrentSynth && current_only){
|
|
|
laSpinLock(&MAIN.Audio->CurrentSynth->Lock);
|
|
@@ -712,6 +775,10 @@ int laEvalSingleSynth(laSynth* ss){
|
|
|
}
|
|
|
FirstInst=ss->PolyphonicInstances.pFirst;
|
|
|
laSpinUnlock(&ss->Lock);
|
|
|
+
|
|
|
+ laSpinLock(&MAIN.Audio->AudioStatusLock);
|
|
|
+ laSynth* pending; while(pending=lstPopPointer(&MAIN.Audio->PolyLaterTrigger)){ laSynthTriggerNew(pending); }
|
|
|
+ laSpinUnlock(&MAIN.Audio->AudioStatusLock);
|
|
|
|
|
|
laListHandle to_remove={0}; int count=0;
|
|
|
for(laSynth* inst=FirstInst;inst;inst=inst->Item.pNext){
|
|
@@ -720,7 +787,7 @@ int laEvalSingleSynth(laSynth* ss){
|
|
|
}
|
|
|
|
|
|
laSynth* inst; while(inst=lstPopPointer(&to_remove)){
|
|
|
- la_SynthRemovePolyInstance(ss,inst); printf("rem\n");
|
|
|
+ la_SynthRemovePolyInstance(ss,inst);
|
|
|
}
|
|
|
|
|
|
return any;
|
|
@@ -929,12 +996,18 @@ int OPINV_SynthTriggerNew(laOperator* a, laEvent* e){
|
|
|
}
|
|
|
|
|
|
void la_SynthAddPolyInstance(laSynth* s){
|
|
|
+ int count=0;
|
|
|
+ laSpinLock(&s->Lock); count = s->PolyCount; laSpinUnlock(&s->Lock);
|
|
|
+ if(s->PolyCount>s->MaxPoly){ return; }
|
|
|
+
|
|
|
+ laSpinLock(&s->Lock);
|
|
|
+
|
|
|
laSynth* ns=memAcquire(sizeof(laSynth)); ns->Length=s->Length; ns->Playing=1;
|
|
|
ns->Page = laDuplicateRackPage(0,s->Page);
|
|
|
laRebuildPageEval(ns->Page);
|
|
|
laSpinInit(&ns->Lock);
|
|
|
|
|
|
- laSpinLock(&s->Lock);
|
|
|
+
|
|
|
lstPushItem(&s->PolyphonicInstances,ns);
|
|
|
s->PolyCount++;
|
|
|
laSpinUnlock(&s->Lock);
|
|
@@ -952,7 +1025,13 @@ void la_SynthRemovePolyInstance(laSynth* s, laSynth* inst){
|
|
|
memFree(inst);
|
|
|
}
|
|
|
void laSynthTriggerNew(laSynth* s){
|
|
|
- if(!s->Poly){ return; } la_SynthAddPolyInstance(s);
|
|
|
+ if(!s || !s->Poly){ return; } la_SynthAddPolyInstance(s);
|
|
|
+}
|
|
|
+void laSynthTriggerLater(laSynth* s){
|
|
|
+ if(!s || !s->Poly){ return; }
|
|
|
+ laSpinLock(&MAIN.Audio->AudioStatusLock);
|
|
|
+ lstAppendPointer(&MAIN.Audio->PolyLaterTrigger,s);
|
|
|
+ laSpinUnlock(&MAIN.Audio->AudioStatusLock);
|
|
|
}
|
|
|
|
|
|
laAudioChannel* laNewAudioChannel(char* Name){
|
|
@@ -1116,6 +1195,10 @@ void laset_AudioChannelMove(laAudioChannel* ac, int move){
|
|
|
if(move<0 && ac->Item.pPrev){ lstMoveUp(&MAIN.Audio->Channels, ac); laNotifyUsers("la.audio"); }
|
|
|
elif(move>0 && ac->Item.pNext){ lstMoveDown(&MAIN.Audio->Channels, ac); laNotifyUsers("la.audio"); }
|
|
|
}
|
|
|
+void laset_SynthTriggerTrigger(laSynthNodeTrigger* n, int trig){
|
|
|
+ if(trig){ laSynthTriggerNew(n->Target); } n->iTrigger=trig;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
void la_AudioPreFrame(){
|
|
|
if(MAIN.GraphNeedsRebuild){ laRebuildSynthGraphs(1); }
|
|
@@ -1273,7 +1356,7 @@ void laInitAudio(){
|
|
|
laAddEnumItemAs(p,"TRIG","Trigger","Trigger active",1,0);
|
|
|
|
|
|
pc=laAddPropertyContainer("la_node_synth_driver", "Synth Driver Node", "Control playing status of the synth",0,laui_SynthDriverNode,sizeof(laSynthNodeDriver),lapost_Node,0,1);
|
|
|
- LA_PC_IDN_DRIVER=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
|
|
|
+ LA_PC_IDN_SYNTH_DRIVER=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,"prev", "Previous","Previous node","la_in_socket",0,0,0,offsetof(laSynthNodeDriver,Prev),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
|
laAddSubGroup(pc,"next", "Next","Next node","la_out_socket",0,0,0,offsetof(laSynthNodeDriver,Next),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
@@ -1286,6 +1369,20 @@ void laInitAudio(){
|
|
|
laAddEnumItemAs(p,"IDLE","Idle","Trigger idle",0,0);
|
|
|
laAddEnumItemAs(p,"TRIG","Trigger","Trigger active",1,0);
|
|
|
laAddSubGroup(pc,"target","Target","Target synth to control","la_synth",0,0,0,offsetof(laSynthNodeDriver,Target),laget_FirstSynth,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
|
|
|
+
|
|
|
+ pc=laAddPropertyContainer("la_node_synth_trigger", "Synth Trigger Node", "Trigger a new polyphonic note",0,laui_SynthTriggerNode,sizeof(laSynthNodeTrigger),lapost_Node,0,1);
|
|
|
+ LA_PC_IDN_SYNTH_TRIGGER=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,"prev", "Previous","Previous node","la_in_socket",0,0,0,offsetof(laSynthNodeTrigger,Prev),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
|
+ laAddSubGroup(pc,"next", "Next","Next node","la_out_socket",0,0,0,offsetof(laSynthNodeTrigger,Next),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
|
+ laAddSubGroup(pc,"in_trigger", "In Trigger","Trigger input","la_in_socket",0,0,0,offsetof(laSynthNodeTrigger,InTrigger),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
|
+ p=laAddEnumProperty(pc,"trigger","Trigger","Trigger new note",LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laSynthNodeTrigger,iTrigger),0,laset_SynthTriggerTrigger,0,0,0,0,0,0,0,0);
|
|
|
+ laAddEnumItemAs(p,"IDLE","Idle","Trigger idle",0,0);
|
|
|
+ laAddEnumItemAs(p,"TRIG","Trigger","Trigger active",1,0);
|
|
|
+ p=laAddEnumProperty(pc,"trigger_edge","Edge","Trigger on rising edge",LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laSynthNodeTrigger,TriggerEdge),0,0,0,0,0,0,0,0,0,0);
|
|
|
+ laAddEnumItemAs(p,"LEVEL","Level","Trigger on high level",0,'-');
|
|
|
+ laAddEnumItemAs(p,"RISING_EDGE","Rising Edge","Trigger on rising edge",1,U'⮥');
|
|
|
+ laAddSubGroup(pc,"target","Target","Target synth to trigger","la_synth",0,0,0,offsetof(laSynthNodeTrigger,Target),laget_FirstSynth,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
|
|
|
|
|
|
pc=laAddPropertyContainer("la_node_synth_quantize", "Quantize Node", "Quantize control voltages",0,laui_QuantizeNode,sizeof(laSynthNodeQuantize),lapost_Node,0,1);
|
|
|
LA_PC_IDN_QUANTIZE=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
|
|
@@ -1306,15 +1403,16 @@ void laInitAudio(){
|
|
|
LA_IDN_REGISTER("Sound Output",U'🕪',LA_IDN_OUTPUT, LA_PC_IDN_OUTPUT, IDN_Output, laSynthNodeOutput);
|
|
|
LA_IDN_REGISTER("Envelope",U'^',LA_IDN_ENVELOPE, LA_PC_IDN_ENVELOPE, IDN_Envelope, laSynthNodeEnvelope);
|
|
|
LA_IDN_REGISTER("Quantize",U'#',LA_IDN_QUANTIZE, LA_PC_IDN_QUANTIZE, IDN_Quantize, laSynthNodeQuantize);
|
|
|
- LA_IDN_REGISTER("Sound Player",U'⏯',LA_IDN_SYNTH_DRIVER, LA_PC_IDN_DRIVER, IDN_SynthDriver, laSynthNodeDriver);
|
|
|
+ LA_IDN_REGISTER("Sound Player",U'⏯',LA_IDN_SYNTH_DRIVER, LA_PC_IDN_SYNTH_DRIVER, IDN_SynthDriver, laSynthNodeDriver);
|
|
|
+ LA_IDN_REGISTER("Sound Trigger",U'🎵',LA_IDN_SYNTH_TRIGGER, LA_PC_IDN_SYNTH_TRIGGER, IDN_SynthTrigger, laSynthNodeTrigger);
|
|
|
|
|
|
LA_NODE_CATEGORY_SYNTHESIZER=laEnsureNodeCategory("OSC",0,LA_RACK_TYPE_AUDIO);
|
|
|
LA_NODE_CATEGORY_SYSTEM_SOUND=laEnsureNodeCategory("System",0,LA_RACK_TYPE_AUDIO);
|
|
|
|
|
|
laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_SYNTHESIZER,
|
|
|
- &LA_IDN_INPUT,&LA_IDN_PULSE,&LA_IDN_FM,&LA_IDN_NOISE,&LA_IDN_VCA,&LA_IDN_ENVELOPE,&LA_IDN_QUANTIZE,&LA_IDN_SCOPE,0);
|
|
|
+ &LA_IDN_INPUT,&LA_IDN_PULSE,&LA_IDN_FM,&LA_IDN_NOISE,&LA_IDN_VCA,&LA_IDN_ENVELOPE,&LA_IDN_QUANTIZE,&LA_IDN_SCOPE,&LA_IDN_SYNTH_TRIGGER,0);
|
|
|
laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_SYSTEM_SOUND, &LA_IDN_OUTPUT,0);
|
|
|
|
|
|
LA_NODE_CATEGORY_DRIVER=laEnsureNodeCategory("Driver",0,LA_RACK_TYPE_DRIVER);
|
|
|
- laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_DRIVER, &LA_IDN_SYNTH_DRIVER,0);
|
|
|
+ laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_DRIVER, &LA_IDN_SYNTH_DRIVER, &LA_IDN_SYNTH_TRIGGER,0);
|
|
|
}
|