QML mod for S5 to change default behavior of the 8 pad buttons when using a STEM file

canton
canton Member Posts: 3 Member
edited October 22 in Mapping Traktor

I'm a programmer and I've done some basic QML modifications in the past, but I'd like some help with this trickier challenge. I want like to modify the default behavior for the 8 pads on the Kontrol S5 when loading up a STEM file.

Normally, the upper 4 top pads mute/unmutes the stems. That's great, no change. But the lower 4 pads do a "stem select" which lets you adjust the volume and/or filter when combined with the two encoders. This is useless to me.

The change I'd like to make is so that instead the lower 4 pads toggle FX on/off for the stems:

FX toggling is already implemented, but right now it requires holding down the SHIFT button, at which point the top row turns into four red toggle pads.

So I guess essentially what I want to do is swap this functionality:

UPPER PADS (NO SHIFT) = MUTE/UNMUTE
LOWER PADS (NO SHIFT) = STEM SELECT for vol/filter
UPPER PADS (SHIFT) = TOGGLE FX SEND
LOWER PADS (SHIFT) = RESET VOL/FILTER

to this, which makes a lot more sense to me:

UPPER PADS (NO SHIFT) = MUTE/UNMUTE
LOWER PADS (NO SHIFT) = TOGGLE FX SEND
UPPER PADS (SHIFT) = STEM SELECT for vol/filter
LOWER PADS (SHIFT) = RESET VOL/FILTER

I'd love some help figuring out which files / functions to look at. Thanks for any guidance!

Tagged:

