Advanced mapping for M-Audio Xponent

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

Moderators: garth, User Customization Moderators

Advanced mapping for M-Audio Xponent

Postby MelGrubb » Tue Feb 07, 2017 4:09 am

I have created an alternate mapping for the M-Audio Xponent, and before submitting a pull request to get it into future versions of Mixxx, I'd like to solicit some feedback. The Xponent seems like a great match for Mixxx in my mind. I has a ton of control surfaces beyond the basics, and I wanted to take advantage of them.

I took the stock 2.0 mapping and made some pretty drastic changes, mostly additions. I'll just start from the top of the controller and work my way down, and hopefully I don't leave anything out.

You may want to refer to the diagram in the Xponent documentation for reference (https://www.manualslib.com/manual/569175/M-Audio-Torq-Xponent.html?page=7#manual)
I'll refer to the diagram numbers in the list below.

10, 11, 12) The PFL (headphone) and scratch-enable buttons are stock, as are the jog wheels.

21) The Big-X button is mapped to the Brake effect. If you let go before the track comes to a complete stop, it will continue playing. If you hold it until the track stops, it stays stopped.

21) The Big Minus button is reverse-play, but it's momentary rather than a toggle like the standard mapping.

26) The nudge buttons are the same as usual, but mapped in the opposite direction from the Mixxx UI to be more mnemonic in my mind. Pressing the left nudge speeds the track UP, "nudging" it further to the left if you're watching the beatgrid.

24, 25) The deck knobs and buttons perform different duties on the left and right sides. The left side controls the samplers, with the knobs controlling the volume, and the buttons firing the samples. The volume knobs are all soft-takeover-enabled.
The right side controls handle the effects, and need a little explanation. Pressing the buttons changes which effect (1-4) currently has the focus, and will light up accordingly. Pressing the button again will toggle that effect on and off. The knobs control the parameters of whichever effect currently has the focus. The first three knobs will correspond to the first three parameters of the effect, and the fourth knob will always control the wet/dry mix. Most of Mixxx's effects only have two or three parameters, so this works well. The Echo effect has four parameters so there is unfortunately no knob for the PingPong parameter. Due to a limitation in Mixxx 2.0, the parameter knobs are not soft-takeover, so be careful. Hopefully they'll work better in 2.1. holding shift (15) while pressing the buttons will cycle which effect is in that slot.

29) The row of LEDs below the deck buttons shows the progress through the song and will start to flash at 75%. This probably won't align with Mixxx's end of track warning, which default to 30 seconds before the end of the track. I may revisit this one to make them match, but I didn't want to over-burden the script with math just yet. I'll try to fix that up next.

30, 35, 37) Fast-forward/Rewind, Cue, and Play are what you'd expect, nothing unusual here.

31) Buttons 1-5 are hotcues. Press them to set or play a hotcue. Holding shift while pressing 1-5 will clear that hotcue. Pressing a hotcue while the track is playing will jump to that hotcue and continue playing. Pressing a hotcue while the track is stopped will play the hotcue but stop when the button is released.
The |< and >| buttons will shift the beatgrid on that deck to the left or right so you can make minor adjustments on the fly. Holding shit while pressing either button will align the beatgrid to the current position.
The padlock button toggles the keylock on that deck. Holding shift while pressing the lock button will toggle "quantize" for that deck.
The small + and - buttons increase or decrease the track speed accordingly.

32) The deck volume sliders do what you expect, but are soft-takeover-enabled in this mapping. If you don't use them, you can safely "stow" them at either extreme so that you don't accidentally upset them.

36) The looping section is fully functional. 1,2,4, and 8 will set loops of 1,2,4, or 8 beats. Holding shift while pressing one of them will do a rolling loop of 1, 1/2, 1/4, or 1/8th beat, resuming playback where it would have been without the loop when they are released. The loop enable, begin, and end buttons do their normal thing.

Everything in the center EQ section is normal, with EQ band kills doing what you'd expect.

34) The Sync buttons behave as usual, but flash to the beat of the song playing on that side.

38) Punch-in momentarily centers the cross-fader. If the cross-fader is all the way to the left, then the right punch-in will center it until released and vice versa.

39) The cross-fader is soft-takeover-enabled in this mapping.

