*/}}
Browse Source

Synth trigger node and thread safe mem

YimingWu 2 months ago
parent
commit
c3b2e7799e
4 changed files with 126 additions and 9 deletions
  1. 106 8
      la_audio.c
  2. 11 0
      la_interface.h
  3. 2 0
      la_kernel.c
  4. 7 1
      la_util.c

+ 106 - 8
la_audio.c

@@ -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);
+    //laSplitColumn(uil,cl,0.4); cll=laLeftColumn(cl,3); clr=laRightColumn(cl,0);
+    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);
+    //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);
 }

+ 11 - 0
la_interface.h

@@ -477,6 +477,7 @@ STRUCTURE(LA){
     laUiDefineFunc AboutAuthor;
     const char* MenuProgramName;
 
+    SYSLOCK MemLock;
     laHash256 GlobalMemPool;
     int ByteCount;
     int TotalByteCount;
@@ -1925,6 +1926,13 @@ STRUCTURE(laSynthNodeDriver){
     laSynth* Target;
 };
 
+STRUCTURE(laSynthNodeTrigger){
+    laBaseNode Base;
+    laNodeInSocket* Prev; laNodeOutSocket* Next;
+    laNodeInSocket* InTrigger; int Triggered,TriggerEdge,iTrigger;
+    laSynth* Target;
+};
+
 #include "miniaudio.h"
 
 STRUCTURE(laAudioChannel){
@@ -1949,6 +1957,8 @@ STRUCTURE(laAudio){
 
     laListHandle Channels;
 
+    laListHandle PolyLaterTrigger;
+
     SYSLOCK AudioStatusLock;
     ma_context MiniAudioContext;
     ma_device AudioDevice;
@@ -1985,6 +1995,7 @@ laSynth* laFindSynth(const char* Name);
 void la_SynthAddPolyInstance(laSynth* s);
 void la_SynthRemovePolyInstance(laSynth* s, laSynth* inst);
 void laSynthTriggerNew(laSynth* s);
+void laSynthTriggerLater(laSynth* s);
 
 #define LA_INPUT_CONTROLLER_NODE_MODE_BTN 0
 #define LA_INPUT_CONTROLLER_NODE_MODE_AXIS 1

+ 2 - 0
la_kernel.c

@@ -923,6 +923,8 @@ int laGetReadyWith(laInitArguments* ia){
     //signal(SIGSEGV,la_HandlerSIGSEGV);
 #endif
 
+    laSpinInit(&MAIN.MemLock);
+
     memcpy(&MAIN.InitArgs,ia,sizeof(laInitArguments));
     if(MAIN.InitArgs.GLMajor>4||MAIN.InitArgs.GLMajor<1){ MAIN.InitArgs.GLMajor=3; }
     if(MAIN.InitArgs.GLMinor<1){ MAIN.InitArgs.GLMinor=3; }

+ 7 - 1
la_util.c

@@ -1135,11 +1135,14 @@ void *memAcquireH(laMemoryPool *Handle){
 void *memAcquire_(int Size, int Hyper){
     laMemoryPool *mp;
     u8bit Buckle = Size;
+    laSpinLock(&MAIN.MemLock);
     mp = MAIN.GlobalMemPool.Entries[Buckle].pFirst;
     while (mp && (mp->NodeSize != Size || mp->Hyperlevel!=Hyper))
         mp = mp->Item.pNext;
     if (!mp) mp = memInitPool(Size, Hyper);
-    return memAcquireH(mp);
+    void* ret=memAcquireH(mp); 
+    laSpinUnlock(&MAIN.MemLock);
+    return ret;
 }
 void *memAcquireSimple(int Size){
     void *mpn = memAcquire_(Size, 0);
@@ -1174,6 +1177,8 @@ void memFree(void *Data){
     if(level==1) { mp = ((laMemNode*)head)->InPool; laDataBlockNoLongerExists(Data,&((laMemNode*)head)->Users); }
     if(level==0) { mp = ((laMemNode0*)head)->InPool; }
     laMemoryPool *mph = mp->PoolRoot;
+    
+    laSpinLock(&MAIN.MemLock);
 
     //lstRemoveItem(&mp->MemoryNodes, head);
     mp->UsedCount--;
@@ -1186,6 +1191,7 @@ void memFree(void *Data){
         lstRemoveItem(&mph->Pools, mp);
         FreeMem(mp);
     }
+    laSpinUnlock(&MAIN.MemLock);
     //if (!mph->Pools.pFirst) {
     //	mph->CountPerPool = 0;
     //	mph->NodeSize = 0;