helgoboss / helgobox

Helgobox: ReaLearn & Playtime
https://www.helgoboss.org/projects/helgobox
GNU General Public License v3.0
204 stars 20 forks source link

Support for Arturia Minilab 3 #785

Open jonasdiemer opened 1 year ago

jonasdiemer commented 1 year ago

Hello,

I am interested in getting support for the Minilab 3 - I assume it can be done quite quickly as the Minilab mk2 is already supported.

I've tried to use the Minilab mk2 preset, but have a few questions:

  1. The Minilab 3 (as well as mk2) has a shift button, which sends a MIDI CC, but also affects other controls while pressed (e.g. the pads send a CC when pressed while shift is held). At first glance, shift interfere with the learning of commands, because the shift button is learned instead of the CC from e.g. a pad. How can I handle that?
  2. The JSON has "id"s defined - could I just keep them, or should I create new UUIDs (I assume that's what they are)?
  3. Would it be possible to send feedback to the Minilab, e.g. changing the colors of the PAD LED when playing/recording?

Thanks a lot in advance!

Regards Jonas

jonasdiemer commented 1 year ago

Realizing I can edit the preset in the GUI, so question 2 is obsolete :)

jonasdiemer commented 1 year ago

Workaround found for point 1: Press shift before "learn source".

So only point 3 to figure out...

jonasdiemer commented 1 year ago

Current status of my mapping: minilab-3.json.zip It is based on minilab-mkii with the following changes:

TODOs:

jonasdiemer commented 1 year ago

Was able to make some progress, here is an updated mapping: minilab-3.json.zip

Current status:

TODOs:

@helgoboss, let me know if you'd like a PR to include the mapping into the mainline.

jonasdiemer commented 1 year ago

Regarding color, something like this seems to work (from manual, added bit mask and multiplication with value).

local color = context.feedback_event.color

if color == nil then
    -- This means no specific color is set. Choose whatever you need.
    color = { r = 0, g = 0, b = 220 }
end
return {
    address = 0x4b,
    -- Whatever messages your device needs to set that color.
    messages = {
        { 0xf0, 00, 0x20, 0x6b, 0x7f, 0x42, 0x02, 0x02, 0x16, 0x34, y * color.r & 0x7F, y * color.g & 0x7F, y * color.b & 0x7F, 0xf7 }
    }
}
LePretreD commented 1 year ago

Hello and happy new year,

I'm looking forward to your preset (actually i work with an Arturia Keylab Essential 88, and the pad colors seem to work like the Minilab 3), so i have downloaded your last update and tested the pad feedback with the script on your last comment (adjusting the script messages to my keyboard), but it doesn't work, so i was wondering if you could help me ?

As i don't really know anything in coding/scripting or developping, i'm trying but i don't understand how it works (also i'm new on Github, and as a "non-developper" i don't know if this is the right place for this message, sorry if that is the case.

Thank you

jonasdiemer commented 1 year ago

Hello @LePretreD,

I would be surprised if the script for the MiniLab 3 would directly work with the Keylab Essential 88 keyboard.

I grabbed some of the info required to set the color from the DAW Integration Script that Arturia provided for the Reason DAW. AFAIR, I also took a MIDI grab (with the MIDI Monitor app on Mac) while changing colors of the pads via Ableton - this helped understand the last line in the color script.

You can probably do the same for your keyboard, but it would require a bit of "developer savviness".

Also, please note that the snippet I mentioned in my previous post is only a proof-of-concept, i.e. it changes the color of one pad. For a full implementation, this script would have to be copied to all pad items, which I didn't feel was the right solution (a LUA-based preset would be better, I think). Especially, as the script currently only moves the color from the Glue section and doesn't really take it from e.g. the track.

Thus, the color snippet in the current form is not useful in practice.

LePretreD commented 1 year ago

Thanks for your reply, I'll try to do the same with the DAW integration script for Reason, and a MIDI grab app

Regarding the snippet for one pad, i took the following expression from the Keylab 88 mkII (not essential) preset : F0 00 20 6B 7F 42 02 00 10 70 [0gfe dcba] F7 The number '70' related to the number of the pad (from 70 to 77) .

I first used it as a Raw MIDI/SysEx command, but i could only toggle the pads in blue. Still it triggered it, so i think the expression correctly refer to my pads on the Essential 88

