*/}}
Browse Source

Polyphonic audio ok

YimingWu 2 months ago
parent
commit
3fb2f3bfd4
6 changed files with 135 additions and 53 deletions
  1. 111 49
      la_audio.c
  2. 9 2
      la_interface.h
  3. 1 1
      la_tns_shape.c
  4. 2 1
      la_util.c
  5. 1 0
      la_util.h
  6. 11 0
      resources/la_lualibs.cpp

+ 111 - 49
la_audio.c

@@ -143,7 +143,7 @@ int IDN_FMEval(laSynthNodeFM* n){
 }
 void IDN_FMCopy(laSynthNodeFM* new, laSynthNodeFM* old, int DoRematch){
     if(DoRematch){ LA_IDN_NEW_LINK(InFrequency) return; }
-    LA_IDN_OLD_DUPL(Out); new->Frequency=old->Frequency; new->Slow=old->Slow;
+    LA_IDN_OLD_DUPL(Out); new->Frequency=old->Frequency; new->Slow=old->Slow; new->rInfluence=old->rInfluence;
 }
 void laui_FMNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laSynthNodeFM*n=This->EndInstance;
@@ -190,7 +190,7 @@ int IDN_VCAEval(laSynthNodeVCA* n){
 }
 void IDN_VCACopy(laSynthNodeVCA* new, laSynthNodeVCA* old, int DoRematch){
     if(DoRematch){ LA_IDN_NEW_LINK(InAmp) LA_IDN_NEW_LINK(In) return; }
-    LA_IDN_OLD_DUPL(Out); new->Amp=old->Amp;
+    LA_IDN_OLD_DUPL(Out); new->Amp=old->Amp; new->rInfluence = old->rInfluence;
 }
 void laui_VCANode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laSynthNodeVCA*n=This->EndInstance;
@@ -363,6 +363,7 @@ int IDN_OutputEval(laSynthNodeOutput* n){
 }
 void IDN_OutputCopy(laSynthNodeOutput* new, laSynthNodeOutput* old, int DoRematch){
     if(DoRematch){ LA_IDN_NEW_LINK(In) return; }
+    strSafeSet(&new->SendName,SSTR(old->SendName)); new->Send=old->Send;
 }
 void laui_OutputNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laSynthNodeOutput*n=This->EndInstance;
@@ -456,7 +457,9 @@ void IDN_EnvelopeCopy(laSynthNodeEnvelope* new, laSynthNodeEnvelope* old, int Do
         LA_IDN_NEW_LINK(Sustain) LA_IDN_NEW_LINK(Release) LA_IDN_NEW_LINK(Trigger) return;
     }
     LA_IDN_OLD_DUPL(Out);
+    new->rGate=old->rGate;
     new->rAttack=old->rAttack;new->rSustain=old->rSustain;new->rDelay=old->rDelay;new->rRelease=old->rRelease;
+    new->iAttack=old->iAttack;new->iSustain=old->iSustain;new->iDelay=old->iDelay;new->iRelease=old->rRelease;
 }
 void laui_EnvelopeNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laSynthNodeEnvelope*n=This->EndInstance;
@@ -628,21 +631,40 @@ void laRebuildSynthGraphs(int current_only){
         }
     }
 }
