Remapping of Twitch questions thread.

Going to be slowly working on this but will try and keep all my question to this thread. Luckily there is an existing mapping to work from so I will mostly be editing that. I plan to ge something similar, but not exactly the same, as the official Traktor mapping which seems a lot more suited for the Basic mode which the unit has been mapped in and is expected to use outside of Serato.
us.novationmusic.com/sites/defa … orgsg2.pdf

The first question is a basic one on xml manipulation. The existing mapping has been made to use only two decks whereas the overlap and Traktor mapping are designed for four decks. The current Shift button should be a deck toggle and as it’s latching, not momentary, in action really is not suitable for Shift.

The Twitch uses a different Channel for each Deck, so Deck A is on Ch.8, Deck B Ch.9 etc. Hopefully this makes search and replace a bit easier, but I have no idea how to search for a string and then once that string has been found modify a different line within that class/object.

I run Ubuntu and the little bits of xml and json manipulation I’ve done in the past have been done with Sublime, which seems pretty capable (if you have the know-how.)

So this is one example of a class that would need to be changed:

<control> <group>[Channel2]</group> <key>reverse</key> <status>0x9A</status> <midino>0x16</midino> <options> <normal/> </options> </control>

So I want to search the value for “0x9A” and if found (like in the above) change the value to be “[Channel4]” making sure not to edit anything outside of the class/object the value was found in.


The second question which is instantly obvious before even starting is to get the correct Shift button working. I would imagine there are loads of examples of controllers that use this exact method so I would be happy to be just pointed at a working example.

Unlike the deck toggle buttons the Shift buttons do not actually change the output from the controller in Basic mode. Rather they just output a Note On message on note 0x00. I believe each Shift button transmits on the Channel for the associated deck it is nearby but I would treat them both as being the same.

If possible I was thinking it would be most simple to have the Shift buttons take the incoming MIDI message and change the value by subtracting five from the channel number (as default is uses channel 8 - 12, subtracting 5 would give us the same controls transposed to channels 3 - 7.) Is this possible? Or does each control made while Shift is held have to be done through scripting itself?

If the XML file is organized with all the elements for a MIDI channel together, you can open a new file in your text editor and do a mass find-and-replace for the MIDI channel number, then copy and past the modified text into the old file.

You’d have to do that through scripting. It would be possible to compute the deck string ("[Group1]" … “[Group4]”) from the MIDI channel number in every function. That is what I did for my first mapping, but that makes lots of annoyingly redundant code. With the Components library you could make 4 Deck objects in the script’s init function and reference each Component’s group property (“this.group”) instead of repeating the same line of code at the top of every function.

Unfortunately they seem to be in a completely random order! Found an answer using Sublime and the xquery plugin. Just in case anybody else might ever want to do similar.

forum.sublimetext.com/t/advance … ss/34993/4

That’s a shame but what I suspected. It’s likely to take me a while before I get to trying to get that all working, especially as I haven’t even decided on many Shift functions as of yet…

Wow! The Twitch script file is over 8500 lines long!! And that’s with basically zero annotation! :astonished: Surely not much of that is really required. Looked at a few other similar size controllers and their scripts are more between 200-2000 including annotations…

Something doesn’t seem right and that makes me wonder if maybe I should just try and work from the ground up, at least with the script side, even if not with the entire mapping…

How do I have two accessible mapping for the same controller? I tried this in the past by editing the name field within the xml file for my QuNeo but it didn’t work. Sure it must be simple and I just did something wrong though…

As noted on the Twitch wiki page, the mapping is made with the Mixco mapping framework which autogenerates those 8500 lines of JavaScript. The source for the code that is processed by Mixco is in mixco folder in the res/controllers folder in the Mixxx Git repository. If you want to modify that mapping, you’d have to learn how Mixco works and modify that code. If you don’t want to bother with that you could start from scratch.

You can make an alternate mapping by changing the name of mapping in a new XML file.

