realearn icon indicating copy to clipboard operation
realearn copied to clipboard

Support for Arturia Minilab 3

Open jonasdiemer opened this issue 3 years ago • 24 comments

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 avatar Dec 22 '22 13:12 jonasdiemer

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

jonasdiemer avatar Dec 22 '22 13:12 jonasdiemer

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

So only point 3 to figure out...

jonasdiemer avatar Dec 22 '22 21:12 jonasdiemer

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

  • Removed non-existent PADs 9-16 --> need to revert, as MiniLab 3 has a secondary bank.
  • Removed non-existent encoders 9-16
  • Added 4 faders
  • Renamed Main encoder

TODOs:

  • Feedback for PADs not working yet. I've captured the corresponding SysEx message from Ableton, e.g. for PAD 1: F0 00 20 6B 7F 42 02 02 16 34 [3 * 7-bit of RGB] F7, but haven't gotten it to work
    • Maybe it needs some initialization
  • Feedback on Display. Ableton uses a message like this: "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" (snippet taken from Arturias integration script for Reason).

jonasdiemer avatar Dec 23 '22 08:12 jonasdiemer

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

Current status:

  • All Encoders and Faders work
  • All Pads work (incl. the 8 on Bank B)
  • Transport control works
  • Feedback works for Pads (white) and transport control (in respective color)
    • I even managed to get transport control feedback update when pressing shift by mapping it to ReaLearn: Send feedback to all instances
      • @helgoboss Maybe it makes sense to include this function into the "short list" of real targets - I had to go via Project: Invoke REAPER action
  • Limitations
    • Encoders are absolute on the MiniLab 3 and currently cannot be changed via MIDI Control Center (or have I missed something?)
    • Encoders display their current value that the MiniLab 3 "thinks", but this doesn't update on MIDI feedback. Not sure if this is a limitation of MiniLab 3 or it can somehow be accomplished.

TODOs:

  • Color feedback for Pads (I know what to send, but haven't figured out how to "grab" a color and preprocess it for the SysEx message)
  • Feedback on LCD (see above)
  • Maybe rename Pads on Bank B to B1-8 (and others to A1-8?)? Maybe rename Encoders to "Knob" (as shown in display)

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

jonasdiemer avatar Dec 23 '22 13:12 jonasdiemer

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 }
    }
}

jonasdiemer avatar Dec 23 '22 14:12 jonasdiemer

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

LePretreD avatar Jan 03 '23 11:01 LePretreD

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.

jonasdiemer avatar Jan 03 '23 12:01 jonasdiemer

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

LePretreD avatar Jan 03 '23 13:01 LePretreD

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

jonasdiemer avatar Jan 03 '23 14:01 jonasdiemer

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

LePretreD avatar Jan 03 '23 16:01 LePretreD

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 avatar Jan 04 '23 21:01 helgoboss

  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.

helgoboss avatar Jan 04 '23 21:01 helgoboss

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 avatar Jan 05 '23 10:01 jonasdiemer

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 avatar Jan 05 '23 14:01 jonasdiemer

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 },
}

jonasdiemer avatar Jan 05 '23 16:01 jonasdiemer

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 avatar Jan 05 '23 21:01 helgoboss

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 avatar Jan 05 '23 22:01 helgoboss

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.

helgoboss avatar Jan 05 '23 22:01 helgoboss

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 avatar Jan 06 '23 10:01 jonasdiemer

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. `"||<normalized_value>"). 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 avatar Jan 06 '23 14:01 jonasdiemer

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.

jonasdiemer avatar Jan 16 '23 10:01 jonasdiemer

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

helgoboss avatar Jan 16 '23 21:01 helgoboss

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

изображение

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

toby3d avatar Apr 27 '23 18:04 toby3d

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

Nightflying60 avatar Mar 05 '24 09:03 Nightflying60