+int laEvalSingleSynth(laSynth* ss){
+    laSynth* FirstInst=0; int any=0;
+    laSpinLock(&ss->Lock);
+    if(ss->Playing){
+        MAIN.Audio->AudioEvalSynth=ss;
+        laRunPage(ss->Page,1);
+        ss->EvalSamples += LA_SYNTH_PLEN;
+        ss->EvalTime = (real)ss->EvalSamples/MAIN.Audio->AudioSampleRate;
+        any=1;
+    }
+    FirstInst=ss->PolyphonicInstances.pFirst;
+    laSpinUnlock(&ss->Lock);
+    
+    laListHandle to_remove={0}; int count=0;
+    for(laSynth* inst=FirstInst;inst;inst=inst->Item.pNext){
+        any+=laEvalSingleSynth(inst); count++;
+        if(inst->EvalTime > ss->Length || count>ss->MaxPoly){ lstAppendPointer(&to_remove,inst); }
+    }
+
+    laSynth* inst; while(inst=lstPopPointer(&to_remove)){
+        la_SynthRemovePolyInstance(ss,inst); printf("rem\n");
+    }
+
+    return any;
+}
 int laEvalSynthGraphs(){
     laSynth* ss; int any=0;
     memset(MAIN.Audio->AudioSamples,0,sizeof(real)*LA_SYNTH_PLEN);
     for(laAudioChannel* ac=MAIN.Audio->Channels.pFirst;ac;ac=ac->Item.pNext){
         memset(ac->Samples,0,sizeof(real)*LA_SYNTH_PLEN);
     }
+
     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;
-        laRunPage(ss->Page,1);
-        ss->EvalSamples += LA_SYNTH_PLEN;
-        ss->EvalTime = (real)ss->EvalSamples/MAIN.Audio->AudioSampleRate;
-        laSpinUnlock(&ss->Lock);
-        any=1;
+        if(laEvalSingleSynth(ss)){ any=1; }
     }
 
     for(laAudioChannel* ac=MAIN.Audio->Channels.pFirst;ac;ac=ac->Item.pNext){
@@ -654,7 +676,7 @@ int laEvalSynthGraphs(){
 }
 
 void laaudio_DataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount){
-    float* out=pOutput; int any=1; int Paused;
+    float* out=pOutput; int Paused; int any=0;
     laSpinLock(&MAIN.Audio->AudioStatusLock);
     Paused = MAIN.Audio->Paused;
     laSpinUnlock(&MAIN.Audio->AudioStatusLock);
@@ -664,17 +686,11 @@ void laaudio_DataCallback(ma_device* pDevice, void* pOutput, const void* pInput,
     for(int i=0;i<frameCount;i++){
         if(MAIN.Audio->NextAudioSample>=LA_SYNTH_PLEN){
             MAIN.Audio->NextAudioSample=0;
-            if(laEvalSynthGraphs()){
-                MAIN.Audio->AudioEvalTotalSamples+=LA_SYNTH_PLEN;
-                MAIN.Audio->AudioEvalTime=(real)MAIN.Audio->AudioEvalTotalSamples/MAIN.Audio->AudioSampleRate;
-            }else{ any=0;}
+            if(laEvalSynthGraphs()){ any=1; }
         }
         out[i]=MAIN.Audio->AudioSamples[MAIN.Audio->NextAudioSample]/10;
         MAIN.Audio->NextAudioSample++;
     }
-    laSpinLock(&MAIN.Audio->AudioStatusLock);
-    MAIN.Audio->AudioAny=any;
-    laSpinUnlock(&MAIN.Audio->AudioStatusLock);
 }
 
 
@@ -764,14 +780,21 @@ void laResetAudio(){
 
 // Operations
 
-int OPINV_laRefreshAudioDevices(laOperator* a, laEvent* e){
+int OPINV_RefreshAudioDevices(laOperator* a, laEvent* e){
     laRefreshAudioDevices(); return LA_FINISHED;
 }
 
+laSynth* laFindSynth(const char* Name){
+    for(laSynth* ss=MAIN.Audio->Synths.pFirst;ss;ss=ss->Item.pNext){
+        if(strSame(SSTR(ss->Name),Name)) return ss;
+    }
+    return 0;
+}
 laSynth* laNewSynth(char* Name){
     laSynth* ss=memAcquireHyper(sizeof(laSynth));
     ss->Page=memAcquire(sizeof(laRackPage));
     ss->Page->RackType=LA_RACK_TYPE_AUDIO;
+    ss->Length=5; ss->MaxPoly=8;
     strSafeSet(&ss->Name,Name);
     lstAppendItem(&MAIN.Audio->Synths,ss);
     memAssignRef(MAIN.Audio,&MAIN.Audio->CurrentSynth,ss);
@@ -814,12 +837,11 @@ void laui_RemoveSynth(laUiList *uil, laPropPack *This, laPropPack *Extra, laColu
     laShowItemFull(uil,c,This,"remove_synth",0,"confirm=true;text=Confirm",0,0);
 }
 
-int OPINV_laSynthPlay(laOperator* a, laEvent* e){
+int OPINV_SynthPlay(laOperator* a, laEvent* e){
     laSynth* ss=a->This?a->This->EndInstance:0; if(!ss)return LA_CANCELED;
 
     char* str=strGetArgumentString(a->ExtraInstructionsP, "mode");
     int play=0; if(str && strSame(str,"PLAY")) play=1;
-    if(play){ MAIN.Audio->AudioAny=1; }
 
     laRebuildPageEval(ss->Page);
 
@@ -830,6 +852,39 @@ int OPINV_laSynthPlay(laOperator* a, laEvent* e){
     if(play){ laStartAudio(); } laNotifyInstanceUsers(ss);
     return LA_FINISHED;
 }
+int OPINV_SynthTriggerNew(laOperator* a, laEvent* e){
+    laSynth* ss=a->This?a->This->EndInstance:0; if(!ss)return LA_CANCELED;
+    laSynthTriggerNew(ss);
+    laStartAudio();
+    return LA_FINISHED;
+}
+
+void la_SynthAddPolyInstance(laSynth* s){
+    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);
+}
+void la_SynthRemovePolyInstance(laSynth* s, laSynth* inst){
+    laSpinLock(&inst->Lock);
+    while(inst->Page->Racks.pFirst){ laDestroyRack(inst->Page->Racks.pFirst); } memFree(inst->Page); //ss->Page=0;
+    laSpinUnlock(&inst->Lock);
+    laSpinDestroy(&inst->Lock);
+
+    laSpinLock(&s->Lock);
+    lstRemoveItem(&s->PolyphonicInstances,inst);
+    s->PolyCount--;
+    laSpinUnlock(&s->Lock);
+    memFree(inst);
+}
+void laSynthTriggerNew(laSynth* s){
+    if(!s->Poly){ return; } la_SynthAddPolyInstance(s);
+}
 
 laAudioChannel* laNewAudioChannel(char* Name){
     laAudioChannel* ac=memAcquireHyper(sizeof(laAudioChannel)); lstAppendItem(&MAIN.Audio->Channels,ac);
@@ -883,29 +938,35 @@ void laui_Synthersizers(laUiList *uil, laPropPack *This, laPropPack *Extra, laCo
     laShowItemFull(uil,cl,Extra,"detached",0,0,0,0)->Flags|=LA_UI_FLAGS_HIGHLIGHT|LA_UI_FLAGS_ICON;
 
 #define ADD_PAGE \
-    laUiItem* b=laBeginRow(uil,crl,0,0);\
-    laShowItemFull(uil,crl,&rb->PP,"",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0)\
-        ->Flags|=LA_UI_COLLECTION_SIMPLE_SELECTOR;\
-    laUiItem* b3=laOnConditionThat(uil,cr,laPropExpression(&rb->PP,""));{\
-        laShowItem(uil,crl,&rb->PP,"name");\
-        laShowItem(uil,crl,0,"LA_new_synth")->Flags|=LA_UI_FLAGS_ICON;\
-        laShowSeparator(uil,crl);\
-        laShowItem(uil,crl,&rb->PP,"remove_synth")->Flags|=LA_UI_FLAGS_ICON;\
-        laShowSeparator(uil,crl)->Expand=1;\
-    }laElse(uil,b3);{\
-        laShowItem(uil,crl,0,"LA_new_synth");\
-    }laEndCondition(uil,b3);\
-    laEndRow(uil,b);\
-    b3=laOnConditionThat(uil,cr,laPropExpression(&rb->PP,""));{\
-        laUiItem* b4=laOnConditionThat(uil,crr,laPropExpression(&rb->PP,"playing"));{\
+    laUiItem* b=laBeginRow(uil,crl,0,0); \
+    laShowItemFull(uil,crl,&rb->PP,"",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0) \
+        ->Flags|=LA_UI_COLLECTION_SIMPLE_SELECTOR; \
+    laUiItem* b3=laOnConditionThat(uil,cr,laPropExpression(&rb->PP,""));{ \
+        laShowItem(uil,crl,&rb->PP,"name"); \
+        laShowItem(uil,crl,0,"LA_new_synth")->Flags|=LA_UI_FLAGS_ICON; \
+        laShowSeparator(uil,crl); \
+        laShowItem(uil,crl,&rb->PP,"remove_synth")->Flags|=LA_UI_FLAGS_ICON; \
+        laShowSeparator(uil,crl)->Expand=1; \
+        laUiItem* b4=laOnConditionThat(uil,cr,laPropExpression(&rb->PP,"poly"));{ \
+            laShowItem(uil,crl,&rb->PP,"max_poly"); \
+            laShowItem(uil,crl,&rb->PP,"length"); \
+            laShowItemFull(uil,crl,&rb->PP,"trigger",0,"icon=▶;",0,0)->Flags|=LA_UI_FLAGS_ICON; \
+        }laEndCondition(uil,b4); \
+        laUiItem* poly=laShowItem(uil,crl,&rb->PP,"poly");poly->Flags|=LA_UI_FLAGS_EXPAND; \
+    }laElse(uil,b3);{ \
+        laShowItem(uil,crl,0,"LA_new_synth"); \
+    }laEndCondition(uil,b3); \
+    laEndRow(uil,b); \
+    b3=laOnConditionThat(uil,cr,laPropExpression(&rb->PP,""));{ \
+        laUiItem* b4=laOnConditionThat(uil,crr,laPropExpression(&rb->PP,"playing"));{ \
             laShowItemFull(uil,crr,&rb->PP,"play",0,"text=❚❚;",0,0) \
                 ->Flags|=LA_UI_FLAGS_EXIT_WHEN_TRIGGERED|LA_TEXT_ALIGN_CENTER; \
-        }laElse(uil,b4);{\
+        }laElse(uil,b4);{ \
             laShowItemFull(uil,crr,&rb->PP,"play",0,"icon=▶;mode=PLAY;",0,0) \
                 ->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED; \
-        }laEndCondition(uil,b4);\
-        laShowItemFull(uil,c,&rb->PP,"page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0)->Flags|=LA_UI_FLAGS_NO_DECAL;\
-    }laEndCondition(uil,b3);\
+        }laEndCondition(uil,b4); \
+        laShowItemFull(uil,c,&rb->PP,"page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0)->Flags|=LA_UI_FLAGS_NO_DECAL; \
+    }laEndCondition(uil,b3); \
 
     laUiItem* b1=laOnConditionThat(uil,cr,laPropExpression(Extra,"detached"));{
         laUiItem* rb=laShowInvisibleItem(uil,cr,Extra,"page");
@@ -989,13 +1050,6 @@ void laset_AudioChannelMove(laAudioChannel* ac, int move){
 
 void la_AudioPreFrame(){
     if(MAIN.GraphNeedsRebuild){ laRebuildSynthGraphs(1); }
-    
-    int anyaudio=0;
-    laSpinLock(&MAIN.Audio->AudioStatusLock);
-    anyaudio=MAIN.Audio->AudioAny;
-    laSpinUnlock(&MAIN.Audio->AudioStatusLock);
-    //if(anyaudio){ ma_device_start(&MAIN.Audio->AudioDevice); }
-    //else{ ma_device_stop(&MAIN.Audio->AudioDevice); }
 }
 
 void laInitAudio(){
@@ -1009,8 +1063,9 @@ void laInitAudio(){
     laCreateOperatorType("LA_new_synth", "New Synthesizer", "Add a synthesizer",0,0,0,OPINV_ShedNewSynth,0,'+',0);
     laCreateOperatorType("LA_remove_synth", "Remove Synthesizer", "Remove a synthesizer",OPCHK_IslaSynth,0,0,OPINV_ShedRemoveSynth,OPMOD_FinishOnData,L'🗴',0)
         ->UiDefine=laui_RemoveSynth;
-    laCreateOperatorType("LA_synth_play", "Play Synthesizer", "Play a synthesizer",OPCHK_IslaSynth,0,0,OPINV_laSynthPlay,0,0,0);
-    laCreateOperatorType("LA_refresh_audio_devices", "Refresh Audio Devices", "Enumerate audio devices for selection",0,0,0,OPINV_laRefreshAudioDevices,0,U'🗘',0);
+    laCreateOperatorType("LA_synth_play", "Play Synthesizer", "Play a synthesizer",OPCHK_IslaSynth,0,0,OPINV_SynthPlay,0,0,0);
+    laCreateOperatorType("LA_synth_trigger_new", "Trigger New", "Trigger a new polyphonic note",OPCHK_IslaSynth,0,0,OPINV_SynthTriggerNew,0,0,0);
+    laCreateOperatorType("LA_refresh_audio_devices", "Refresh Audio Devices", "Enumerate audio devices for selection",0,0,0,OPINV_RefreshAudioDevices,0,U'🗘',0);
 
     laCreateOperatorType("LA_new_channel", "New Channel", "Add an audio channel",0,0,0,OPINV_ShedNewAudioChannel,0,'+',0);
     laCreateOperatorType("LA_remove_channel", "Remove Channel", "Remove an audio channel",OPCHK_IslaAudioChannel,0,0,OPINV_ShedRemoveAudioChannel,OPMOD_FinishOnData,L'🗴',0)
@@ -1027,8 +1082,15 @@ void laInitAudio(){
         p=laAddEnumProperty(pc,"playing","Playing","Synth is playing",0,0,0,0,0,-1,laget_SynthPlaying,0,0,0,0,0,0,0,0,LA_READ_ONLY);
         laAddEnumItemAs(p,"IDLE","Idle","Synth is not playing",0,0);
         laAddEnumItemAs(p,"PLAYING","Playing","Synth is playing",1,0);
+        laAddFloatProperty(pc,"length","Length","Length of this sound",0,0,"s",10,0,0,5,0,offsetof(laSynth,Length),0,0,0,0,0,0,0,0,0,0,0);
+        p=laAddEnumProperty(pc,"poly","Polyphonic","Whether this synth supports polyphonic triggering",0,0,0,0,0,offsetof(laSynth,Poly),0,0,0,0,0,0,0,0,0,0);
+        p->ElementBytes=2;
+        laAddEnumItemAs(p,"MONO","Mono","Synth is monophonic",0,0);
+        laAddEnumItemAs(p,"POLY","Poly","Synth is polyphonic",1,0);
+        laAddIntProperty(pc,"max_poly","Max Poly","Max polyphonic sound count",0,0,0,16,0,0,0,0,offsetof(laSynth,MaxPoly),0,0,0,0,0,0,0,0,0,0,0)->ElementBytes=2;
         laAddOperatorProperty(pc,"remove_synth","Remove Synth", "Remove synth", "LA_remove_synth", L'🗴', 0);
         laAddOperatorProperty(pc,"play","Play Synth", "Play synth", "LA_synth_play", 0, 0);
+        laAddOperatorProperty(pc,"trigger","Trigger", "Trigger a new polyphonic node", "LA_synth_trigger_new", 0, 0);
     }
 
     pc = laAddPropertyContainer("la_audio_channel", "LaGUI Audio Channel", "LaGUI Audio Channel", 0,0,sizeof(laAudioChannel),0,0,2);{

+ 9 - 2
la_interface.h

@@ -1810,11 +1810,15 @@ STRUCTURE(laSynth){
     laSafeString* Name;
     laRackPage* Page;
     real Length;
+    u16bit Poly,MaxPoly;
 
     SYSLOCK Lock;
     int Playing;
     real EvalTime;
     int EvalSamples;
+
+    int PolyCount;
+    laListHandle PolyphonicInstances;
 };
 
 STRUCTURE(laSynthNodeInput){
@@ -1925,7 +1929,6 @@ STRUCTURE(laAudioDevice){
 };
 
 STRUCTURE(laAudio){
-    int AudioAny;
     int Paused;
     
     laListHandle Synths;
@@ -1940,7 +1943,6 @@ STRUCTURE(laAudio){
     laListHandle AudioDevices;
     laSynth* AudioEvalSynth;
     real AudioFrameInterval;
-    real AudioEvalTime; uint64_t AudioEvalTotalSamples;
     real* AudioSamples; int NextAudioSample,AudioSampleRate;
 };
 
@@ -1966,6 +1968,11 @@ void laPauseAudio();
 void laStopAudio();
 void laResetAudio();
 
+laSynth* laFindSynth(const char* Name);
+void la_SynthAddPolyInstance(laSynth* s);
+void la_SynthRemovePolyInstance(laSynth* s, laSynth* inst);
+void laSynthTriggerNew(laSynth* s);
+
 #define LA_INPUT_CONTROLLER_NODE_MODE_BTN 0
 #define LA_INPUT_CONTROLLER_NODE_MODE_AXIS 1
 

+ 1 - 1
la_tns_shape.c

@@ -405,7 +405,7 @@ int OPCHK_IsAnyPointSelected(laPropPack *This, laStringSplitor *ss){
     if(This && This->EndInstance){
         laCanvasExtra* ex=This->EndInstance; laUiItem* ui=ex->ParentUi;
         tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0;
-        if(root->Active->Type!=TNS_OBJECT_SHAPE) return 0;
+        if(root->Active && root->Active->Type!=TNS_OBJECT_SHAPE) return 0;
         tnsShapeObject* so=root->Active; return tnsShapeAnySelected(so);
     }
     return 0;

+ 2 - 1
la_util.c

@@ -2404,7 +2404,8 @@ void la_luaLoadLibs(lua_State *L){
     
     luaL_openlibs(L);
     lua_register(L,"log",lalua_Log);
-    if(luaL_loadstring(L, LA_LUA_LIB_COMMON) || lua_pcall(L, 0, 0, 0)){ logPrint("    Error loading lagui lua libs\n"); };
+    if(luaL_loadstring(L, LA_LUA_LIB_COMMON) || lua_pcall(L, 0, 0, 0)){ logPrint("    Error loading lagui lua libs.\n"); };
+    if(luaL_loadstring(L, LA_LUA_LIB_AUDIO) || lua_pcall(L, 0, 0, 0)){ logPrint("    Error loading lua libs for audio.\n"); };
 
     lua_gc(L, LUA_GCRESTART, -1);
 }

+ 1 - 0
la_util.h

@@ -89,6 +89,7 @@ extern "C"{
 #endif
 
 extern const char* LA_LUA_LIB_COMMON;
+extern const char* LA_LUA_LIB_AUDIO;
 
 #ifdef __cplusplus
 }

+ 11 - 0
resources/la_lualibs.cpp

@@ -55,4 +55,15 @@ function tdump(t)
 end
 
 ffi = require("ffi")
+)";
+
+
+extern "C" const char* LA_LUA_LIB_AUDIO=R"(
+ffi.cdef[[
+
+typedef struct laSynth laSynth;
+
+laSynth* laFindSynth(const char* Name);
+void laSynthTriggerNew(laSynth* s);
+]]
 )";