Other notes: This mapping implements the "secret handshake" required to get the lights to work, so you don't need to hold down anything when powering up the controller, but if you are using M-Audio's ASIO drivers under windows, the lights still won't work. I don't know what's up with that, but I explained it on the controller's wiki page (http://www.mixxx.org/wiki/doku.php/m-audio_xponent)

If you have an Xponent, please try this out and give me your feedback.
Just drop these in your user controller mapping folder (http://www.mixxx.org/wiki/doku.php/controller_mapping_file_locations#user_controller_mapping_folder), and Mixxx will display it as M-Audio Xponent Alternate.
Attachments
M-Audio-Xponent-Advanced-scripts.js
(32.03 KiB) Downloaded 323 times
M-Audio Xponent (Advanced).midi.xml
(120.62 KiB) Downloaded 309 times
Last edited by MelGrubb on Fri Mar 17, 2017 12:45 am, edited 5 times in total.
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

Re: Advanced mapping for M-Audio Xponent

Postby MelGrubb » Wed Feb 08, 2017 4:11 am

I got the 30-second warning to work, but I thought I'd see what anyone else thinks of the approach. The calls to update the play position send a percentage value, so rather than doing the math on each call, which seems like it would drag things down unnecessarily, I decided to do the math just once whenever a track is loaded.

The guts look like this:
Code: Select all
// Array to hold the track warning percentage (1-based... deal with it)
warnAt = [];

// React to track load
engine.connectControl("[Channel1]", "duration", "MaudioXponent.trackLoaded");
engine.connectControl("[Channel2]", "duration", "MaudioXponent.trackLoaded");

// Position listener (from original 2.0 mapping, I guess it doesn't tell you which deck, so it's copy/pasted twice)
MaudioXponent.playPositionMeter1 = function(value) {
    print("Deck 1 position = " + value + ", warnAt[1] = " + warnAt[1]);
    if (value >= warnAt[1]) {
       MaudioXponent.flashdur1++;;
       if (MaudioXponent.flashdur1 == MaudioXponent.flashprogress) {
           midi.sendShortMsg(0xB3,0x14,0x00);
       }
       if (MaudioXponent.flashdur1 >= MaudioXponent.flashprogress*2) {
           midi.sendShortMsg(0xB3,0x14,MaudioXponent.convert(value));
           MaudioXponent.flashdur1 = 0;
       }
    } else {
       midi.sendShortMsg(0xB3,0x14,MaudioXponent.convert(value));
    }
};

// Math to figure out what percentage is 30 seconds from the end
MaudioXponent.trackLoaded = function(duration, group) {
    var currentDeck = parseInt(group.substring(8)); // We get the string [Channel1] here rather than an integer value... oh well
    var duration = engine.getValue(group, "duration");
    warnAt[currentDeck] = (duration - 30) / parseFloat(duration); // parseFloat to force floating point math
};


The only things I'd change are:
1) I'd rather the call from duration gave me an integer rather than a string ("[Channel1]") but that's out of my control.
2) I'd like to pull the 30-second number from the engine itself, but I couldn't find its name in the wiki. I know you can configure this, so I'd like to automatically account for user preferences.
3) I could get more precise by using the number of samples in the track, rather than the duration in seconds but that's probably getting too nit-picky. It works.
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

Re: Advanced mapping for M-Audio Xponent

Postby Be. » Wed Feb 08, 2017 4:17 am

The best solution would be to create a new read-only ControlObject in C++ that indicates when the track is nearing the end. Then it would be trivially easy to connect that to a JS callback that lights an LED without any fancy tricks in JavaScript. Considering that Mixxx already calculates that somewhere, it should not be difficult to toggle a ControlObject when the waveform starts blinking. Do you have any experience with C++? You can find an introduction to the ControlObject system on the wiki?
I heard FLAC and I haven't gone back.
Protect your hearing with earplugs!

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

Re: Advanced mapping for M-Audio Xponent

Postby MelGrubb » Wed Feb 08, 2017 1:19 pm

The only C++ I've done is years (like decades) ago, but it's not something I'm afraid of. I work in the C# world, so syntax is not a problem. It's probably more a matter of setting up a workable build environment. I think I saw somewhere that Visual Studio (Community) has everything we need to build Mixxx, and I have that on my other machine already.

A simple boolean ControlObject to control whether or not to start flashing would simplify things conceptually, but I think the method doing the flashing would have to remain largely as it is. I'm assuming that the callback that's connected to playPositionMeterX in the above code has a defined frequency that it gets called at. I'm certainly not getting a callback on each sample played. The original code implements a simple counter and toggles the lights on and off every X number of calls (I think it's set to 8), so maybe it's every 1/10th of a second or something like that. That part would have to stay, although I'm kind of tempted to roll this in with the reaction to beat_active and have the progress meters flash in time to the beat rather than at some arbitrary frequency... maybe if I'm bored next weekend.

I'll look at building Mixxx from source again at some point, but I have enough other things going on at the moment to get my head into a larger codebase like this. Single controller mappings fit within my available mindwidth.

I know there are things I'd love to look at. For instance, many controller mappings will define parameter values up at the top to enable/disable features. It would be great if there were a way to expose these to Mixxx itself so that they could be represented in the preferences dialog rather than having to open up the .js file to change them. For instance, I made my sync buttons flash to the beat. That might be irritating to others though. The ability to change the value from the preferences dialog would allow individual users to make their own choice. Similarly, my backwards nudge buttons make more sense to me, but might throw others off. A preference dialog would alleviate that problem.