Yeah I had a brief look at it some time ago, seems the whole midi mapping xml is generated from the mixco js… I think I will edited the existing xml but likely try and do the scripting myself from scratch rather than edit the mixco script.

That’s what I thought. I must have made some basic mistake last time. I will try again.

Sorry being slow but I’m trying to at least get some of the background reading done at times I haven’t got the controller to physically work on it. As such I’ve been reading the MIDI Scripting and the Components JS pages (even if it’s taking a bit long for bits of it to sync in…)

Before I even start trying to make the Deck object can you clarify: As the shift buttons are sending MIDI Note On/Off can I still map the normal functions via the normal MIDI Mapping method (IE saved in the xml file)? Or will everything have to be done through the JS file? I’m assuming through JS as it will need an IF statement and JS can’t currently intercept the MIDI directly (if I understand what I’ve been reading.)

Although as it seems you are looking to completely update the mapping system and get rid of control mappings within the xml file it feels this method might be more future proof even if not technically required…

SOLVED

(Deleted)
and has been renamed to: kazas_twitch-midi.xml

What am I doing wrong? SOLVED

SOLUTION: Ahhh needs to be (name).midi.xml and had used a hyphen.

OK why isn’t this working? Only trying to get the Hot Cues working for now as a starting point. Going with the Components JS method. As my controller uses a separate MIDI Channel for each Deck channel I really don’t understand why I would need the leftDeck, rightDeck and currentDeck constructs but for the moment I’m trying to get it working as per the guide and then tinker from there, but can’t even get that working…

Decks 1 & 2 should hopefully have the syntax for how it’s set up. There is a comments out line in the script file to work out the Group I was going to uncomment and try 3 & 4 once I had the initial ones working.

midi.xml

<?xml version='1.0' encoding='utf-8'?>
<MixxxControllerPreset mixxxVersion="2.1.x" schemaVersion="1">
   <info>
       <name>Kazas Twitch</name>
       <author>Kazakore &lt;dj_kaza@hotmail.com&gt;</author>
       <description>Revised mapping for Novation Twitch (in basic mode)</description>
       <wiki></wiki>
       <forum></forum>
   </info>
   <controller id="kazas_twitch">
       <scriptfiles>
       		<file filename="lodash.mixxx.js"/>
			<file filename="midi-components-0.0.js"/>
         	<file functionprefix="KazasTwitch" filename="kazas_twitch-scripts.js"/>
       </scriptfiles>
       <controls>
        	<control>
	 			<group>[Channel1]</group>
				<key>KazasTwitch.leftDeck.hotcueButtons.input</key>
	    		<status>0x97</status>
	    		<midino>0x60</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
        	<control>
	 			<group>[Channel2]</group>
				<key>KazasTwitch.rightDeck.hotcueButtons.input</key>
	    		<status>0x98</status>
	    		<midino>0x60</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
        	<control>
	 			<group></group>
				<key>KazasTwitch.Deck.hotcueButtons.input</key>
	    		<status>0x99</status>
	    		<midino>0x60</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
        	<control>
	 			<group></group>
				<key>KazasTwitch.Deck.hotcueButtons.input</key>
	    		<status>0x9a</status>
	    		<midino>0x60</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
       </controls>
       <outputs>
          
       </outputs>
   </controller>
</MixxxControllerPreset>

script file:

[code]
/*

  • Script file for Novation Twitch by Kazakore for Mixxx 2.1

*/

var KazasTwitch = {};

KazasTwitch.init = function (id, debugging) {
midi.sendShortMsg(0xb7,0x00,0x00); // init Basic Mode
// midi.sendShortMsg(0xb7,0x00,0x01); // Read current controler staus (should’t be needed as done during init 0 above)
KazasTwitch.leftDeck = new KazasTwitch.Deck ([1, 3], 1);
KazasTwitch.rightDeck = new KazasTwitch.Deck ([2, 4], 2);

};