Comments

  • Sûlherokhh
    Sûlherokhh Member, Traktor Mapping Mod Posts: 2,626 mod
    edited August 8

    You can find all the functions you need in the files for S4MK3 (stem pad mode module).

    Excerpts from the file '…\Native Instruments\Traktor Pro 4\Resources64\qml\CSI\S4MK3\S4MK3Stems.qml'

      StemDeckStreams { name: "stems"; channel: deckIdx; encoderStep: 0.05 }
    
    …
      
      //----------------------- Top Pads to stem mutes -----------------------------
      
      Wire { from: "%surface%.pads.1"; to: "stems.1.muted" }
      Wire { from: "%surface%.pads.2"; to: "stems.2.muted" }
      Wire { from: "%surface%.pads.3"; to: "stems.3.muted" }
      Wire { from: "%surface%.pads.4"; to: "stems.4.muted" }
    
    …
    
          //----------------------- Lower Pads to stem FX send -----------------------------
          
          Wire { from: "%surface%.pads.5"; to: "stems.1.fx_send_on" }
          Wire { from: "%surface%.pads.6"; to: "stems.2.fx_send_on" }
          Wire { from: "%surface%.pads.7"; to: "stems.3.fx_send_on" }
          Wire { from: "%surface%.pads.8"; to: "stems.4.fx_send_on" }
    
    

    Enable WiresGroups for shift / !shift to your liking.

    For separate encoder controls for each stem, you need the stem selected identifier:

    MappingPropertyDescriptor { id: propActiveStem; path: deckPropertiesPath + ".stems.active_stem"; type: MappingPropertyDescriptor.Integer; value: 0; }
    

    and the buttons (hold function) to trigger it.

      //------------------------- Lower pads to stem focus ---------------------------
    
      Wire { from: "%surface%.pads.5"; to: HoldPropertyAdapter { path: deckPropertiesPath + ".stems.active_stem"; value: 1; defaultValue: 0 } }
      Wire { from: "%surface%.pads.6"; to: HoldPropertyAdapter { path: deckPropertiesPath + ".stems.active_stem"; value: 2; defaultValue: 0 } }
      Wire { from: "%surface%.pads.7"; to: HoldPropertyAdapter { path: deckPropertiesPath + ".stems.active_stem"; value: 3; defaultValue: 0 } }
      Wire { from: "%surface%.pads.8"; to: HoldPropertyAdapter { path: deckPropertiesPath + ".stems.active_stem"; value: 4; defaultValue: 0 } }
    

    Then you can use 'propActiveStem.value' as a condition (values 1-4) that enables the specific wires for the encoders, like this:

    //------- Loop and beatjump to focused stem's volume and filter amount -------
    
    WiresGroup
    {
      enabled: !module.shift
    
      WiresGroup
      {
        enabled: propActiveStem.value == 1
        
        Wire { from: "%surface%.loop_move";     to: "stems.1.volume_stepped" }
        Wire { from: "%surface%.loop_size";     to: "stems.1.filter_stepped" }
      }
      WiresGroup
      {
        enabled: propActiveStem.value == 2
        
        Wire { from: "%surface%.loop_move";     to: "stems.2.volume_stepped" }
        Wire { from: "%surface%.loop_size";     to: "stems.2.filter_stepped" }
      }
      WiresGroup
      {
        enabled: propActiveStem.value == 3
        
        Wire { from: "%surface%.loop_move";     to: "stems.3.volume_stepped" }
        Wire { from: "%surface%.loop_size";     to: "stems.3.filter_stepped" }
      }
      WiresGroup
      {
        enabled: propActiveStem.value == 4
        
        Wire { from: "%surface%.loop_move";     to: "stems.4.volume_stepped" }
        Wire { from: "%surface%.loop_size";     to: "stems.4.filter_stepped" }
      }
    }
    
    

    I also made a custom group of wires just for resetting all values:

      //---------------- Top pads to reset stem mute, volume and filter -------------
    
      Wire { from: "%surface%.pads.1"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.1.muted"; value: false } }
      Wire { from: "%surface%.pads.1"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.1.volume"; value: 1.0; output: false } }
      Wire { from: "%surface%.pads.1"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.1.filter_value"; value: 0.5; output: false } }
      Wire { from: "%surface%.pads.2"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.2.muted"; value: false } }
      Wire { from: "%surface%.pads.2"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.2.volume"; value: 1.0; output: false } }
      Wire { from: "%surface%.pads.2"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.2.filter_value"; value: 0.5; output: false } }
      Wire { from: "%surface%.pads.3"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.3.muted"; value: false } }
      Wire { from: "%surface%.pads.3"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.3.volume"; value: 1.0; output: false } }
      Wire { from: "%surface%.pads.3"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.3.filter_value"; value: 0.5; output: false } }
      Wire { from: "%surface%.pads.4"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.4.muted"; value: false } }
      Wire { from: "%surface%.pads.4"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.4.volume"; value: 1.0; output: false } }
      Wire { from: "%surface%.pads.4"; to: SetPropertyAdapter { path: "app.traktor.decks." + deckIdx + ".stems.4.filter_value"; value: 0.5; output: false } }
    
    

    Note 1: All names of physical control elements posted here are from S4MK3. S5 uses it's own ID's.

    Note 2: There are two ways to control the stem parameters.

    The first one uses the internal module 'StemDeckStreams', with its 'name' as an ID for the the path (' path: "name."+channel#+"parameter").

    The second is a direct manipulation of the parameters, with the path from this list:

    "app.traktor.decks.X.stems.Y.stem_info_exists"
    "app.traktor.decks.X.stems.Y.color_id"
    "app.traktor.decks.X.stems.Y.volume"
    "app.traktor.decks.X.stems.Y.muted"
    "app.traktor.decks.X.stems.Y.filter_value"
    "app.traktor.decks.X.stems.Y.filter_on"
    "app.traktor.decks.X.stems.Y.fx_send_on"
    "app.traktor.decks.X.stems.Y.fx_send"
    "app.traktor.decks.X.stems.Y.level"

    and with adapters depending on the control element used.

    TriggerPropertyAdapter, DirectPropertAdapter, TriggerPropertyAdapter, EncoderPropertyAdapter etc. Search in all the qml files for the name 'Adapter' and you will find a lot of instances of how they are used and with what arguments to control the parameters. In the 'custom reset list' i implemented the SetPropertyAdapter as an example.

    Note 3: I almost forgot! Happy hunting and much joy in coding! Nice to have someone new in the coding crew! 😄

  • canton
    canton Member Posts: 3 Member
    edited August 8

    I think I understand: you're showing me where one would look in the case of a S4MK3, but I should be looking in that huge qml/CSI/S5/S5Deck.qml file (181KB!) where I see wirings like this.

    Update: Worked GREAT. Super simple to swap out the appropriate !shift and shifts and pad locations. Now if only I could gain some confidence that Traktor won't crash when loading in stems! 😜 BTW I noticed you also replied to my crash report. I thought you should know that I experienced those crashes with unmodified QML files (a fresh re-install.)

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

    Awesome. :)

    Well, you can use the same pieces of code for different controllers. Just make sure to use the right names/ID's.

    I have yet to connect my S5 to test if i can crash Traktor the way you did.

This discussion has been closed.
Back To Top