|
@@ -107,7 +107,7 @@ int IDN_InputEval(laSynthNodeInput* n){
|
|
|
}
|
|
|
if(ss->EvalSamples==0){ n->rTrigger[0] = 10; }else{ n->rTrigger[0]=0; }
|
|
|
n->OutTime->Data=n->rTime; n->OutTrigger->Data=n->rTrigger; n->OutTime->ArrLen=LA_SYNTH_PLEN; n->OutTrigger->ArrLen=LA_SYNTH_PLEN;
|
|
|
- n->OutL->Data=MAIN.Audio->InputSamples; n->OutR->Data=MAIN.Audio->InputSamples; n->OutL->ArrLen=LA_SYNTH_PLEN; n->OutR->ArrLen=LA_SYNTH_PLEN;
|
|
|
+ n->OutL->Data=MAIN.Audio->InputSamplesL; n->OutR->Data=MAIN.Audio->InputSamplesR; n->OutL->ArrLen=LA_SYNTH_PLEN; n->OutR->ArrLen=LA_SYNTH_PLEN;
|
|
|
return 1;
|
|
|
}
|
|
|
void IDN_InputCopy(laSynthNodeInput* new, laSynthNodeInput* old, int DoRematch){
|
|
@@ -423,14 +423,18 @@ void IDN_OutputInit(laSynthNodeOutput* n, int NoCreate){
|
|
|
if(NoCreate){
|
|
|
laAudioChannel* outchannel = laGetAudioChannel(SSTR(n->SendName));
|
|
|
if(outchannel) memAssignRef(n,&n->Send,outchannel);
|
|
|
- return; }
|
|
|
- n->In=laCreateInSocket("IN",0); strSafeSet(&n->Base.Name,"Output");
|
|
|
+ if(!n->InL) n->InL=laCreateInSocket("L/M",0);
|
|
|
+ if(!n->InR) n->InR=laCreateInSocket("R",0);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ n->InL=laCreateInSocket("L/M",0); n->InR=laCreateInSocket("R",0); strSafeSet(&n->Base.Name,"Output");
|
|
|
}
|
|
|
void IDN_OutputDestroy(laSynthNodeOutput* n){
|
|
|
- laDestroyInSocket(n->In); strSafeDestroy(&n->Base.Name);
|
|
|
+ laDestroyInSocket(n->InL); laDestroyInSocket(n->InR); strSafeDestroy(&n->Base.Name);
|
|
|
}
|
|
|
int IDN_OutputVisit(laSynthNodeOutput* n, laNodeVisitInfo* vi){
|
|
|
- if(LA_SRC_AND_PARENT(n->In)){ laBaseNode*bn=n->In->Source->Parent; LA_VISIT_NODE(bn,vi); }
|
|
|
+ if(LA_SRC_AND_PARENT(n->InL)){ laBaseNode*bn=n->InL->Source->Parent; LA_VISIT_NODE(bn,vi); }
|
|
|
+ if(LA_SRC_AND_PARENT(n->InR)){ laBaseNode*bn=n->InR->Source->Parent; LA_VISIT_NODE(bn,vi); }
|
|
|
LA_ADD_THIS_NODE(n,vi);
|
|
|
laAudioChannel* outchannel = laGetAudioChannel(SSTR(n->SendName));
|
|
|
memAssignRef(n,&n->Send,outchannel);
|
|
@@ -438,15 +442,21 @@ int IDN_OutputVisit(laSynthNodeOutput* n, laNodeVisitInfo* vi){
|
|
|
}
|
|
|
int IDN_OutputEval(laSynthNodeOutput* n){
|
|
|
if(!n->Send)return 1;
|
|
|
- real* input; INPUTPACKET(input,n->In); if(!input) return 1;
|
|
|
+ real* inputL; INPUTPACKET(inputL,n->InL); real* inputR; INPUTPACKET(inputR,n->InR); if(!inputL && !inputR) return 1;
|
|
|
real Samples[LA_SYNTH_PLEN];
|
|
|
- for(int i=0;i<LA_SYNTH_PLEN;i++){
|
|
|
- n->Send->Samples[i] += input[i];
|
|
|
+ if(inputL){
|
|
|
+ for(int i=0;i<LA_SYNTH_PLEN;i++){ n->Send->SamplesL[i] += inputL[i]; }
|
|
|
+ if(n->Send){ n->Send->IsMono=1; }
|
|
|
+ }
|
|
|
+ if(inputR){
|
|
|
+ for(int i=0;i<LA_SYNTH_PLEN;i++){ n->Send->SamplesR[i] += inputR[i]; }
|
|
|
+ if(n->Send){ n->Send->IsMono=0; }
|
|
|
}
|
|
|
return 1;
|
|
|
}
|
|
|
void IDN_OutputCopy(laSynthNodeOutput* new, laSynthNodeOutput* old, int DoRematch){
|
|
|
- if(DoRematch){ LA_IDN_NEW_LINK(In) return; }
|
|
|
+ if(DoRematch){ LA_IDN_NEW_LINK(InL) return; }
|
|
|
+ if(DoRematch){ LA_IDN_NEW_LINK(InR) return; }
|
|
|
strSafeSet(&new->SendName,SSTR(old->SendName)); new->Send=old->Send;
|
|
|
}
|
|
|
void laui_OutputNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
|
|
@@ -455,7 +465,8 @@ void laui_OutputNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColum
|
|
|
laColumn* cl,*cr; laSplitColumn(uil,c,0.3); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
|
|
|
|
|
|
laUiItem* b=laBeginRow(uil,c,0,0);
|
|
|
- laShowNodeSocket(uil,c,This,"in",0)->Flags|=LA_UI_SOCKET_LABEL_E;
|
|
|
+ laShowNodeSocket(uil,c,This,"in_l",0)->Flags|=LA_UI_SOCKET_LABEL_E;
|
|
|
+ laShowNodeSocket(uil,c,This,"in_r",0)->Flags|=LA_UI_SOCKET_LABEL_E;
|
|
|
laShowLabel(uil,c,"🕪",0,0);
|
|
|
laShowItemFull(uil,c,This,"send_channel_selector",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0)->Flags|=LA_UI_COLLECTION_SIMPLE_SELECTOR;
|
|
|
laShowItem(uil,c,This,"send_channel_name")->Expand = 1;
|
|
@@ -807,9 +818,11 @@ int laEvalSingleSynth(laSynth* ss){
|
|
|
}
|
|
|
int laEvalSynthGraphs(){
|
|
|
laSynth* ss; int any=0;
|
|
|
- memset(MAIN.Audio->OutputSamples,0,sizeof(real)*LA_SYNTH_PLEN);
|
|
|
+ memset(MAIN.Audio->OutputSamplesL,0,sizeof(real)*LA_SYNTH_PLEN);
|
|
|
+ memset(MAIN.Audio->OutputSamplesR,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);
|
|
|
+ memset(ac->SamplesL,0,sizeof(real)*LA_SYNTH_PLEN);
|
|
|
+ memset(ac->SamplesR,0,sizeof(real)*LA_SYNTH_PLEN);
|
|
|
}
|
|
|
|
|
|
for(ss=MAIN.Audio->Synths.pFirst;ss;ss=ss->Item.pNext){
|
|
@@ -817,8 +830,10 @@ int laEvalSynthGraphs(){
|
|
|
}
|
|
|
|
|
|
for(laAudioChannel* ac=MAIN.Audio->Channels.pFirst;ac;ac=ac->Item.pNext){
|
|
|
+ real *RSamples = ac->IsMono?ac->SamplesL:ac->SamplesR;
|
|
|
for(int i=0;i<LA_SYNTH_PLEN;i++){
|
|
|
- MAIN.Audio->OutputSamples[i] += ac->Samples[i]*(ac->Volume/10);
|
|
|
+ MAIN.Audio->OutputSamplesL[i] += ac->SamplesL[i]*(ac->Volume/10);
|
|
|
+ MAIN.Audio->OutputSamplesR[i] += RSamples[i]*(ac->Volume/10);
|
|
|
}
|
|
|
}
|
|
|
return any;
|
|
@@ -831,7 +846,8 @@ void laaudio_DataCallback(ma_device* pDevice, void* pOutput, const void* pInput,
|
|
|
laSpinUnlock(&MAIN.Audio->AudioStatusLock);
|
|
|
|
|
|
if(Paused){ memset(out,0,sizeof(float)*LA_SYNTH_PLEN);
|
|
|
- memset(MAIN.Audio->InputSamples,0,sizeof(float)*LA_SYNTH_PLEN); return;
|
|
|
+ memset(MAIN.Audio->InputSamplesL,0,sizeof(float)*LA_SYNTH_PLEN);
|
|
|
+ memset(MAIN.Audio->InputSamplesR,0,sizeof(float)*LA_SYNTH_PLEN); return;
|
|
|
}
|
|
|
|
|
|
for(int i=0;i<frameCount;i++){
|
|
@@ -839,8 +855,11 @@ void laaudio_DataCallback(ma_device* pDevice, void* pOutput, const void* pInput,
|
|
|
MAIN.Audio->NextAudioSample=0;
|
|
|
if(laEvalSynthGraphs()){ any=1; }
|
|
|
}
|
|
|
- out[i]=MAIN.Audio->OutputSamples[MAIN.Audio->NextAudioSample]/10;
|
|
|
- MAIN.Audio->InputSamples[MAIN.Audio->NextAudioSample] = in[i] * 10;
|
|
|
+ int s=i*2;
|
|
|
+ out[s]=MAIN.Audio->OutputSamplesL[MAIN.Audio->NextAudioSample]/10;
|
|
|
+ out[s+1]=MAIN.Audio->OutputSamplesR[MAIN.Audio->NextAudioSample]/10;
|
|
|
+ MAIN.Audio->InputSamplesL[MAIN.Audio->NextAudioSample] = in[s] * 10;
|
|
|
+ MAIN.Audio->InputSamplesR[MAIN.Audio->NextAudioSample] = in[s+1] * 10;
|
|
|
MAIN.Audio->NextAudioSample++;
|
|
|
}
|
|
|
}
|
|
@@ -855,14 +874,14 @@ void la_SelectAudioDevice(laAudioDevice* ad){
|
|
|
ma_device_config config = ma_device_config_init(ma_device_type_duplex);
|
|
|
config.capture.pDeviceID = ad->MiniAudioID;
|
|
|
config.capture.format = ma_format_f32;
|
|
|
- config.capture.channels = 1;
|
|
|
+ config.capture.channels = 2;
|
|
|
config.playback.pDeviceID= ad->MiniAudioID;
|
|
|
config.playback.format = ma_format_f32;
|
|
|
- config.playback.channels = 1;
|
|
|
+ config.playback.channels = 2;
|
|
|
config.sampleRate = SampleRate;
|
|
|
config.dataCallback = laaudio_DataCallback;
|
|
|
config.pUserData = 0;
|
|
|
- if (ma_device_init(NULL, &config, &MAIN.Audio->AudioDevice) != MA_SUCCESS){ return; }
|
|
|
+ if (ma_device_init(&MAIN.Audio->MiniAudioContext, &config, &MAIN.Audio->AudioDevice) != MA_SUCCESS){ return; }
|
|
|
MAIN.Audio->AudioSampleRate=SampleRate;
|
|
|
MAIN.Audio->NextAudioSample=LA_SYNTH_PLEN;
|
|
|
MAIN.Audio->AudioFrameInterval=1.0f/SampleRate;
|
|
@@ -892,6 +911,7 @@ void laRefreshAudioDevices(){
|
|
|
}
|
|
|
|
|
|
void laInitMiniAudio(){
|
|
|
+ MAIN.Audio->MiniAudioBackend=ma_backend_null;
|
|
|
|
|
|
if (ma_context_init(NULL, 0, NULL, &MAIN.Audio->MiniAudioContext) != MA_SUCCESS) {
|
|
|
printf("Can't init miniaudio context\n");
|
|
@@ -900,14 +920,18 @@ void laInitMiniAudio(){
|
|
|
laRefreshAudioDevices();
|
|
|
|
|
|
laSpinInit(&MAIN.Audio->AudioStatusLock);
|
|
|
- INITPACKET(MAIN.Audio->InputSamples);
|
|
|
- INITPACKET(MAIN.Audio->OutputSamples);
|
|
|
+ INITPACKET(MAIN.Audio->InputSamplesL);
|
|
|
+ INITPACKET(MAIN.Audio->InputSamplesR);
|
|
|
+ INITPACKET(MAIN.Audio->OutputSamplesL);
|
|
|
+ INITPACKET(MAIN.Audio->OutputSamplesR);
|
|
|
|
|
|
la_SelectAudioDevice(MAIN.Audio->AudioDevices.pFirst);
|
|
|
}
|
|
|
void laDeinitAudio(){
|
|
|
- memFree(MAIN.Audio->InputSamples);
|
|
|
- memFree(MAIN.Audio->OutputSamples);
|
|
|
+ memFree(MAIN.Audio->InputSamplesL);
|
|
|
+ memFree(MAIN.Audio->InputSamplesR);
|
|
|
+ memFree(MAIN.Audio->OutputSamplesR);
|
|
|
+ memFree(MAIN.Audio->OutputSamplesL);
|
|
|
laSpinDestroy(&MAIN.Audio->AudioStatusLock);
|
|
|
ma_device_stop(&MAIN.Audio->AudioDevice);
|
|
|
ma_device_uninit(&MAIN.Audio->AudioDevice);
|
|
@@ -921,7 +945,7 @@ void laStartAudio(){
|
|
|
ma_device_start(&MAIN.Audio->AudioDevice);
|
|
|
}
|
|
|
void laStopAudio(){
|
|
|
- ma_device_start(&MAIN.Audio->AudioDevice);
|
|
|
+ ma_device_stop(&MAIN.Audio->AudioDevice);
|
|
|
}
|
|
|
void laPauseAudio(){
|
|
|
laSpinLock(&MAIN.Audio->AudioStatusLock);
|
|
@@ -1221,6 +1245,22 @@ void laset_SynthTriggerTrigger(laSynthNodeTrigger* n, int trig){
|
|
|
if(trig){ laSynthTriggerNew(n->Target); } n->iTrigger=trig;
|
|
|
}
|
|
|
|
|
|
+void laset_MiniAudioBackend(void* unused, int backend){
|
|
|
+ laStopAudio();
|
|
|
+ ma_device_uninit(&MAIN.Audio->AudioDevice);
|
|
|
+ ma_context_uninit(&MAIN.Audio->MiniAudioContext);
|
|
|
+
|
|
|
+ MAIN.Audio->MiniAudioBackend = backend;
|
|
|
+ ma_backend _backends[1]; _backends[0] = MAIN.Audio->MiniAudioBackend; ma_backend* backends=_backends;
|
|
|
+ int numback=1;
|
|
|
+ if(backend==ma_backend_null){ numback=0; backends=0; }
|
|
|
+ if (ma_context_init(backends, numback, NULL, &MAIN.Audio->MiniAudioContext) != MA_SUCCESS) {
|
|
|
+ printf("Can't init miniaudio context\n"); MAIN.Audio->MiniAudioBackend=ma_backend_null;
|
|
|
+ }
|
|
|
+
|
|
|
+ laRefreshAudioDevices();
|
|
|
+ la_SelectAudioDevice(MAIN.Audio->AudioDevices.pFirst);
|
|
|
+}
|
|
|
|
|
|
void la_AudioPreFrame(){
|
|
|
if(MAIN.GraphNeedsRebuild){ laRebuildSynthGraphs(1); }
|
|
@@ -1351,7 +1391,8 @@ void laInitAudio(){
|
|
|
pc=laAddPropertyContainer("la_node_synth_output", "Output Node", "Sound output to system",0,laui_OutputNode,sizeof(laSynthNodeOutput),lapost_Node,0,1);
|
|
|
LA_PC_IDN_OUTPUT=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,"in", "Input","Input sound","la_in_socket",0,0,0,offsetof(laSynthNodeOutput,In),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
|
+ laAddSubGroup(pc,"in_l", "Input Left","Input sound left channel","la_in_socket",0,0,0,offsetof(laSynthNodeOutput,InL),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
|
+ laAddSubGroup(pc,"in_r", "Input Right","Input sound right channel","la_in_socket",0,0,0,offsetof(laSynthNodeOutput,InR),0,0,0,0,0,0,0,LA_UDF_SINGLE);
|
|
|
laAddStringProperty(pc,"send_channel_name","Channel Name","Channel name of this send target",0,0,0,0,1,offsetof(laSynthNodeOutput,SendName),0,0,laset_OutputSendName,0,0);
|
|
|
laAddSubGroup(pc,"send_channel_selector","Send","Send to channel","la_audio_channel",0,0,0,-1,laget_FirstAudioChannel,0,laget_ListNext,laset_OutputSendChannel,0,0,0,LA_UDF_REFER|LA_UDF_IGNORE);
|
|
|
|