KazasTwitch.shutdown = function () {
midi.sendShortMsg(0xb7,0x00,0x00); // init Basic Mode
midi.sendShortMsg(0xb7,0x00,0x70); // turn off all LEDs on exit
};

KazasTwitch.Deck = function (deckNumbers, midiChannel) {
components.Deck.call(this, deckNumbers);
this.hotcueButtons = [];
for (var i = 1; i <= 8; i++) {
this.hotcueButtons[i] = new components.HotcueButton({
midi: [0x90 + midiChannel, 0x5f + i],
// group: “[Channel”+midiChannel-6+"]",
number: i,
});
}
this.reconnectComponents(function © {
if (c.group === undefined) {
c.group = this.currentDeck;
}
});
};

KazasTwitch.Deck.prototype = new components.Deck();[/code]

Error message:
Uncaught exception at line 1 in passed code: TypeError: Result of expression ‘(KazasTwitch.leftDeck.hotcueButtons.input)’ [undefined] is not a function.

Also your manual clearly states I should be able to edit the files and Mixxx will automatically update. This does not work! It now shows kazas_twitch in the list twice and to get the changes seen by Mixxx I have to try them both and work out which is the one with the latest edits (currently by checking MIDI Mappings as there’s only a handful but this would become hard in the future.) These even seem to stay there after restarting the software even though there is no second file saves I can find!

The idea is that the leftDeck and rightDeck objects represent the physical controls, not virtual decks. This is necessary for controllers that do not send separate MIDI messages for decks 3 & 4. For the case of the Twitch, you could do it that way, or you could create an instance of the Deck object for every virtual deck.

KazasTwitch.leftDeck.hotcueButtons is an array of Components. You need to access members of the array like KazasTwitch.leftDeck.hotcueButtons[1].input.

Ahh that makes sense. The only example in the guide is for a quantise button, so not in an array. It seems to give a similar error for the leftDeck object now though.

Uncaught exception at line 1 in passed code: TypeError: Result of expression 'KazasTwitch.leftDeck' [undefined] is not an object.

leftDeck[1] doesn’t work. When declared it is passed an array as an argument so thought I should be able to access them with [0] and [1] but seems not… :frowning:

leftDeck and rightDeck are not arrays. I am guessing you are getting that error because there is a syntax error or something in your code preventing the KazasTwitch.Deck constructor from running. If the JS code is still what you posted on the last page, it looks like you try to reference “this.currentDeck” but never declare that variable anywhere. When you try to reference an undefined variable the JS interpreter will give not so helpful errors.

