HOW TO work with Modifiers in the MIDI Mapping XML

Hello,

While working on my Denon MC7000 monster controller mapping, I’ve defined a secondary/custom SHIFT+ key, and that suddenly opened up a VAST field of available buttons for extra control mappings. For many buttons, it was straight forward just press the custom SHIFT and the button, and voila - one more layer of control options is suddenly becoming available.

In the case of MC7000, we have a pair of buttons on both sides that are for controlling the Views in Serato. I chose the L.PREP button as “Controller Shift Key”, and now DJ allows me to overmap ANY (almost) button on the controller with an additional command, on top of the built-in Shift button, which in my case makes the controller itself to send different message than the No-Shift state.

For most buttons, it was straight forward to make the custom Shift actions. But not for all. Examples are the Pad Modes. The Pads send the same MIDI message, so the app evaluates the selected PadMode, when performing actions in the context of Cue/Loop/Slicer/Sampler and their extra sub modes. So in my case, mapping the Pads was limited to only one extra command with the Custom SHIFT key by default. But I wasn’t happy with this. I want to overmap more pads and use extra functions in the context of the different pad modes.

Another example is the FX controll buttons/knobs/rotaries. MC7000 is designed for Serato primarily, and (at that time) Serato has only 2FX units, assignable to the 4 decks.In Djay, we have FX unit bound to each of the decks, so I need to be able to separately control 4 FX units, while the controller is sending unique messages for only two FX units. Yet, Djay is able to independently control 4FX units. They somehow do it.

So I started to reverse engineer the way Algoriddim is doing their mappings and how they implement the separation of the Pad Modes, doing different stuff in the context of the selected PadMode, and how we can control 4FX slots, while the controller is sending unique messages for only 2.

The great news is that Djay has “modifiers” built in. They are not exposed for the users to mess with them in the UI, but are there, and freely available to use as conditions, given that the user is brave enough to mess with the XML (fairly simple job, with a moderate risk though).

Following my work on the XML and the good progress that I did with the MC7000 mapping, I want to share my Know How, and allow others to benefit from the available modifiers work there is. I’ve also created an enhancement request to expose the XML “condition” string in the UI, so we don’t need to mess with the XML and get the needed context filtering right from the UI

So here is what I found:

  1. The Pad Modes are stored in 4 modifiers - one for each of the decks. Here is an example:
			<key>condition</key>
			<string>modifier2 == 13</string>
			<key>keyPath</key>
			<string>turntable2.clearSavedLoop8</string>

modifier2 sits for deck2 Pad Mode state flag.
The value “13” is the “Pad Mode Saved Loop”
The Command that the button does in the mapping is “turntable2.clearSavedLoop8” to clear the Saved Loop at Slot 8 on Deck2. Simple.

Another example is the Cue Points control:

			<key>condition</key>
			<string>modifier1 == 0</string>
			<key>keyPath</key>
			<string>turntable1.clearCuePoint3</string>

modifier1 holds the pad mode state for Deck1 and the “0” is Pad Mode “Cue”. The quoted action is to clearCuePoint3 on Deck1 (turntable1).

Bellow is the list of the modifiers and their values known to me at this point:

  1. modifier1-4 == 0 - Pad Mode Hot Cue
  2. modifier1-4 == 2 - Instant FX
  3. modifier1-4 == 3 - Pad Mode Cue Loop
  4. modifier1-4 == 7 - Pad Mode Bounce Loop a.k.a Loop Roll. It’s “8” in the Mixon8 mapping?
  5. modifier1-4 == 9 - Pad Mode Slicer
  6. modifier1-4 == 10 - Pad Mode Slicer Loop
  7. modifier1-4 == 11 - Pad Mode Sampler
  8. modifier1-4 == 12 - Pad Mode Sampler - maybe one of them is velocity sensitive, I don’t know. They seem to invoke the same action
  9. modifier1-4 == 13 - Pad Mode Saved Loop
  10. modifier1-4 == 15 - PitchPlay?
  11. modifier5 == 0 - Deck1 selected
  12. modifier5 == 1 - Deck3 selected
  13. modifier6 == 0 - Deck2 selected
  14. modifier6 == 1 - Deck4 selected

