Mapping something (e.g. loop size/out) to jog wheel

1151618202123

Answers

  • DuneArchitect
    DuneArchitect Member Posts: 19 Member

    Wow this was amazing to find. Years ago with the S4 mk2 I had an elaborate mapping that would allow me to scroll through my Library using the jogwheels when the browser is open. I was very disappointed to see this had been removed in my new S4 MK3, so this thread gives me hope. I'm a programmer as well, so once I get a better understanding of the NI qml structure I will be able to help out as well.


    I want to clarify something though: I use Modifiers in the mapping screen to change the function of many buttons. ie. pressing "View" to open the browser fullscreen sets M1 to 1, which is then a condition for various mappings to override their default behavior. Having the jogwheels disabled for track control and instead scrolling the library was dependent on the M1 modifier. Did I read correctly that you cannot access the values of Modifiers from within the qml files?

    In that case I wonder if there's some other unused state I could use and flick "on" when I open the browser and use that as a condition for override the jogwheel control to Scroll the browser. Having to hold Grid is really not ideal. (Also I had a mapping where holding Shift made the jogwheel scroll through the Library at a much fast rate, and I would like to be able to replicate this again)

  • Sûlherokhh
    Sûlherokhh Member, Traktor Mapping Mod Posts: 2,627 mod

    You can create any 'modfier' you like, as many as you like, and use each one as a 'condition' for any kind of function with the qml file. There is one in use when you press the GRID button, just as there is one in use when you press the SHIFT button...

  • DuneArchitect
    DuneArchitect Member Posts: 19 Member
    edited December 2023

    Okay I don't fully understand everything in the qml yet, but here's what I've come up with:

    // CSI/Common/ExtendedBrowserModule.qml
    property bool isFullScreen: false
    		
    // Toggle isFullScreen
    // May cause issues if for some reason Traktor starts with the browser fullscreen..		    
    Wire {
      from: "%surface%.browse.view";
      to: ButtonScriptAdapter {
        onPress: {
          module.isFullScreen = !module.isFullScreen;
        }
      } // end ButtonScriptAdapter
    } // end Wire view
    
    WiresGroup {
      Wire {
        from: "%surface%.jogwheel.rotation";
        to: "browser.list_navigation";
        enabled: module.isFullScreen;
      } // end Wire jogwheel.rotation
    } // end WiresGroup
    


    // CSI/S4MK3/S4MK3Deck.qml
    
    property bool browserFullScreen: false
    
    // Toggle isFullScreen
    // May cause issues if for some reason Traktor starts with the browser fullscreen..
    Wire {
      from: "%surface%.browse.view";
      to: ButtonScriptAdapter {
        onPress: {
          module.browserFullScreen = !module.browserFullScreen;
        }
      } // end ButtonScriptAdapter
    } // end Wire view
    
    // Disable Jogwheel on Track when in fullscreen
    Wire {
      from: "%surface%.jogwheel"
      to: "turntable"
      enabled: !gridAdjustEnableProp.value && !module.browserFullScreen
    }
    
    // Also disabled haptic ticks using same method
    Wire {
      from: "%surface%.jogwheel.haptic_ticks"
      to: DirectPropertyAdapter { path: "mapping.settings.haptic.ticks_density"; input: false }
      enabled: (jogMode.value === JogwheelMode.Jogwheel || jogMode.value === JogwheelMode.CDJ) && !module.shift && !module.browserFullScreen
    }
    

    Now the above "works" in achieving scrolling of the browser via the jogwheels when the browser is full screen, however it's not quite ergonomic enough to be practical. I would like to achieve the following:

    1. Slightly lower sensitivity of the jogwheel to the browser list scroll
    2. Hold shift to greatly increase sensitivity and scroll speed / acceleration
    3. Drop jogwheel haptic tension to 0 when opening up the fullscreen browser

    Now I see in the Grid Adjust section there is an EncoderScriptAdapter that is obviously adjusting sensitivity of the ticks, but it was not obvious to me how to use this adapter to plug in to the browser.list_navigation

    Also when scrolling quickly with the jogwheel, the scrolling would occasionally stop for a moment before continuing, not sure what is causing that. Previously when mapping my S4 Mk2, I was able to increase acceleration of the scrolling while shift was held and actually make it to the bottom of a 10,000+ track library fairly quickly.

    Finally, I tried a few different techniques based on the code relating to s4mk3.jogwheel.tension in the S4MK3.qml file, but with no luck. If you have any advice (or resources) on how I might achieve the above, please let me know!

  • Sûlherokhh
    Sûlherokhh Member, Traktor Mapping Mod Posts: 2,627 mod

    @DuneArchitect :

    Seeing you work on this makes me grin in happiness. :)


    (1) Slightly lower sensitivity of the jogwheel to the browser list scroll

    For the first, using an RelativePropertyAdapter for finetuning is the easiest way to achieve it.

    WiresGroup {
      Wire {
        from: "%surface%.jogwheel.rotation";
        to: "browser.list_navigation";
        enabled: module.isFullScreen;
      } // end Wire jogwheel.rotation
    } // end WiresGroup
    
    

    instead of 

        to: "browser.list_navigation";
    
    

    try this one (for a direct approach)

        to: RelativePropertyAdapter { path: "app.traktor.browser.list.select_up_down"; wrap: true; step: 1; mode: RelativeMode.Stepped }
    

    decrease 'step: 1' to 'step: 0.5' or less to reduce sensitivity.


    (2) Hold shift to greatly increase sensitivity and scroll speed / acceleration

    You can create two wires, one with more fine control, one that jumps in larger chunks through the list. Just add SHIFT to the list of enabling switches.

    enabled: module.isFullScreen && module.shift // for crude control
    
    
    enabled: module.isFullScreen && !module.shift // for fine control
    


    (3) Drop jogwheel haptic tension to 0 when opening up the fullscreen browser


    You tried with 'ticks_density. But those are the 'bumps', not the tension.

     MappingPropertyDescriptor { path: "mapping.settings.haptic.ticks_density"; type: MappingPropertyDescriptor.Integer; value: 0; min: 0; max: 2 }
    

    This is what you need to manipulate:

     MappingPropertyDescriptor { path: "mapping.settings.haptic.tension"; type: MappingPropertyDescriptor.Integer; value: 50; min: 0; max: 255 }
    

    You need to give it a name and save the current value as a temporary integer before setting the value to zero (no tension), so that you can return the tension to it's original value when you exit the browser mode.


    I hope you get it to work. Tell us what you find... :)

  • Quade
    Quade Member Posts: 245 Advisor

    For Jogwheel for S3, this is the complete code:

     Turntable { name: "turntable"; channel: module.deckIdx; color: deckColor }
    
     AppProperty { id: loopActive; path: "app.traktor.decks." + module.deckIdx + ".loop.is_in_active_loop" }
    
     WiresGroup {
       enabled: !gridAdjustEnableProp.value && module.active
       Wire { from: "%surface%.jogwheel.rotation"; to: "turntable.rotation" }
       Wire { from: "%surface%.jogwheel.speed"; to: "turntable.speed" }
       Wire { from: "%surface%.jogwheel.touch"; to: "turntable.touch"; enabled: !jogMode.value }
       Wire { from: "%surface%.shift"; to: "turntable.shift" }
    
       ...
     }
    

    As you can see, all four jogwheel functions are directed by 'turntable.' functions. So, all details are hidden inside this compiled (not open) library module:

     Turntable { name: "turntable"; channel: module.deckIdx; color: deckColor }
    

    Too bad it doesn't accept any arguments for tension, speed, or basically anything. It is possible to construct an open module containing all needed basic functions, but it's a lot of work and may not work at all...

    ---------------------------------------------------------------------------------------------------------------------------------------------------------

    So basically, There is NO WAY of adjusting the Values at all, other than only having this.... That Sucks.

    I was planning on changing the values myself to see what the best values are for both the Jog Wheel Tension & the Hot Cue performance pad sensitivity.

    Anyhoooo Thanks @Sûlherokhh Appreciate all your efforts.

    Warm Regards

  • DuneArchitect
    DuneArchitect Member Posts: 19 Member

    Thank you for the response! Here's what I've found:

    I had no problem utilizing shift to differentiate two different speeds for scrolling, however: RelativePropertyAdapter seems to perform rounding, so does not actually accomplish what I need of a slower scroll speed. From my tests, setting the value to 0.5 and 1 has the same result, and values lower than 0.5 perform no navigation at all. Values greater than 1 do jump the list faster, which led me to believe 0.5 was just being rounded up to 1.

    I'm so curious as to how you found the app.traktor.browser.list.select_up_down. as I don't see it anywhere in the qml folder. I think the best option still may be to use the EncoderScriptAdapter, though I have no idea how I might utilize the "path" from the RelativePropertyAdapter within it, based on the example from the Grid shift. Similarly don't understand how to plug into modifying the haptic tension.

    You tried with 'ticks_density'. But those are the 'bumps', not the tension

    Removing the haptic bumps were intentional, I didn't post my attempts to resolve tension because I didn't find any success. I saw both of those MappingPropertyDescriptors in the S4MK3.qml, but I don't understand how to set or use them.

    For example, let's say we wanted to remove haptic tension whe the shift button is pressed:

        Wire
        {
          from: "%surface%.shift";
          to: ButtonScriptAdapter
          {
            onPress:
            {
              module.encoderMode = module.treeMode;
              module.shift = true;
              // Something like this?
              mapping.settings.haptic.tension = 0;
            }
            onRelease:
            {
              module.encoderMode = module.listMode;
              module.shift = false;
              mapping.settings.haptic.tension = 150;
            }
          }
        }
    


    Thank you for the feedback! I'm really excited to get this script tuned as I understand a bit more about qml and what functions / modules are available.

  • Sûlherokhh
    Sûlherokhh Member, Traktor Mapping Mod Posts: 2,627 mod
    edited December 2023

    To manipulate the tension you first have to adjust the following line

    MappingPropertyDescriptor { path: "mapping.settings.haptic.tension"; type: MappingPropertyDescriptor.Integer; value: 50; min: 0; max: 255 }
    

    so that it looks something like this (the name is your choice, but the first letter must be lower case):

    MappingPropertyDescriptor { id: hapticTensionProp; path: "mapping.settings.haptic.tension"; type: MappingPropertyDescriptor.Integer; value: 50; min: 0; max: 255 }
    

    Then, define a variable to store the original value:

    property int storeHapticTension: 0
    

    Then you can do the ButtonScriptAdapter:

    Wire
        {
          from: "%surface%.shift";
          to: ButtonScriptAdapter
          {
            onPress:
            {
              module.encoderMode = module.treeMode;
              module.shift = true;
              // Something like this:
              storeHapticTension = hapticTensionProp.value;
              hapticTensionProp.value = 0;
            }
            onRelease:
            {
              module.encoderMode = module.listMode;
              module.shift = false;
              // Something like this:
              hapticTensionProp.value = storeHapticTension ;
            }
          }
        }
    


    ********

    app.traktor.browser.list.select_up_down

    This is one of those Traktor parameter that we (those that have done qml mods in the past) have scraped from the original Traktor exe file by loading it into a hex-editor and reading it. The most useful info found is usually bunched up in a few places and once you find one piece (by accident, by using good search terms or simply by browsing the text), there is usually something useful close by.

    Here is a bare bones compilation (a lot you can play with):

    If the RelativePropertyAdapter is not suited, you may want to try the EncoderScriptAdapter.

    There is an example which is used for adjusting the Beat Grid (turn jogwheel while holding the Grid button).

     Wire
     {
       enabled: gridAdjustEnableProp.value;
       from: "%surface%.jogwheel";
       to: EncoderScriptAdapter
       {
         onTick:
         {
           const minimalTickValue = 0.0035;
           const rotationScaleFactor = 20;
           if (value < -minimalTickValue || value > minimalTickValue)
             gridAdjust.value = value * rotationScaleFactor;
         }
       }
     }
    

    You will need to name the browser list value like this to plug it in:

     AppProperty { id: listSelectBrowserProp; path: "app.traktor.browser.list.select_up_down" }
    

    and then you can manipulate listSelectBrowserProp.value instead of gridAdjust.value.

    Btw, minimalTickValue is used to define a minimum turn speed for the jogwheel to react at all, rotaionScaleFactor defines the turning sensitivity. value is the tick count of the jogwheel (or other endless encoder) and is usually a fractional real number, as you can see when observing the used minimalTickValue.

    Plug it in and play with the numbers. Enjoy! :)

  • DuneArchitect
    DuneArchitect Member Posts: 19 Member

    So I haven't been able to affect the MappingPropertyDescriptor or AppProperty as described.

    First I consolidated all my changes to the S4MK3Deck.qml file, and when everything was setup neither the tension drop nor the scrolling worked. When I didn't have success there I tried manipulating the tension property from within the S4MK3.qml where it was already defined instead. Still no luck:

    MappingPropretyDescriptor { id: hapticTensionProp; path: "mapping.settings.haptic.tension"; type: MappingPropertyDescriptor.Integer; value: 50; min: 0; max: 255; }
    
    Wire {
      from: "%surface%.browse.view";
      to: ButtonScriptAdapter {
        onPress: {
          hapticTensionProp.value = 0;
        }
      }
    }
    

    Based on what we discussed, the above should set the tension to 0 the first time the View button is pressed (and it would remain at 0 in this example). However there is no change. So clearly this is not directly editing the value like we intend. This was just adding the id property to the already existing MappingPropertyDescriptor, and adding the Wire block.

    Similarly using AppProperty for app.traktor.browser.select_up_down in S4MK3Deck.qml , there seems to be no effect. I added a line of code inside the minimum-tick if statement to make sure it and the EncoderScriptAdapter were being run, and indeed it was. Just not moving the list, or, based on the issue with the haptics, not actually editing the value in the correct way:

    // S4MK3Deck.qml
    
    AppProperty { id: browserListSelectProp; path: "app.traktor.browser.list.select_up_down" }
    
    WiresGroup {
      enabled: module.isFullScreen
      
      Wire {
        enabled: !module.shift;
        from: "%surface%.jogwheel.rotation";
        to: EncoderScriptAdapter {
          onTick: {
            const minimalTickValue = 0.0035;
            const rotationScaleFactor = 20;
            if (value < -minimalTickValue || value > minimalTickValue) {
              browserListSelectProp.value = value * rotationScaleFactor;
            }
          }
        }
      }
    }
    

    Are there any example mods using this method I can peek into?

  • Sûlherokhh
    Sûlherokhh Member, Traktor Mapping Mod Posts: 2,627 mod

    Hm.

    Did you make a syntax error? These things always happen to be the reason for me why things don't work. Some syntax errors don't crash Traktor, some do:

    MappingPropretyDescriptor { id: hapticTensionProp; path: "mapping.settings.haptic.tension"; type: MappingPropertyDescriptor.Integer; value: 50; min: 0; max: 255; }
    
    Wire {
      from: "%surface%.browse.view";
      to: ButtonScriptAdapter {
        onPress: {
          hapticTensionProp.value = 0;
        }
      }
    }
    

    In particular this

    MappingPropretyDescriptor 
    

    should be this

    MappingPropertyDescriptor 
    

    I am always hoping it's something stupidly simple at the root... :p

  • DuneArchitect
    DuneArchitect Member Posts: 19 Member

    Because I'm doing this development on the ubuntu shell on my windows machine, copying the changes over is a bit cumbersome, so when posting here I've been just re-typing out the code in many cases. I reviewed my changes line-by-line, in particular just in the S4MK3.qml file that should be a very straightforward setting: and I see no syntax errors. In particular in that file the MappingPropertyDescriptor is already defined, I just added the id and the Wire block.

  • Sûlherokhh
    Sûlherokhh Member, Traktor Mapping Mod Posts: 2,627 mod

    I always use copy-paste in the forum, that's why i asked.

    Anyway, if you edit S4MK3.qml (the basic controller file) you can't write it like this:

    "%surface%.browse.view"
    

    %surface% is defined at the bottom of the file for S4MK3Side.qml

     S4MK3Side
     {
       id: left
       name: "left"
       surface: "s4mk3.left"
       propertiesPath: "mapping.state.left"
       topDeckIdx: 1
       bottomDeckIdx: 3
       hapticHotcuesEnabled: hapticHotcuesEnabledProp.value
     }
    
     S4MK3Side
     {
       id: right
       name: "right"
       surface: "s4mk3.right"
       propertiesPath: "mapping.state.right"
       topDeckIdx: 2
       bottomDeckIdx: 4
       hapticHotcuesEnabled: hapticHotcuesEnabledProp.value
     }
    
    

    So, either place the wire in S4MK3Side.qml (for both buttons at the same time), or write the wire(s) like this:

    Wire {
      from: "s4mk3.left.browse.view";
      to: ButtonScriptAdapter {
        onPress: {
          hapticTensionProp.value = 0;
        }
      }
    }
    

    and/or

    Wire {
      from: "s4mk3.right.browse.view";
      to: ButtonScriptAdapter {
        onPress: {
          hapticTensionProp.value = 0;
        }
      }
    }
    

    See, if it works now.

    Also, the EncoderScriptAdapter may have trouble making browserListSelectProp.value accept anything other than an integer. In addition, the value does not gravitate/centre around zero, with values that are not zero having an effect plus resetting the value back to zero (as with the grid adjust property). The EncoderScriptAdapter may work with addition/subtraction rather than multiplication.

      Wire {
        enabled: !module.shift;
        from: "%surface%.jogwheel.rotation";
        to: EncoderScriptAdapter {
          onTick: {
            const minimalTickValue = 0.0035;
            const rotationScaleFactor = 20;
            if (value < -minimalTickValue || value > minimalTickValue) {
              // browserListSelectProp.value = value * rotationScaleFactor;
              browserListSelectProp.value = browserListSelectProp.value + (value * rotationScaleFactor);
            }
          }
        }
      }
    

    🦋

  • DuneArchitect
    DuneArchitect Member Posts: 19 Member

    Okay so changing the S4MK3.qml file to use "s4mk3.left.browse.view" did work on resetting the tension! Now I just need to figure out why adding that MappingPropertyDescriptor to the S4MK3Deck.qml file is not having the same effect.. I'll also look into the EncoderScript changes, I also had a hunch that the floats might not be sitting well with the Integer values of the list_up_down.

    Thank you so much for the feedback! This would otherwise take me forever in tinkering.

  • Sûlherokhh
    Sûlherokhh Member, Traktor Mapping Mod Posts: 2,627 mod

    Once you have the MappingPropertyDescriptor in the foundation file (S4MK3.qml), you don't have to (and shouldn't) define it anywhere else (apart from the basic screen file, which needs an additional MappingProperty). Once you give it an ID, you can read and adjust it's name.value from everywhere.

    The browser list property as well as the jogwheel wires are best placed in S4MK3Deck.qml. Too bad my S4 is sitting in storage or i would co-tinker to get it right.

    Please post your progress... :)

  • DuneArchitect
    DuneArchitect Member Posts: 19 Member

    @Sûlherokhh okay commenting out the original MappingPropertyDescriptor from within the S4MK3.qml file made the tension reset work as expected within S4MK3Deck.qml. So it seems having the descriptor defined twice was causing the issue.

    Also, using the addition in the EncoderScript as you suggested has gotten me results. I'm not playing around with two variants, seeing which will give me the fine-grained control I'm looking for. Getting the accelerated scrolling was easy enough using the rotation scale factor method, but still trying to find the right settings for appropriate "normal" scrolling behavior:

    Wire {
      enabled: !module.shift;
      from: "%surface%.jogwheel.rotation";
      to: EncoderScriptAdapter {
        onTick: {
          const minTickValue = 0.0010;
          const maxTickValue = 0.0020;
          if (value > -maxTickValue || value < maxTickValue) {
            if (value < -minTickValue || value > minTickValue) {
              if (value < 0) {
                browserListSelectProp.value = browserListSelectProp.value - 1;
              } else if (value > 0) {
                browserListSelectProp.value = browserListSelectProp.value + 1;
              }
            }
          }
        } // end onTick
      }
    }
    
    
    Wire {
      enabled: module.shift;
      from: "%surface%.jogwheel.rotation";
      to: EncoderScriptAdapter {
        onTick: {
          const maxTickValue = 0.0030;
          const rotationScaleFactor = 800;
          if (value > -maxTickValue || value < maxTickValue) {
            browserListSelectProp.value = browserListSelectProp.value + value * rotationScaleFactor;
          }
        } // end onTick
      }
    }
    


  • Sûlherokhh
    Sûlherokhh Member, Traktor Mapping Mod Posts: 2,627 mod

    ... commenting out the original MappingPropertyDescriptor from within the S4MK3.qml file made the tension reset work as expected within S4MK3Deck.qml. So it seems having the descriptor defined twice was causing the issue.

    I think you should keep the MappingPropertyDescriptor in S4MK3.qml. You can still access the value from all subfiles, like from S4MK3Deck.qml.

    I may copy your EncoderScriptAdapter code for my own S4 once you get it fine-tuned. :)

    For your information: The encoder spits out a value that is exactly zero when it isn't turned, a positive number when it is turned clockwise (exact value depends on the turn speed) and a negative number when turned counterclockwise.

    That means this code

    (value < -minTickValue || value > minTickValue )
    

    simply measures if the absolute value (positive or negative) exceeds minTickValue. Not sure if that was understood. ;-)

Back To Top