That's why i assumed it would work in LUA script by replacing this snippet messages = { { 0xf0, 00, 0x20, 0x6b, 0x7f, 0x42, 0x02, 0x02, 0x16, 0x34, y * color.r & 0x7F, y * color.g & 0x7F, y * color.b & 0x7F, 0xf7 } by this one messages = { { 0xf0, 00, 0x20, 0x6b, 0x7f, 0x42, 0x02, 0x00, 0x10, 0x70, y * color.r & 0x7F, y * color.g & 0x7F, y * color.b & 0x7F, 0xf7 } Supposed to correspond to the first pad on my keyboard.

Just to be sure, don't i need to delete those phrases (boxed in red), or modify those boxed in green ? Or may be my adress wrong (in yellow) ? Sans titre

jonasdiemer commented 1 year ago

F0 00 20 6B 7F 42 02 00 10 70 [0gfe dcba] F7 was also present in the MiniLab Mk2 preset. I think it controls only the "value" of a pad, not the color. [0gfe dcba] uses the input from Reaper, i.e. 00 or 127, thus toggling the pad between on and off (I am assuming it keeps its color to whatever was preset - could be wrong).

The message for color is a different one (and seems to also use different addresses).

You're right that in my snippet, the id of the pad is 0x34 - but the color change is a different message than the toggle.

BTW: The script needs to be entered as MIDI Script, not Raw MIDI:

image
LePretreD commented 1 year ago

Yes that's what i understood too, i just took this expression for the pad ID, then i applied the ID to your expression in MIDI script (LUA), Isn't the script you commented the message for changing color ?

And Realearn said "Failed to invoke LUA script" when typing your script, so i thought maybe i typed it wrong ? Should i delete or modify those snippets boxed ? image

helgoboss commented 1 year ago

Hi, sorry for the late reply. I have some catch up to do. I'll see if I can gather the still open questions and give some answers.

helgoboss commented 1 year ago
  1. Concerning the Shift button: The best thing for ReaLearn (and most flexible way in general) would be if you configure the MiniLab to not do anything special when Shift is pressed. Just treat it as a normal button that sends a special CC, nothing more. Is that possible? Then you can use the conditional activation feature in ReaLearn to change the function of other buttons and also let feedback follow accordingly.
  2. IDs don't need to be UUIDs, you can use quite arbitrary identifiers. They should be unique and can be even somewhat descriptive (like variables in a programming language). ReaLearn should preserve them.
  3. Changing colors of a pad when REAPER play state changes ... totally possible. You need to use target "Project: Invoke transport action" ... but probably you have figured that out already.
  4. I have the Minilab 2 and the encoders are configurable to send relative messages. I would be super surprised if they took away this possibility from Minilab 3. I mean, what's the point of having encoders that send absolute messages? Concerning the feedback
  5. Feedback on LCD: I guess that needs some reverse engineering of the MIDI protocol. Would be surprised if Arturia would publish the MIDI specs.
  6. The structure of the MIDI script to translate color information to sys-ex looks about right. So it works?

PR welcome. Is it a controller preset (which just labels control elements and assigns them to virtual targets) or a main preset (which actually assigns functions)? Or both? The respective places would be https://github.com/helgoboss/realearn/tree/master/resources/controller-presets/unofficial and https://github.com/helgoboss/realearn/tree/master/resources/main-presets/unofficial.

jonasdiemer commented 1 year ago

Hi,

thanks for the feedback.

Re 1: Probably this could be done, but I don't see an immediate advantage. Right now, when I press shift, I immediately see the current status of the transport controls on the pad LEDs (which is the behavior I want)... Not sure if this would require extra fiddling if I used shift as a regular button...

Re 2, 3: OK, already figured it out

Re 4: Yes, unfortunately, the current version of MIDI Control Center doesn't let me select relative for the encoders (unless I have completely overlooked it).

Re 5: There is an integration script provided by Arturia for Reason, which seems to have what's needed.