And I have no clue what the values for these PadModes are:

  • Pad Mode Manual Loop - modifier1-4 == ?
  • Pad Mode Skipping - modifier1-4 == ?
  • Pad Mode Neural Mix - modifier1-4 == ?
  • Pad Mode Looper - modifier1-4 ==
  • Pad Mode User1 - modifier1-4 == ?
  • Pad Mode User2 - modifier1-4 == ?
  • Pad Mode User3 - modifier1-4 == ?
  • Pad Mode User4 - modifier1-4 == ?
  • Pad Mode Empty - modifier1-4 == ?

I guess you start seeing the picture now. Please note that I’m not sure if the PadMode values are common for all controllers. I suspect they are, and there’s a well thought-out convention for modifiers and their states that the Algoriddim Dev & Controller Mapping Teams are using, but that’s just a guess. Perhaps there will be an official public document at some future point describing this. For now, I can only explain what I’ve concluded by my mapping efforts. Further to that, it appears that windows mapping for MC7000 is in JSON, not XML. There’s not a big diff between XML & JSON, and both do the same, but apparently Djay knows JSON too.

Another goal that I’ve had was to map the FX Slot ON/Off buttons for assigning NM effects. NM effects are assignable in the 3 way mode only - Drums/Harmonics/Accapella. MC7000 has 3FX knobs with On/Off buttons underneath, so I want the buttons to set C.SHIFT+FX1->Drums; C.SHIFT+FX2->Harmonics; C.SHIFT+FX3->Vocals. By default, it worked well but only for Deck 1 & 2. As explained above, Serato has only 2FX units, and Denon made the 7K sending unique MIDI messages for only 2 FX control units. I needed to have separate control over Deck 3&4, and that wasn’t possible out of the box.

So I opened up the XML, and figured out that switching from Deck 1 to 3 is tracked in “modifier5” as:

			<key>condition</key>
			<string>modifier5 == 0</string>

			<key>condition</key>
			<string>modifier5 == 1</string>

The value “0” stands for Deck 1 active on the controller, the value “1” stands for Deck 3 active on the controller.

Respectively, Decks 2/4 are tracked in modifier6 as 0 for Deck2 and 1 for Deck4

			<key>condition</key>
			<string>modifier6 == 0</string>

			<key>condition</key>
			<string>modifier6 == 1</string>

You can probably get the picture now.

While researching how other controllers (e.g. RANE One) are mapped, I found dual condition logic in the format bellow, which is welcome too.

			<key>condition</key>
			<string>(modifier2 == 1) &amp;&amp; (modifier4 == 0)</string>

Again dual condition in the Numark NS4FX mapping

			<key>condition</key>
			<string>modifier1 == 1 AND modifier5 == 0</string>

I’ve also checked how the RANE Performer mapping looks like, and there seem to be a totally different names of the modifiers, like “LeftDeck”, PadMode, etc … The modifier names are not absolute, but the principals are the same.

HOWTO map it and navigate in this mess (TODO: Create screenshots for easier understanding).

First step:

  1. Choose a Custom Shift buttom. I chose the L.PREP button to be my Custom Shift button. It has to be Unassigned from Any other action than acting as a “Controller Shift Key” in the “General” mapping target. I will now refer to Custom Shift as the LP button.
  2. Keep the LP pressed (as if a SHIFT), and press any other key on the controller. For example, I’m pressing Deck1->FX1->ON button. This creates a new entry in the mapping table, in my case it’s “NOTE C-1 (SHIFT)” with empty action.
  3. Now just map the message “NOTE C-1 (SHIFT)” to do something. I did “Deck1” → “FX1 Assignment Drums”.

