Vendor helgoboss/helgobox (ReaLearn) as basis for custom UI fork
Stripped upstream git history; starting point for replacing the native SWELL/Win32 mapping UI with something more suited to bulk editing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+330
@@ -0,0 +1,330 @@
|
||||
--!strict
|
||||
|
||||
local midi_script = require("midi_script_source_runtime")
|
||||
|
||||
local module = {}
|
||||
|
||||
--- There's no way to put pads in a pulsing state with the true RGB sys-ex messages, so
|
||||
--- we are back to an old-fashioned color palette. Whatever, close enough.
|
||||
local color_palette: { midi_script.RgbColor } = {
|
||||
{ r = 0x61, g = 0x61, b = 0x61 },
|
||||
{ r = 0xb3, g = 0xb3, b = 0xb3 },
|
||||
{ r = 0xdd, g = 0xdd, b = 0xdd },
|
||||
{ r = 0xff, g = 0xff, b = 0xff },
|
||||
{ r = 0xfc, g = 0xb3, b = 0xb3 },
|
||||
{ r = 0xfa, g = 0x61, b = 0x61 },
|
||||
{ r = 0xdd, g = 0x61, b = 0x61 },
|
||||
{ r = 0xb3, g = 0x61, b = 0x61 },
|
||||
{ r = 0xfe, g = 0xf3, b = 0xd5 },
|
||||
{ r = 0xfb, g = 0xb3, b = 0x61 },
|
||||
{ r = 0xdd, g = 0x8c, b = 0x61 },
|
||||
{ r = 0xb3, g = 0x76, b = 0x61 },
|
||||
{ r = 0xfc, g = 0xee, b = 0xa1 },
|
||||
{ r = 0xfb, g = 0xff, b = 0x61 },
|
||||
{ r = 0xdd, g = 0xdd, b = 0x61 },
|
||||
{ r = 0xb3, g = 0xb3, b = 0x61 },
|
||||
{ r = 0xdd, g = 0xff, b = 0xa1 },
|
||||
{ r = 0xc2, g = 0xff, b = 0x61 },
|
||||
{ r = 0xa1, g = 0xdd, b = 0x61 },
|
||||
{ r = 0x81, g = 0xb3, b = 0x61 },
|
||||
{ r = 0xc2, g = 0xff, b = 0xb3 },
|
||||
{ r = 0x61, g = 0xfe, b = 0x61 },
|
||||
{ r = 0x61, g = 0xdd, b = 0x61 },
|
||||
{ r = 0x61, g = 0xb3, b = 0x61 },
|
||||
{ r = 0xc2, g = 0xff, b = 0xc2 },
|
||||
{ r = 0x61, g = 0xfe, b = 0x8c },
|
||||
{ r = 0x61, g = 0xdd, b = 0x76 },
|
||||
{ r = 0x61, g = 0xb3, b = 0x6b },
|
||||
{ r = 0xc2, g = 0xfe, b = 0xcc },
|
||||
{ r = 0x61, g = 0xfe, b = 0xcc },
|
||||
{ r = 0x61, g = 0xdd, b = 0xa1 },
|
||||
{ r = 0x61, g = 0xb3, b = 0x81 },
|
||||
{ r = 0xc2, g = 0xfe, b = 0xf3 },
|
||||
{ r = 0x61, g = 0xfd, b = 0xe9 },
|
||||
{ r = 0x61, g = 0xdd, b = 0xc2 },
|
||||
{ r = 0x61, g = 0xb3, b = 0x96 },
|
||||
{ r = 0xc2, g = 0xf3, b = 0xff },
|
||||
{ r = 0x61, g = 0xee, b = 0xff },
|
||||
{ r = 0x61, g = 0xc7, b = 0xdd },
|
||||
{ r = 0x61, g = 0xa1, b = 0xb3 },
|
||||
{ r = 0xc2, g = 0xdd, b = 0xff },
|
||||
{ r = 0x61, g = 0xc7, b = 0xff },
|
||||
{ r = 0x61, g = 0xa1, b = 0xdd },
|
||||
{ r = 0x61, g = 0x81, b = 0xb3 },
|
||||
{ r = 0xa1, g = 0x8c, b = 0xff },
|
||||
{ r = 0x61, g = 0x61, b = 0xff },
|
||||
{ r = 0x61, g = 0x61, b = 0xdd },
|
||||
{ r = 0x61, g = 0x61, b = 0xb3 },
|
||||
{ r = 0xcc, g = 0xb3, b = 0xff },
|
||||
{ r = 0xa1, g = 0x61, b = 0xff },
|
||||
{ r = 0x81, g = 0x61, b = 0xdd },
|
||||
{ r = 0x76, g = 0x61, b = 0xb3 },
|
||||
{ r = 0xfe, g = 0xb3, b = 0xff },
|
||||
{ r = 0xfe, g = 0x61, b = 0xff },
|
||||
{ r = 0xdd, g = 0x61, b = 0xdd },
|
||||
{ r = 0xb3, g = 0x61, b = 0xb3 },
|
||||
{ r = 0xfd, g = 0xb3, b = 0xd5 },
|
||||
{ r = 0xfc, g = 0x61, b = 0xc2 },
|
||||
{ r = 0xdd, g = 0x61, b = 0xa1 },
|
||||
{ r = 0xb3, g = 0x61, b = 0x8c },
|
||||
{ r = 0xfa, g = 0x76, b = 0x61 },
|
||||
{ r = 0xe9, g = 0xb3, b = 0x61 },
|
||||
{ r = 0xdd, g = 0xc2, b = 0x61 },
|
||||
{ r = 0xa1, g = 0xa1, b = 0x61 },
|
||||
{ r = 0x61, g = 0xb3, b = 0x61 },
|
||||
{ r = 0x61, g = 0xb3, b = 0x8c },
|
||||
{ r = 0x61, g = 0x8c, b = 0xd5 },
|
||||
{ r = 0x61, g = 0x61, b = 0xff },
|
||||
{ r = 0x61, g = 0xb3, b = 0xb3 },
|
||||
{ r = 0x8c, g = 0x62, b = 0xf3 },
|
||||
{ r = 0xcc, g = 0xb3, b = 0xc2 },
|
||||
{ r = 0x8c, g = 0x76, b = 0x81 },
|
||||
{ r = 0xfa, g = 0x61, b = 0x61 },
|
||||
{ r = 0xf3, g = 0xff, b = 0xa1 },
|
||||
{ r = 0xee, g = 0xfc, b = 0x61 },
|
||||
{ r = 0xcc, g = 0xff, b = 0x60 },
|
||||
{ r = 0x76, g = 0xdd, b = 0x61 },
|
||||
{ r = 0x61, g = 0xfe, b = 0xcc },
|
||||
{ r = 0x61, g = 0xe9, b = 0xff },
|
||||
{ r = 0x61, g = 0xa1, b = 0xff },
|
||||
{ r = 0x8c, g = 0x61, b = 0xff },
|
||||
{ r = 0xcc, g = 0x61, b = 0xfc },
|
||||
{ r = 0xee, g = 0x8c, b = 0xdd },
|
||||
{ r = 0xa1, g = 0x76, b = 0x61 },
|
||||
{ r = 0xfb, g = 0xa1, b = 0x61 },
|
||||
{ r = 0xdd, g = 0xf9, b = 0x62 },
|
||||
{ r = 0xd5, g = 0xff, b = 0x8c },
|
||||
{ r = 0x61, g = 0xfe, b = 0x61 },
|
||||
{ r = 0xb3, g = 0xff, b = 0xa1 },
|
||||
{ r = 0xcc, g = 0xfc, b = 0xd5 },
|
||||
{ r = 0xb3, g = 0xfe, b = 0xf6 },
|
||||
{ r = 0xcc, g = 0xe4, b = 0xff },
|
||||
{ r = 0xa1, g = 0xc2, b = 0xf6 },
|
||||
{ r = 0xd5, g = 0xc2, b = 0xf9 },
|
||||
{ r = 0xf9, g = 0x8c, b = 0xff },
|
||||
{ r = 0xfc, g = 0x61, b = 0xcc },
|
||||
{ r = 0xfb, g = 0xc2, b = 0x61 },
|
||||
{ r = 0xf3, g = 0xee, b = 0x61 },
|
||||
{ r = 0xe4, g = 0xff, b = 0x61 },
|
||||
{ r = 0xdd, g = 0xcc, b = 0x61 },
|
||||
{ r = 0xb3, g = 0xa1, b = 0x61 },
|
||||
{ r = 0x61, g = 0xba, b = 0x76 },
|
||||
{ r = 0x76, g = 0xc2, b = 0x8c },
|
||||
{ r = 0x81, g = 0x81, b = 0xa1 },
|
||||
{ r = 0x81, g = 0x8c, b = 0xcc },
|
||||
{ r = 0xcc, g = 0xaa, b = 0x81 },
|
||||
{ r = 0xdd, g = 0x61, b = 0x61 },
|
||||
{ r = 0xf9, g = 0xb3, b = 0xa1 },
|
||||
{ r = 0xf9, g = 0xba, b = 0x76 },
|
||||
{ r = 0xfc, g = 0xf3, b = 0x8d },
|
||||
{ r = 0xe9, g = 0xf9, b = 0xa1 },
|
||||
{ r = 0xd5, g = 0xee, b = 0x76 },
|
||||
{ r = 0x81, g = 0x81, b = 0xa1 },
|
||||
{ r = 0xf9, g = 0xf9, b = 0xd5 },
|
||||
{ r = 0xdd, g = 0xfc, b = 0xe4 },
|
||||
{ r = 0xe9, g = 0xe9, b = 0xff },
|
||||
{ r = 0xe4, g = 0xd5, b = 0xff },
|
||||
{ r = 0xb3, g = 0xb3, b = 0xb3 },
|
||||
{ r = 0xd5, g = 0xd5, b = 0xd5 },
|
||||
{ r = 0xf9, g = 0xff, b = 0xff },
|
||||
{ r = 0xe9, g = 0x61, b = 0x61 },
|
||||
{ r = 0xaa, g = 0x62, b = 0x61 },
|
||||
{ r = 0x81, g = 0xf6, b = 0x62 },
|
||||
{ r = 0x61, g = 0xb3, b = 0x61 },
|
||||
{ r = 0xf3, g = 0xee, b = 0x61 },
|
||||
{ r = 0xb3, g = 0xa1, b = 0x61 },
|
||||
{ r = 0xee, g = 0xc2, b = 0x61 },
|
||||
{ r = 0xc2, g = 0x76, b = 0x61 },
|
||||
}
|
||||
|
||||
--- Returns the 0-based index within the given palette array.
|
||||
local function find_closest_color_in_palette(color: midi_script.RgbColor, palette: { midi_script.RgbColor }): number
|
||||
local ifurthest = 0
|
||||
local furthest = 3 * math.pow(255, 2) + 1
|
||||
for i, c in palette do
|
||||
if color.r == c.r and color.g == c.g and color.b == c.b then
|
||||
return i - 1
|
||||
end
|
||||
local distance = math.pow((color.r - c.r), 2) + math.pow((color.g - c.g), 2) + math.pow((color.b - c.b), 2)
|
||||
if distance < furthest then
|
||||
furthest = distance
|
||||
ifurthest = i - 1
|
||||
end
|
||||
end
|
||||
return ifurthest
|
||||
end
|
||||
|
||||
type FeedbackEntry = {
|
||||
behavior: number,
|
||||
color: (number | midi_script.RgbColor)?,
|
||||
brightness: number?,
|
||||
}
|
||||
local solid = 0x90
|
||||
local flashing = 0x91
|
||||
local pulsing = 0x92
|
||||
local black_color = 0
|
||||
local green_color = 0x15
|
||||
local white_color = { r = 255, g = 255, b = 255 }
|
||||
local red_color = { r = 255, g = 0, b = 0 }
|
||||
|
||||
local dark = 0.75
|
||||
local medium = 0.90
|
||||
|
||||
local function create_feedback_table(play_color: number?): { [string]: FeedbackEntry? }
|
||||
return {
|
||||
["playtime.slot_state.empty"] = {
|
||||
behavior = solid,
|
||||
color = black_color,
|
||||
},
|
||||
["playtime.slot_state.armed"] = {
|
||||
behavior = solid,
|
||||
color = red_color,
|
||||
brightness = dark,
|
||||
},
|
||||
["playtime.slot_state.stopped"] = {
|
||||
behavior = solid,
|
||||
brightness = dark,
|
||||
},
|
||||
["playtime.slot_state.ignited"] = {
|
||||
behavior = solid,
|
||||
brightness = dark,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_play_start"] = {
|
||||
behavior = flashing,
|
||||
color = play_color,
|
||||
},
|
||||
["playtime.slot_state.playing"] = {
|
||||
behavior = pulsing,
|
||||
color = play_color,
|
||||
},
|
||||
["playtime.slot_state.paused"] = {
|
||||
behavior = solid,
|
||||
brightness = medium,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_play_stop"] = {
|
||||
behavior = flashing,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_play_restart"] = {
|
||||
behavior = flashing,
|
||||
color = play_color,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_record_start"] = {
|
||||
behavior = solid,
|
||||
color = red_color,
|
||||
brightness = medium,
|
||||
},
|
||||
["playtime.slot_state.recording"] = {
|
||||
behavior = pulsing,
|
||||
color = red_color,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_record_stop"] = {
|
||||
behavior = flashing,
|
||||
color = red_color,
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
-- Uses original color when playing (currently disabled, will probably be a configuration option in the future)
|
||||
local colorful_feedback_table = create_feedback_table(nil)
|
||||
|
||||
-- Uses green color when playing
|
||||
local green_feedback_table = create_feedback_table(green_color)
|
||||
|
||||
local function build_messages(
|
||||
pad_index: number,
|
||||
behavior: number,
|
||||
color_index: number
|
||||
): { midi_script.MidiMessage }
|
||||
if behavior == pulsing then
|
||||
return {
|
||||
{ 0x92, pad_index, color_index },
|
||||
}
|
||||
elseif behavior == flashing then
|
||||
-- Flashing between actual color and black
|
||||
return {
|
||||
{ 0x90, pad_index, color_index },
|
||||
{ 0x91, pad_index, black_color },
|
||||
}
|
||||
else
|
||||
-- Solid
|
||||
return {
|
||||
{ 0x90, pad_index, color_index },
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function adjust_brightness(c: midi_script.RgbColor, factor: number): midi_script.RgbColor
|
||||
return {
|
||||
r = math.floor(c.r * factor),
|
||||
g = math.floor(c.g * factor),
|
||||
b = math.floor(c.b * factor),
|
||||
}
|
||||
end
|
||||
|
||||
local function transform_and_find_closest_color_in_palette(original_color: midi_script.RgbColor, brightness: number?): number
|
||||
local transformed_color = if brightness then adjust_brightness(original_color, brightness) else original_color
|
||||
local index = find_closest_color_in_palette(transformed_color, color_palette)
|
||||
if index == 0 then
|
||||
-- Don't let it become black just because of the darkening
|
||||
return find_closest_color_in_palette(original_color, color_palette)
|
||||
end
|
||||
return index
|
||||
end
|
||||
|
||||
local function get_palette_color_index(entry: FeedbackEntry, rgb_color: midi_script.RgbColor?): number
|
||||
if type(entry.color) == "number" then
|
||||
return entry.color
|
||||
end
|
||||
local final_rgb_color = entry.color or rgb_color or white_color
|
||||
return transform_and_find_closest_color_in_palette(final_rgb_color, entry.brightness)
|
||||
end
|
||||
|
||||
|
||||
function module.pad_script(
|
||||
pad_index: number,
|
||||
y: midi_script.InputValue,
|
||||
context: midi_script.Context
|
||||
): midi_script.Output
|
||||
local address = pad_index
|
||||
local off_output = {
|
||||
address = pad_index,
|
||||
messages = build_messages(pad_index, solid, black_color),
|
||||
}
|
||||
-- Handle "off" feedback
|
||||
if y == nil then
|
||||
return off_output
|
||||
end
|
||||
-- Handle numeric feedback
|
||||
if type(y) == "number" then
|
||||
-- Translate number to solid feedback color
|
||||
local color_index = math.floor(y * 127)
|
||||
return {
|
||||
address = address,
|
||||
messages = {
|
||||
{ solid, pad_index, color_index },
|
||||
},
|
||||
}
|
||||
end
|
||||
-- Handle text feedback
|
||||
if type(y) == "string" then
|
||||
-- This is a grid controller, typically used for controlling a Playtime matrix, so we should be able to
|
||||
-- handle text feedback that conforms to Playtime's clip state convention.
|
||||
local entry = green_feedback_table[y]
|
||||
if entry == nil then
|
||||
-- Unknown texts switch the LED off
|
||||
return off_output
|
||||
end
|
||||
local color_index = get_palette_color_index(entry, context.feedback_event.color)
|
||||
return {
|
||||
address = pad_index,
|
||||
messages = build_messages(pad_index, entry.behavior, color_index),
|
||||
}
|
||||
end
|
||||
-- Complex feedback is highly individual. It doesn't make sense to handle this in a general-purpose controller preset.
|
||||
return {
|
||||
address = address,
|
||||
messages = {},
|
||||
}
|
||||
end
|
||||
|
||||
return module
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
--- name: Launchpad Mini mk3 - Live mode
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad Mini mk3
|
||||
--- description: |
|
||||
--- This controller preset exposes all control elements of the Launchpad Mini mk3 in a neutral way. It supports
|
||||
--- ReaLearn color feedback as well as Playtime slot status feedback.
|
||||
--- midi_identity_pattern: F0 7E ? 06 02 00 20 29 13 01 00 00 * F7
|
||||
--- midi_output_port_patterns: ["macos:*DAW*", "windows:LPMiniMK3 MIDI"]
|
||||
--- provided_schemes: [novation/launchpad-mini-mk3/live, grid]
|
||||
|
||||
--!strict
|
||||
|
||||
-- Requires
|
||||
|
||||
local preset_runtime = require("preset_runtime")
|
||||
local realearn = require("realearn")
|
||||
|
||||
-- Define MIDI scripts
|
||||
|
||||
local common_lua = preset_runtime.include_str("novation/launchpad-lib/compartment-common.luau")
|
||||
|
||||
local function build_pad_script(pad_index: number): string
|
||||
return `return require("compartment").pad_script({pad_index}, y, context)`
|
||||
end
|
||||
|
||||
-- Single buttons
|
||||
|
||||
local function simple_button(id: string, name: string, cc: number): realearn.Mapping
|
||||
return realearn.Mapping {
|
||||
id = id,
|
||||
name = name,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = cc,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local init_mapping = realearn.Mapping {
|
||||
name = "Enter DAW mode and select session layout",
|
||||
source = realearn.Source.MidiScript {
|
||||
script_kind = "Lua",
|
||||
script = [[
|
||||
local standalone_mode = 0x00
|
||||
local daw_mode = 0x01
|
||||
local session_layout = 0x00
|
||||
local mode = if y == nil then standalone_mode else daw_mode
|
||||
local enter_or_leave_daw_mode_msg = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x0D, 0x10, mode, 0xF7 }
|
||||
local select_session_layout = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x0D, 0x00, session_layout, 0xF7 }
|
||||
|
||||
local messages = {
|
||||
enter_or_leave_daw_mode_msg,
|
||||
}
|
||||
if y then
|
||||
table.insert(messages, select_session_layout)
|
||||
end
|
||||
|
||||
return {
|
||||
messages = messages
|
||||
}
|
||||
]]
|
||||
},
|
||||
target = realearn.Target.Dummy {
|
||||
}
|
||||
}
|
||||
|
||||
local mappings = {
|
||||
init_mapping,
|
||||
|
||||
simple_button("cursor-up", "Up", 91),
|
||||
simple_button("cursor-down", "Down", 92),
|
||||
simple_button("cursor-left", "Left", 93),
|
||||
simple_button("cursor-right", "Right", 94),
|
||||
simple_button("session", "Session", 95),
|
||||
simple_button("drums", "Drums", 96),
|
||||
simple_button("keys", "Keys", 97),
|
||||
simple_button("user", "User", 98),
|
||||
}
|
||||
|
||||
-- Clip launch buttons
|
||||
|
||||
for col = 0, 7 do
|
||||
local human_col = col + 1
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local key_number_offset = 11 + (7 - row) * 10
|
||||
local id = `col{human_col}/row{human_row}/pad`
|
||||
local control_mapping = realearn.Mapping {
|
||||
id = id,
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = key_number_offset + col,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
local feedback_mapping = realearn.Mapping {
|
||||
id = `{id}-feedback`,
|
||||
control_enabled = false,
|
||||
source = realearn.Source.MidiScript {
|
||||
script_kind = "Lua",
|
||||
script = build_pad_script(key_number_offset + col),
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, control_mapping)
|
||||
table.insert(mappings, feedback_mapping)
|
||||
end
|
||||
end
|
||||
|
||||
-- Scene launch buttons
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local id = `row{human_row}/play`
|
||||
local mapping = realearn.Mapping {
|
||||
id = id,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = 19 + (7 - row) * 10,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
common_lua = common_lua,
|
||||
mappings = mappings,
|
||||
custom_data = {
|
||||
grid = {
|
||||
column_count = 8,
|
||||
row_count = 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
--- name: Launchpad mk1 - Live mode
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad mk1
|
||||
--- description: |
|
||||
--- This controller preset exposes all control elements of the Launchpad mk1 in a neutral way. It supports Playtime slot status feedback.
|
||||
---
|
||||
--- This device cannot be auto-detected because it doesn't support the MIDI device inquiry protocol.
|
||||
--- provided_schemes: [novation/launchpad-mk1, grid]
|
||||
|
||||
--!strict
|
||||
|
||||
-- Requires
|
||||
|
||||
local realearn = require("realearn")
|
||||
|
||||
-- Single buttons
|
||||
|
||||
local function simple_button(id: string, name: string, cc: number): realearn.Mapping
|
||||
return realearn.Mapping {
|
||||
id = id,
|
||||
name = name,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = cc,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local init_mapping = realearn.Mapping {
|
||||
name = "Enable flashing LEDs",
|
||||
source = realearn.Source.MidiRaw {
|
||||
pattern = "B0 00 28",
|
||||
},
|
||||
target = realearn.Target.Dummy {
|
||||
}
|
||||
}
|
||||
|
||||
local mappings = {
|
||||
init_mapping,
|
||||
simple_button("cursor-up", "Up", 104),
|
||||
simple_button("cursor-down", "Down", 105),
|
||||
simple_button("cursor-left", "Left", 106),
|
||||
simple_button("cursor-right", "Right", 107),
|
||||
simple_button("session", "session", 108),
|
||||
simple_button("user-1", "user 1", 109),
|
||||
simple_button("user-2", "user 2", 110),
|
||||
simple_button("mixer", "mixer", 111),
|
||||
}
|
||||
|
||||
-- Clip launch buttons
|
||||
|
||||
local leds = {
|
||||
off = 12,
|
||||
red_low = 13,
|
||||
red_full = 15,
|
||||
amber_low = 29,
|
||||
amber_full = 63,
|
||||
yellow_full = 62,
|
||||
green_low = 28,
|
||||
green_full = 60,
|
||||
red_flashing = 11,
|
||||
amber_flashing = 59,
|
||||
yellow_flashing = 58,
|
||||
green_flashing = 56,
|
||||
}
|
||||
|
||||
for col = 0, 7 do
|
||||
local human_col = col + 1
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local key_number_offset = row * 16
|
||||
local id = `col{human_col}/row{human_row}/pad`
|
||||
local control_mapping = realearn.Mapping {
|
||||
id = id,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = key_number_offset + col,
|
||||
},
|
||||
glue = realearn.Glue {
|
||||
feedback_value_table = {
|
||||
kind = "FromTextToDiscrete",
|
||||
value = {
|
||||
["playtime.slot_state.empty"] = leds.off,
|
||||
["playtime.slot_state.armed"] = leds.red_low,
|
||||
["playtime.slot_state.stopped"] = leds.amber_low,
|
||||
["playtime.slot_state.ignited"] = leds.amber_low,
|
||||
["playtime.slot_state.scheduled_for_play_start"] = leds.green_flashing,
|
||||
["playtime.slot_state.playing"] = leds.green_full,
|
||||
["playtime.slot_state.paused"] = leds.amber_low,
|
||||
["playtime.slot_state.scheduled_for_play_stop"] = leds.amber_flashing,
|
||||
["playtime.slot_state.scheduled_for_play_restart"] = leds.green_flashing,
|
||||
["playtime.slot_state.scheduled_for_record_start"] = leds.red_flashing,
|
||||
["playtime.slot_state.recording"] = leds.red_full,
|
||||
["playtime.slot_state.scheduled_for_record_stop"] = leds.green_flashing,
|
||||
},
|
||||
}
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, control_mapping)
|
||||
end
|
||||
end
|
||||
|
||||
-- Scene launch buttons
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local id = `row{human_row}/play`
|
||||
local mapping = realearn.Mapping {
|
||||
id = id,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = 8 + row * 16,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
mappings = mappings,
|
||||
custom_data = {
|
||||
grid = {
|
||||
column_count = 8,
|
||||
row_count = 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
--- name: Launchpad Pro mk2 - Live mode
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad Pro mk2
|
||||
--- description: |
|
||||
--- This controller preset exposes all control elements of the Launchpad Pro mk2 in a neutral way. It supports
|
||||
--- ReaLearn color feedback as well as Playtime slot status feedback.
|
||||
--- # I leave part of the family code dynamic. It's not always 00: https://github.com/helgoboss/helgobox/issues/939
|
||||
--- midi_identity_pattern: F0 7E ? 06 02 00 20 29 51 00 ? 00 * F7
|
||||
--- midi_output_port_patterns: ["macos:*Live*", "windows:*Launchpad Pro"]
|
||||
--- provided_schemes: [novation/launchpad-pro-mk2/live, grid]
|
||||
|
||||
--!strict
|
||||
|
||||
-- Requires
|
||||
|
||||
local preset_runtime = require("preset_runtime")
|
||||
local realearn = require("realearn")
|
||||
|
||||
-- Define MIDI scripts
|
||||
|
||||
local common_lua = preset_runtime.include_str("novation/launchpad-lib/compartment-common.luau")
|
||||
|
||||
local function build_pad_script(pad_index: number): string
|
||||
return `return require("compartment").pad_script({pad_index}, y, context)`
|
||||
end
|
||||
|
||||
-- Single buttons
|
||||
|
||||
local function simple_button(id: string, name: string, cc: number): realearn.Mapping
|
||||
return realearn.Mapping {
|
||||
id = id,
|
||||
name = name,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = cc,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local init_mapping = realearn.Mapping {
|
||||
name = "Enter live mode",
|
||||
source = realearn.Source.MidiScript {
|
||||
script_kind = "Lua",
|
||||
script = [[
|
||||
local live_mode = 0x00
|
||||
local standalone_mode = 0x01
|
||||
local mode = if y == nil then standalone_mode else live_mode
|
||||
|
||||
return {
|
||||
messages = {
|
||||
{ 0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0x21, mode, 0xF7 }
|
||||
}
|
||||
}
|
||||
]]
|
||||
},
|
||||
target = realearn.Target.Dummy {
|
||||
}
|
||||
}
|
||||
|
||||
local mappings = {
|
||||
init_mapping,
|
||||
simple_button("cursor-up", "Up", 91),
|
||||
simple_button("cursor-down", "Down", 92),
|
||||
simple_button("cursor-left", "Left", 93),
|
||||
simple_button("cursor-right", "Right", 94),
|
||||
simple_button("session", "Session", 95),
|
||||
simple_button("note", "Note", 96),
|
||||
simple_button("device", "Device", 97),
|
||||
simple_button("user", "User", 98),
|
||||
simple_button("shift", "Shift", 80),
|
||||
simple_button("click", "Click", 70),
|
||||
simple_button("undo", "Undo", 60),
|
||||
simple_button("delete", "Delete", 50),
|
||||
simple_button("quantize", "Quantise", 40),
|
||||
simple_button("duplicate", "Duplicate", 30),
|
||||
simple_button("double", "Double", 20),
|
||||
simple_button("record", "Record", 10),
|
||||
simple_button("record-arm", "Record Arm", 1),
|
||||
simple_button("track-select", "Track Select", 2),
|
||||
simple_button("mute", "Mute", 3),
|
||||
simple_button("solo", "Solo", 4),
|
||||
simple_button("volume", "Volume", 5),
|
||||
simple_button("pan", "Pan", 6),
|
||||
simple_button("sends", "Sends", 7),
|
||||
simple_button("stop-clip", "Stop Clip", 8),
|
||||
}
|
||||
|
||||
-- Clip launch buttons
|
||||
|
||||
for col = 0, 7 do
|
||||
local human_col = col + 1
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local key_number_offset = 11 + (7 - row) * 10
|
||||
local id = `col{human_col}/row{human_row}/pad`
|
||||
local control_mapping = realearn.Mapping {
|
||||
id = id,
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = key_number_offset + col,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Multi",
|
||||
},
|
||||
}
|
||||
local feedback_mapping = realearn.Mapping {
|
||||
id = `{id}-feedback`,
|
||||
control_enabled = false,
|
||||
source = realearn.Source.MidiScript {
|
||||
script_kind = "Lua",
|
||||
script = build_pad_script(key_number_offset + col),
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Multi",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, control_mapping)
|
||||
table.insert(mappings, feedback_mapping)
|
||||
end
|
||||
end
|
||||
|
||||
-- Scene launch buttons
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local id = `row{human_row}/play`
|
||||
local mapping = realearn.Mapping {
|
||||
id = id,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = 19 + (7 - row) * 10,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
common_lua = common_lua,
|
||||
mappings = mappings,
|
||||
custom_data = {
|
||||
grid = {
|
||||
column_count = 8,
|
||||
row_count = 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
--- name: Launchpad Pro mk3 - Live mode
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad Pro mk3
|
||||
--- description: |
|
||||
--- This controller preset exposes all control elements of the Launchpad Pro mk3 in a neutral way. It supports
|
||||
--- ReaLearn color feedback as well as Playtime slot status feedback.
|
||||
--- midi_identity_pattern: F0 7E ? 06 02 00 20 29 23 01 00 00 * F7
|
||||
--- midi_output_port_patterns: ["macos:*DAW*", "windows:*MIDIOUT3*"]
|
||||
--- provided_schemes: [novation/launchpad-pro-mk3/live, grid]
|
||||
|
||||
--!strict
|
||||
|
||||
-- Requires
|
||||
|
||||
local preset_runtime = require("preset_runtime")
|
||||
local realearn = require("realearn")
|
||||
|
||||
-- Define MIDI scripts
|
||||
|
||||
local common_lua = preset_runtime.include_str("novation/launchpad-lib/compartment-common.luau")
|
||||
|
||||
local function build_pad_script(pad_index: number): string
|
||||
return `return require("compartment").pad_script({pad_index}, y, context)`
|
||||
end
|
||||
|
||||
-- Single buttons
|
||||
|
||||
local function simple_button(id: string, name: string, cc: number): realearn.Mapping
|
||||
return realearn.Mapping {
|
||||
id = id,
|
||||
name = name,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = cc,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local init_mapping = realearn.Mapping {
|
||||
name = "Enter DAW mode and select session layout",
|
||||
source = realearn.Source.MidiScript {
|
||||
script_kind = "Lua",
|
||||
script = [[
|
||||
local standalone_mode = 0x00
|
||||
local daw_mode = 0x01
|
||||
local session_layout = 0x00
|
||||
local session_layout_page = 0x00
|
||||
local mode = if y == nil then standalone_mode else daw_mode
|
||||
local enter_or_leave_daw_mode_msg = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x0E, 0x10, mode, 0xF7 }
|
||||
local select_session_layout = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x0E, 0x00, session_layout, session_layout_page, 0x00, 0xF7 }
|
||||
|
||||
local messages = {
|
||||
enter_or_leave_daw_mode_msg,
|
||||
}
|
||||
if y then
|
||||
table.insert(messages, select_session_layout)
|
||||
end
|
||||
|
||||
return {
|
||||
messages = messages
|
||||
}
|
||||
]]
|
||||
},
|
||||
target = realearn.Target.Dummy {
|
||||
}
|
||||
}
|
||||
|
||||
local mappings = {
|
||||
init_mapping,
|
||||
simple_button("shift", "Shift", 90),
|
||||
simple_button("cursor-left", "Left", 91),
|
||||
simple_button("cursor-right", "Right", 92),
|
||||
simple_button("session", "Session", 93),
|
||||
|
||||
simple_button("cursor-up", "Up", 80),
|
||||
simple_button("cursor-down", "Down", 70),
|
||||
simple_button("delete", "Clear", 60),
|
||||
simple_button("duplicate", "Duplicate", 50),
|
||||
simple_button("quantize", "Quantise", 40),
|
||||
simple_button("fixed-length", "Fixed length", 30),
|
||||
simple_button("play", "Play", 20),
|
||||
simple_button("record", "Record", 10),
|
||||
|
||||
simple_button("record-arm", "Record Arm", 1),
|
||||
simple_button("mute", "Mute", 2),
|
||||
simple_button("solo", "Solo", 3),
|
||||
simple_button("volume", "Volume", 4),
|
||||
simple_button("pan", "Pan", 5),
|
||||
simple_button("sends", "Sends", 6),
|
||||
simple_button("device", "Device", 7),
|
||||
simple_button("stop-clip", "Stop Clip", 8),
|
||||
}
|
||||
|
||||
-- Clip launch buttons
|
||||
|
||||
for col = 0, 7 do
|
||||
local human_col = col + 1
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local key_number_offset = 11 + (7 - row) * 10
|
||||
local id = `col{human_col}/row{human_row}/pad`
|
||||
local control_mapping = realearn.Mapping {
|
||||
id = id,
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = key_number_offset + col,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Multi",
|
||||
},
|
||||
}
|
||||
local feedback_mapping = realearn.Mapping {
|
||||
id = `{id}-feedback`,
|
||||
control_enabled = false,
|
||||
source = realearn.Source.MidiScript {
|
||||
script_kind = "Lua",
|
||||
script = build_pad_script(key_number_offset + col),
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Multi",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, control_mapping)
|
||||
table.insert(mappings, feedback_mapping)
|
||||
end
|
||||
end
|
||||
|
||||
-- Scene launch buttons
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local id = `row{human_row}/play`
|
||||
local mapping = realearn.Mapping {
|
||||
id = id,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = 19 + (7 - row) * 10,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
-- Column action buttons
|
||||
for col = 0, 7 do
|
||||
local human_col = col + 1
|
||||
local id = `col{human_col}/action`
|
||||
local mapping = realearn.Mapping {
|
||||
id = id,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = 101 + col,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
common_lua = common_lua,
|
||||
mappings = mappings,
|
||||
custom_data = {
|
||||
grid = {
|
||||
column_count = 8,
|
||||
row_count = 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
--- name: Launchpad X - Live mode
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad X
|
||||
--- description: |
|
||||
--- This controller preset exposes all control elements of the Launchpad X in a neutral way. It supports
|
||||
--- ReaLearn color feedback as well as Playtime slot status feedback.
|
||||
--- midi_identity_pattern: F0 7E ? 06 02 00 20 29 03 01 00 00 * F7
|
||||
--- midi_output_port_patterns: ["macos:*DAW*", "windows:LPX MIDI"]
|
||||
--- provided_schemes: [novation/launchpad-x/live, grid]
|
||||
|
||||
--!strict
|
||||
|
||||
-- Requires
|
||||
|
||||
local preset_runtime = require("preset_runtime")
|
||||
local realearn = require("realearn")
|
||||
|
||||
-- Define MIDI scripts
|
||||
|
||||
local common_lua = preset_runtime.include_str("novation/launchpad-lib/compartment-common.luau")
|
||||
|
||||
local function build_pad_script(pad_index: number): string
|
||||
return `return require("compartment").pad_script({pad_index}, y, context)`
|
||||
end
|
||||
|
||||
-- Single buttons
|
||||
|
||||
local function simple_button(id: string, name: string, cc: number): realearn.Mapping
|
||||
return realearn.Mapping {
|
||||
id = id,
|
||||
name = name,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = cc,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local init_mapping = realearn.Mapping {
|
||||
name = "Enter DAW mode and select session layout",
|
||||
source = realearn.Source.MidiScript {
|
||||
script_kind = "Lua",
|
||||
script = [[
|
||||
local standalone_mode = 0x00
|
||||
local daw_mode = 0x01
|
||||
local session_layout = 0x00
|
||||
local mode = if y == nil then standalone_mode else daw_mode
|
||||
local enter_or_leave_daw_mode_msg = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x0C, 0x10, mode, 0xF7 }
|
||||
local select_session_layout = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x0C, 0x00, session_layout, 0xF7 }
|
||||
|
||||
local messages = {
|
||||
enter_or_leave_daw_mode_msg,
|
||||
}
|
||||
if y then
|
||||
table.insert(messages, select_session_layout)
|
||||
end
|
||||
|
||||
return {
|
||||
messages = messages
|
||||
}
|
||||
]]
|
||||
},
|
||||
target = realearn.Target.Dummy {
|
||||
}
|
||||
}
|
||||
|
||||
local mappings = {
|
||||
init_mapping,
|
||||
|
||||
simple_button("cursor-up", "Up", 91),
|
||||
simple_button("cursor-down", "Down", 92),
|
||||
simple_button("cursor-left", "Left", 93),
|
||||
simple_button("cursor-right", "Right", 94),
|
||||
simple_button("session", "Session", 95),
|
||||
simple_button("note", "Note", 96),
|
||||
simple_button("custom", "Custom", 97),
|
||||
simple_button("record", "Record", 98),
|
||||
}
|
||||
|
||||
-- Clip launch buttons
|
||||
|
||||
for col = 0, 7 do
|
||||
local human_col = col + 1
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local key_number_offset = 11 + (7 - row) * 10
|
||||
local id = `col{human_col}/row{human_row}/pad`
|
||||
local control_mapping = realearn.Mapping {
|
||||
id = id,
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = key_number_offset + col,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Multi",
|
||||
},
|
||||
}
|
||||
local feedback_mapping = realearn.Mapping {
|
||||
id = `{id}-feedback`,
|
||||
control_enabled = false,
|
||||
source = realearn.Source.MidiScript {
|
||||
script_kind = "Lua",
|
||||
script = build_pad_script(key_number_offset + col),
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Multi",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, control_mapping)
|
||||
table.insert(mappings, feedback_mapping)
|
||||
end
|
||||
end
|
||||
|
||||
-- Scene launch buttons
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local id = `row{human_row}/play`
|
||||
local mapping = realearn.Mapping {
|
||||
id = id,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = 19 + (7 - row) * 10,
|
||||
character = "Button",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
common_lua = common_lua,
|
||||
mappings = mappings,
|
||||
custom_data = {
|
||||
grid = {
|
||||
column_count = 8,
|
||||
row_count = 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user