*/}}

2 Commits 4a9590d172 ... e1549ee0e8

Tác giả SHA1 Thông báo Ngày
  YimingWu e1549ee0e8 Stereo audio and backend selection 11 tháng trước cách đây
  YimingWu c60b2d2e68 prevent node from stopping mid way 11 tháng trước cách đây
6 tập tin đã thay đổi với 100 bổ sung31 xóa
  1. 66 25
      la_audio.c
  2. 10 3
      la_interface.h
  3. 16 0
      resources/la_properties.c
  4. 5 1
      resources/la_templates.c
  5. 2 1
      resources/la_translations.c
  6. 1 1
      resources/la_widgets.c

+ 66 - 25
la_audio.c

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

+ 10 - 3
la_interface.h

@@ -1971,7 +1971,7 @@ NEED_STRUCTURE(laAudioChannel);
 
 STRUCTURE(laSynthNodeOutput){
     laBaseNode Base;
-    laNodeInSocket* In;
+    laNodeInSocket* InL,*InR; int IsMono;
     laSafeString* SendName;
     /* Runtime */
     laAudioChannel* Send;
@@ -2019,7 +2019,9 @@ STRUCTURE(laAudioChannel){
     laSafeString* Name;
     real Volume;
     laSafeString* OutputChannel;
-    real Samples[LA_SYNTH_PLEN];
+    real SamplesL[LA_SYNTH_PLEN];
+    real SamplesR[LA_SYNTH_PLEN];
+    int IsMono;
 };
 
 STRUCTURE(laAudioDevice){
@@ -2039,13 +2041,16 @@ STRUCTURE(laAudio){
     laListHandle PolyLaterTrigger;
 
     SYSLOCK AudioStatusLock;
+    ma_backend MiniAudioBackend;
     ma_context MiniAudioContext;
     ma_device AudioDevice;
     laAudioDevice* UsingDevice;
     laListHandle AudioDevices;
     laSynth* AudioEvalSynth;
     real AudioFrameInterval;
-    real* InputSamples,*OutputSamples; int NextAudioSample,AudioSampleRate;//,NextInputSample;
+    real* InputSamplesL,*InputSamplesR;
+    real *OutputSamplesL,*OutputSamplesR;
+    int NextAudioSample,AudioSampleRate;//,NextInputSample;
 };
 
 
@@ -2061,6 +2066,8 @@ laAudioChannel* laGetAudioChannel(char* Name);
 
 void la_AudioPreFrame();
 
+void laset_MiniAudioBackend(void* unused, int backend);
+
 void laRebuildSynthGraphs(int current_only);
 void laInitAudio();
 void laDeinitAudio();

+ 16 - 0
resources/la_properties.c

@@ -1595,6 +1595,22 @@ void la_RegisterInternalProps(){
                 laAddSubGroup(p,"audio_devices","Audio Devices","Enumerated audio devices","la_audio_device",0,0,0,-1,0,0,0,0,0,0,offsetof(laAudio,AudioDevices),LA_UDF_IGNORE);
                 laAddSubGroup(p, "current_audio_device", "Current Audio Device", "Current audio device","la_audio_device",0,0,0,offsetof(laAudio,UsingDevice),laget_FirstAudioDevice,0,laget_ListNext,laset_CurrentAudioDevice,0,0,0,LA_UDF_REFER|LA_UDF_IGNORE);
                 laAddSubGroup(p,"channels","Channels","Audio channels","la_audio_channel",0,0,0,-1,0,0,0,0,0,0,offsetof(laAudio,Channels),0);
+                ep=laAddEnumProperty(p,"backend","Backend","Audio backend",0,0,0,ma_backend_null,0,offsetof(laAudio,MiniAudioBackend),0,laset_MiniAudioBackend,0,0,0,0,0,0,0,0);
+                laAddEnumItemAs(ep,"AUTO","Auto","Automatic backend selection",ma_backend_null,0);
+                laAddEnumItemAs(ep,"JACK","Jack","Jack backend",ma_backend_jack,0);
+#ifdef LA_LINUX
+                laAddEnumItemAs(ep,"PULSEAUDIO","Pulseaudio","Pulseaudio backend",ma_backend_pulseaudio,0);
+                laAddEnumItemAs(ep,"ALSA","Alsa","Alsa backend",ma_backend_alsa,0);
+#endif
+#ifdef _WIN32
+                laAddEnumItemAs(ep,"WASAPI","WASAPI"," WASAPI backend",ma_backend_wasapi,0);
+                laAddEnumItemAs(ep,"DIRECTSOUND","DirectSound","DirectSound backend",ma_backend_dsound, 0);
+                laAddEnumItemAs(ep,"WINMM","WinMM","WinMM backend",ma_backend_winmm,0);
+#endif
+#ifdef LAGUI_ANDROID
+                laAddEnumItemAs(ep,"AAUDIO","AAudio"," AAudio backend",ma_backend_aaudio,0);
+                laAddEnumItemAs(ep,"OpenSL ES","OpenSL ES","OpenSL ES backend",ma_backend_opensl,0);
+#endif
             }
         }
 

+ 5 - 1
resources/la_templates.c

@@ -1387,7 +1387,11 @@ void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorIn
             muil = laAddTabPage(bracket, "Audio");{
                 mc = laFirstColumn(muil); laSplitColumn(muil, mc, 0.5);
                 mcl = laLeftColumn(mc, 0); mcr = laRightColumn(mc, 0);
-                laShowLabel(muil,mc,"Audio Device:",0,0);
+                b=laBeginRow(muil,mcl,0,0);
+                laShowLabel(muil,mcl,"Backend:",0,0);
+                laShowItem(muil,mcl,0,"la.audio.backend")->Expand=1;
+                laEndRow(muil,b);
+                laShowLabel(muil,mc,"Device:",0,0);
                 laShowItemFull(muil,mcl,0,"la.audio.current_audio_device",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0);
                 laShowItem(muil,mcr,0,"LA_refresh_audio_devices");
             }

+ 2 - 1
resources/la_translations.c

@@ -231,7 +231,8 @@ static const char *entries[]={
 "Enable OpenGL Debug","启用 OpenGL 调试",
 "Synthesizers","合成器",
 "Loc:","位置:",
-"Audio Device:","音频设备:",
+"Backend:","后端:",
+"Device:","设备:",
 "OpenGL Debugging:","OpenGL 调试:",
 "Show Details","显示详细",
 "Scale","缩放",

+ 1 - 1
resources/la_widgets.c

@@ -3338,7 +3338,7 @@ int OPMOD_NodeSocket(laOperator *a, laEvent *e){
 
     if ((!uit->Dragging) && (!laIsInUiItem(ui, e->x, e->y))){ return LA_FINISHED_PASS; }
 
-    if (e->type==LA_TIME_IDLE){ return LA_RUNNING_PASS; }
+    if (e->type==LA_TIME_IDLE){ return LA_RUNNING; }
     if (e->type == LA_L_MOUSE_DOWN){
         uit->Dragging=1; MAIN.NextWireColor+=3;
         if(pc==LA_PC_SOCKET_IN)   {