What I might do next is gather and document up some of the preferences at the top of the script file, maybe offering multiple choices for certain buttons. Maybe you'd like the big minus button to spinback or a latching "play backwards" rather than the momentary version I've chosen. Maybe you like the |<, >|, lock, + and - buttons to navigate the library. A couple toggles at the top should make the mapping much easier to customize.

If you have time for one question, I'm a little confused by what I'll call "scoping rules" around script-level variables. Most of the state variables in the original script start with MaudioXponent. For instance "MaudioXponent.flashdur1", which is defined near the top of the script. Nothing ever changes this value, so it's basically a constant. I tried to define my warnAt array this way, but it didn't work right. Within the trackLoaded function, I could change the value, and immediately read the value out again to verify that it was set, but in the playPositionMeter1 function the value would always appear to be zero, as if it were a completely separate variable. If I removed the "MaudioXponent." from the beginning so that the variable is named simply "warnAt", then the value set in one function is visible from the other. I'd like to scope my variable to be defined inside the MaudioXponent object so that there can be no collisions with other loaded scripts, but it just doesn't seem to work for me when I try it. Javascript is not my first language, so I'm sure it's something simple, but it's not working the way I would think it should. What am I missing here?
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

Re: Advanced mapping for M-Audio Xponent

Postby Be. » Wed Feb 08, 2017 5:56 pm

MelGrubb wrote:The only C++ I've done is years (like decades) ago, but it's not something I'm afraid of. I work in the C# world, so syntax is not a problem. It's probably more a matter of setting up a workable build environment. I think I saw somewhere that Visual Studio (Community) has everything we need to build Mixxx, and I have that on my other machine already.


We use the Visual Studio C++ compiler for the Mixxx build server and AppVeyor continuous integration, but I do not know if anyone uses the Visual Studio IDE to work on Mixxx. There are pages on the wiki with tips to set up Eclipse and KDevelop to work on Mixxx. Personally I use KDevelop. It recently got ported to Windows, but I have not tried it on Windows.

As for setting up a build environment, a lot of work has been done lately to make it easier to set up on Windows. I have not done it myself, so I'm not sure how easy it is, but there are a few developers who have working Windows build environments now.

MelGrubb wrote:A simple boolean ControlObject to control whether or not to start flashing would simplify things conceptually, but I think the method doing the flashing would have to remain largely as it is.


What about a ControlObject that toggles between 0 and 1 in sync with the flashing of the waveform on screen? This would be similar to the cue_indicator and play_indicator COs; no logic is needed in controller mappings, just toggle the LED when the value of the CO changes.

MelGrubb wrote:I'll look at building Mixxx from source again at some point, but I have enough other things going on at the moment to get my head into a larger codebase like this. Single controller mappings fit within my available mindwidth.


Totally understandable. Mixxx is a lot of code and it takes time to learn your way around. There is some documentation to help new developers get started, but it is far from complete.

MelGrubb wrote:I know there are things I'd love to look at. For instance, many controller mappings will define parameter values up at the top to enable/disable features. It would be great if there were a way to expose these to Mixxx itself so that they could be represented in the preferences dialog rather than having to open up the .js file to change them.


Yeah, this is definitely something Mixxx should have. Owen started a design document and proof of concept for this, but I think it needs more discussion and planning to come up with a comprehensive solution. Hopefully someone will take it up for Mixxx 2.2.

MelGrubb wrote:If you have time for one question, I'm a little confused by what I'll call "scoping rules" around script-level variables.


JavaScript has some weird scoping rules. Variable declarations are hoisted to the top of their scope. Scope is confined to functions in JavaScript (at least in the ancient JS interpreter Mixxx still uses; modern JavaScript uses let instead of var for block scoping).

MelGrubb wrote:Most of the state variables in the original script start with MaudioXponent. For instance "MaudioXponent.flashdur1", which is defined near the top of the script.


It can be misleading to think of that as just a variable; it is a property of the MaudioXponent object.

MelGrubb wrote:I tried to define my warnAt array this way, but it didn't work right.


I do not know what went wrong without seeing the code you tried. It's possible there was just a silly typo messing something up.

MelGrubb wrote:I'd like to scope my variable to be defined inside the MaudioXponent object so that there can be no collisions with other loaded scripts, but it just doesn't seem to work for me when I try it. Javascript is not my first language, so I'm sure it's something simple, but it's not working the way I would think it should. What am I missing here?


Indeed, each script loaded runs in one execution context for that controller. There are some plans for improving this situation and to work like other JavaScript environments.
I heard FLAC and I haven't gone back.
Protect your hearing with earplugs!

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

Re: Advanced mapping for M-Audio Xponent

