Advanced mapping for M-Audio Xponent

XML preset files and script files (.js) for MIDI and other controllers.

Moderators: garth, User Customization Moderators

Re: Advanced mapping for M-Audio Xponent

Postby JosepMa » Mon Feb 13, 2017 8:53 pm

MelGrubb wrote:[...]Throw in multiple loops, and this started to get out of control, so I thought I'd just make a for loop from 1 to the number of decks, and repeat the same initialization for each line... and that's when closures stepped in to ruin my day. Without a simple closure-busting trick, I can't dynamically fill in values based on the loop variable at initialization time because they'll all pass 5 (the ending state of the loop variable) at runtime, and there IS no deck 5. [...]


Are you sure you didn't make a mistake somewhere? What's wrong with this?
Code: Select all
    var myVar = {};
    var i;
    for (i=0;i<5;i++) {
        myVar[i]=i;
    }
    for (i=0;i<5;i++) {
        engine.log("myVar["+i+"]="+myVar[i]);
    }
-----
Debug [Controller]: "myVar[0]=0"
Debug [Controller]: "myVar[1]=1"
Debug [Controller]: "myVar[2]=2"
Debug [Controller]: "myVar[3]=3"
Debug [Controller]: "myVar[4]=4"
JosepMa
 
Posts: 888
Joined: Sat Oct 10, 2015 7:02 pm

Re: Advanced mapping for M-Audio Xponent

Postby MelGrubb » Tue Feb 14, 2017 3:33 am

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.
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

Re: Advanced mapping for M-Audio Xponent

Postby JosepMa » Tue Feb 14, 2017 7:52 pm

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

Code: Select all
        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:

Code: Select all
init
{
        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.

}
JosepMa
 
Posts: 888
Joined: Sat Oct 10, 2015 7:02 pm

Re: Advanced mapping for M-Audio Xponent

Postby Be. » Tue Feb 14, 2017 8:42 pm

JosepMa wrote:Do you mean to generate javascript functions dynamically?


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.
Mixxx is free because it's yours!

I heard FLAC and I haven't gone back.
Protect your hearing with earplugs!

Hear my mixes
User avatar
Be.
Mixxx Developer
 
Posts: 2573
Joined: Tue Jan 06, 2015 1:00 am
Location: Chicago, USA

Re: Advanced mapping for M-Audio Xponent

Postby MelGrubb » Tue Feb 14, 2017 8:56 pm

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.
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

Re: Advanced mapping for M-Audio Xponent

Postby MelGrubb » Wed Feb 15, 2017 4:26 am

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:

Code: Select all
        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.
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

Re: Advanced mapping for M-Audio Xponent

Postby Be. » Wed Feb 15, 2017 5:28 am

MelGrubb wrote: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.
Mixxx is free because it's yours!

I heard FLAC and I haven't gone back.
Protect your hearing with earplugs!

Hear my mixes
User avatar
Be.
Mixxx Developer
 
Posts: 2573
Joined: Tue Jan 06, 2015 1:00 am
Location: Chicago, USA

Re: Advanced mapping for M-Audio Xponent

Postby JosepMa » Wed Feb 15, 2017 9:03 pm

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.
Code: Select all
    for (i = 1; i <= 4; i++) {
        var chanfunct = function(index) {
            return function(value) {
                engine.log("this is " + index + " with some value " + value);
            }
        }(i);
        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"
JosepMa
 
Posts: 888
Joined: Sat Oct 10, 2015 7:02 pm

Re: Advanced mapping for M-Audio Xponent

Postby Be. » Wed Feb 15, 2017 9:21 pm

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:

Code: Select all
    for (i = 1; i <= 4; i++) {
        var chanfunct = (function(index) {
            return function(value) {
                engine.log("this is " + index + " with some value " + value);
            }
        })(i);
        engine.connectControl("[Channel" + i + "]", "stop", chanfunct);
    }
Mixxx is free because it's yours!

I heard FLAC and I haven't gone back.
Protect your hearing with earplugs!

Hear my mixes
User avatar
Be.
Mixxx Developer
 
Posts: 2573
Joined: Tue Jan 06, 2015 1:00 am
Location: Chicago, USA

Re: Advanced mapping for M-Audio Xponent

Postby MelGrubb » Wed Feb 15, 2017 9:24 pm

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.
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

PreviousNext

Return to Controller presets/mappings

Who is online

Users browsing this forum: No registered users and 5 guests