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

canton
canton Member Posts: 10 Member
edited October 2024 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,996 mod
    edited August 2024

    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: 10 Member
    edited August 2024

    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,996 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