*/}}
Browse Source

Input mapper

YimingWu 1 year ago
parent
commit
3ed143672a
9 changed files with 260 additions and 123 deletions
  1. 46 22
      la_audio.c
  2. 4 4
      la_controllers.c
  3. 41 14
      la_interface.h
  4. 28 8
      la_kernel.c
  5. 1 51
      resources/la_nodes_basic.c
  6. 46 0
      resources/la_operators.c
  7. 30 8
      resources/la_properties.c
  8. 36 13
      resources/la_templates.c
  9. 28 3
      resources/la_widgets.c

+ 46 - 22
la_audio.c

@@ -87,7 +87,7 @@ int IDN_FMEval(laSynthNodeFM* n){
     real f=n->Frequency, constf=0; real* inputf;
     real f=n->Frequency, constf=0; real* inputf;
     INPUTPACKET(inputf,n->InFrequency); LA_GET_SRC_AS_VALUE(constf,n->InFrequency); 
     INPUTPACKET(inputf,n->InFrequency); LA_GET_SRC_AS_VALUE(constf,n->InFrequency); 
     for(int i=0;i<LA_SYNTH_PLEN;i++){
     for(int i=0;i<LA_SYNTH_PLEN;i++){
-        real useF=inputf?(inputf[i]+f):(f+constf); if(n->Slow) useF-=10;
+        real useF=inputf?(inputf[i]*n->rInfluence+f):(f+constf); if(n->Slow) useF-=10;
         n->Phase+=FRAME_INTERVAL*VAL2FREQ(useF)*_2PI;
         n->Phase+=FRAME_INTERVAL*VAL2FREQ(useF)*_2PI;
         WRAPPHASE(n->Phase);
         WRAPPHASE(n->Phase);
         n->OutSamples[i]=sin(n->Phase)*10;
         n->OutSamples[i]=sin(n->Phase)*10;
@@ -104,8 +104,9 @@ void laui_FMNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *U
     LA_BASE_NODE_HEADER(uil,c,This);
     LA_BASE_NODE_HEADER(uil,c,This);
 
 
     laUiItem* b=laBeginRow(uil,c,0,0);
     laUiItem* b=laBeginRow(uil,c,0,0);
-    laShowNodeSocket(uil,c,This,"in_frequency",0)->Flags|=LA_UI_SOCKET_LABEL_E;
     laShowItem(uil,c,This,"slow");
     laShowItem(uil,c,This,"slow");
+    laShowNodeSocket(uil,c,This,"in_frequency",0)->Flags|=LA_UI_SOCKET_LABEL_E;
+    laShowItem(uil,c,This,"influence");
     laShowItem(uil,c,This,"frequency")->Expand=1;
     laShowItem(uil,c,This,"frequency")->Expand=1;
     laShowNodeSocket(uil,c,This,"out",0);
     laShowNodeSocket(uil,c,This,"out",0);
     laEndRow(uil,b);
     laEndRow(uil,b);
@@ -136,7 +137,7 @@ int IDN_VCAEval(laSynthNodeVCA* n){
     n->Out->Data=n->OutSamples; n->Out->ArrLen=LA_SYNTH_PLEN;
     n->Out->Data=n->OutSamples; n->Out->ArrLen=LA_SYNTH_PLEN;
     if(!input){ memset(n->OutSamples,0,sizeof(real)*LA_SYNTH_PLEN); return 0; }
     if(!input){ memset(n->OutSamples,0,sizeof(real)*LA_SYNTH_PLEN); return 0; }
     for(int i=0;i<LA_SYNTH_PLEN;i++){
     for(int i=0;i<LA_SYNTH_PLEN;i++){
-        real useA=(inputamp?(inputamp[i]/10):1)*constamp;
+        real useA=(inputamp?(inputamp[i]*n->rInfluence/10):1)*constamp;
         n->OutSamples[i]= input[i]*useA;
         n->OutSamples[i]= input[i]*useA;
     }
     }
     return 1;
     return 1;
@@ -153,6 +154,7 @@ void laui_VCANode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *
     laUiItem* b=laBeginRow(uil,c,0,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",0)->Flags|=LA_UI_SOCKET_LABEL_E;
     laShowNodeSocket(uil,c,This,"in_amp",0);
     laShowNodeSocket(uil,c,This,"in_amp",0);
+    laShowItem(uil,c,This,"influence");
     laShowItem(uil,c,This,"amp")->Expand=1;
     laShowItem(uil,c,This,"amp")->Expand=1;
     laShowNodeSocket(uil,cr,This,"out",0);
     laShowNodeSocket(uil,cr,This,"out",0);
     laEndRow(uil,b);
     laEndRow(uil,b);
@@ -324,13 +326,14 @@ void IDN_EnvelopeInit(laSynthNodeEnvelope* n, int NoCreate){
         n->Attack=laCreateInSocket("ATTACK",0); n->Delay=laCreateInSocket("DELAY",0);
         n->Attack=laCreateInSocket("ATTACK",0); n->Delay=laCreateInSocket("DELAY",0);
         n->Sustain=laCreateInSocket("SUSTAIN",0); n->Release=laCreateInSocket("RELEASE",0);
         n->Sustain=laCreateInSocket("SUSTAIN",0); n->Release=laCreateInSocket("RELEASE",0);
         n->Trigger=laCreateInSocket("TRIGGER",0); n->Out=laCreateOutSocket(n, "OUT",0);
         n->Trigger=laCreateInSocket("TRIGGER",0); n->Out=laCreateOutSocket(n, "OUT",0);
+        n->Time=20; n->rAttack=1; n->rDelay=5; n->rSustain=5; n->rRelease=7; n->rGate=5;
     }
     }
+    if(!n->Gate){ n->Gate=laCreateInSocket("GATE",0); }
     if(!n->OutSamples){ n->OutSamples=memAcquireSimple(sizeof(real)*LA_SYNTH_PLEN); }
     if(!n->OutSamples){ n->OutSamples=memAcquireSimple(sizeof(real)*LA_SYNTH_PLEN); }
     n->Out->Data = n->OutSamples; n->Out->DataType = LA_PROP_FLOAT|LA_PROP_ARRAY; n->Out->ArrLen=LA_SYNTH_PLEN;
     n->Out->Data = n->OutSamples; n->Out->DataType = LA_PROP_FLOAT|LA_PROP_ARRAY; n->Out->ArrLen=LA_SYNTH_PLEN;
-    n->Time=20; n->rAttack=1; n->rDelay=5; n->rSustain=5; n->rRelease=7;
 }
 }
 void IDN_EnvelopeDestroy(laSynthNodeEnvelope* n){
 void IDN_EnvelopeDestroy(laSynthNodeEnvelope* n){
-    laDestroyInSocket(n->Attack); laDestroyInSocket(n->Delay);
+    laDestroyInSocket(n->Attack); laDestroyInSocket(n->Delay); laDestroyInSocket(n->Gate);
     laDestroyInSocket(n->Sustain); laDestroyInSocket(n->Release);
     laDestroyInSocket(n->Sustain); laDestroyInSocket(n->Release);
     laDestroyInSocket(n->Trigger); laDestroyOutSocket(n->Out);
     laDestroyInSocket(n->Trigger); laDestroyOutSocket(n->Out);
     memFree(n->OutSamples);
     memFree(n->OutSamples);
@@ -347,16 +350,24 @@ int IDN_EnvelopeVisit(laSynthNodeEnvelope* n, laNodeVisitInfo* vi){
     return LA_DAG_FLAG_PERM;
     return LA_DAG_FLAG_PERM;
 }
 }
 int IDN_EnvelopeEval(laSynthNodeEnvelope* n){
 int IDN_EnvelopeEval(laSynthNodeEnvelope* n){
-    int trigger_at=-1; real trig=n->bTrigger*100;
+    int trigger_at=-1; real trig=n->bTrigger?n->bTrigger*100:-100;
     real* trigger; INPUTPACKET(trigger,n->Trigger); if(!trigger){ LA_GET_SRC_AS_VALUE(trig,n->Trigger); }
     real* trigger; INPUTPACKET(trigger,n->Trigger); if(!trigger){ LA_GET_SRC_AS_VALUE(trig,n->Trigger); }
+    real *att,*del,*sus,*rel,*gat;
     real attack=n->rAttack,delay=n->rDelay,sustain=n->rSustain,release=n->rRelease;
     real attack=n->rAttack,delay=n->rDelay,sustain=n->rSustain,release=n->rRelease;
-    LA_GET_SRC_AS_VALUE(attack,n->Attack); LA_GET_SRC_AS_VALUE(delay,n->Delay);
-    LA_GET_SRC_AS_VALUE(sustain,n->Sustain); LA_GET_SRC_AS_VALUE(release,n->Release);
+    real gate = n->rGate; INPUTPACKET(gat,n->Gate); if(!gat) LA_GET_SRC_AS_VALUE(gate,n->Gate);
+    INPUTPACKET(att,n->Attack); if(!att) LA_GET_SRC_AS_VALUE(attack,n->Attack);
+    INPUTPACKET(del,n->Delay); if(!del) LA_GET_SRC_AS_VALUE(delay,n->Delay);
+    INPUTPACKET(sus,n->Sustain); if(!sus) LA_GET_SRC_AS_VALUE(sustain,n->Sustain);
+    INPUTPACKET(rel,n->Release); if(!rel) LA_GET_SRC_AS_VALUE(release,n->Release);
     attack=attack?pow(attack/10,2):0; delay=delay?pow(delay/10,2):0; release=release?pow(release/10,2):0;
     attack=attack?pow(attack/10,2):0; delay=delay?pow(delay/10,2):0; release=release?pow(release/10,2):0;
     for(int i=0;i<LA_SYNTH_PLEN;i++){
     for(int i=0;i<LA_SYNTH_PLEN;i++){
-        if(trigger){ trig=trigger[i]; }
-        if(!n->Triggered){ if(trig > 5.0f){ n->Triggered = 1; n->Time = 0; n->ReleaseTime=10000; n->AtLevel=0; } }
-        else{ if(trig < 2.0f){ n->Triggered = 0; n->ReleaseTime = n->Time; } }
+        if(trigger){ trig=trigger[i]; } if(gat){ gate=gat[i]; }
+        if(att){ attack=att[i]*n->iAttack+n->rAttack; }
+        if(del){ delay=del[i]*n->iDelay+n->rDelay; }
+        if(sus){ sustain=sus[i]*n->iSustain+n->rSustain; }
+        if(rel){ release=rel[i]*n->iRelease+n->rRelease; }
+        if(!n->Triggered){ if(trig > gate){ n->Triggered = 1; n->Time = 0; n->ReleaseTime=10000; n->AtLevel=0; } }
+        else{ if(trig < gate-0.0001f){ n->Triggered = 0; n->ReleaseTime = n->Time; } }
         if(n->Time < n->ReleaseTime){
         if(n->Time < n->ReleaseTime){
             if(n->Time<=attack){
             if(n->Time<=attack){
                 n->OutSamples[i]=10.0f*pow(n->Time/attack,0.3);
                 n->OutSamples[i]=10.0f*pow(n->Time/attack,0.3);
@@ -393,18 +404,23 @@ void laui_EnvelopeNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laCol
 
 
     laUiItem* b,*b1;
     laUiItem* b,*b1;
 #define ADSR_ROW(what) \
 #define ADSR_ROW(what) \
-    b=laBeginRow(uil,c,0,0); \
+    b=laBeginRow(uil,c,0,0);{ \
     laShowNodeSocket(uil,c,This,"in_" what,0)->Flags|=LA_UI_SOCKET_LABEL_E; \
     laShowNodeSocket(uil,c,This,"in_" what,0)->Flags|=LA_UI_SOCKET_LABEL_E; \
-    b1=laOnConditionThat(uil,c,laNot(laPropExpression(This,"in_" what ".source")));{ \
-        laUiItem* value=laShowItem(uil,c,This,what);value->Expand=1; value->Flags|=LA_UI_FLAGS_KNOB;\
-    } laEndCondition(uil,b1); \
-    laEndRow(uil,b);
+    laUiItem* value=laShowItem(uil,c,This,"i" what); \
+    value=laShowItem(uil,c,This,what);value->Expand=1; \
+    }laEndRow(uil,b);
 
 
     ADSR_ROW("attack") ADSR_ROW("delay") ADSR_ROW("sustain") ADSR_ROW("release")
     ADSR_ROW("attack") ADSR_ROW("delay") ADSR_ROW("sustain") ADSR_ROW("release")
     
     
     b=laBeginRow(uil,c,0,0);
     b=laBeginRow(uil,c,0,0);
-    laShowItem(uil,c,This,"in_trigger"); laShowItem(uil,c,This,"trigger")->Flags|=LA_UI_FLAGS_MOMENTARY|LA_UI_FLAGS_HIGHLIGHT;
-    laShowSeparator(uil,c)->Expand=1; laShowItem(uil,c,This,"out")->Flags|=LA_UI_SOCKET_LABEL_W;
+    laShowItem(uil,c,This,"gate")->Expand=1;
+    laShowNodeSocket(uil,c,This,"in_gate",0)->Flags|=LA_UI_SOCKET_LABEL_W;
+    laShowNodeSocket(uil,c,This,"in_trigger",0)->Flags|=LA_UI_SOCKET_LABEL_E;
+    laEndRow(uil,b);
+    b=laBeginRow(uil,c,0,0);
+    laShowSeparator(uil,c)->Expand=1; 
+    laShowItem(uil,c,This,"trigger")->Flags|=LA_UI_FLAGS_MOMENTARY|LA_UI_FLAGS_HIGHLIGHT;
+    laShowItem(uil,c,This,"out")->Flags|=LA_UI_SOCKET_LABEL_W;
     laEndRow(uil,b);
     laEndRow(uil,b);
 }
 }
 
 
@@ -758,6 +774,7 @@ void laInitAudio(){
     laAddSubGroup(pc,"in_frequency", "In Frequency","Input frequency","la_in_socket",0,0,0,offsetof(laSynthNodeFM,InFrequency),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_frequency", "In Frequency","Input frequency","la_in_socket",0,0,0,offsetof(laSynthNodeFM,InFrequency),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"out", "Out","Output","la_out_socket",0,0,0,offsetof(laSynthNodeFM,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"out", "Out","Output","la_out_socket",0,0,0,offsetof(laSynthNodeFM,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddFloatProperty(pc,"frequency","Frequency","Frequency of the oscilliator",0,0,0,10,0,0.1,0,0,offsetof(laSynthNodeFM,Frequency),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"frequency","Frequency","Frequency of the oscilliator",0,0,0,10,0,0.1,0,0,offsetof(laSynthNodeFM,Frequency),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"influence","Influence","Influence of the molulation input",LA_WIDGET_KNOB,0,0,1,-1,0.01,0,0,offsetof(laSynthNodeFM,rInfluence),0,0,0,0,0,0,0,0,0,0,0);
     p=laAddEnumProperty(pc,"slow","Slow","Low frequency oscilliator",LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laSynthNodeFM,Slow),0,0,0,0,0,0,0,0,0,0);
     p=laAddEnumProperty(pc,"slow","Slow","Low frequency oscilliator",LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laSynthNodeFM,Slow),0,0,0,0,0,0,0,0,0,0);
     laAddEnumItemAs(p,"NONE","None","Regular frequency range",0,0);
     laAddEnumItemAs(p,"NONE","None","Regular frequency range",0,0);
     laAddEnumItemAs(p,"SLOW","Slow","Slow frequency range",1,0);
     laAddEnumItemAs(p,"SLOW","Slow","Slow frequency range",1,0);
@@ -769,6 +786,7 @@ void laInitAudio(){
     laAddSubGroup(pc,"in", "Input","Input","la_in_socket",0,0,0,offsetof(laSynthNodeVCA,In),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in", "Input","Input","la_in_socket",0,0,0,offsetof(laSynthNodeVCA,In),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"out", "Out","Output","la_out_socket",0,0,0,offsetof(laSynthNodeVCA,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"out", "Out","Output","la_out_socket",0,0,0,offsetof(laSynthNodeVCA,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddFloatProperty(pc,"amp","Amptitude","Amptitude of the output signal",0,0,0,10,0,0.1,0,0,offsetof(laSynthNodeVCA,Amp),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"amp","Amptitude","Amptitude of the output signal",0,0,0,10,0,0.1,0,0,offsetof(laSynthNodeVCA,Amp),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"influence","Influence","Influence of the modulating signal",LA_WIDGET_KNOB,0,0,1,-1,0.01,0,0,offsetof(laSynthNodeVCA,rInfluence),0,0,0,0,0,0,0,0,0,0,0);
 
 
     pc=laAddPropertyContainer("la_node_synth_noise", "Noise Node", "Noise node",0,laui_NoiseNode,sizeof(laSynthNodeFM),lapost_Node,0,1);
     pc=laAddPropertyContainer("la_node_synth_noise", "Noise Node", "Noise node",0,laui_NoiseNode,sizeof(laSynthNodeFM),lapost_Node,0,1);
     LA_PC_IDN_NOISE=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     LA_PC_IDN_NOISE=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
@@ -809,16 +827,22 @@ void laInitAudio(){
     pc=laAddPropertyContainer("la_node_synth_envelope", "Envelope Node", "Sound envelope",0,laui_EnvelopeNode,sizeof(laSynthNodeEnvelope),lapost_Node,0,1);
     pc=laAddPropertyContainer("la_node_synth_envelope", "Envelope Node", "Sound envelope",0,laui_EnvelopeNode,sizeof(laSynthNodeEnvelope),lapost_Node,0,1);
     LA_PC_IDN_ENVELOPE=pc; laPropContainerExtraFunctions(pc,0,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     LA_PC_IDN_ENVELOPE=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,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
+    laAddSubGroup(pc,"in_gate", "Gate","Gate input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Gate),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_attack", "Attack","Attack input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Attack),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_attack", "Attack","Attack input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Attack),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_delay", "Delay","Delay input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Delay),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_delay", "Delay","Delay input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Delay),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_sustain", "Sustain","Sustain input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Sustain),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_sustain", "Sustain","Sustain input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Sustain),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_release", "Release","Release input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Release),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_release", "Release","Release input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Release),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_trigger", "Trigger","Trigger input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Trigger),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_trigger", "Trigger","Trigger input","la_in_socket",0,0,0,offsetof(laSynthNodeEnvelope,Trigger),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"out", "Output","Envelope output","la_out_socket",0,0,0,offsetof(laSynthNodeEnvelope,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"out", "Output","Envelope output","la_out_socket",0,0,0,offsetof(laSynthNodeEnvelope,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
-    laAddFloatProperty(pc,"attack","Attack","Attack value",0,0,0,10,0,0.1,0,0,offsetof(laSynthNodeEnvelope,rAttack),0,0,0,0,0,0,0,0,0,0,0);
-    laAddFloatProperty(pc,"delay","Delay","Delay value",0,0,0,10,0,0.1,0,0,offsetof(laSynthNodeEnvelope,rDelay),0,0,0,0,0,0,0,0,0,0,0);
-    laAddFloatProperty(pc,"sustain","Sustain","Sustain value",0,0,0,10,0,0.1,0,0,offsetof(laSynthNodeEnvelope,rSustain),0,0,0,0,0,0,0,0,0,0,0);
-    laAddFloatProperty(pc,"release","Release","Release value",0,0,0,10,0,0.1,0,0,offsetof(laSynthNodeEnvelope,rRelease),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"gate","Gate","Gate for the trigger",0,0,0,10,-10,0.01,0,0,offsetof(laSynthNodeEnvelope,rGate),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"attack","Attack","Attack value",0,0,0,10,0,0.01,0,0,offsetof(laSynthNodeEnvelope,rAttack),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"delay","Delay","Delay value",0,0,0,10,0,0.01,0,0,offsetof(laSynthNodeEnvelope,rDelay),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"sustain","Sustain","Sustain value",0,0,0,10,0,0.01,0,0,offsetof(laSynthNodeEnvelope,rSustain),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"release","Release","Release value",0,0,0,10,0,0.01,0,0,offsetof(laSynthNodeEnvelope,rRelease),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"iattack","Attack Influence","Attack modulation influence value",LA_WIDGET_KNOB,0,0,1,-1,0.01,0,0,offsetof(laSynthNodeEnvelope,iAttack),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"idelay","Delay Influence","Delay modulation influence value",LA_WIDGET_KNOB,0,0,1,-1,0.01,0,0,offsetof(laSynthNodeEnvelope,iDelay),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"isustain","Sustain Influence","Sustain modulation influence value",LA_WIDGET_KNOB,0,0,1,-1,0.01,0,0,offsetof(laSynthNodeEnvelope,iSustain),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"irelease","Release Influence","Release modulation influence value",LA_WIDGET_KNOB,0,0,1,-1,0.01,0,0,offsetof(laSynthNodeEnvelope,iRelease),0,0,0,0,0,0,0,0,0,0,0);
     p=laAddEnumProperty(pc,"trigger","Trigger","Trigger the envelope",LA_WIDGET_ENUM_CYCLE,0,0,0,0,offsetof(laSynthNodeEnvelope,bTrigger),0,0,0,0,0,0,0,0,0,0);
     p=laAddEnumProperty(pc,"trigger","Trigger","Trigger the envelope",LA_WIDGET_ENUM_CYCLE,0,0,0,0,offsetof(laSynthNodeEnvelope,bTrigger),0,0,0,0,0,0,0,0,0,0);
     laAddEnumItemAs(p,"IDLE","Idle","Trigger idle",0,0);
     laAddEnumItemAs(p,"IDLE","Idle","Trigger idle",0,0);
     laAddEnumItemAs(p,"TRIG","Trigger","Trigger active",1,0);
     laAddEnumItemAs(p,"TRIG","Trigger","Trigger active",1,0);

+ 4 - 4
la_controllers.c

@@ -175,13 +175,13 @@ void la_AddAxisProp(laPropContainer* pc, char* id, char* name, char* id_min, cha
     laProp* p=laAddIntProperty(pc,id,name,desc,array_len>1?LA_WIDGET_VALUE_METER_2D:LA_WIDGET_METER_TYPE2,
     laProp* p=laAddIntProperty(pc,id,name,desc,array_len>1?LA_WIDGET_VALUE_METER_2D:LA_WIDGET_METER_TYPE2,
         array_prefix,0,32767,-32768,1,0,0,offsetof(laController, AxisValues[i]),0,0,array_len,0,0,0,0,0,0,0,LA_READ_ONLY);
         array_prefix,0,32767,-32768,1,0,0,offsetof(laController, AxisValues[i]),0,0,array_len,0,0,0,0,0,0,0,LA_READ_ONLY);
     if(id_min) p=laAddIntProperty(pc,id_min,id_min,desc,0,
     if(id_min) p=laAddIntProperty(pc,id_min,id_min,desc,0,
-        array_prefix,0,32767,-32768,1,0,0,offsetof(laController, AxisLimitMins[i]),0,0,array_len,0,0,0,0,0,0,0,0);
+        array_prefix,0,-25000,-32768,1,0,0,offsetof(laController, AxisLimitMins[i]),0,0,array_len,0,0,0,0,0,0,0,0);
     if(id_max) p=laAddIntProperty(pc,id_max,id_max,desc,0,
     if(id_max) p=laAddIntProperty(pc,id_max,id_max,desc,0,
-        array_prefix,0,32767,-32768,1,0,0,offsetof(laController, AxisLimitMaxes[i]),0,0,array_len,0,0,0,0,0,0,0,0);
+        array_prefix,0,32767,25000,1,0,0,offsetof(laController, AxisLimitMaxes[i]),0,0,array_len,0,0,0,0,0,0,0,0);
     if(id_min) p=laAddIntProperty(pc,id_cmax,id_cmax,desc,0,
     if(id_min) p=laAddIntProperty(pc,id_cmax,id_cmax,desc,0,
-        array_prefix,0,32767,-32768,1,0,0,offsetof(laController, AxisCenterMins[i]),0,0,array_len,0,0,0,0,0,0,0,0);
+        array_prefix,0,-25000,-32768,1,0,0,offsetof(laController, AxisCenterMins[i]),0,0,array_len,0,0,0,0,0,0,0,0);
     if(id_cmax) p=laAddIntProperty(pc,id_cmin,id_cmin,desc,0,
     if(id_cmax) p=laAddIntProperty(pc,id_cmin,id_cmin,desc,0,
-        array_prefix,0,32767,-32768,1,0,0,offsetof(laController, AxisCenterMaxes[i]),0,0,array_len,0,0,0,0,0,0,0,0);
+        array_prefix,0,32767,25000,1,0,0,offsetof(laController, AxisCenterMaxes[i]),0,0,array_len,0,0,0,0,0,0,0,0);
 }
 }
 void la_AddGenericButtonProps(laPropContainer* pc){
 void la_AddGenericButtonProps(laPropContainer* pc){
     for(int i=0;i<LA_JS_MAX_BUTTONS;i++){ char* name=LA_JS_BTN_NAMES[i]; la_AddButtonProp(pc,name,name,name,i,0,0); }
     for(int i=0;i<LA_JS_MAX_BUTTONS;i++){ char* name=LA_JS_BTN_NAMES[i]; la_AddButtonProp(pc,name,name,name,i,0,0); }

+ 41 - 14
la_interface.h

@@ -304,6 +304,7 @@ STRUCTURE(laInitArguments){
 
 
 NEED_STRUCTURE(laAnimation);
 NEED_STRUCTURE(laAnimation);
 NEED_STRUCTURE(laAudio);
 NEED_STRUCTURE(laAudio);
+NEED_STRUCTURE(laInputMappingBundle);
 
 
 STRUCTURE(LA){
 STRUCTURE(LA){
     laListItem Hyper;
     laListItem Hyper;
@@ -525,6 +526,8 @@ STRUCTURE(LA){
 
 
     //preference:
     //preference:
 
 
+    laInputMappingBundle* InputMapping;
+
     //color
     //color
     real ColorPickerGamma;
     real ColorPickerGamma;
     real ViewportHalftoneFactor;
     real ViewportHalftoneFactor;
@@ -568,8 +571,7 @@ STRUCTURE(LA){
 
 
     int GraphNeedsRebuild; // current mechanism only allows global.
     int GraphNeedsRebuild; // current mechanism only allows global.
     int InputNeedsEval;
     int InputNeedsEval;
-    laRackPageCollection* InputMapping;
-    //laRackPageCollection* Drivers;
+    
     laListHandle NodeCategories;
     laListHandle NodeCategories;
     laNodeCategory* CurrentNodeCategory;
     laNodeCategory* CurrentNodeCategory;
     int FilterNodeCategory;
     int FilterNodeCategory;
@@ -1176,6 +1178,7 @@ extern laWidget *LA_WIDGET_RAW;
 extern laWidget *LA_WIDGET_MAPPER;
 extern laWidget *LA_WIDGET_MAPPER;
 extern laWidget *LA_WIDGET_IMAGE;
 extern laWidget *LA_WIDGET_IMAGE;
 extern laWidget *LA_WIDGET_SCOPE;
 extern laWidget *LA_WIDGET_SCOPE;
+extern laWidget *LA_WIDGET_KNOB;
 
 
 #define LA_CONDITION_TRUE 1
 #define LA_CONDITION_TRUE 1
 #define LA_CONDITION_FALSE 2
 #define LA_CONDITION_FALSE 2
@@ -1413,6 +1416,29 @@ STRUCTURE(laOperator){
     laSafeString* RuntimeHint;
     laSafeString* RuntimeHint;
 };
 };
 
 
+#define LA_INPUT_DEVICE_KEYBOARD 0
+#define LA_INPUT_DEVICE_JOYSTICK 1
+
+STRUCTURE(laInputMapping){
+    laListItem Item;
+    laSafeString* Name;
+    laListHandle Entries;
+};
+STRUCTURE(laInputMappingEntry){
+    laListItem Item;
+    laInputMapping* Parent;
+    int DeviceType;
+    int JoystickDevice;
+    laSafeString* Key; int KeyValue;
+    int SpecialKeyBits;
+    laSafeString* TargetMessage; int MessageValue;
+};
+STRUCTURE(laInputMappingBundle){
+    void* _pad;
+    laListHandle InputMappings;
+    laInputMapping* CurrentInputMapping;
+};
+
 #define LA_JS_MAX_AXES 32
 #define LA_JS_MAX_AXES 32
 #define LA_JS_MAX_BUTTONS 128
 #define LA_JS_MAX_BUTTONS 128
 STRUCTURE(laController){
 STRUCTURE(laController){
@@ -1438,7 +1464,7 @@ void la_RegisterControllerProps();
 laPropContainer* laget_ControllerType(laController* c);
 laPropContainer* laget_ControllerType(laController* c);
 laController* la_FindControllerWithID(int id);
 laController* la_FindControllerWithID(int id);
 
 
-void la_RegisterInputMapperOperators();
+void la_RegisterBasicNodes();
 
 
 #define LA_DAG_FLAG_ERR  0
 #define LA_DAG_FLAG_ERR  0
 #define LA_DAG_FLAG_TEMP 1
 #define LA_DAG_FLAG_TEMP 1
@@ -1448,16 +1474,12 @@ int laRebuildPageEval(laRackPage* rp);
 int laRunPage(laRackPage* rp, uint64_t mask);
 int laRunPage(laRackPage* rp, uint64_t mask);
 
 
 void laGraphRequestRebuild();
 void laGraphRequestRebuild();
-void laMappingRequestEval();
-int la_RebuildInputMapping();
-int la_RunInputMapping();
 
 
 int la_RunDrivers();
 int la_RunDrivers();
 
 
 void lapost_Node(laBaseNode *bn);
 void lapost_Node(laBaseNode *bn);
 
 
 laBaseNode* la_CreateDriverNode(laNodeRack* ir, laBaseNodeType* NodeType);
 laBaseNode* la_CreateDriverNode(laNodeRack* ir, laBaseNodeType* NodeType);
-laBaseNode* la_CreateInputMapperNode(laNodeRack* ir, laBaseNodeType* NodeType);
 
 
 void* laget_InstanceSelf(void* instace);
 void* laget_InstanceSelf(void* instace);
 laPropContainer* laget_BaseNodeType(laBaseNode* bn);
 laPropContainer* laget_BaseNodeType(laBaseNode* bn);
@@ -1747,7 +1769,7 @@ STRUCTURE(laSynth){
 STRUCTURE(laSynthNodeFM){
 STRUCTURE(laSynthNodeFM){
     laBaseNode Base;
     laBaseNode Base;
     int Slow;
     int Slow;
-    real Frequency;
+    real Frequency,rInfluence;
     laNodeInSocket* InFrequency;
     laNodeInSocket* InFrequency;
     laNodeOutSocket* Out;
     laNodeOutSocket* Out;
     //runtime
     //runtime
@@ -1768,7 +1790,7 @@ STRUCTURE(laSynthNodeNoise){
 
 
 STRUCTURE(laSynthNodeVCA){
 STRUCTURE(laSynthNodeVCA){
     laBaseNode Base;
     laBaseNode Base;
-    real Amp;
+    real Amp, rInfluence;
     laNodeInSocket* In;
     laNodeInSocket* In;
     laNodeInSocket* InAmp;
     laNodeInSocket* InAmp;
     laNodeOutSocket* Out;
     laNodeOutSocket* Out;
@@ -1796,11 +1818,11 @@ STRUCTURE(laSynthNodeOutput){
 
 
 STRUCTURE(laSynthNodeEnvelope){
 STRUCTURE(laSynthNodeEnvelope){
     laBaseNode Base;
     laBaseNode Base;
-    laNodeInSocket* Trigger; int bTrigger; int Triggered;
-    laNodeInSocket* Attack; real rAttack;
-    laNodeInSocket* Delay; real rDelay;
-    laNodeInSocket* Sustain; real rSustain;
-    laNodeInSocket* Release; real rRelease;
+    laNodeInSocket* Trigger,*Gate; real rGate; int bTrigger; int Triggered;
+    laNodeInSocket* Attack;  real rAttack,iAttack;
+    laNodeInSocket* Delay;   real rDelay,iDelay;
+    laNodeInSocket* Sustain; real rSustain,iSustain;
+    laNodeInSocket* Release; real rRelease,iRelease;
     laNodeOutSocket* Out; real* OutSamples;
     laNodeOutSocket* Out; real* OutSamples;
     real Time,ReleaseTime,AtLevel;
     real Time,ReleaseTime,AtLevel;
 };
 };
@@ -2084,6 +2106,11 @@ void laSetMenuBarTemplates(laUiDefineFunc MenuButtons, laUiDefineFunc MenuExtras
 void laSetAboutTemplates(laUiDefineFunc AboutContent, laUiDefineFunc AboutVersion, laUiDefineFunc AboutAuthor);
 void laSetAboutTemplates(laUiDefineFunc AboutContent, laUiDefineFunc AboutVersion, laUiDefineFunc AboutAuthor);
 void laSetPreferenceTemplates(laUiDefineFunc PreferencePageDisplay, laUiDefineFunc PreferencePageInput, laUiDefineFunc PreferencePageResource, laUiDefineFunc PreferencePageTheme);
 void laSetPreferenceTemplates(laUiDefineFunc PreferencePageDisplay, laUiDefineFunc PreferencePageInput, laUiDefineFunc PreferencePageResource, laUiDefineFunc PreferencePageTheme);
 
 
+laInputMapping* laNewInputMapping(char* Name);
+laInputMappingEntry* laNewInputMappingEntry(laInputMapping* im, int DeviceType, int JoystickDevice, char* Key, int SpecialKeyBit, char* TargetMessage);
+void laRemoveInputMappingEntry(laInputMapping* im, laInputMappingEntry* e);
+void laRemoveInputMapping(laInputMapping* im);
+
 laWindow *laDesignWindow(int X, int Y, int W, int H);
 laWindow *laDesignWindow(int X, int Y, int W, int H);
 laLayout *laDesignLayout(laWindow *w, char *Title);
 laLayout *laDesignLayout(laWindow *w, char *Title);
 void laDestroyLayout(laWindow *w, laLayout* l);
 void laDestroyLayout(laWindow *w, laLayout* l);

+ 28 - 8
la_kernel.c

@@ -1167,10 +1167,9 @@ int laGetReadyWith(laInitArguments* ia){
     arrEnsureLength(&MAIN.InputBufU,0,&MAIN.InputBufUMax,sizeof(uint32_t));
     arrEnsureLength(&MAIN.InputBufU,0,&MAIN.InputBufUMax,sizeof(uint32_t));
 
 
     arrEnsureLength(&MAIN.NodeTypes,0,&MAIN.NodeTypeMax,sizeof(laBaseNode*));
     arrEnsureLength(&MAIN.NodeTypes,0,&MAIN.NodeTypeMax,sizeof(laBaseNode*));
-
-    MAIN.InputMapping=memAcquire(sizeof(laRackPageCollection));
-    //MAIN.Drivers=memAcquire(sizeof(laRackPageCollection));
     
     
+    MAIN.InputMapping=memAcquire(sizeof(laInputMappingBundle));
+
     MAIN.Animation=memAcquire(sizeof(laAnimation));
     MAIN.Animation=memAcquire(sizeof(laAnimation));
     MAIN.Audio=memAcquire(sizeof(laAudio));
     MAIN.Audio=memAcquire(sizeof(laAudio));
 
 
@@ -1200,7 +1199,7 @@ int laGetReadyWith(laInitArguments* ia){
     MAIN.SolidShadowLength = 20;
     MAIN.SolidShadowLength = 20;
     MAIN.WireColorSlices = 16;
     MAIN.WireColorSlices = 16;
     MAIN.WireThickness = 5;
     MAIN.WireThickness = 5;
-    MAIN.WireSaggyness = 5;
+    MAIN.WireSaggyness = 0.5;
 
 
     laAddResourceFolder(".");
     laAddResourceFolder(".");
 
 
@@ -1230,7 +1229,8 @@ int laGetReadyWith(laInitArguments* ia){
 
 
     la_InitControllers();
     la_InitControllers();
     la_RegisterControllerProps();
     la_RegisterControllerProps();
-    la_RegisterInputMapperOperators();
+    
+    la_RegisterBasicNodes();
     tns_RegisterNodes();
     tns_RegisterNodes();
 
 
     la_InitThreadEnviornment();
     la_InitThreadEnviornment();
@@ -1334,6 +1334,7 @@ void laSaveUserPreferences(){
     laUDF* udf=laPrepareUDF(path);
     laUDF* udf=laPrepareUDF(path);
     laWriteProp(udf,"la.windows");
     laWriteProp(udf,"la.windows");
     laWriteProp(udf,"la.user_preferences");
     laWriteProp(udf,"la.user_preferences");
+    laWriteProp(udf,"la.input_mapping");
     for(laListItemPointer* lip=MAIN.ExtraPreferencePaths.pFirst;lip;lip=lip->pNext){
     for(laListItemPointer* lip=MAIN.ExtraPreferencePaths.pFirst;lip;lip=lip->pNext){
         laWriteProp(udf,lip->p);
         laWriteProp(udf,lip->p);
     }
     }
@@ -1383,6 +1384,26 @@ void laSetPreferenceTemplates(laUiDefineFunc PreferencePageDisplay, laUiDefineFu
     MAIN.PreferencePageResource=PreferencePageResource; MAIN.PreferencePageTheme=PreferencePageTheme;
     MAIN.PreferencePageResource=PreferencePageResource; MAIN.PreferencePageTheme=PreferencePageTheme;
 }
 }
 
 
+laInputMapping* laNewInputMapping(char* Name){
+    laInputMapping* im=memAcquireHyper(sizeof(laInputMapping)); lstAppendItem(&MAIN.InputMapping->InputMappings,im);
+    memAssignRef(MAIN.InputMapping,&MAIN.InputMapping->CurrentInputMapping,im);
+    strSafeSet(&im->Name,Name);
+}
+laInputMappingEntry* laNewInputMappingEntry(laInputMapping* im, int DeviceType, int JoystickDevice, char* Key, int SpecialKeyBit, char* TargetMessage){
+    laInputMappingEntry* e=memAcquire(sizeof(laInputMappingEntry)); lstAppendItem(&im->Entries,e);
+    e->DeviceType = DeviceType; e->JoystickDevice=JoystickDevice; e->SpecialKeyBits=SpecialKeyBit;
+    memAssignRef(e,&e->Parent,im);
+    if(TargetMessage){ strSafeSet(&e->TargetMessage,TargetMessage); } if(Key){ strSafeSet(&e->Key,Key); }
+}
+void laRemoveInputMappingEntry(laInputMapping* im, laInputMappingEntry* e){
+    memAssignRef(e,&e->Parent,0); lstRemoveItem(&im->Entries,e); strSafeDestroy(&e->TargetMessage); strSafeDestroy(&e->Key); memFree(e);
+}
+void laRemoveInputMapping(laInputMapping* im){
+    laInputMappingEntry* e; while(e=im->Entries.pFirst){ laRemoveInputMappingEntry(im,e); }
+    laInputMapping* use=im->Item.pNext?im->Item.pNext:im->Item.pPrev;
+    memAssignRef(MAIN.InputMapping,&MAIN.InputMapping->CurrentInputMapping,use);
+    lstRemoveItem(&MAIN.InputMapping->InputMappings,im); strSafeDestroy(&im->Name); memFree(im);
+}
 
 
 //MSG====================================================
 //MSG====================================================
 
 
@@ -5831,7 +5852,7 @@ void la_SendWireVerts(real x1, real y1, real x2, real y2, real circle_r){
     tnsVector2d v1,vi,v2,v1s,v2s; real dist=0;
     tnsVector2d v1,vi,v2,v1s,v2s; real dist=0;
     v1[0]=x1; v1[1]=y1; v2[0]=x2; v2[1]=y2;
     v1[0]=x1; v1[1]=y1; v2[0]=x2; v2[1]=y2;
     tnsInterpolate2dv(v1,v2,0.5,vi);
     tnsInterpolate2dv(v1,v2,0.5,vi);
-    if(MAIN.WireSaggyness>0.01){ dist=tnsDist2dv(v1,v2); vi[1]+=log(dist+1)*MAIN.WireSaggyness; }
+    if(MAIN.WireSaggyness>0.01){ dist=tnsDist2dv(v1,v2); vi[1]+=dist*MAIN.WireSaggyness; }
     tnsVectorMinus2d(v1s,vi,v1); tnsNormalizeSelf2d(v1s); tnsVectorMultiSelf2d(v1s,circle_r);
     tnsVectorMinus2d(v1s,vi,v1); tnsNormalizeSelf2d(v1s); tnsVectorMultiSelf2d(v1s,circle_r);
     tnsVectorMinus2d(v2s,vi,v2); tnsNormalizeSelf2d(v2s); tnsVectorMultiSelf2d(v2s,circle_r);
     tnsVectorMinus2d(v2s,vi,v2); tnsNormalizeSelf2d(v2s); tnsVectorMultiSelf2d(v2s,circle_r);
     tnsVectorAccum2d(v1, v1s); tnsVectorAccum2d(v2, v2s);
     tnsVectorAccum2d(v1, v1s); tnsVectorAccum2d(v2, v2s);
@@ -7623,8 +7644,7 @@ void laMainLoop(){
 
 
         la_UpdateControllerStatus();
         la_UpdateControllerStatus();
 
 
-        if(MAIN.GraphNeedsRebuild){ la_RebuildInputMapping(); MAIN.GraphNeedsRebuild = 0; }
-        if(MAIN.InputNeedsEval){ la_RunInputMapping(); }
+        if(MAIN.GraphNeedsRebuild){ MAIN.GraphNeedsRebuild = 0; }
 
 
         for (w=MAIN.Windows.pFirst;w;w = NextW){
         for (w=MAIN.Windows.pFirst;w;w = NextW){
             NextW = w->Item.pNext;
             NextW = w->Item.pNext;

+ 1 - 51
resources/la_nodes_basic.c

@@ -968,15 +968,6 @@ void laui_LoopIndexNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laCo
 }
 }
 
 
 
 
-
-int OPINV_AddInputMapperPage(laOperator* a, laEvent *e){
-    laRackPage* dp=memAcquire(sizeof(laRackPage));
-    strSafeSet(&dp->Name,"New Page");
-    lstAppendItem(&MAIN.InputMapping->Pages, dp);
-    memAssignRef(MAIN.InputMapping,&MAIN.InputMapping->CurrentPage,dp); dp->RackType=LA_RACK_TYPE_INPUT;
-    laNotifyUsers("la.input_mapping"); laRecordAndPush(0,"la.input_mapping","Add Page", 0);
-    return LA_FINISHED;
-}
 int OPINV_AddNodesRack(laOperator* a, laEvent *e){
 int OPINV_AddNodesRack(laOperator* a, laEvent *e){
     laRackPage* dp=a->This?a->This->EndInstance:0; if(!dp) return LA_FINISHED;
     laRackPage* dp=a->This?a->This->EndInstance:0; if(!dp) return LA_FINISHED;
     laNodeRack* ir=memAcquire(sizeof(laNodeRack));
     laNodeRack* ir=memAcquire(sizeof(laNodeRack));
@@ -987,36 +978,6 @@ int OPINV_AddNodesRack(laOperator* a, laEvent *e){
     return LA_FINISHED;
     return LA_FINISHED;
 
 
 }
 }
-int OPINV_RebuildInputMapping(laOperator* a, laEvent *e){
-    laGraphRequestRebuild();
-    return LA_FINISHED;
-}
-int OPCHK_RemoveInputMappingPage(laPropPack *This, laStringSplitor *ss){
-    if ((!This) || (la_EnsureSubTarget(This->LastPs->p,0) != LA_PC_RACK_PAGE)) return 0;
-    laRackPage* dp=This->EndInstance; if(dp->ParentObject) return 0;
-    return 1;
-}
-int OPINV_RemoveInputMappingPage(laOperator* a, laEvent *e){
-    laRackPage* dp=a->This?a->This->EndInstance:0; if((!dp) || dp->ParentObject) return LA_CANCELED;
-    if(strSame(strGetArgumentString(a->ExtraInstructionsP,"confirm"),"true")){
-        strSafeDestroy(&dp->Name);
-        while(dp->Racks.pFirst){ laDestroyRack(dp->Racks.pFirst); }
-        while(lstPopPointer(&dp->Eval)); while(lstPopPointer(&dp->AlwaysBranchers));
-        if(MAIN.InputMapping->CurrentPage==dp){
-            laRackPage* adp=dp->Item.pNext?dp->Item.pNext:dp->Item.pPrev;
-            memAssignRef(MAIN.InputMapping,&MAIN.InputMapping->CurrentPage,adp);
-        }laNotifyInstanceUsers(dp);
-        lstRemoveItem(&MAIN.InputMapping->Pages, dp); memLeave(dp);
-        laNotifyInstanceUsers(MAIN.InputMapping); laRecordAndPush(a->This,"","Remove input mapping page", 0);
-        return LA_FINISHED;
-    }
-    laEnableOperatorPanel(a,a->This,e->x,e->y,200,200,0,0,0,0,0,0,0,0,e);
-    return LA_RUNNING;
-}
-void laui_RemoveInputMappingPage(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil);
-    laShowItemFull(uil,c,This,"remove_input_mapping_page",0,"confirm=true;text=Confirm",0,0);
-}
 
 
 laBaseNode* la_CreateNode(laNodeRack* ir, laBaseNodeType* NodeType){
 laBaseNode* la_CreateNode(laNodeRack* ir, laBaseNodeType* NodeType){
     laBaseNode* bn=memAcquire(NodeType->NodeSize);
     laBaseNode* bn=memAcquire(NodeType->NodeSize);
@@ -1201,24 +1162,20 @@ void la_AddValuesNodeEnumValue(laProp* p){
     laAddEnumItemAs(p,"IDLE","Idle","Idle",0,0);
     laAddEnumItemAs(p,"IDLE","Idle","Idle",0,0);
     laAddEnumItemAs(p,"ACTIVE","Active","Active",1,0);
     laAddEnumItemAs(p,"ACTIVE","Active","Active",1,0);
 }
 }
-void la_RegisterInputMapperOperators(){
+void la_RegisterBasicNodes(){
     laPropContainer *pc; laProp *p;
     laPropContainer *pc; laProp *p;
     laOperatorType *at;
     laOperatorType *at;
     laEnumProp *ep;
     laEnumProp *ep;
 
 
-    laCreateOperatorType("LA_add_input_mapping_page", "Add Page", "Add a page for inpur mapping", 0,0,0,OPINV_AddInputMapperPage,0,'+',0);
     laCreateOperatorType("LA_add_rack", "Add Rack", "Add a rack for nodes", 0,0,0,OPINV_AddNodesRack,0,'+',0);
     laCreateOperatorType("LA_add_rack", "Add Rack", "Add a rack for nodes", 0,0,0,OPINV_AddNodesRack,0,'+',0);
     at=laCreateOperatorType("OPINV_AddNode", "Add Node", "Add a node to the rack",0,0,0,OPINV_AddNode,OPMOD_AddNode,'+',0);
     at=laCreateOperatorType("OPINV_AddNode", "Add Node", "Add a node to the rack",0,0,0,OPINV_AddNode,OPMOD_AddNode,'+',0);
     at->UiDefine=laui_AddNode;
     at->UiDefine=laui_AddNode;
-    laCreateOperatorType("LA_input_mapping_rebuild", "Rebuild Input Mapping", "Rebuild input mapping for evaluation",0,0,0,OPINV_RebuildInputMapping,0,U'⭮',0);
     laCreateOperatorType("LA_move_node_to_rack", "Move Node", "Move node to another rack",0,0,0,OPINV_MoveNodeToRack,0,0,0);
     laCreateOperatorType("LA_move_node_to_rack", "Move Node", "Move node to another rack",0,0,0,OPINV_MoveNodeToRack,0,0,0);
     laCreateOperatorType("LA_delete_node", "Delete Node", "Delete this node",0,0,0,OPINV_DeleteNode,0,0,0);
     laCreateOperatorType("LA_delete_node", "Delete Node", "Delete this node",0,0,0,OPINV_DeleteNode,0,0,0);
     laCreateOperatorType("LA_move_rack", "Move Rack", "Move this rack",0,0,0,OPINV_MoveRack,0,0,0);
     laCreateOperatorType("LA_move_rack", "Move Rack", "Move this rack",0,0,0,OPINV_MoveRack,0,0,0);
     laCreateOperatorType("LA_insert_rack", "Insert Rack", "Insert a new rack",0,0,0,OPINV_InsertRack,0,0,0);
     laCreateOperatorType("LA_insert_rack", "Insert Rack", "Insert a new rack",0,0,0,OPINV_InsertRack,0,0,0);
     at=laCreateOperatorType("LA_delete_rack", "Delete Rack", "Delete a rack",0,0,0,OPINV_DeleteRack,OPMOD_FinishOnData,U'🞫',0);
     at=laCreateOperatorType("LA_delete_rack", "Delete Rack", "Delete a rack",0,0,0,OPINV_DeleteRack,OPMOD_FinishOnData,U'🞫',0);
     at->UiDefine=laui_DeleteRack;
     at->UiDefine=laui_DeleteRack;
-    laCreateOperatorType("LA_remove_input_mapping_page", "Remove Page", "Remove a input mapper page",OPCHK_RemoveInputMappingPage,0,0,OPINV_RemoveInputMappingPage,OPMOD_FinishOnData,L'🗴',0)
-        ->UiDefine=laui_RemoveInputMappingPage;
     
     
     pc=laAddPropertyContainer("la_node_rack", "Input Rack", "Input rack for putting input mapping nodes",0,0,sizeof(laNodeRack),lapost_NodeRack,0,1);
     pc=laAddPropertyContainer("la_node_rack", "Input Rack", "Input rack for putting input mapping nodes",0,0,sizeof(laNodeRack),lapost_NodeRack,0,1);
     laAddStringProperty(pc,"name","Name","Name of this rack",0,0,0,0,1,offsetof(laNodeRack,Name),0,0,0,0,LA_AS_IDENTIFIER);
     laAddStringProperty(pc,"name","Name","Name of this rack",0,0,0,0,1,offsetof(laNodeRack,Name),0,0,0,0,LA_AS_IDENTIFIER);
@@ -1568,13 +1525,6 @@ int laRunPage(laRackPage* rp, uint64_t mask){
     return 1;
     return 1;
 }
 }
 
 
-int la_RunInputMapping(){
-    MAIN.InputNeedsEval = 0;
-    return laRunPage(MAIN.InputMapping->CurrentPage, 1);
-}
-int la_RebuildInputMapping(){
-    return laRebuildPageEval(MAIN.InputMapping->CurrentPage);
-}
 
 
 //==================================================================================================
 //==================================================================================================
 
 

+ 46 - 0
resources/la_operators.c

@@ -724,6 +724,47 @@ int OPINV_UDFPropagate(laOperator *a, laEvent *e){
     return LA_FINISHED;
     return LA_FINISHED;
 }
 }
 
 
+int OPINV_NewInputMapping(laOperator *a, laEvent *e){
+    laNewInputMapping("New Mapping"); laNotifyUsers("la.input_mapping"); return LA_FINISHED;
+}
+int OPINV_RemoveInputMapping(laOperator *a, laEvent *e){
+    if(!a->This || !a->This->EndInstance) return LA_CANCELED; laInputMapping* im=a->This->EndInstance;
+    char* buf[256];sprintf(buf,"%s \"%s\".",transLate("Will remove input mapping"),SSTR(im->Name));
+    laEnableYesNoPanel(0, 0, "Confirm?", buf, e->x, e->y, 200, e);
+    return LA_RUNNING;
+}
+int OPINV_NewInputMappingEntry(laOperator *a, laEvent *e){
+    if(!a->This || !a->This->EndInstance) return LA_CANCELED; laInputMapping* im=a->This->EndInstance;
+    laNewInputMappingEntry(im,0,0,"a",0,"none"); laNotifyUsers("la.input_mapping"); return LA_FINISHED;
+}
+int OPINV_RemoveInputMappingEntry(laOperator *a, laEvent *e){
+    if(!a->This || !a->This->EndInstance) return LA_CANCELED; laInputMappingEntry* ie=a->This->EndInstance;
+    laEnableYesNoPanel(0, 0, "Confirm?", "Will remove this key map entry", e->x, e->y, 200, e);
+    return LA_RUNNING;
+}
+
+int OPMOD_RemoveInputMapping(laOperator *a, laEvent *e){
+    if(!a->This || !a->This->EndInstance) return LA_CANCELED; laInputMapping* im=a->This->EndInstance;
+    if(a->ConfirmData){
+        if(a->ConfirmData->Mode == LA_CONFIRM_OK){
+            laRemoveInputMapping(im); laNotifyUsers("la.input_mapping");
+        }
+        return LA_FINISHED;
+    }
+    return LA_RUNNING;
+}
+int OPMOD_RemoveInputMappingEntry(laOperator *a, laEvent *e){
+    if(!a->This || !a->This->EndInstance) return LA_CANCELED; laInputMappingEntry* ie=a->This->EndInstance;
+    if(a->ConfirmData){
+        if(a->ConfirmData->Mode == LA_CONFIRM_OK){
+            laRemoveInputMappingEntry(ie->Parent,ie); laNotifyUsers("la.input_mapping");
+        }
+        return LA_FINISHED;
+    }
+    return LA_RUNNING;
+}
+
+
 int OPINV_SaveUserPreferences(laOperator *a, laEvent *e){
 int OPINV_SaveUserPreferences(laOperator *a, laEvent *e){
     laSaveUserPreferences(); return LA_FINISHED;
     laSaveUserPreferences(); return LA_FINISHED;
 }
 }
@@ -2152,6 +2193,11 @@ void la_RegisterBuiltinOperators(){
     laCreateOperatorType("LA_remove_resource_folder", "Remove Resource Folder", "Remove a resource folder entry",
     laCreateOperatorType("LA_remove_resource_folder", "Remove Resource Folder", "Remove a resource folder entry",
                                0, 0, 0, OPINV_RemoveResourceFolder, 0, U'🞫', LA_ACTUATOR_SYSTEM);
                                0, 0, 0, OPINV_RemoveResourceFolder, 0, U'🞫', LA_ACTUATOR_SYSTEM);
 
 
+    laCreateOperatorType("LA_new_input_mapping", "New Mapping", "New input mapping", 0, 0, 0, OPINV_NewInputMapping, 0, '+', LA_ACTUATOR_SYSTEM);
+    laCreateOperatorType("LA_new_input_mapping_entry", "New Entry", "New input mapping entry", 0, 0, 0, OPINV_NewInputMappingEntry, 0, '+', LA_ACTUATOR_SYSTEM);
+    laCreateOperatorType("LA_new_remove_mapping", "Remove Mapping", "Remove input mapping", 0, 0, 0, OPINV_RemoveInputMapping, OPMOD_RemoveInputMapping, U'🞫', LA_ACTUATOR_SYSTEM);
+    laCreateOperatorType("LA_new_remove_mapping_entry", "Remove Entry", "Remove input mapping entry", 0, 0, 0, OPINV_RemoveInputMappingEntry, OPMOD_RemoveInputMappingEntry, U'🞫', LA_ACTUATOR_SYSTEM);
+
     laCreateOperatorType("LA_save_user_preferences", "Save Preferences", "Save user preferences", 0, 0, 0, OPINV_SaveUserPreferences, 0, 0, LA_ACTUATOR_SYSTEM);
     laCreateOperatorType("LA_save_user_preferences", "Save Preferences", "Save user preferences", 0, 0, 0, OPINV_SaveUserPreferences, 0, 0, LA_ACTUATOR_SYSTEM);
     laCreateOperatorType("LA_restore_factory", "Restore Factory Settings", "Restore factory settings", 0, 0, 0, OPINV_RestoreFactorySettings, OPMOD_RestoreFactorySettings, 0, LA_ACTUATOR_SYSTEM);
     laCreateOperatorType("LA_restore_factory", "Restore Factory Settings", "Restore factory settings", 0, 0, 0, OPINV_RestoreFactorySettings, OPMOD_RestoreFactorySettings, 0, LA_ACTUATOR_SYSTEM);
 
 

+ 30 - 8
resources/la_properties.c

@@ -807,6 +807,13 @@ int lafilter_NodeCategory(void* unused, laNodeCategory* c){
     if(c->For&MAIN.FilterNodeCategory) return 1; return 0;
     if(c->For&MAIN.FilterNodeCategory) return 1; return 0;
 }
 }
 
 
+void* laget_FirstInputMapping(void* unused1,void* unused2){
+    return MAIN.InputMapping->InputMappings.pFirst;
+}
+void* laget_CurrentInputMapping(laInputMappingBundle* imb){
+    return imb->CurrentInputMapping;
+}
+
 void tnspost_World(tnsWorld *w){
 void tnspost_World(tnsWorld *w){
     tnsRefreshMaterialLibraries();
     tnsRefreshMaterialLibraries();
     laAnimationUpdateHolderList();
     laAnimationUpdateHolderList();
@@ -1489,11 +1496,11 @@ void la_RegisterInternalProps(){
             sp=laAddSubGroup(p, "node_categories", "Node Categories", "Node categories","la_node_category",0,0,0,offsetof(LA,CurrentNodeCategory),0,0,0,0,0,0,offsetof(LA,NodeCategories),0);
             sp=laAddSubGroup(p, "node_categories", "Node Categories", "Node categories","la_node_category",0,0,0,offsetof(LA,CurrentNodeCategory),0,0,0,0,0,0,offsetof(LA,NodeCategories),0);
             laSubGroupExtraFunctions(sp,lafilter_NodeCategory,0,0,0,0);
             laSubGroupExtraFunctions(sp,lafilter_NodeCategory,0,0,0,0);
 
 
-            laAddSubGroup(p, "input_mapping", "Input Mapping", "Input mapping page collection","la_input_mapping_collection",0,0,0,offsetof(LA,InputMapping),0,0,0,0,0,0,0,LA_UDF_SINGLE);
-            
             laAddSubGroup(p, "animation", "Animation", "Animation data","la_animation",0,0,0,offsetof(LA,Animation),0,0,0,0,0,0,0,LA_UDF_SINGLE);
             laAddSubGroup(p, "animation", "Animation", "Animation data","la_animation",0,0,0,offsetof(LA,Animation),0,0,0,0,0,0,0,LA_UDF_SINGLE);
             if(MAIN.InitArgs.HasAudio) laAddSubGroup(p, "audio", "Audio", "Audio data","la_audio",0,0,0,offsetof(LA,Audio),0,0,0,0,0,0,0,LA_UDF_SINGLE);
             if(MAIN.InitArgs.HasAudio) laAddSubGroup(p, "audio", "Audio", "Audio data","la_audio",0,0,0,offsetof(LA,Audio),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 
 
+            laAddSubGroup(p,"input_mapping","Input Mapping","Input mapping bundle","la_input_mapping_bundle",0,0,0,offsetof(LA,InputMapping),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+
             laAddStringProperty(p, "identifier", "Identifier", "Identifier", 0,0,0,0,0,0,0,laget_MainIdentifier, 0,0,LA_AS_IDENTIFIER|LA_READ_ONLY);
             laAddStringProperty(p, "identifier", "Identifier", "Identifier", 0,0,0,0,0,0,0,laget_MainIdentifier, 0,0,LA_AS_IDENTIFIER|LA_READ_ONLY);
             laAddSubGroup(p, "test_ucn", "TEST UCN", "---", "udf_content_node",0,0,0,offsetof(LA, TEST_Link), 0,0,0,0,0,0,0,0);
             laAddSubGroup(p, "test_ucn", "TEST UCN", "---", "udf_content_node",0,0,0,offsetof(LA, TEST_Link), 0,0,0,0,0,0,0,0);
             
             
@@ -1523,10 +1530,6 @@ void la_RegisterInternalProps(){
             laAddIntProperty(p, "for_racks", "For Rack Types", "For rack types", 0,0,0,0,0,0,0,0,offsetof(laNodeCategory, For), 0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
             laAddIntProperty(p, "for_racks", "For Rack Types", "For rack types", 0,0,0,0,0,0,0,0,offsetof(laNodeCategory, For), 0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
         }
         }
 
 
-        p = laAddPropertyContainer("la_input_mapping_collection", "Input Mapping Collection", "Input mapping collection", 0,0,sizeof(laRackPageCollection), 0,0,1);{
-            laAddSubGroup(p, "pages", "Pages", "Rack pages","la_rack_page",0,0,0,offsetof(laRackPageCollection,CurrentPage),0,0,0,0,0,0,offsetof(laRackPageCollection,Pages),0);
-            laAddSubGroup(p, "current_page", "Current Page", "Current page","la_rack_page",0,0,0,offsetof(laRackPageCollection,CurrentPage),0,0,0,0,0,0,0,LA_UDF_REFER);
-        }
         p = laAddPropertyContainer("la_driver_collection", "Driver Collection", "Driver collection", 0,0,sizeof(laRackPageCollection), 0,0,1);{
         p = laAddPropertyContainer("la_driver_collection", "Driver Collection", "Driver collection", 0,0,sizeof(laRackPageCollection), 0,0,1);{
             laAddSubGroup(p, "pages", "Pages", "Rack pages","la_rack_page",0,0,0,-1,0,laget_CurrentRackPage,0,0,0,0,offsetof(laRackPageCollection,Pages),0);
             laAddSubGroup(p, "pages", "Pages", "Rack pages","la_rack_page",0,0,0,-1,0,laget_CurrentRackPage,0,0,0,0,offsetof(laRackPageCollection,Pages),0);
             sp=laAddSubGroup(p, "current_page", "Current Page", "Current page","la_rack_page",0,0,0,offsetof(laRackPageCollection,CurrentPage),laget_FirstDriverPage,0,laget_ListNext,laset_CurrentRackPage,0,0,0,LA_UDF_REFER);
             sp=laAddSubGroup(p, "current_page", "Current Page", "Current page","la_rack_page",0,0,0,offsetof(laRackPageCollection,CurrentPage),laget_FirstDriverPage,0,laget_ListNext,laset_CurrentRackPage,0,0,0,LA_UDF_REFER);
@@ -1552,7 +1555,6 @@ void la_RegisterInternalProps(){
             laAddStringProperty(p, "script", "Script", "Script content",LA_WIDGET_STRING_MULTI,0,0,0,1,offsetof(laRackPage, Script), 0,0,0,0,0);
             laAddStringProperty(p, "script", "Script", "Script content",LA_WIDGET_STRING_MULTI,0,0,0,1,offsetof(laRackPage, Script), 0,0,0,0,0);
             laAddOperatorProperty(p,"add_rack","Add Rack", "Add a rack into the page", "LA_add_rack", '+', 0);
             laAddOperatorProperty(p,"add_rack","Add Rack", "Add a rack into the page", "LA_add_rack", '+', 0);
             laAddOperatorProperty(p,"remove_driver_page","Remove Page", "Remove this page", "LA_remove_driver_page", L'🗴', 0);
             laAddOperatorProperty(p,"remove_driver_page","Remove Page", "Remove this page", "LA_remove_driver_page", L'🗴', 0);
-            laAddOperatorProperty(p,"remove_input_mapping_page","Remove Page", "Remove this page", "LA_remove_input_mapping_page", L'🗴', 0);
         }
         }
 
 
         //p = laAddPropertyContainer("udf_fail_node", "UDF Failed Node", "Single Wild Data Block Reference", laui_UDFFailNodeItem, sizeof(laUDFFailNode), 0,0,0,0,0); {
         //p = laAddPropertyContainer("udf_fail_node", "UDF Failed Node", "Single Wild Data Block Reference", laui_UDFFailNodeItem, sizeof(laUDFFailNode), 0,0,0,0,0); {
@@ -1672,7 +1674,7 @@ void la_RegisterInternalProps(){
             laAddIntProperty(p, "solid_shadow_length", "Solid Shadow Length", "Solid shadow length for floating panels", 0,0,0,40,0,1, 6, 0,offsetof(LA, SolidShadowLength), 0,0,0,0,0,0,0,0,0,0,0);
             laAddIntProperty(p, "solid_shadow_length", "Solid Shadow Length", "Solid shadow length for floating panels", 0,0,0,40,0,1, 6, 0,offsetof(LA, SolidShadowLength), 0,0,0,0,0,0,0,0,0,0,0);
             laAddIntProperty(p, "wire_color_slices", "Wire color slices", "How many slices of color to give to node sockets", 0,0,0,32, 1, 1, 16, 0,offsetof(LA, WireColorSlices), 0,laset_WireColorSlices, 0,0,0,0,0,0,0,0,0);
             laAddIntProperty(p, "wire_color_slices", "Wire color slices", "How many slices of color to give to node sockets", 0,0,0,32, 1, 1, 16, 0,offsetof(LA, WireColorSlices), 0,laset_WireColorSlices, 0,0,0,0,0,0,0,0,0);
             laAddFloatProperty(p, "wire_thickness", "Wire thickness", "How thick is a wire", 0,0,0,10,0.1, 0.5, 5, 0,offsetof(LA, WireThickness), 0,laset_WireThickness, 0,0,0,0,0,0,0,0,0);
             laAddFloatProperty(p, "wire_thickness", "Wire thickness", "How thick is a wire", 0,0,0,10,0.1, 0.5, 5, 0,offsetof(LA, WireThickness), 0,laset_WireThickness, 0,0,0,0,0,0,0,0,0);
-            laAddFloatProperty(p, "wire_saggyness", "Wire saggyness", "How saggy is a wire", 0,0,0,20,0,0.5, 5, 0,offsetof(LA, WireSaggyness), 0,laset_WireSaggyness, 0,0,0,0,0,0,0,0,0);
+            laAddFloatProperty(p, "wire_saggyness", "Wire saggyness", "How saggy is a wire", 0,0,0,1,0,0.01, 0.5, 0,offsetof(LA, WireSaggyness), 0,laset_WireSaggyness, 0,0,0,0,0,0,0,0,0);
 
 
             ep = laAddEnumProperty(p, "enable_translation", "Enable Translation", "Translate user interface into another language", 0,0,0,1, 0,offsetof(LA, Translation.EnableTranslation), 0,transState, 0,0,0,0,0,0,0,0);{
             ep = laAddEnumProperty(p, "enable_translation", "Enable Translation", "Translate user interface into another language", 0,0,0,1, 0,offsetof(LA, Translation.EnableTranslation), 0,transState, 0,0,0,0,0,0,0,0);{
                 laAddEnumItem(ep, "no", "No", "Use original english string", 0);
                 laAddEnumItem(ep, "no", "No", "Use original english string", 0);
@@ -1705,6 +1707,26 @@ void la_RegisterInternalProps(){
             laAddStringProperty(p,"theme_name","Theme Name","Using theme name",0,0,0,0,0,0,0,laget_UsingTheme,0,laread_UsingTheme,0);
             laAddStringProperty(p,"theme_name","Theme Name","Using theme name",0,0,0,0,0,0,0,laget_UsingTheme,0,laread_UsingTheme,0);
         }
         }
 
 
+        p = laAddPropertyContainer("la_input_mapping_bundle", "Input Mapping Bundle", "Bundle of input mapping data", 0,0,sizeof(laInputMappingBundle), 0,0,1);{
+            laAddSubGroup(p, "mappings","Mappings","Input mappings","la_input_mapping",0,0,0,-1,0,laget_CurrentInputMapping,0,0,0,0,offsetof(laInputMappingBundle,InputMappings),0);
+            laAddSubGroup(p, "current","Current Mapping","Current input mapping","la_input_mapping",0,0,0,offsetof(laInputMappingBundle,CurrentInputMapping),laget_FirstInputMapping,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
+        }
+        p = laAddPropertyContainer("la_input_mapping", "Input Mapping", "Input mapping data", 0,0,sizeof(laInputMapping), 0,0,2);{
+            laAddStringProperty(p, "name", "Name", "The name of this mapping", 0,0,0,"Mapping", 1, offsetof(laInputMapping, Name), 0,0,0,0,LA_AS_IDENTIFIER);
+            laAddSubGroup(p, "entries","Entries","Input mapping entries","la_input_mapping_entry",0,0,0,-1,0,0,0,0,0,0,offsetof(laInputMapping,Entries),0);
+            laAddOperatorProperty(p,"remove","Remove","Remove this mapping","LA_new_remove_mapping",U'🞫',0);
+            laAddOperatorProperty(p,"new_entry","New Entry","New mapping entry","LA_new_input_mapping_entry",'+',0);
+        }
+        p = laAddPropertyContainer("la_input_mapping_entry", "Input Mapping Entry", "Input mapping entry", 0,0,sizeof(laInputMappingEntry), 0,0,1);{
+            ep=laAddEnumProperty(p,"device_type","Device Type","Device type of the input event",0,0,0,0,0,offsetof(laInputMappingEntry,DeviceType),0,0,0,0,0,0,0,0,0,0);
+            laAddEnumItemAs(ep, "KEYBOARD", "Keyboard", "Keyboard input", LA_INPUT_DEVICE_KEYBOARD, 0);
+            laAddEnumItemAs(ep, "JOYSTICK", "Joystick", "Joystick input", LA_INPUT_DEVICE_JOYSTICK, 0);
+            laAddIntProperty(p,"joystick_device","Joystick Device","Joystick device number",0,0,0,0,0,0,0,0,offsetof(laInputMappingEntry,JoystickDevice),0,0,0,0,0,0,0,0,0,0,0);
+            laAddStringProperty(p,"key","Key","Event key",0,0,0,0,1,offsetof(laInputMappingEntry,Key),0,0,0,0,0);
+            laAddStringProperty(p,"message","Message","Target message",0,0,0,0,1,offsetof(laInputMappingEntry,TargetMessage),0,0,0,0,0);
+            laAddOperatorProperty(p,"remove","Remove","Remove this entry","LA_new_remove_mapping_entry",U'🞫',0);
+        }
+
         p = laAddPropertyContainer("la_translation_language", "Language", "Translation language pack", 0,0,sizeof(laTranslationNode), 0,0,1);{
         p = laAddPropertyContainer("la_translation_language", "Language", "Translation language pack", 0,0,sizeof(laTranslationNode), 0,0,1);{
             laAddStringProperty(p, "name", "Name", "The name of this language", 0,0,0,"Unknown", 1, offsetof(laTranslationNode, LanguageName), 0,0,0,0,LA_AS_IDENTIFIER);
             laAddStringProperty(p, "name", "Name", "The name of this language", 0,0,0,"Unknown", 1, offsetof(laTranslationNode, LanguageName), 0,0,0,0,LA_AS_IDENTIFIER);
         }
         }

+ 36 - 13
resources/la_templates.c

@@ -1301,7 +1301,7 @@ void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorIn
             laShowLabel(muil, mc, "Not yet implemened on Windows.", 0, 0);
             laShowLabel(muil, mc, "Not yet implemened on Windows.", 0, 0);
 #endif
 #endif
             laShowLabel(muil, mcl, "Input Mapping:", 0, 0)->Flags|=LA_TEXT_ALIGN_RIGHT;
             laShowLabel(muil, mcl, "Input Mapping:", 0, 0)->Flags|=LA_TEXT_ALIGN_RIGHT;
-            laShowItemFull(muil, mcr, 0, "LA_panel_activator",0,"panel_id=LAUI_input_mapper;text=Configure",0,0);
+            laShowItemFull(muil, mcr, 0, "LA_panel_activator",0,"panel_id=LAUI_input_mapping;text=Configure",0,0);
             laShowLabel(muil, mcl, "Pads/Joysticks:", 0, 0)->Flags|=LA_TEXT_ALIGN_RIGHT;
             laShowLabel(muil, mcl, "Pads/Joysticks:", 0, 0)->Flags|=LA_TEXT_ALIGN_RIGHT;
             laShowItemFull(muil, mcr, 0, "LA_panel_activator",0,"panel_id=LAUI_controllers;text=Configure",0,0);
             laShowItemFull(muil, mcr, 0, "LA_panel_activator",0,"panel_id=LAUI_controllers;text=Configure",0,0);
 
 
@@ -1545,24 +1545,47 @@ void laui_GameController(laUiList *uil, laPropPack *This, laPropPack *Extra, laC
 void lauidetached_GameController(laPanel* p){
 void lauidetached_GameController(laPanel* p){
     la_MakeDetachedProp(p, "la.controllers", "controllers");
     la_MakeDetachedProp(p, "la.controllers", "controllers");
 }
 }
-void laui_InputMapper(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+void laui_InputMappingEntry(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+
+    laUiItem* b=laBeginRow(uil,c,0,0);
+    laShowItem(uil,c,This,"device_type");
+    laUiItem* b1=laOnConditionThat(uil,c,laEqual(laPropExpression(This,"device_type"),laIntExpression(LA_INPUT_DEVICE_JOYSTICK)));{
+        laShowItem(uil,c,This,"joystick_device");
+    }laEndCondition(uil,b1);
+
+    laShowItem(uil,c,This,"key")->Expand=1; laShowItem(uil,c,This,"message")->Expand=1;
+    laShowItem(uil,c,This,"remove")->Flags|=LA_UI_FLAGS_ICON;
+        
+    laEndRow(uil,b);
+}
+void laui_InputMapping(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+
+    laUiItem* b=laBeginRow(uil,c,0,0);
+    laShowItem(uil,c,This,"new_entry");
+    laEndRow(uil,b);
+    
+    laShowItemFull(uil,c,This,"entries",0,0,laui_InputMappingEntry,0)->Flags|=LA_UI_FLAGS_NO_DECAL;
+}
+void laui_InputMappingBundle(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil);
     laColumn* c=laFirstColumn(uil);
     laUiItem* b=laBeginRow(uil,c,0,0);
     laUiItem* b=laBeginRow(uil,c,0,0);
-    laShowItemFull(uil,c,0,"la.input_mapping.pages",LA_WIDGET_COLLECTION_SELECTOR,0,0,0)
+    laShowItemFull(uil,c,0,"la.input_mapping.current",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0)
         ->Flags|=LA_UI_COLLECTION_SIMPLE_SELECTOR;
         ->Flags|=LA_UI_COLLECTION_SIMPLE_SELECTOR;
-    laUiItem* b3=laOnConditionThat(uil,c,laPropExpression(0,"la.input_mapping.current_page"));{
-        laShowItem(uil,c,0,"la.input_mapping.current_page.name");
-        laShowItem(uil,c,0,"LA_add_input_mapping_page")->Flags|=LA_UI_FLAGS_ICON;
-        laShowItem(uil,c,Extra,"LA_input_mapping_rebuild")->Flags|=LA_UI_FLAGS_ICON;
+    laUiItem* b3=laOnConditionThat(uil,c,laPropExpression(0,"la.input_mapping.current"));{
+        laShowItem(uil,c,0,"la.input_mapping.current.name");
+        laShowItem(uil,c,0,"LA_new_input_mapping")->Flags|=LA_UI_FLAGS_ICON;
         laShowSeparator(uil,c);
         laShowSeparator(uil,c);
-        laUiItem* cp=laShowInvisibleItem(uil,c,0,"la.input_mapping.current_page");
-        laShowItem(uil,c,&cp->PP,"remove_input_mapping_page")->Flags|=LA_UI_FLAGS_ICON;;
+        laUiItem* cp=laShowInvisibleItem(uil,c,0,"la.input_mapping.current");
+        laShowItem(uil,c,&cp->PP,"remove");
     }laElse(uil,b3);{
     }laElse(uil,b3);{
-        laShowItem(uil,c,0,"LA_add_input_mapping_page");
+        laShowItem(uil,c,0,"LA_new_input_mapping");
     }laEndCondition(uil,b3);
     }laEndCondition(uil,b3);
     laEndRow(uil,b);
     laEndRow(uil,b);
-    b3=laOnConditionThat(uil,c,laPropExpression(0,"la.input_mapping.current_page"));{
-        laShowItemFull(uil,c,0,"la.input_mapping.current_page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0)->Flags|=LA_UI_FLAGS_NO_DECAL;
+    b3=laOnConditionThat(uil,c,laPropExpression(0,"la.input_mapping.current"));{
+        laUiItem* gui=laMakeEmptyGroup(uil,c,0,0); laUiList* guil=gui->Page; laColumn* gc=laFirstColumn(guil); guil->HeightCoeff=-1;
+        laShowItemFull(guil,gc,0,"la.input_mapping.current",LA_WIDGET_COLLECTION_SINGLE,0,laui_InputMapping,0)->Flags|=LA_UI_FLAGS_NO_DECAL;
     }laEndCondition(uil,b3);
     }laEndCondition(uil,b3);
 }
 }
 void lauidetached_Drivers(laPanel* p){
 void lauidetached_Drivers(laPanel* p){
@@ -2092,7 +2115,7 @@ void la_RegisterBuiltinTemplates(){
     int his=MAIN.InitArgs.HasHistories;
     int his=MAIN.InitArgs.HasHistories;
     int ter=MAIN.InitArgs.HasTerminal;
     int ter=MAIN.InitArgs.HasTerminal;
     int act=MAIN.InitArgs.HasAction&&obj;
     int act=MAIN.InitArgs.HasAction&&obj;
-            laRegisterUiTemplate("LAUI_input_mapper","Input Mapper",laui_InputMapper,0,0,"Controlling",0,0,0);
+            laRegisterUiTemplate("LAUI_input_mapping","Input Mapping",laui_InputMappingBundle,0,0,"Controlling",0,0,0);
             laRegisterUiTemplate("LAUI_controllers", "Controllers", laui_GameController, lauidetached_GameController, 0,0,0,0,0);
             laRegisterUiTemplate("LAUI_controllers", "Controllers", laui_GameController, lauidetached_GameController, 0,0,0,0,0);
     if(obj) laRegisterUiTemplate("LAUI_scene", "Scene", tnsui_ScenePanel, tnsui_DetachedScenePanel, 0, 0, 0,25,25);
     if(obj) laRegisterUiTemplate("LAUI_scene", "Scene", tnsui_ScenePanel, tnsui_DetachedScenePanel, 0, 0, 0,25,25);
     if(obj) laRegisterUiTemplate("LAUI_world_hierachy","World",tnsui_WorldHierachy,0,0,0,0,0,0);
     if(obj) laRegisterUiTemplate("LAUI_world_hierachy","World",tnsui_WorldHierachy,0,0,0,0,0,0);

+ 28 - 3
resources/la_widgets.c

@@ -64,6 +64,7 @@ laWidget _LA_WIDGET_RAW={0};
 laWidget _LA_WIDGET_MAPPER={0};
 laWidget _LA_WIDGET_MAPPER={0};
 laWidget _LA_WIDGET_IMAGE={0};
 laWidget _LA_WIDGET_IMAGE={0};
 laWidget _LA_WIDGET_SCOPE={0};
 laWidget _LA_WIDGET_SCOPE={0};
+laWidget _LA_WIDGET_KNOB={0,LA_UI_FLAGS_KNOB};
 
 
 laWidget *LA_WIDGET_FIXED_GROUP=&_LA_WIDGET_FIXED_GROUP;
 laWidget *LA_WIDGET_FIXED_GROUP=&_LA_WIDGET_FIXED_GROUP;
 laWidget *LA_WIDGET_TAB=&_LA_WIDGET_TAB;
 laWidget *LA_WIDGET_TAB=&_LA_WIDGET_TAB;
@@ -108,6 +109,7 @@ laWidget *LA_WIDGET_RAW=&_LA_WIDGET_RAW;
 laWidget *LA_WIDGET_MAPPER=&_LA_WIDGET_MAPPER;
 laWidget *LA_WIDGET_MAPPER=&_LA_WIDGET_MAPPER;
 laWidget *LA_WIDGET_IMAGE=&_LA_WIDGET_IMAGE;
 laWidget *LA_WIDGET_IMAGE=&_LA_WIDGET_IMAGE;
 laWidget *LA_WIDGET_SCOPE=&_LA_WIDGET_SCOPE;
 laWidget *LA_WIDGET_SCOPE=&_LA_WIDGET_SCOPE;
+laWidget *LA_WIDGET_KNOB=&_LA_WIDGET_KNOB;
 
 
 //============================================== [Draw]
 //============================================== [Draw]
 
 
@@ -211,6 +213,7 @@ int la_ColorSelectorGetMinWidth(laUiItem *ui){
 int la_ValueGetMinWidth(laUiItem *ui){
 int la_ValueGetMinWidth(laUiItem *ui){
     int ExtraW=0;
     int ExtraW=0;
     if(ui->Flags&LA_UI_FLAGS_NO_LABEL){ return LA_RH; }
     if(ui->Flags&LA_UI_FLAGS_NO_LABEL){ return LA_RH; }
+    if(ui->Flags&LA_UI_FLAGS_KNOB){ return LA_RH*TNS_MAX2(1,ui->Extra->HeightCoeff); }
     if((ui->Flags&LA_UI_FLAGS_EXPAND)&&(!(ui->Flags&LA_UI_FLAGS_NO_LABEL))){
     if((ui->Flags&LA_UI_FLAGS_EXPAND)&&(!(ui->Flags&LA_UI_FLAGS_NO_LABEL))){
         ExtraW+=tnsStringGetWidth(transLate(ui->PP.LastPs->p->Name),0,0);
         ExtraW+=tnsStringGetWidth(transLate(ui->PP.LastPs->p->Name),0,0);
     }
     }
@@ -609,6 +612,16 @@ void la_IntDraw(laUiItem *ui, int h){
         if(IsKnob && !ui->Extra->Edit){
         if(IsKnob && !ui->Extra->Edit){
             real ctrx = (real)(_L+_R)/2.0f, ctry=(real)(_U+_B)/2.0f, radius=TNS_MIN2((_B-_U)/2.0f,(_R-_L)/2.0f);
             real ctrx = (real)(_L+_R)/2.0f, ctry=(real)(_U+_B)/2.0f, radius=TNS_MIN2((_B-_U)/2.0f,(_R-_L)/2.0f);
             tnsUseNoTexture();
             tnsUseNoTexture();
+            real dgx=0,dgy=0;
+            if(radius*2>(_B-_U)-LA_RH/5){ dgx = radius/1.5; }
+            if(radius*2>(_R-_L)-LA_RH/5){ dgy = radius/1.5; }
+
+            tnsColor4dv(laThemeColor(bt,LA_BT_BORDER));
+            tnsVertex2d(_L, ctry-dgy); tnsVertex2d(_L, _U); tnsVertex2d(ctrx-dgx, _U); tnsPackAs(GL_LINE_STRIP);
+            tnsVertex2d(_R, ctry-dgy); tnsVertex2d(_R, _U); tnsVertex2d(ctrx+dgx, _U); tnsPackAs(GL_LINE_STRIP);
+            tnsVertex2d(_R, ctry+dgy); tnsVertex2d(_R, _B); tnsVertex2d(ctrx+dgx, _B); tnsPackAs(GL_LINE_STRIP);
+            tnsVertex2d(_L, ctry+dgy); tnsVertex2d(_L, _B); tnsVertex2d(ctrx-dgx, _B); tnsPackAs(GL_LINE_STRIP);
+                
             real verts[52];//24*2+ctr+overlap
             real verts[52];//24*2+ctr+overlap
             tnsMakeCircle2d(&verts[2],24,ctrx,ctry,radius,0);
             tnsMakeCircle2d(&verts[2],24,ctrx,ctry,radius,0);
             tnsVectorCopy2d(&verts[2],&verts[50]);
             tnsVectorCopy2d(&verts[2],&verts[50]);
@@ -723,6 +736,16 @@ void la_FloatDraw(laUiItem *ui, int h){
         if(IsKnob && !ui->Extra->Edit){
         if(IsKnob && !ui->Extra->Edit){
             real ctrx = (real)(_L+_R)/2.0f, ctry=(real)(_U+_B)/2.0f, radius=TNS_MIN2((_B-_U)/2.0f,(_R-_L)/2.0f);
             real ctrx = (real)(_L+_R)/2.0f, ctry=(real)(_U+_B)/2.0f, radius=TNS_MIN2((_B-_U)/2.0f,(_R-_L)/2.0f);
             tnsUseNoTexture();
             tnsUseNoTexture();
+            real dgx=0,dgy=0;
+            if(radius*2>(_B-_U)-LA_RH/5){ dgx = radius/1.5; }
+            if(radius*2>(_R-_L)-LA_RH/5){ dgy = radius/1.5; }
+
+            tnsColor4dv(laThemeColor(bt,LA_BT_BORDER));
+            tnsVertex2d(_L, ctry-dgy); tnsVertex2d(_L, _U); tnsVertex2d(ctrx-dgx, _U); tnsPackAs(GL_LINE_STRIP);
+            tnsVertex2d(_R, ctry-dgy); tnsVertex2d(_R, _U); tnsVertex2d(ctrx+dgx, _U); tnsPackAs(GL_LINE_STRIP);
+            tnsVertex2d(_R, ctry+dgy); tnsVertex2d(_R, _B); tnsVertex2d(ctrx+dgx, _B); tnsPackAs(GL_LINE_STRIP);
+            tnsVertex2d(_L, ctry+dgy); tnsVertex2d(_L, _B); tnsVertex2d(ctrx-dgx, _B); tnsPackAs(GL_LINE_STRIP);
+            
             real verts[52];//24*2+ctr+overlap
             real verts[52];//24*2+ctr+overlap
             tnsMakeCircle2d(&verts[2],24,ctrx,ctry,radius,0);
             tnsMakeCircle2d(&verts[2],24,ctrx,ctry,radius,0);
             tnsVectorCopy2d(&verts[2],&verts[50]);
             tnsVectorCopy2d(&verts[2],&verts[50]);
@@ -734,9 +757,11 @@ void la_FloatDraw(laUiItem *ui, int h){
             tnsVertexArray2d(verts,24); tnsPackAs(GL_LINE_LOOP);
             tnsVertexArray2d(verts,24); tnsPackAs(GL_LINE_LOOP);
             if(Ranged){
             if(Ranged){
                 real arc[52];  int arcindex[26]; // 12 slices 13 ends
                 real arc[52];  int arcindex[26]; // 12 slices 13 ends
-                real range = (real)(Data[i] - min) / (real)(max - min) * TNS_PI * 1.5;
-                tnsMakeArc2d(arc,12,ctrx,ctry,radius,TNS_PI*2.75, TNS_PI*2.75 + range);
-                tnsMakeArc2d(&arc[26],12,ctrx,ctry,radius*0.6,TNS_PI*2.75, TNS_PI*2.75 + range);
+                real range,rstart;
+                if(min<0&&max>0){ rstart=TNS_PI*3.5; range = (real)(Data[i]) / (real)(max) * TNS_PI * 1.5 * max/(max-min);}
+                else{ rstart=TNS_PI*2.75; range = (real)(Data[i] - min) / (real)(max - min) * TNS_PI * 1.5; }
+                tnsMakeArc2d(arc,12,ctrx,ctry,radius,rstart, rstart + range);
+                tnsMakeArc2d(&arc[26],12,ctrx,ctry,radius*0.6,rstart, rstart + range);
                 tnsMakeBridgedIndex(arcindex, 13, 0, 0);
                 tnsMakeBridgedIndex(arcindex, 13, 0, 0);
                 real *color = laThemeColor(bt,LA_BT_TEXT|ui->State); //tnsColor4d(LA_COLOR3(color),0.3);
                 real *color = laThemeColor(bt,LA_BT_TEXT|ui->State); //tnsColor4d(LA_COLOR3(color),0.3);
                 tnsColor4dv(color);
                 tnsColor4dv(color);