|
@@ -37,6 +37,7 @@ laBaseNodeType LA_IDN_OUTPUT;
|
|
|
laBaseNodeType LA_IDN_SCOPE;
|
|
|
laBaseNodeType LA_IDN_ENVELOPE;
|
|
|
laBaseNodeType LA_IDN_QUANTIZE;
|
|
|
+laBaseNodeType LA_IDN_SYNTH_DRIVER;
|
|
|
|
|
|
laPropContainer* LA_PC_IDN_INPUT;
|
|
|
laPropContainer* LA_PC_IDN_FM;
|
|
@@ -46,6 +47,7 @@ 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;
|
|
|
|
|
|
#define _2PI 6.283185307
|
|
|
|
|
@@ -546,6 +548,72 @@ void laui_QuantizeNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laCol
|
|
|
laEndRow(uil,b);
|
|
|
}
|
|
|
|
|
|
+void IDN_SynthDriverInit(laSynthNodeDriver* n, int NoCreate){
|
|
|
+ if(NoCreate){ return; }
|
|
|
+ n->Prev=laCreateInSocket("PREV",0); n->Next=laCreateOutSocket(n,"NEXT",0);
|
|
|
+ n->InTransport=laCreateInSocket("TRANSPORT",LA_PROP_FLOAT|LA_PROP_ARRAY);
|
|
|
+ n->InReset=laCreateInSocket("RESET",LA_PROP_FLOAT|LA_PROP_ARRAY);
|
|
|
+ strSafeSet(&n->Base.Name,"Sound Player");
|
|
|
+}
|
|
|
+void IDN_SynthDriverDestroy(laSynthNodeDriver* n){
|
|
|
+ laDestroyInSocket(n->InTransport); laDestroyInSocket(n->InReset); strSafeDestroy(&n->Base.Name);
|
|
|
+ laDestroyInSocket(n->Prev); laDestroyOutSocket(n->Next);
|
|
|
+}
|
|
|
+int IDN_SynthDriverVisit(laSynthNodeDriver* 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->InTransport)){ laBaseNode*bn=n->InTransport->Source->Parent; LA_VISIT_NODE(bn,vi); }
|
|
|
+ if(LA_SRC_AND_PARENT(n->InReset)){ laBaseNode*bn=n->InReset->Source->Parent; LA_VISIT_NODE(bn,vi); }
|
|
|
+ LA_ADD_THIS_NODE(n,vi);
|
|
|
+ return LA_DAG_FLAG_PERM;
|
|
|
+}
|
|
|
+int IDN_SynthDriverEval(laSynthNodeDriver* n){
|
|
|
+ laSynth*ss=n->Target; if(!ss){ return 0; }
|
|
|
+ int trans=n->iTransport; LA_GET_SRC_AS_VALUE(trans,n->InTransport);
|
|
|
+ int res=n->iReset; LA_GET_SRC_AS_VALUE(res,n->InReset); int dif=0;
|
|
|
+ if(n->AlreadyReset && res==0){ n->AlreadyReset=0; res=0; }
|
|
|
+ elif(!n->AlreadyReset && res!=0){ n->AlreadyReset=1; }
|
|
|
+ else{ res=0; }
|
|
|
+
|
|
|
+ laSpinLock(&ss->Lock);
|
|
|
+ if(ss->Playing!=trans){ dif=1; }
|
|
|
+ ss->Playing = trans; if(res){ ss->EvalSamples=0; ss->EvalTime=0; }
|
|
|
+ laSpinUnlock(&ss->Lock);
|
|
|
+
|
|
|
+ if(dif){ laNotifyInstanceUsers(ss); }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+void IDN_SynthDriverCopy(laSynthNodeDriver* new, laSynthNodeDriver* old, int DoRematch){
|
|
|
+ if(DoRematch){ LA_IDN_NEW_LINK(InTransport); LA_IDN_NEW_LINK(InReset); return; }
|
|
|
+ new->iTransport = old->iTransport; new->iReset = old->iReset;
|
|
|
+ memAssignRef(new,&new->Target,old->Target);
|
|
|
+}
|
|
|
+void laui_SynthDriverNode(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);
|
|
|
+ laShowNodeSocket(uil,cl,This,"in_transport",0); b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"in_transport.source")));{
|
|
|
+ laShowItemFull(uil,cl,This,"transport",0,"text=Transport",0,0)->Expand=1;
|
|
|
+ }laElse(uil,b2);{
|
|
|
+ laShowLabel(uil,cl,"Transport",0,0)->Expand=1;
|
|
|
+ }laEndCondition(uil,b2);
|
|
|
+ laShowNodeSocket(uil,cl,This,"in_reset",0); b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"in_reset.source")));{
|
|
|
+ laUiItem* u=laShowItemFull(uil,cl,This,"reset",0,"text=Reset",0,0);u->Expand=1;u->Flags|=LA_UI_FLAGS_MOMENTARY;
|
|
|
+ }laElse(uil,b2);{
|
|
|
+ laShowLabel(uil,cl,"Reset",0,0)->Expand=1;
|
|
|
+ }laEndCondition(uil,b2);
|
|
|
+ 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){
|
|
@@ -586,7 +654,13 @@ int laEvalSynthGraphs(){
|
|
|
}
|
|
|
|
|
|
void laaudio_DataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount){
|
|
|
- float* out=pOutput; int any=1;
|
|
|
+ float* out=pOutput; int any=1; int Paused;
|
|
|
+ laSpinLock(&MAIN.Audio->AudioStatusLock);
|
|
|
+ Paused = MAIN.Audio->Paused;
|
|
|
+ laSpinUnlock(&MAIN.Audio->AudioStatusLock);
|
|
|
+
|
|
|
+ if(Paused){ memset(out,0,sizeof(float)*LA_SYNTH_PLEN); return; }
|
|
|
+
|
|
|
for(int i=0;i<frameCount;i++){
|
|
|
if(MAIN.Audio->NextAudioSample>=LA_SYNTH_PLEN){
|
|
|
MAIN.Audio->NextAudioSample=0;
|
|
@@ -653,11 +727,11 @@ void laInitMiniAudio(){
|
|
|
}
|
|
|
|
|
|
laRefreshAudioDevices();
|
|
|
- la_SelectAudioDevice(MAIN.Audio->AudioDevices.pFirst);
|
|
|
|
|
|
- INITPACKET(MAIN.Audio->AudioSamples);
|
|
|
laSpinInit(&MAIN.Audio->AudioStatusLock);
|
|
|
+ INITPACKET(MAIN.Audio->AudioSamples);
|
|
|
|
|
|
+ la_SelectAudioDevice(MAIN.Audio->AudioDevices.pFirst);
|
|
|
}
|
|
|
void laDeinitAudio(){
|
|
|
laSpinDestroy(&MAIN.Audio->AudioStatusLock);
|
|
@@ -666,6 +740,28 @@ void laDeinitAudio(){
|
|
|
ma_context_uninit(&MAIN.Audio->MiniAudioContext);
|
|
|
}
|
|
|
|
|
|
+void laStartAudio(){
|
|
|
+ laSpinLock(&MAIN.Audio->AudioStatusLock);
|
|
|
+ MAIN.Audio->Paused = 0;
|
|
|
+ laSpinUnlock(&MAIN.Audio->AudioStatusLock);
|
|
|
+ ma_device_start(&MAIN.Audio->AudioDevice);
|
|
|
+}
|
|
|
+void laStopAudio(){
|
|
|
+ ma_device_start(&MAIN.Audio->AudioDevice);
|
|
|
+}
|
|
|
+void laPauseAudio(){
|
|
|
+ laSpinLock(&MAIN.Audio->AudioStatusLock);
|
|
|
+ MAIN.Audio->Paused = 1;
|
|
|
+ laSpinUnlock(&MAIN.Audio->AudioStatusLock);
|
|
|
+}
|
|
|
+void laResetAudio(){
|
|
|
+ for(laSynth* s=MAIN.Audio->Synths.pFirst;s;s=s->Item.pNext){
|
|
|
+ laSpinLock(&s->Lock);
|
|
|
+ s->EvalTime = 0; s->EvalSamples = 0; s->Playing = 0;
|
|
|
+ laSpinUnlock(&s->Lock);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// Operations
|
|
|
|
|
|
int OPINV_laRefreshAudioDevices(laOperator* a, laEvent* e){
|
|
@@ -731,7 +827,7 @@ int OPINV_laSynthPlay(laOperator* a, laEvent* e){
|
|
|
ss->Playing=play; if(!play){ ss->EvalSamples=0; ss->EvalTime=0; }
|
|
|
laSpinUnlock(&ss->Lock);
|
|
|
|
|
|
- ma_device_start(&MAIN.Audio->AudioDevice); laNotifyInstanceUsers(ss);
|
|
|
+ if(play){ laStartAudio(); } laNotifyInstanceUsers(ss);
|
|
|
return LA_FINISHED;
|
|
|
}
|
|
|
|
|
@@ -898,7 +994,8 @@ void la_AudioPreFrame(){
|
|
|
laSpinLock(&MAIN.Audio->AudioStatusLock);
|
|
|
anyaudio=MAIN.Audio->AudioAny;
|
|
|
laSpinUnlock(&MAIN.Audio->AudioStatusLock);
|
|
|
- if(!anyaudio){ ma_device_stop(&MAIN.Audio->AudioDevice); }
|
|
|
+ //if(anyaudio){ ma_device_start(&MAIN.Audio->AudioDevice); }
|
|
|
+ //else{ ma_device_stop(&MAIN.Audio->AudioDevice); }
|
|
|
}
|
|
|
|
|
|
void laInitAudio(){
|
|
@@ -1030,6 +1127,22 @@ void laInitAudio(){
|
|
|
laAddEnumItemAs(p,"IDLE","Idle","Trigger idle",0,0);
|
|
|
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);
|
|
|
+ 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);
|
|
|
+ laAddSubGroup(pc,"in_transport", "In Transport","Transport input","la_in_socket",0,0,0,offsetof(laSynthNodeDriver,InTransport),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
|
+ laAddSubGroup(pc,"in_reset", "In Reset","Reset input (trigger on rising edge)","la_in_socket",0,0,0,offsetof(laSynthNodeDriver,InReset),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
|
+ p=laAddEnumProperty(pc,"transport","Transport","Play or stop the synth playing",LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laSynthNodeDriver,iTransport),0,0,0,0,0,0,0,0,0,0);
|
|
|
+ laAddEnumItemAs(p,"STOPPED","Stopped","Synth is stopped",0,0);
|
|
|
+ laAddEnumItemAs(p,"PLAYING","Playing","Synth is playing",1,0);
|
|
|
+ p=laAddEnumProperty(pc,"reset","Reset","Reset Synth",LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laSynthNodeDriver,iReset),0,0,0,0,0,0,0,0,0,0);
|
|
|
+ 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_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);
|
|
|
laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
|
|
@@ -1048,12 +1161,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_NODE_CATEGORY_SYNTHESIZER=laAddNodeCategory("OSC",0,LA_RACK_TYPE_AUDIO);
|
|
|
- LA_NODE_CATEGORY_SYSTEM_SOUND=laAddNodeCategory("System",0,LA_RACK_TYPE_AUDIO);
|
|
|
+ LA_IDN_REGISTER("Sound Player",U'⏯',LA_IDN_SYNTH_DRIVER, LA_PC_IDN_DRIVER, IDN_SynthDriver, laSynthNodeDriver);
|
|
|
+
|
|
|
+ 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_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);
|
|
|
+
|
|
|
+ LA_NODE_CATEGORY_DRIVER=laEnsureNodeCategory("Driver",0,LA_RACK_TYPE_DRIVER);
|
|
|
+ laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_DRIVER, &LA_IDN_SYNTH_DRIVER,0);
|
|
|
}
|
|
|
|
|
|
void laAudioDummy(){
|