Postby MelGrubb » Wed Feb 08, 2017 6:47 pm

Be. wrote:It can be misleading to think of that as just a variable; it is a property of the MaudioXponent object.


Yeah, that's how I was thinking of them, as properties on the MaudioXponent object. They just didn't seem to work that way when I tried it. I can try again, in case it was a stupid typo. It wouldn't be the first time.

Be. wrote:Indeed, each script loaded runs in one execution context for that controller. There are some plans for improving this situation and to work like other JavaScript environments.


I figured that was the purpose behind the top-level object being named for the controller rather than just being called "controller". I've never tried running with multiple controllers attached, but I know that Mixxx supports that.
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

Re: Advanced mapping for M-Audio Xponent

Postby MelGrubb » Thu Feb 09, 2017 1:21 pm

I did some refactoring last night, and in the end I got everything working, but I have a question about what's on the horizon for Mixxx.

I went ahead and made the progress bars flash when they reach 30 seconds from the end (the 30 is still hard-coded, unfortunately), but the momentary flash on each beat was not attractive, so I made a "beatState" array that toggles on each beat_active where the value is 1. Instead of "flash flash flash flash" for four beats, you now get "on off on off". It looks really good, but you can pause with the bar on or off. I'm going to add another condition so that when the deck is stopped, you always see the bar. I tried tying this same behavior to the pulsing Sync buttons I implemented earlier and that's where I noticed a problem.

So far, Mixxx has no concept of WHICH beat it is (1 2 3 4), so it was very possible to have songs on both decks that are perfectly in sync, but the Sync buttons were pulsing 180 degrees out of phase. It was distracting enough that I put the Sync buttons back to simple flashes per beat. I don't think I'll ever be in a situation where I have two tracks playing, both within 30 seconds of the end, so the progress bars flashing out of phase shouldn't really happen in the real world. What I'd like to know is whether there are any plans for more advanced beatgrid intelligence in the future. Will the beatgrid know which beat is #1 anytime soon? When that happens, I'll modify my script so that the bars turn on with the odd beats (1 & 3) and off with the even ones (2 & 4), and maybe revisit on/off Sync buttons.
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

Re: Advanced mapping for M-Audio Xponent

Postby Be. » Thu Feb 09, 2017 4:06 pm

MelGrubb wrote:I did some refactoring last night, and in the end I got everything working, but I have a question about what's on the horizon for Mixxx.


Cool. Please update the files in the first post.

No one is planning on implementing phase detection any time soon, unless you want to make that happen. There is an old Launchpad ticket for this where there is a brief outline of how much work would be involved and some hints on what tools could be used.
I heard FLAC and I haven't gone back.
Protect your hearing with earplugs!

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

Re: Advanced mapping for M-Audio Xponent

Postby MelGrubb » Fri Feb 10, 2017 4:15 am

I've updated the mapping files in the original post with an evening's worth of refactoring.

I replaced some mappings where the target function was left to do a lot of string parsing or math to determine the right channel or control with function mappings that provide the correct answer to begin with. It seems to me like that ought to make things run more efficiently. One example is the hotcue led callbacks. The arguments to that callback are not in the normal order, and so "control" ended up holding the complete group name etc. The function now takes just three parameters with names that match their purpose, so the mapping looks like this:

Code: Select all
engine.connectControl("[Channel1]", "hotcue_1_enabled", function(value, group) { MaudioXponent.onHotCue(0, 0, value); });


Where the first zero is the channel, and the second one is the hotcue number. The function looks like this:

Code: Select all
MaudioXponent.onHotCue = function(channel, cue, value) {
    midi.sendShortMsg(MaudioXponent.on + channel, MaudioXponent.leds.cue1 + cue, value);
}


If doing it this way is somehow LESS efficient, then I'd like to know. For something like the hotcue states toggling, which doesn't happen many times a second, I wouldn't expect there to be an appreciable difference anyway, but there are other places where I've inlined a function call in order to better sort out and name the parameters, or to spare the function from having to parse strings to get the values it needs.

Also, I've renamed the scripts to "Advanced" instead of "Alternate".

Feedback is appreciated.
MelGrubb
 
Posts: 141
Joined: Tue Apr 27, 2010 12:00 pm

Re: Advanced mapping for M-Audio Xponent

Postby Be. » Fri Feb 10, 2017 5:24 am

Unnecessary string manipulation is not good, but I don't think it makes a practical difference for controller mappings. Perhaps it might for sensitive controls like jog wheels, but I have no data to back that up. One of the benefits of Components is removing the need for most of the string manipulation that mappings have regularly done before because the strings are calculated infrequently then stored as properties of the Component object.
I heard FLAC and I haven't gone back.
Protect your hearing with earplugs!

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

Next

Return to Controller presets/mappings

Who is online

Users browsing this forum: No registered users and 1 guest