Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Components JS HotcueColor integration #2030

Merged
merged 10 commits into from
Jul 10, 2019

Conversation

Swiftb0y
Copy link
Member

@Swiftb0y Swiftb0y commented Feb 15, 2019

I am trying to natively integrate the hotcue colors feature into components js. It's not nearly finished yet but I need some help so I decided to open this PR prematurely. For some reason, the custom connect method doesn't get called. The sampler buttons use a hack and set the outKey to null which causes connect to get called but I don't understand why that works...

SampleCode:

var TEST = {};

TEST.btn1 = new components.HotcueColorButton({
  midi: [0x90,0x01],
  number: 1,
  colors: [1,2,3,4,5,6,7,8,9],
});
TEST.btn2 = new components.HotcueColorButton({
  midi: [0x90,0x02],
  number: 2,
  sendRGB: function (color) {
    var msg = [12,34,56,color.red>>1,color.green>>1,color.blue>>1]
    midi.sendSysexMsg(msg,msg.length);
  }
});
TEST.btn3 = new components.HotcueColorButton({
  midi: [0x90,0x03],
  number: 3,
  colors: [{red:1,green:2,blue:3},{red:4,green:5,blue:6}],
  sendRGB: function (color) {
    var msg = [12,34,56,color.red>>1,color.green>>1,color.blue>>1]
    midi.sendSysexMsg(msg,msg.length);
  }
});
  • If you want to set the color on the controller by a midi value, you can define an array with the corresponding colors (their index correspond to the color id) (see TEST.btn1).
  • if the controller supports setting the colors directly, you can define a method (sendRGB) which is a custom send function for translating the color and sending it out via sysex (see TEST.btn2).
    It will not perform any translation of the color and use the color codes of the predefined colors list.
  • If you need a convert the predifined colors into custom rgb components, you can do it like outlined in TEST.btn3)

@Swiftb0y
Copy link
Member Author

I have also noticed that the HotcueButton complains ('ERROR: No hotcue number specified for new HotcueButton.') when subclassing it via HotcueColorButton.prototype = new HotcueButton({}). Is that an Issue?

@ferranpujolcamins
Copy link
Contributor

I have also noticed that the HotcueButton complains ('ERROR: No hotcue number specified for new HotcueButton.') when subclassing it via HotcueColorButton.prototype = new HotcueButton({}). Is that an Issue?

This is expected behaviour, you are required to specify the hotcue number.

@ferranpujolcamins
Copy link
Contributor

* If you want to set the color on the controller by a midi value, you can define an array with the corresponding colors

This is cool! Will come super handy when mapping the MidiFighter!

@Swiftb0y
Copy link
Member Author

Swiftb0y commented Feb 17, 2019

This is expected behaviour, you are required to specify the hotcue number.

I know, but that doesn't make any sense when you just want to inherit the prototype of the HotcueButton. My question was if this will be an issue when inheriting the HotcueButton like that (because it will cause the constructor to return early).

This is cool! Will come super handy when mapping the MidiFighter!

I think this is essential since many controllers either don't support setting colors via SysEx or don't have it documented (like most numark products).

@ferranpujolcamins
Copy link
Contributor

In your examples you specify the property 'colors' as an array. This is assuming that the predefined colors have consecutive ids starting with 0. This assumption is true now, but maybe it's not in the future. You should pass an object with color ids as keys instead of an array. I think you don't need to change anything in the code, just keep this in mind when documenting.

@ferranpujolcamins
Copy link
Contributor

@Be-ing can you gives us a hand?

@Be-ing
Copy link
Contributor

Be-ing commented Feb 17, 2019

Okay, I'll take a look. My first thought is that I'd rather add new, optional functionality to HotcueButton than add a new class.

@Swiftb0y
Copy link
Member Author

I did that initially at first but it made the code more polluted so figured to just split it into its separate class.

This is how the class looks when the color functionality is integrated:

