Shift behavior in Components JS

Mod your Mixxx: Share your controller mappings, keyboard mappings, skin and script tweaks here!

Moderators: garth, User Customization Moderators

Shift behavior in Components JS

Postby caelia » Wed May 30, 2018 3:54 am

Hello, Mixxx people!

I've been absent for a while, so first of all: to the development team, great work on the 2.1 release! I'm particularly happy to see the new Effect Unit design - I expect that now working with fx will be an enjoyable challenge - as it should be - instead of a nightmare.

And to whoever was trying to contact me a while back about the Denon MCX8000 mapping - sorry I didn't respond. I've been going through some big life changes, and sometimes keeping myself together is the only project I have time for. And I guess I have a bit of seasonal depression on top of that ;-)

The good news is that I still love DJing, and I still love Mixxx and my MCX8000, and I'm eager to get them working together perfectly. So I've gotten back to that mapping, and I've been attempting to convert it to the components framework. For my own purposes, it would be fine to use the new EffectUnit component and leave the rest as is, but in case someone else wants to read my code, I suppose it is a good idea to consistently follow the recommended architecture. And there were a few things that needed to be overhauled anyway.

I've run into a few issues. It's mostly random details that I don't quite understand (and yes, I think the documentation could use some work - but that's another discussion, and I may be able to help with that task). But there is one aspect of the library that I think I do understand, and which seems to me very poorly designed, and that is the shift/unshift behavior.

Maybe I just need to understand the rationale behind the design. If this issue has already been discussed, please feel free to point me to the record of that discussion (I've done a bit of searching in the forum archive, and didn't come up with anything, though I did not dig back very far). But anyway, here's what I'm having a hard time accepting:

In midicomponents-0.0.js, I find the following:

Code: Select all
ComponentContainer.prototype = {
    ....
    shift: function () {
        this.forEachComponent(function(component) {
            ....
            component.shift();

So ... have I got this right? By default, every time you press or release the Shift button, [un]shift() is called recursively for the entire hierarchy of components, and every component has its state reset? When in all likelihood you are only going to operate one of those state-altered controls per shift press (unless people are using their controllers in ways I can't imagine)? I find that rather horrifying ... the more so since in many cases a controller will handle the shift internally - i.e., the control sends a different MIDI signal when Shift is active - and isn't the point of doing that so that one's device handling code doesn't have to know about the state of the Shift button?

If you asked me to implement shift/unshift right now, I would write a default input function something like:

Code: Select all
SomeComponent.prototype = {
    ....
    this.input = function(bla bla bla ...) {
        if (MyController.shifted) {
            this.shiftAction();
        } else {
            this.defaultAction();
        }

And of course, when you press or release Shift, it would simply set the Boolean property MyController.shifted.

I get that this is not quite as clean a design from an OO perspective, but I think it is vastly more efficient. Surely the one extra conditional wouldn't cause a significant performance hit, would it? And if you have a controller like mine, where most controls send a different signal when shifted (and that's pretty common, isn't it?), then you don't even need that - you can simply map those alternate signals to ... either alternate methods of the same component, or the same method on alternate components. E.g., the standard Component might have input and shiftedInput methods.

IDK ... am I missing something important here? Please help before I rewrite the whole Components library! Uhhh, just kidding - I'm not actually planning on doing that, but I kinda feel like it right now.
caelia
 
Posts: 28
Joined: Thu Sep 15, 2016 12:01 pm

Re: Shift behavior in Components JS

Postby JosepMa » Wed May 30, 2018 6:48 pm

Hello.

In components, when defining the controls, you define a shifted and an unshifted action. Then, you define what button alternates between shifting and unshifting, and then tells the controls about this new status.
In your plain code, there is a global variable that all controls know and check. In components, it alternates the actions.

Also, sure, there are controllers that have a stateful shift button that switches controls so that they do alternate actions, but that does not prevent you to have additional shift-like buttons, or to apply shift to buttons or controls that the controller didn't think that they would be switchable.

In other words, shifting in components is meant to explicitly switch in software some group of actions that the controller itself is not aware of.
JosepMa
 
Posts: 636
Joined: Sat Oct 10, 2015 7:02 pm

Re: Shift behavior in Components JS

Postby Be. » Thu Jun 07, 2018 1:11 am

Welcome back. I think JosepMa explained it pretty well, but I'll elaborate in more detail. I'm open to ideas for how to improve the library. That requires that people use the library and think critically about its design, so thank you.

caelia wrote:in many cases a controller will handle the shift internally - i.e., the control sends a different MIDI signal when Shift is active - and isn't the point of doing that so that one's device handling code doesn't have to know about the state of the Shift button?


IMO this is bad design and I wish manufacturers would stop doing this because it requires the mapping code to be overcomplicated. For example, the Hercules P32 flickers all the LEDs when the shift button is pressed. To avoid this, the Components have to send MIDI output for both the unshifted and shifted MIDI messages.

However, other DJ software has even clunkier controller mapping systems than Mixxx, so hardware manufacturers hardcode application logic into controller firmware instead of putting it in the application. The core idea of the Components library is to have one object in the code for every component of the hardware. This makes it easier for the controller mapping to handle all the layer switching logic in whatever way the mapping developer imagines. When manufacturers put layer management logic in firmware, it creates an awkward situation for mappings. Either there need to be multiple Component objects in the code for each physical piece of the hardware or the code needs to hack around the the firmware's layer management hacks by using one Component object to handle MIDI input/output for every layer on the controller. The former approach gets ugly when the mapping developer wants to design the mapping in a way the hardware manufacture did not anticipate.

For example, for the Hercules P32 controller I used to develop the Components library, I implemented a shift function for the headphone button. The controller's firmware sends different MIDI messages depending on the state of the shift button for most of the controls, but not for the controls in the middle mixer section. If I created two Component objects in the code for each physical control in the shifted/unshifted state, I couldn't do that consistently with the headphone button. As another example, consider the EffectUnit in the Components library which implements its own complex layer management logic. The code is designed to be reusable for different controllers, which may or may not change the MIDI messages used by those controls when a shift button is pressed, so it cannot assume that the controller has any shift layer logic in its firmware. Indeed, it is now used by both types of controllers (the Allen & Heath Xone K2 does not send different MIDI messages when shift is held).

caelia wrote:By default, every time you press or release the Shift button, [un]shift() is called recursively for the entire hierarchy of components, and every component has its state reset? When in all likelihood you are only going to operate one of those state-altered controls per shift press (unless people are using their controllers in ways I can't imagine)? I find that rather horrifying
...
I get that this is not quite as clean a design from an OO perspective, but I think it is vastly more efficient. Surely the one extra conditional wouldn't cause a significant performance hit, would it?


Have you actually noticed this to be a problem? I think you're looking for a way to optimize this prematurely. Surely any modern computer can execute a few small JavaScript functions in an unnoticeably fast time when a shift button is pressed.

caelia wrote:If you asked me to implement shift/unshift right now, I would write a default input function something like:

Code: Select all
SomeComponent.prototype = {
    ....
    this.input = function(bla bla bla ...) {
        if (MyController.shifted) {
            this.shiftAction();
        } else {
            this.defaultAction();
        }

And of course, when you press or release Shift, it would simply set the Boolean property MyController.shifted.


This does not fit well with the design goals of the library in multiple ways. First, the Components library is intended to be a generic system for developing mappings with any arbitrary layer switching logic, not merely for handling the most common use cases. Also, the prototype input and output functions are designed in such a way that layer switching functions can merely change the properties of Components (such as their inKey and/or outKey) to change their behavior without writing custom input/output handling functions for each layer.
I heard FLAC and I haven't gone back.
Protect your hearing with earplugs!

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


Return to User Customizations

Who is online

Users browsing this forum: No registered users and 1 guest