I now have a 3rd action assigned to FX1 On/Off. The default is FX1 ON/OFF; the Built-in Shift performs “FX1 Select Next”, and now the LP (Custom Shift) does “FX1 Assignment Drums”. Wow. that’s just great. But it’s only for Deck1. It wouldn’t work if I switch to Deck3 on the controller, as the MIDI message of FX1/2 doesn’t take into account if Deck 1 or 3 is selected. It’s always the same. So I banged my head a little, and decided to do this research and figure out how the Djay team is handling it for the FX controls mapping - they have the same issue, and have made it to work right for Decks 1/3 & 2/4. So I did what they do:

  1. Now that you have the mapping for “Deck1” → “FX1 Assignment Drums”, Duplicate it. On the Duplicate, change the Deck to “Deck3”. Now, when you press the FX1 ON button, it will assign the NM Drums channel to both Deck 1 and Deck 3. Not ideal, for sure - I want Dry signal on the Deck1 Drums, and wet on the Deck3 Drums.

  2. Get & Open the XML of the mapping and search for “turntable1.fx1RoutingInstrumental”. In my case it looks like this

		<dict>
			<key>keyPath</key>
			<string>turntable1.fx1RoutingInstrumental</string>
			<key>midiChannel</key>
			<integer>8</integer>
			.... other tags....
		</dict>

As you can see there is no “condition” tag in here. The command is executed unconditionally, every time the button is pressed. Because we’ve created Duplicate entry to handle Deck3, we should also have the same section, but for the other deck (turntable3)

		<dict>
			<key>keyPath</key>
			<string>turntable3.fx1RoutingInstrumental</string>
			<key>midiChannel</key>
			<integer>8</integer>
			.... other tags....
		</dict>

Once you’ve located them,

  1. For “turntable1” add the set of “condition” lines, bellow the “dict” tag, so the entry will look like.
		<dict>
			<key>condition</key>
			<string>modifier5 == 0</string>
			<key>keyPath</key>
			<string>turntable1.fx1RoutingInstrumental</string>
			<key>midiChannel</key>
			<integer>8</integer>
			.... other tags....
		</dict>

And for Deck3, it’s the same, but with “modifier5” value of “1”

		<dict>
			<key>condition</key>
			<string>modifier5 == 1</string>
			<key>keyPath</key>
			<string>turntable3.fx1RoutingInstrumental</string>
			<key>midiChannel</key>
			<integer>8</integer>
			.... other tags....
		</dict>
  1. Save and put the modified XML mapping file back in place. (TODO: Add backup instructions, how to do it on iOS, default locations of the files etc.)
    You need to restart the app to reload the new mapping XML from the file - this restart applies to iOS, MacOS, Windows.

  2. It’s done - now Deck 1 & Deck 3 will have independent management of the FX1 Slot assignment - be in on the Deck, or the Drums NM channel.

  3. The same approach is applicable to everything else.
    a. Hit the Custom Shift + the button and set a mapping command
    b. Duplicate it as needed (either FX units or Pad Modes, whatever you need)
    c. Edit the XML, insert the “condition section” where appropriate
    d. Put the XML back in place, restart the app and enjoy!

I’m very excited about it, and wanted to describe it quickly, so other can benefit and enjoy from it too. There’s some more work to make this a propper manual/HOWTO, and I’ve left few “TODO” sections here and there. This deserves screenshots, per-platform instructions, etc. Probably clarity of the description too. Yet, hopefully, this is already clear enough and this knowledge and instructions will already be useful to others too.

Happy Mapping!

3 Likes

Thanks for sharing @Kaloyan. This is very helpful.

Testing more on other controller, my assumption that setting the modifier1-6 states happens automatically when selecting PadModes on decks or that the DeckSelect action is altering the states of modifier5/6 is false. The modifier states do not get populated by simply selecting a mode through the built-in MIDI command. There’s some other logic & mechanism for setting these modifier states, which will need to be figured out. It’s most likely not something built in the program code, but within the controller mapping configuration files.

