|
@@ -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);{
|