Advanced mapping for M-Audio Xponent

It’s the intersection of closures and an attempt to pre-compute some method parameters. Normal loops are fine. You just can’t use them to set up calls to connectControl, where any of the parameters to the called function would be based on a loop variable. At runtime, all of those parameters will return the same value, which is in whatever state the loop last left it.

Do you mean to generate javascript functions dynamically? Something like this:

        for (i = 1; i <= 4; i++) {
            engine.connectControl("[Channel" + i + "]", "VuMeter", myMethod() { var a=i; });

I’m not even sure if that is supported, but I don’t think that you need that. Your method signature would be like this:

        for (i = 1; i <= 4; i++) {
            engine.connectControl("[Channel" + i + "]", "VuMeter", "Controller.onSomeControlChange");

Controller.onSomeControlChange = function(value, group, control)

where group = “[Channel1]”, and you can always do script.deckFromGroup(group) to get the deck index.


That’s definitely possible, but I think it would likely be cumbersome and overcomplicated for this situation. Refer to my post on the previous page for an example.

No, this was just wiring then up, and trying to avoid 20 lines of registration that only varied by one or two parameters. I would have gotten away with it too, if it hadn’t been for those meddling closures.

I take that back… That’s very close to what I was trying to do, actually.

Okay, now I’m at a proper computer and can respond better. I was, in fact, trying to do a loop like that, but rather than just filling in the name of the method, I was trying to inline a function over on the right. Some of the callbacks don’t send along the channel number, but only specify the group as a string (e.g. “[Channel1]”), leaving the function to do string parsing in order to get the interesting piece of information (the “1”). I wanted to connect them like this:

        for (i = 1; i < 5; i++) {
            engine.connectControl("[Channel" + i + "]", "VuMeter", function(value) { Controller.onSomeControlChange(i, value); });

Note: I may have that syntax slightly wrong… going by memory here.

… but the inlined function becomes a closure, and at runtime, it always has the value 5, because that’s where the loop left it. If anything else re-used the variable “i” in the same scope, then it would always have whatever value THAT function had left it at.

In the end, I changed to just pass the name of the function, and let the function do the work. None of the things I’ve wired up this way are super-critical controls that take a ton of messages like a reaction to a jog-wheel, so parsing out the string once per button press isn’t going to hurt anything. If I were wiring up a bunch of controls that expected to receive a lot of messages, then I’d probably just write them out long-hand and be done with it. Fortunately, these hookups are only for output controls, and those are just on or off lights generally, so it’s not really an issue. I’m just playing down here in the weeds is all.

The example you gave is actually a perfect example though. If the VUMeter output were only given the full channel name, but needed just the number for some reason, then I would not want to do the string parsing hundreds of times a second (if they even send that often), so I’d skip the loop and wire it up long-hand. There are only two VU meters anyway. In my case I was wanting to remove a lot of code that assumes there will only ever be two channels so that I can turn this into a 4-deck mapping. I’m hooking up the general lights in a loop, and I’ll collect any stragglers long-hand I guess.

Now I just need to get this PR submitted, but I’m not having any luck. I believe we need to sign a contributor agreement or some such before we can submit anything, right? I can’t seem to find where that is, and I don’t know whether that has any impact on my ability to push to the repo or not anyway. I’ll make another pass over the wiki, but I didn’t find it last time through. I know it’s here somewhere.

Normally you’d have to sign the contributor agreement before your first PR is merged, but that is not required for controller mappings. Regardless, only a few people have push access to the main repository. Anyone can open a pull request though, and it will be merged when someone with push access to the main repository approves it.

I looked a bit more on that, since I remember I had seen it. It’s is just a bit more complicated, but not too much.
You need to construct a function with an initializer.

    for (i = 1; i <= 4; i++) {
        var chanfunct = function(index) {
            return function(value) {
                engine.log("this is " + index + " with some value " + value);
        engine.connectControl("[Channel" + i + "]", "stop", chanfunct);

This prints (when pressing the stop button on deck 1 and deck 2):
Debug [Controller]: “this is 1 with some value 0”
Debug [Controller]: “this is 2 with some value 0”

That’s effectively the same as what I posted before but assigning the function to a variable instead of inlining it in the call to engine.connectControl.

It is good practice to put parentheses around IIFEs:

    for (i = 1; i <= 4; i++) {
        var chanfunct = (function(index) {
            return function(value) {
                engine.log("this is " + index + " with some value " + value);
        engine.connectControl("[Channel" + i + "]", "stop", chanfunct);

I had seen a few examples of closure-busting like this, but thought the maintainability hit was too great. I will refer back to this though. I like the idea of optimizing things to once-per-run where possible. Convention-based registrations in an IoC are my happy place. Maybe I’ll make a “premature optimization” pass over the whole thing once I’m more or less finished with it. I suppose then, by definition, it won’t be “premature” anymore… just “the only thing left to do”. I’m happy with that.

I’m having some difficulty getting the effects knobs to soft-takeover in 2.1, and maybe there’s a way to code around it, but I’m not sure. In 2.0, setParameter calls could not soft-takeover at all, so I’m already a bit ahead. In my new mapping, there is a pseudo-focus mechanism in place. There are four knobs on each deck that I can wire up to the effect parameters. I’m reserving the fourth knob to always be the wet/dry mix, and the first three to be the parameters.

I had been registering the controls for soft-takeover like this:

    for(i = 1; i <= 4; i++) {
        // engine.softTakeover("[EffectRack1_EffectUnit" + i + "_Effect1]", "parameter1", true);
        // engine.softTakeover("[EffectRack1_EffectUnit" + i + "_Effect1]", "parameter2", true);
        // engine.softTakeover("[EffectRack1_EffectUnit" + i + "_Effect1]", "parameter3", true);
        engine.softTakeover("[EffectRack1_EffectUnit1_Effect" + i + "]", "parameter1", true);
        engine.softTakeover("[EffectRack1_EffectUnit1_Effect" + i + "]", "parameter2", true);
        engine.softTakeover("[EffectRack1_EffectUnit1_Effect" + i + "]", "parameter3", true);
        engine.softTakeover("[EffectRack1_EffectUnit" + i + "]", "mix", true);

Note: The commented out part is me trying to figure out whether I’m putting the “i” in the right place. EffectRack1 is the only one in existence right now, but the wiki isn’t making the difference between EffectUnitX and EffectX totally clear, so I tried both. In either case, what I’m seeing is this. If I move the knobs left, then use the UI to move them right, I get a soft takeover like I want. When I shift my focus to control the second effect, the soft-takeover is lost. As soon as I move a knob, the control jumps to its current position. Now I’m on the second effect, and I can do the same thing. Once the knobs and the UI are in sync, I can move the UI knobs using the mouse, and the physical knobs will do a soft-takeover as expected. As soon as I shift the focus to another effect, the soft-takeover is lost again.

I’m imagining that within Mixxx, it’s handling the soft-takeover by remembering the last known position of the physical controls, so while I’m kind of subverting that with my own pseudo-focus system, maybe I need to save and restore the parameters whenever the focus shifts from one effect to the next. Before I tackle that, I’d like to make sure I’m saving the right things though. What ARE the EffectUnit and Effect numbers for the basic four on-screen effects? Is it 1/1, 1/2, 1/3, and 1/4 or is it 1/1, 2/1, 3/1, and 4/1. And why do I get the same behavior no matter which one I make the calls to?

Now here’s where things get really weird. I completely commented out my code, and it still does a soft-takeover. I guess 2.1 is enabling that by default for effects parameters, and maybe all of the code I wrote is just not right, or it’s being ignored. I don’t know. If anyone with inside knowledge sees this, is my proposed save/restore even possible? 2.1 has its own effects focus system now, maybe I’ll give that a try in place of my homegrown 2.0 focus system and see if that makes a difference.

Actually, now I’m thinking that something’s just come unglued with the 2.1 build I’m running. Things that WERE working aren’t now. I’m toggling the “enabled” value for an effect, and the UI doesn’t change, although the value returned from the engine is changing. Maybe I’ll just leave this part and come back to it later. I’m not confident that what I’m seeing makes sense. I know this part works on 2.0, because that’s where I wrote and tested it.

If you’re using a 2.1 build, use the Deere skin. So far the other skins have not been updated for the new effects UI and will confuse you if you try to use them.

EffectUnits are chains and Effects are the individual effects with the chain.

You can find the ControlObjects used by any widget in skins in Developer Mode.

Loading effects is currently in a broken state in the sense that you need to move the metaknob after loading the effect before the metaknob will do anything.

If you want to quickly try the new effects UI, you can map to EffectUnitX_Effect[1-3], meta. I wouldn’t recommend duplicating all the effort of handling switching between modes and soft takeover. The EffectUnit in the Components library can do that for you.

That’s great for 2.1, but I still use 2.0 for my actual gigs, and as far as I know, “Components” is a 2.1-only thing. I’m trying to submit something that can be used for any future 2.0 releases, if such a thing happens, as well as being forward-compatible with 2.1. I may just need to create a 2.1 branch and go all-in on components. Didn’t you mention some possible compatibility problems with the Xponent though? It has a goofy way of sending the jog wheel information (a value indicating the speed it’s moving rather than its position), and maps the buttons as two separate midi controls (press and release) rather than two different values from the same control.

There will not be a 2.0.1 release.

You could work with 2.1 separately from your performance install of 2.0 by installing 2.1 to a different location and running it with the --settingsPath command line option to specify a different directory for your 2.1 settings and database.

There was an issue with the Button Component, but I refactored it so you can use it with the Xponent by providing your own components.Button.prototype.isPress function.

I’m working with 2.1 on a separate computer from my DJ rig, so that’s not a problem. I’m trying to set up a build environment so that I can be more up to date. There doesn’t seem to be any Windows “nightly” available right now, so I’m sure I’m out of date.

The Xponent’s buttons are strange. They send 0x90 on press, and 0x80 on release… for Channel 1. On Channel 2, it’s 0x91 and 0x81. Toggling the “bank” switch on the front of the controller changes those numbers to 0x95/0x85 and 0x96/0x86 for the left and right decks respectively. I’m trying to take advantage of this to make a 4-deck mapping. I’ll see what I can work out.

I will eventually port all of this to Components, I hope, but I need to get this mapping working on 2.0 first so that I can use it at my gigs. I’m trying to turn this into a 4-deck mapping, which pretty much requires me to turn almost everything into a script-mapping. I’m not having any luck at all getting soft-takeover to work when I switch banks though, and it’s not limited to just bank switching either.

I think the underlying soft-takeover logic is my problem here. I have noticed that if I use a knob to set an arbitrary value, and then switch that knob to controlling something else, by bank switching or through my effects “focus” mechanism, I get two different behaviors. If I assign the knob to something else, move it to the right, and use the UI to move the original control to the left, when I switch the knob back to controlling that parameter, I get a soft takeover. If I assign the knob to something else, move it to the right, and use the UI to ALSO move the original control to the right, when I switch the knob back to controlling that parameter, I get an instantaneous takeover.

It all seems to do with whether or not the knob is currently on the same “side” as that control’s last move.

I don’t know if there’s a way around this, honestly. I understand the soft-takeover logic, and I can see why it would work that way. You can’t count on getting a signal equal to the current value, so you can’t count on watching the parameter pass “through” it, but maybe there’s a way to do so with some degree of “tolerance”?

This really limits my ability to complete this 4-deck mapping. I can’t count on what any controls have been up to while they were assigned to other controls, or which “side” of the current value they’re on. I see this posing a real problem when it comes to the channel rate and volume sliders. While I was controlling decks 3 & 4, I may very well have made an adjustment that puts the physical control on the wrong “side” to do a soft-takeover when I switch back to decks 1 & 2. Channel volume, pregain, and rate are all things that will have a drastic, show-stopping effect if they don’t do a soft-takeover. As long as you stick to a 2-deck performance, you should be fine, but you’d need to be very careful not to let any of those vary while in 4-deck mode. What if deck 4 is playing something you’re killing the high and mid on, and only letting the low-end through? When you switch back to 1&2, you’d better not touch the low filter knob.

Has anyone gotten around these problems with other 4-deck mappings? What’s the strategy?

The mapping is almost complete. I’ve updated the attachments to the first post. There are some bits I’d like to finish with the status of lights when bank switching, but this is a working, complete 4-deck mapping for the Xponent with a lot of new advanced features. I should be submitting a pull request very soon.

Things I’ve learned today:
The Xponent has an unusual and hard to predict set of midi codes that don’t always make sense. The “analog” lights (VU Meters, progress meters), seem to be addressed via status 0xB3 (Control, channel 3), but when you throw the bank switch over to “B”, they suddenly live on channel 9 instead. Also, channels 3 & 9 seem to allow you to address each LED and fade them up and down individually. I thought I was done with this mapping, and now I just found out that I can dim the buttons. I’m sure I’ll think of a use for it.

This means that I can now make some effects more subtle if needed. Maybe I’ll make the flashing Sync buttons less bright or something. In some ideal future where the beat detection includes phase I’ll use this to blink the sync buttons brighter on the 1s than the other beats. Not now though.

Well… there goes the rest of the evening.

I stopped myself from going down the dimmable button rabbit hole, and have submitted a PR for this mapping. I just can’t come up with anything else to do with it now. Maybe later. The files on this post have been updated accordingly.

There’s possibly something wrong with me. Maybe I’ve gotten too emotionally attached to this controller after working on it this long, but I just bought another one. I now have a spare. Has anyone else done this?

@MelGrubb Thanks for all your hardwork. I actually bought a Xponent years back, wanted to use it for a few Private parties with friends. Used it with Torq software but it was a very buggy experience, so i gave up on my DJ’ing dreams - especially since soon after M-Audio discontinued their product.

Somehow i ended up here, and i saw that there is actually still someone working hard to support this controller. I will soon try to get my system up and running and try your advanced mappings. I will give some feedback soon. I’m new to Mixxx.

Greetings Jan