Yet, for editing on overmapping of already mapped controllers with already populated modifier states from the stock mapping, the instructions above remain fully valid and work well. Evaluating “conditions” works seamlessly in any case.

Until I figure out how to set states, I will not be able to map the original Mixtour or the X1 MK3 that I have for 4 deck controls, unfortunately. If I know how to rise modifiers and their states through the XML/JSON, layers of control will be very easy to be implemented for virtually any controller out there. And I will also write detailed instructions for others to benefit too.

@Slak_Jaw, input/hints from the Dev team for how to rise modifiers and states through the JSON/XML will be very welcome!

Hi @Kaloyan, we do not officially support the editing/modification of XML files, for obvious reasons, so I’m afraid you’ll have to figure this out on your own. Wish I could be of more help!

1 Like

Customers have to go through this troubles :pensive:

Hi @hardbea7, thanks for the feedback. The dev team is aware of user demand for more powerful MIDI modifiers. Please cast your vote in the linked topic below to show support for this feature and to help the team prioritize future djay enhancements. Thanks!

Regardless if the XML editing is supported, or not, the functionality that we are asking for is here. It’s not about “more powerful”. It already is very powerful and completely sufficient. We just don’t have (or know) a way to set modifier states. But the application is very close to provide a complete Out Of the Box Support.

Going over many mapping files, I see this:

  • Most controller mappings use “modifier1-4” for the PadMode state of the 4 decks, but not all.
  • Most controller mappings use “modifier5-6” to track if Deck1 or 3 is selected on the left side in modifier5; and if Deck 2/4 is selected on the right side, but not all.
  • Mixtour is using modifier1 for tracking if the “FX” button is pressed to do the conditions logic for when FX mode is selected and the FIlter know would become FX1 parameter control.
  • RANE Performer mapping is build with a more meaningful names of the modifieres: “PadMode1”/“PadMode2”/ertc for the selected pad mode of the 4 decks; The selected decks are tracked as “LeftDeck1”; “RightDeck2”; “LeftDeck3”; “RightDeck4” with values of 0/1 indicating which is selected, instead of the modifier5 & 6 approach across the majority of the mappings.
  • the PadMode values in modifier1-4 also varies between the different controller mappings.
  • There already is dual conditions support with the keywords “AND” and “&&amp”;
  • There already are equal “==” and does not equal “!=” operands.

While there are similarities in how controller mappings are implemented, there’s apparently no strict/fixed convention. The names differ, the values too. Yet, the approach is very similar across the controllers and in my opinion, a very nice way for “modifiers” functionality - I really like it, and it’s already here and working. Very little extra is needed to make it available to us.

If only selecting a PadMode through the MIDI command would rise/alter a modifier state, it would be great, and we can do anything with mappings. There are 21 distinct MIDI commands for Pad Mode Select. 21 modes per deck! They are already implemented and all it would take is to also set a unique number value in a correspondingly named Modifier name (similar to modifier1-4, or PadMode1-4) upon selection. Once we have this, we can do virtually ANYTHING within our mappings and have all the needed layers available to map anything. Of course, as the names modifier1-6 are already in use across the legacy mappings, the names have to be different but e.g. “DeckPadMode1-4” is probably not in use by any controller yet, and available for an OOB use.

Of course, I realize that due to the variations of the existing mappings, backward compatibility has to be preserved. There are about 183 stock mapping files in the application as Nov 2024. IMO, it would be an unnecessary overkill and unneeded to make them consistent. The much higher value for us would be to just have ability to work fully with them, regardless of the naming conventions used historically.

Please consider enhancing the per-deck “Select Pad Mode” MIDI action to also set a state in a modifier name “DeckPadMode1-4” (or similar), that we can use as condition for building advanced multi-layer mappings. We can do everything with our mappings if the app just does this small thing.

Thanks for the detailed investigation and explanation @Kaloyan. Great suggestion! I have already shared this with our dev team for consideration.

1 Like