Makes sense and I actually thought the same about quite a lot of variables in the script and wondered if they were somehow inherited from the Components script. But it seems the example code on the wiki page does the same thing and I was just copying that as it’s all I have to go on (plus some annotations within the script file itself but it’s much harder to read through and find useful bits there and it’s far from expansive.

To me that made it sound like I didn’t have to declare them myself.

mixxx.org/wiki/doku.php/components_js


Is there a full, preferably fairly basic, complete mapping using the Components method you can point me to so I can study it?
The Pioneer DDJ-SX mapping has it listed in the scripts to load but as the word “prototype” isn’t found anywhere in the document I don’t think it actually uses these methods…

Using grep there is only one single file which contains the string “components.Deck” so hopefully this will be written in a way to give me some pointers. The mapping in question is the Hercules P32 (thanks Be.)


EDIT: and that also doesn’t seem to actually declare deckNumbers anywhere. This variable shows up on three lines and in all of them it is an argument:

[code]P32.Deck = function (deckNumbers, channel) {
components.Deck.call(this, deckNumbers);

this.effectUnit = new components.EffectUnit(deckNumbers);[/code]

I think my brain is starting to melt now though!

The declaration is in the function signature. “function (deckNumbers, channel)” declares “deckNumbers” and “channel” as local variables in the scope of that function.

Yeah I realised that on the way home on the bus. Think I had been staring at a screen too long!

Still don’t see what is different with it in my code though…

First buttons working! At some point I had somehow entered the word prototype in with the Call method. Don’t know if that was me trying multiple things or not used to some of the strange shortcuts on the editor I’m using (I’m using Sublime Text Editor which I think has some of the old Vim functionality, disabled it from going into Command mode so easy though but still occasional do I’m not sure what by accident…)

SOLVED The pads are not lighting on my controller for the corresponding set hotcue. I thought for standard On/Off the defaults were to send 127/0 to the same midi channel and number as the control is configured for, therefore this should work. Or is there something additional I need to do with the connect method somewhere?

– Deleted code for neatness as now irrelevant –

Solution: I had thought from the guide it said the second argument in the left/rightDeck declaration was the default channel meaning the deck, but then realised this was the midi channel of the deck and once this is set to the channel the commands to that deck come on the LEDs worked as expected.

Second current issue:

I added another control in the xml to try and play Deck C from the other layer, as previous mentioned these transmit on a different channel. No matter what it still only triggers Deck A to play though, even though the Group in the xml is set to the correct Channel!

Looking at your mapping for the P32 and you don’t even have the Group set differently for the two Play buttons per controller side! Yet like my controller it is transmitting on a different channel so there should be no reason to be toggling between A/B and C/D like you do with controllers which don’t have unique outputs for each Deck.

Or preferably how do I get rid of the whole leftDeck/rightDeck constructs and just use querying the MIDI Channel to get it to work on the right deck within Mixxx? This seems like it would be far far simpler! Especially as using leftDeck it then ignores what the real channel number received was and replaces it with the one from the second argument from where you create that function! How is that in any way useful? There’s got to be a better way than separate Decks A/B/C/D when all controls have a unique midi value surely…

Sorry for talking to myself so much. Well for now I’ve gone with setting up four Decks, I had hoped I would be able to simplify it but as I’ve managed to simplify the call method I guess it’s not overly needed. I did ask on Zulip whether this would be possible and I guess it was partially from B assuming I really wanted to put it in the xml rather than the javascript code. Anyway I’ve only added the first two for all four decks so far but as you can see from this code they can all have exactly the same Key and no Group set and still work, which is going to make it a lot quicker to generate the xml file. This might be a trick of interest to others so putting it here. :slight_smile:

script:

[code]/*

  • Script file for Novation Twitch by Kazakore for Mixxx 2.1

*/

var KazasTwitch = function() {};

KazasTwitch.init = function (id, debugging) {
midi.sendShortMsg(0xb7,0x00,0x00); // init Basic Mode
// midi.sendShortMsg(0xb7,0x00,0x01); // Read current controler staus (should’t be needed as done during init 0 above)

KazasTwitch.deckA = new KazasTwitch.Deck ([1], 7);
KazasTwitch.deckB = new KazasTwitch.Deck ([2], 8);
KazasTwitch.deckC = new KazasTwitch.Deck ([3], 9);
KazasTwitch.deckD = new KazasTwitch.Deck ([4], 10);

};

KazasTwitch.shutdown = function() {
midi.sendShortMsg(0xb7,0x00,0x00); // init Basic Mode
midi.sendShortMsg(0xb7,0x00,0x70); // turn off all LEDs on exit
};

KazasTwitch.Deck = function(deckNumbers, channel) {
components.Deck.call(this, deckNumbers);

this.playButton = new components.PlayButton([0x90 + channel, 0x17]);

this.hotcueButtons = [];
for (var i = 1; i <= 8; i++) {
    this.hotcueButtons[i] = new components.HotcueButton({
        midi: [0x90 + channel, 0x5f + i],
        group: "[Channel" + (channel-6) + "]",
        number: i,
    });
}

this.reconnectComponents(function(c) {
    if (c.group === undefined) {
        c.group = this.currentDeck;
    }
	});

};

KazasTwitch.cues = function(channel, control, value, status, group) {
var cueNo = (control - 0x5f) ;
var deckNo
if (channel == 7) {group = “[Channel1]” , deckNo = “deckA” ;
}
else if (channel == 8) {group = “[Channel2]” , deckNo = “deckB” ;
}
else if (channel == 9) {group = “[Channel3]” , deckNo = “deckC” ;
}
else {group = “[Channel4]” , deckNo = “deckD” ;
}
KazasTwitch[deckNo].hotcueButtons[cueNo].input(channel, control, value, status, group);
};

KazasTwitch.Deck.prototype = new components.Deck();[/code]

xml

<?xml version='1.0' encoding='utf-8'?>
<MixxxControllerPreset mixxxVersion="2.1.x" schemaVersion="1">
   <info>
       <name>Kazas Twitch</name>
       <author>Kazakore &lt;dj_kaza@hotmail.com&gt;</author>
       <description>Revised mapping for Novation Twitch (in basic mode)</description>
       <wiki></wiki>
       <forum></forum>
   </info>
   <controller id="kazas_twitch">
       <scriptfiles>
       		<file filename="lodash.mixxx.js"/>
			<file filename="midi-components-0.0.js"/>
         	<file functionprefix="KazasTwitch" filename="kazas_twitch-scripts.js"/>
       </scriptfiles>
       <controls>
        	<control>
	 			<group></group>
				<key>KazasTwitch.cues</key>
	    		<status>0x97</status>
	    		<midino>0x60</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
        	<control>
	 			<group></group>
				<key>KazasTwitch.cues</key>
	    		<status>0x98</status>
	    		<midino>0x60</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
        	<control>
	 			<group></group>
				<key>KazasTwitch.cues</key>
	    		<status>0x99</status>
	    		<midino>0x60</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
        	<control>
	 			<group></group>
				<key>KazasTwitch.cues</key>
	    		<status>0x9a</status>
	    		<midino>0x60</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
			        	<control>
	 			<group></group>
				<key>KazasTwitch.cues</key>
	    		<status>0x97</status>
	    		<midino>0x61</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
        	<control>
	 			<group></group>
				<key>KazasTwitch.cues</key>
	    		<status>0x98</status>
	    		<midino>0x61</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
        	<control>
	 			<group></group>
				<key>KazasTwitch.cues</key>
	    		<status>0x99</status>
	    		<midino>0x61</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
        	<control>
	 			<group></group>
				<key>KazasTwitch.cues</key>
	    		<status>0x9a</status>
	    		<midino>0x61</midino>
	    		<options>
	       			<script-binding/>
	  			</options>
			</control>
			
			<control>
			    <group></group>
			    <status>0x97</status>
			    <midino>0x17</midino>
			    <key>KazasTwitch.deckA.playButton.input</key>
			    <options>
			        <script-binding/>
			    </options>
			</control>
        	<control>
			    <group>[Channel3]</group>
			    <status>0x99</status>
			    <midino>0x17</midino>
			    <key>KazasTwitch.deckC.playButton.input</key>
			    <options>
			        <script-binding/>
			    </options>
			</control></controls>

       <outputs>
          
       </outputs>
   </controller>
</MixxxControllerPreset>

You are correct, strictly speaking, there is not a need to have two Deck objects in the Hercules P32 mapping that toggle between decks 1/3 and decks 2/4. I did it that way to ensure that the library would work that way for controllers that do not use separate MIDI channels for each deck.

The Components library ignores the “group” XML element. I’m not sure if Mixxx requires the “group” element to exist. I just haven’t tested deleting it so I kept in in the XML, but maybe it’ll work without that.

The approach of the library is that the “group” should not depend on the MIDI signal or the XML file, but instead the group is a property of each Component. Components with the same “group” are organized into Deck objects. This way, the Deck can easily iterate over all those Components and manipulate their “group” properties. This is what

this.reconnectComponents(function (c) { if (c.group === undefined) { // 'this' inside a function passed to reconnectComponents refers to the ComponentContainer // so 'this' refers to the custom Deck object being constructed c.group = this.currentDeck; } });

in the template on the wiki does. That way, you do not need to define the “group” property when initializing each Component, which would be redundant.