var HotcueButton = function (options) {
    if (options.number === undefined) {
        print('ERROR: No hotcue number specified for new HotcueButton.');
        return;
    }
    if (options.sendRGB !== undefined || options.colors !== undefined) {
        if (options.colors === undefined) {
            options.colors = color.predefinedColorsList();
        }
        this.colorIdKey = 'hotcue_' + options.number + '_color_id';
        this.getColor = function() {
            return color.jsColorFrom(engine.getValue(this.group, this.colorIdKey))
        };
        this.output = function(value) {
            if (value > 0) {
                var id = engine.getValue(this.group, this.colorIdKey)
                var color = this.colors[id]
                if (color instanceof Array) {
                    if (color.length !== 3) {
                        print("ERROR: invalid color array for id: " + id);
                        return;
                    }
                    if (this.sendRGB === undefined) {
                        print("ERROR: no custom method for sending rgb defined!");
                        return;
                    }
                    this.sendRGB(color);
                } else if (typeof color === 'number') {
                    this.send(color);
                }
            } else {
                this.send(this.off);
            }
        };
        this.connect = function() {
            if (undefined !== this.group &&
                undefined !== this.outKey &&
                undefined !== this.output &&
                typeof this.output === 'function') {
                this.connections[0] = engine.makeConnection(this.group, this.outKey, this.output);
            }
            if (undefined !== this.group) {
                this.connections[1] = engine.makeConnection(this.group, this.colorIdKey, this.output);
            }
        }
    }
    this.number = options.number;
    this.outKey = 'hotcue_' + this.number + '_enabled';
    Button.call(this, options);
};
HotcueButton.prototype = new Button({
    unshift: function () {
        this.inKey = 'hotcue_' + this.number + '_activate';
    },
    shift: function () {
        this.inKey = 'hotcue_' + this.number + '_clear';
    },
});

I prefer the separate class approach to be honest.

For some reason, the custom connect method doesn't get called. The sampler buttons use a hack and set the outKey to null which causes connect to get called

@Be-ing since you probably coded that part, can you explain why that works / doesn't work?

@Be-ing
Copy link
Contributor

Be-ing commented Feb 17, 2019

group and outKey properties need to be defined for the Component constructor to call connect:
https://github.com/mixxxdj/mixxx/pull/2030/files#diff-9bedde251ff7d14d89b40d05168275e4L49

@Be-ing
Copy link
Contributor

Be-ing commented Feb 17, 2019

Sorry, I forgot to document that detail. I added that to the documentation on the wiki:

connect: register output as the callback function that gets executed when the value of the Mixxx ControlObject specified by group, outKey changes. This is called automatically by the Component constructor if group and outKey are defined (otherwise it needs to be called after construction). Implement a custom function if you need to connect callbacks for multiple Mixxx ControlObjects in one Component. Refer to the source code of SamplerButton.prototype.connect for an example.

@Swiftb0y
Copy link
Member Author

ohh, right, so my mistake was actually that I forgot to define a group when creating the Buttons in my test code.

@Swiftb0y
Copy link
Member Author

Swiftb0y commented Mar 2, 2019

@Be-ing, have you made a decision if you prefer a separate class or not?
I prefer it separate because it would make the code cleaner. However, Subclassing the HotcueButton fails because the constructor of the HotcueButton exits early when no number is provided. More exactly, It doesn't fail but the actual Component never gets created and all the methods in L317 never get assigned (since the constructor of the underlying Component class never executes).

What approach would you recommend? I really want to finish my NS6II mapping which depends on this feature.

@Swiftb0y Swiftb0y mentioned this pull request Mar 4, 2019
17 tasks
@Swiftb0y
Copy link
Member Author

Swiftb0y commented Apr 8, 2019

@Be-ing, IMO this is now finished. I've used it for a while now in my WIP NS6II mapping and it worked flawlessly. Can we merge this soon?

Copy link
Contributor

@Be-ing Be-ing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made some preliminary comments, but I don't fully understand the code because I have not been following the discussions about hotcue colors too closely. Could you and/or @ferranpujolcamins document how the new color object works on https://mixxx.org/wiki/doku.php/midi_scripting ?

res/controllers/midi-components-0.0.js Outdated Show resolved Hide resolved
res/controllers/midi-components-0.0.js Outdated Show resolved Hide resolved
res/controllers/midi-components-0.0.js Outdated Show resolved Hide resolved
res/controllers/midi-components-0.0.js Show resolved Hide resolved
Copy link
Contributor

@Be-ing Be-ing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made some preliminary comments, but I don't fully understand the code because I have not been following the discussions about hotcue colors too closely. Could you and/or @ferranpujolcamins document how the new color object works on https://mixxx.org/wiki/doku.php/midi_scripting ?

Copy link
Contributor