Re 6: I got it working once, but received an error "Failed to invoke LUA script" in my most recent attempt (haven't tried hard).

From my side, these are the remaining points:

  1. Will you grab the profile I provided here or should I create a PR? Any changes needed before you take it into your codebase?

  2. I will try to look into point 5 (text feedback) soon - but I think the preset is already quite useable.

jonasdiemer commented 1 year ago

Re 5/8, i.e. supporting text output, I am trying to wrap my head around how to integrate Lua. @helgoboss, do I understand correctly that controller presets written in Lua get imported (via clipboard), during which they get executed to produce the preset datastructures? Meaning: Lua controller presets cannot contain "runnable" Lua functions directly?

This would mean that I have to put any Lua code for sending an LCD message as a MIDI Script into a separate mapping?

jonasdiemer commented 1 year ago

OK, so I got something working to send the feedback of an encoder via text by adding the below MIDI Script to a new mapping.

@helgoboss, wondering if there is a generic way to get a text representation of what the encoder is mapped to (e.g. Volume)? I've found that I can use "Textual Feedback" in the glue section, but to get "rich" information (e.g. track name and textual representation of the value), I'd have to build a compound string and then deconstruct it in the MIDI Script. It would be much easier if the script had access to e.g. the target structure. Or have I missed something?

The code needs an initialization of the connection (see comment). @helgoboss, where would I put such initialization code?

-- Hevily inspired by Arturia's DAW Integration script for Reason 

function stringToHex(text)
    local hexStringToReturn = ""
    for i=1, string.len(text) do
        if string.len(hexStringToReturn) > 0 then 
            hexStringToReturn = hexStringToReturn .. " "
        end
        hexStringToReturn = hexStringToReturn .. string.format("%X", string.byte(text,i))   
    end
    return hexStringToReturn
end

-- Displays text on ML3
-- May require initialization messages: f0 00 20 6b 7f 42 02 02 40 6a 21 f7 and f0 00 20 6b 7f 42 02 02 40 6a 10 f7 and 
-- line1 and line2 = text to display
-- w_value = value to display
-- page_type = 10 for status page, 3 for knob, 4 for faders
function make_lcd_midi_message(line1, line2, w_value, page_type)
    data_control = ""
    REC_STATUS = "00"
    PLAY_STATUS = "00"

    if page_type == 1 then
        data_control = ""
    end
    if page_type == 2 then
        data_control = "1F 02 01"
    end
    if page_type == 3 then
        if string.len(string.format("%x",w_value)) == 1 then
            data_control = "1F 03 01 0"..string.format("%x",w_value).." 00"
        else
            data_control = "1F 03 01 "..string.format("%x",w_value).." 00"
        end
    end
    if page_type == 4 then
        if string.len(string.format("%x",w_value)) == 1 then
            data_control = "1F 04 01 0"..string.format("%x",w_value).." 00"
        else
            data_control = "1F 04 01 "..string.format("%x",w_value).." 00"
        end
    end
    if page_type == 5 then
        data_control = "1F 05 01 00 00"
    end

    if page_type == 10 then
        data_control = "1F 07 01 " .. REC_STATUS .. "" .. PLAY_STATUS .. " 01 00"
    end

    local sysex = "f0 00 20 6b 7f 42 04 02 60 " .. data_control .. " 00 01 " .. stringToHex(string.sub(line1,1,31)) .. " 00 02 " .. stringToHex(string.sub(line2,1,31)) .. " 00 f7"

    return sysex
end

function msg_to_hex(msg)
    local integer_list = {}
    for i in string.gmatch(msg, "%S+") do
        local integer = tonumber(i, 16)
        table.insert(integer_list, integer)
    end
    return integer_list
end

local value = math.floor(127*y)
local msg = msg_to_hex(make_lcd_midi_message("Encoder 1", y, value, 3))

return {
    address = 0x4b,
    messages = { msg },
}
helgoboss commented 1 year ago

Re 1: Probably this could be done, but I don't see an immediate advantage. Right now, when I press shift, I immediately see the current status of the transport controls on the pad LEDs (which is the behavior I want)... Not sure if this would require extra fiddling if I used shift as a regular button...

There's no immediate advantage if you just want to build a main preset working for your particular use case. But if you want to build a controller preset that can potentially work in lots of different scenarios (using virtual targets only), then making shift a regular button would be better (because much more reusable). However, I only require that for official presets, so it's not a big deal.

Re 4: Yes, unfortunately, the current version of MIDI Control Center doesn't let me select relative for the encoders (unless I have completely overlooked it).

I just had a look in the MIDI Control Center because I couldn't believe it ... but you are right. Wow, this would be an absolute deal breaker for me. Happy that I didn't order one.

  1. Will you grab the profile I provided here or should I create a PR? Any changes needed before you take it into your codebase?

PR would be more convenient for me, so it will land quicker. It should go into the controller-presets/unofficial folder. I don't usually review 3rd-party presets, not enough time. So I think just push the PR as is.

  1. I will try to look into point 5 (text feedback) soon - but I think the preset is already quite useable.

Cool.

helgoboss commented 1 year ago

Re 5/8, i.e. supporting text output, I am trying to wrap my head around how to integrate Lua. @helgoboss, do I understand correctly that controller presets written in Lua get imported (via clipboard), during which they get executed to produce the preset datastructures? Meaning: Lua controller presets cannot contain "runnable" Lua functions directly?

This would mean that I have to put any Lua code for sending an LCD message as a MIDI Script into a separate mapping?

Yes, ReaLearn executes your Lua only once at import time and the only result is the preset. MIDI scripts, control transformations, feedback transformations are also scripts but they are something entirely separate and they are saved per mapping. I might make this kind of scripts reusable in future, but at the moment they are not.

helgoboss commented 1 year ago

OK, so I got something working to send the feedback of an encoder via text by adding the below MIDI Script to a new mapping.

@helgoboss, wondering if there is a generic way to get a text representation of what the encoder is mapped to (e.g. Volume)? I've found that I can use "Textual Feedback" in the glue section, but to get "rich" information (e.g. track name and textual representation of the value), I'd have to build a compound string and then deconstruct it in the MIDI Script. It would be much easier if the script had access to e.g. the target structure. Or have I missed something?

ReaLearn's textual feedback in its current state is limited: One target info on one display basically. It doesn't support complex use cases. I would need to extend ReaLearn to allow things like gathering info from multiple targets and sending them to the controller as one sys-ex message. I sometimes thought about this but there's now FR yet, feel free to open one. I think I would solve this by adding the possibility to write a Lua script per compartment (not per mapping) which is "active", e.g. can register callbacks for target changes, write arbitrary MIDI whenever it wants. In other words, much more responsibility for the script author but much more possibilities.

The code needs an initialization of the connection (see comment). @helgoboss, where would I put such initialization code?

No place for that.

jonasdiemer commented 1 year ago

Thanks for the feedback.

Regarding the shift button, I see your point. Main motivation for me was that the behavior of shift to access transport control is semi-baked into the design of the MiniLab3 by the markings on the pads. Worst-case, people could disable the related mapping(s).

Regarding relative encoders, I will reach to Arturia, maybe they can add it.

I will create a PR then for the current preset in controller-presets/unofficial/. Will start with a static template (the one I have build from a copy of the MiniLab MkII), but might 'migrate' to a Lua-based one later (would be easier to handle the LCD stuff).

For the LCD initialization, could this be done with a mapping that maps to a target that generates a "feedback" at a reasonable "initialization point"? Worst-case, I'll map the initialization to each display update or the shift button ;). Or maybe a FR for "ReLearn: Init" target?

For the LCD updates, it would be convenient to have access to the target struct - I will create a FR for this.

jonasdiemer commented 1 year ago

PR is created.

Regarding handling of LCD updates, I am thinking of the following approach:

  1. Create a single controller mapping for the Display that expects a textual input composed of multiple values (e.g. `"||"). This would contain the "complex" MIDI Script that decodes the input and sends it to the device. The mapping would be tied to a dedicated virtual multi target (that is not used by any of the buttons).
  2. Then create main mappings for values to display (e.g. Master volume, Master Pan, ...) that use the display mapping as source. The mappings would require a text expression in the glue section, e.g. {{mapping.name}}|{{target.text_value}}|{{target.normalized_value}}.

This would allow users to create mappings of values that they want to see in the display. Any changes to the monitored values would show on the display (with potential race condition/flickering if many updates happen).

Alongside with the "input" mappings (e.g. slider 1 to master volume), the display shows values when the user moves sliders/encoders etc.

@helgoboss I would appreciate your opinion if such mapping makes sense. Initially, I was thinking the have display mappings in each of the controller mappings (e.g. on the feedback part of the encoder mappings), but it would lead to lots of duplication - and the user would anyways have to put "magic glue" into the corresponding main mappings. If the MIDI Script had access to more data (e.g. target struct), there might be a way to build it generic, so the glue would not be required...

jonasdiemer commented 1 year ago

Re 4: Yes, unfortunately, the current version of MIDI Control Center doesn't let me select relative for the encoders (unless I have completely overlooked it).

I just had a look in the MIDI Control Center because I couldn't believe it ... but you are right. Wow, this would be an absolute deal breaker for me. Happy that I didn't order one.

FYI: I received the info from Arturia support that relative encoders are scheduled to be added in the next firmware update.

helgoboss commented 1 year ago

Cool! I'm glad they are listening to the users.

toby3d commented 1 year ago

relative encoders are scheduled to be added in the next firmware update.

изображение

I think it's already here (v1.14.3.15).

Nightflying60 commented 6 months ago

Hi anyone with an updated confiv for arturia minilab 3? Many thanks for your feedback 🙂