@Be-ing Be-ing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made some preliminary comments, but I don't fully understand the code because I have not been following the discussions about hotcue colors too closely. Could you and/or @ferranpujolcamins document how the new color object works on https://mixxx.org/wiki/doku.php/midi_scripting ?

@Be-ing
Copy link
Contributor

Be-ing commented Apr 11, 2019

Sorry for the duplicate comments. GitHub is having some issues right now...

@Be-ing Be-ing added this to the 2.3.0 milestone Apr 22, 2019
@Swiftb0y
Copy link
Member Author

Swiftb0y commented May 13, 2019

I'm sorry, I didn't find time to write some documentation. Here is what I came up so far. I don't want to publish it to the wiki right away because I don't know if I am properly explaining everything:

Colored Hotcues

As most DJing applications, mixxx is capable of colored hotcues. There are several ways of accessing and processing color information in scripts. To keep compability with color limited hardware, we provide a [n] colors colorpalette: [Insert here].
Each of those colors has a unique ID. This ID can be retrieved via the engine.getValue('[ChannelN]', 'hotcue_X_color_id') (where N and X are the respective Deck and hotcue whose information is being accessed).
To prevent some code duplication and to provide a more robust API, a new color object was created. It features methods that return a struct/hashmap/dictionary which contain the properties of the colors in the Color palette. It contains the following properties:

  • red (red color channel (8-bit precision))
  • green (green color channel (8-bit precision))
  • blue (blue color channel (8-bit precision))
  • alpha (alpha color channel (8-bit precision); currently unused)
  • id (internal ID of the color)

The color API features two methods:

  • predefinedColorFromID(id) (returns a single color object by the provided ID)
  • predefinedColorsList() (returns the whole color palette in the form of a color object array)
    Since controllers handle colors differently from model to model, it is up to you to interpret the color and send it to the controller. however, [Components JS] has a hotcuebutton component that is able to take care of the color feature (see [component js hotcue button color]).

Please note that I didn't really care about formatting and all that stuff since this is just a proposal.

@Holzhaus Holzhaus mentioned this pull request May 17, 2019
16 tasks
@Swiftb0y
Copy link
Member Author

ping @Be-ing what do you think about the documentation I wrote?

Holzhaus added a commit to Holzhaus/mixxx that referenced this pull request May 19, 2019
This depends on PR mixxxdj#2030. If the PR is merged, custom hot cue colors
will immediately work with this mapping. In the meantime, the pads will
light up in the mode's color (i.e. white in hot cue mode, blue in cue
loop mode).
@Holzhaus
Copy link
Member

Holzhaus commented Jun 1, 2019

@Swiftb0y Thanks, nice work. The documentation looks good to me. As far as I'm concerned, the only thing blocking this is the current usage of this.connections.push(...) instead of this.connections[1] = ... .

Copy link
Contributor

@Be-ing Be-ing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, sorry for slacking on my response. The documentation you posted above helps me understand the code better but I am still confused on what exactly a color ID is. Is that a number? A hex code? A special JS object created by the script engine?

res/controllers/midi-components-0.0.js Show resolved Hide resolved
res/controllers/midi-components-0.0.js Outdated Show resolved Hide resolved
res/controllers/midi-components-0.0.js Show resolved Hide resolved
@ferranpujolcamins
Copy link
Contributor

Related: #2153

@Swiftb0y
Copy link
Member Author

I am still confused on what exactly a color ID is. Is that a number? A hex code? A special JS object created by the script engine?

The colors API introduced the CO hotcue_X_color_id. This always represents the ID of the color defined in the predefinedcolorsset. So the colorID is a number that references the current color of the hotcue. It can then be used with colors.predefinedColorFromId(id) to retrieve a javascript object representing the full colors. However, this is not needed most of the time as the colorID is enough to retrieve the color value for the controller from the this.colors array.

@Swiftb0y Swiftb0y mentioned this pull request Jul 8, 2019
3 tasks
@Swiftb0y Swiftb0y changed the title [WIP] Components JS HotcueColor integration Components JS HotcueColor integration Jul 8, 2019
Co-Authored-By: Be <be.0@gmx.com>
@Be-ing Be-ing merged commit 5865c5c into mixxxdj:master Jul 10, 2019
@Swiftb0y Swiftb0y deleted the components_hotcue_colors branch July 10, 2019 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants