Reorganize top-level directories with clearer naming convention
app -> app-desktop-macos, presets -> app-presets, server -> remote-server, daw-config-reaper -> osc-config-daw, plugin-reaper-realearn -> plugin-reaper-relearn. Updated run.py and presets.py path references accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+1617
File diff suppressed because it is too large
Load Diff
+1650
File diff suppressed because it is too large
Load Diff
+254
@@ -0,0 +1,254 @@
|
||||
--- name: APC mini mk2
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Akai
|
||||
--- device_name: APC mini mk2
|
||||
--- description: |
|
||||
--- This controller preset exposes all control elements of the APC mini mk2 in a neutral way. It supports
|
||||
--- ReaLearn color feedback as well as Playtime slot status feedback.
|
||||
--- midi_identity_pattern: F0 7E ? 06 02 47 4F 00 19 * F7
|
||||
--- # This device exposes 2 USB MIDI ports, one for "Notes" and one for "Control". We are
|
||||
--- # interested in "Control". Both MIDI ports reply with the same device identity. If we
|
||||
--- # don't provide a MIDI output device pattern, it's possible that ReaLearn's automatic
|
||||
--- # controller detection feature takes the "Notes" port ... and things wouldn't work!
|
||||
--- # On Windows, the naming is different, that's why there's also "MIDIOUT2".
|
||||
--- midi_output_port_patterns: ["macos:*Control*", "windows:APC mini mk2"]
|
||||
--- provided_schemes: [akai/apc-mini-mk2, grid]
|
||||
|
||||
--!strict
|
||||
|
||||
-- Configuration
|
||||
|
||||
local resolve_shift = false
|
||||
|
||||
-- Requires
|
||||
|
||||
local preset_runtime = require("preset_runtime")
|
||||
local realearn = require("realearn")
|
||||
|
||||
-- Define MIDI scripts
|
||||
|
||||
local common_lua = preset_runtime.include_str("akai/apc-mk2-lib/compartment-common.luau")
|
||||
|
||||
local function build_pad_script(pad_index: number): string
|
||||
return `return require("compartment").pad_script({pad_index}, y, context)`
|
||||
end
|
||||
|
||||
-- Parameters
|
||||
|
||||
local shift_param = realearn.Parameter {
|
||||
index = 0,
|
||||
name = "Shift",
|
||||
}
|
||||
local parameters: { realearn.Parameter }? = if resolve_shift then { shift_param } else nil
|
||||
|
||||
-- Single buttons
|
||||
|
||||
local function simple_button(id: string, name: string, key_number: number): realearn.Mapping
|
||||
return realearn.Mapping {
|
||||
id = id,
|
||||
name = name,
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = key_number,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local mappings: { realearn.Mapping } = {
|
||||
-- Main fader
|
||||
{
|
||||
id = "fm",
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = 56,
|
||||
character = "Range",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = "main/fader",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
-- Shift
|
||||
local no_shift_activation_condition: realearn.ActivationCondition?
|
||||
if resolve_shift then
|
||||
-- The activation condition reflecting the state that shift is not pressed.
|
||||
no_shift_activation_condition = realearn.ActivationCondition.Modifier {
|
||||
modifiers = {
|
||||
{
|
||||
parameter = 0,
|
||||
on = false,
|
||||
},
|
||||
},
|
||||
}
|
||||
-- Mapping to make shift button switch to other set of virtual control elements
|
||||
local shift_mapping = realearn.Mapping {
|
||||
name = "Shift",
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = 122,
|
||||
},
|
||||
target = realearn.Target.CompartmentParameterValue {
|
||||
parameter = realearn.CompartmentParameterDescriptor.ById {
|
||||
index = 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
table.insert(mappings, shift_mapping)
|
||||
-- Alternative set of virtual control elements
|
||||
local alt_elements = {
|
||||
{ key = 104, id = "cursor-up" },
|
||||
{ key = 105, id = "cursor-down" },
|
||||
{ key = 106, id = "cursor-left" },
|
||||
{ key = 107, id = "cursor-right" },
|
||||
{ key = 100, id = "volume" },
|
||||
{ key = 101, id = "pan" },
|
||||
{ key = 102, id = "sends" },
|
||||
{ key = 103, id = "device" },
|
||||
{ key = 112, id = "stop-clip" },
|
||||
{ key = 113, id = "solo" },
|
||||
{ key = 114, id = "mute" },
|
||||
{ key = 115, id = "record-arm" },
|
||||
{ key = 116, id = "track-select" },
|
||||
{ key = 117, id = "drum" },
|
||||
{ key = 118, id = "note" },
|
||||
{ key = 119, id = "stop-all-clips" },
|
||||
}
|
||||
for _, element in ipairs(alt_elements) do
|
||||
local mapping = realearn.Mapping {
|
||||
activation_condition = realearn.ActivationCondition.Modifier {
|
||||
modifiers = {
|
||||
{
|
||||
parameter = 0,
|
||||
on = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = element.key,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = element.id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
else
|
||||
no_shift_activation_condition = nil
|
||||
local mapping = simple_button("shift", "Shift", 122)
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
-- Channel faders
|
||||
for i = 0, 7 do
|
||||
local human_i = i + 1
|
||||
local mapping = realearn.Mapping {
|
||||
id = `f{human_i}`,
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = 48 + i,
|
||||
character = "Range",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = i,
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
-- 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 = (7 - row) * 8
|
||||
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
|
||||
|
||||
-- Clip stop buttons
|
||||
for col = 0, 7 do
|
||||
local human_col = col + 1
|
||||
local id = `col{human_col}/stop`
|
||||
local mapping = realearn.Mapping {
|
||||
id = id,
|
||||
activation_condition = no_shift_activation_condition,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = 100 + col,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
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,
|
||||
activation_condition = no_shift_activation_condition,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = 112 + row,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
parameters = parameters,
|
||||
mappings = mappings,
|
||||
common_lua = common_lua,
|
||||
custom_data = {
|
||||
grid = {
|
||||
column_count = 8,
|
||||
row_count = 8,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
--- name: APC mini
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Akai
|
||||
--- device_name: APC mini
|
||||
--- description: |
|
||||
--- This controller preset exposes all control elements of the APC mini mk2 in a neutral way. It supports
|
||||
--- Playtime slot status feedback.
|
||||
--- midi_identity_pattern: F0 7E ? 06 02 47 28 00 19 * F7
|
||||
--- provided_schemes: [akai/apc-mini, grid]
|
||||
|
||||
--!strict
|
||||
|
||||
-- Configuration
|
||||
|
||||
local resolve_shift = false
|
||||
|
||||
-- Requires
|
||||
|
||||
local realearn = require("realearn")
|
||||
|
||||
-- Parameters
|
||||
|
||||
local shift_param = realearn.Parameter {
|
||||
index = 0,
|
||||
name = "Shift",
|
||||
}
|
||||
local parameters: { realearn.Parameter }? = if resolve_shift then { shift_param } else nil
|
||||
|
||||
-- Single buttons
|
||||
|
||||
local function simple_button(id: string, name: string, key_number: number): realearn.Mapping
|
||||
return realearn.Mapping {
|
||||
id = id,
|
||||
name = name,
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = key_number,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local mappings: { realearn.Mapping } = {
|
||||
-- Main fader
|
||||
{
|
||||
id = "fm",
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = 56,
|
||||
character = "Range",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = "main/fader",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
-- Shift
|
||||
local no_shift_activation_condition: realearn.ActivationCondition?
|
||||
if resolve_shift then
|
||||
-- The activation condition reflecting the state that shift is not pressed.
|
||||
no_shift_activation_condition = realearn.ActivationCondition.Modifier {
|
||||
modifiers = {
|
||||
{
|
||||
parameter = 0,
|
||||
on = false,
|
||||
},
|
||||
},
|
||||
}
|
||||
-- Mapping to make shift button switch to other set of virtual control elements
|
||||
local shift_mapping = realearn.Mapping {
|
||||
name = "Shift",
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = 98,
|
||||
},
|
||||
target = realearn.Target.CompartmentParameterValue {
|
||||
parameter = realearn.CompartmentParameterDescriptor.ById {
|
||||
index = 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
table.insert(mappings, shift_mapping)
|
||||
-- Alternative set of virtual control elements
|
||||
local alt_elements = {
|
||||
{ key = 64, id = "cursor-up" },
|
||||
{ key = 65, id = "cursor-down" },
|
||||
{ key = 66, id = "cursor-left" },
|
||||
{ key = 67, id = "cursor-right" },
|
||||
{ key = 68, id = "volume" },
|
||||
{ key = 69, id = "pan" },
|
||||
{ key = 70, id = "sends" },
|
||||
{ key = 71, id = "device" },
|
||||
{ key = 82, id = "stop-clip" },
|
||||
{ key = 83, id = "solo" },
|
||||
{ key = 84, id = "record-arm" },
|
||||
{ key = 85, id = "mute" },
|
||||
{ key = 86, id = "track-select" },
|
||||
{ key = 89, id = "stop-all-clips" },
|
||||
}
|
||||
for _, element in ipairs(alt_elements) do
|
||||
local mapping = realearn.Mapping {
|
||||
activation_condition = realearn.ActivationCondition.Modifier {
|
||||
modifiers = {
|
||||
{
|
||||
parameter = 0,
|
||||
on = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = element.key,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = element.id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
else
|
||||
no_shift_activation_condition = nil
|
||||
local mapping = simple_button("shift", "Shift", 98)
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
-- Channel faders
|
||||
for i = 0, 7 do
|
||||
local human_i = i + 1
|
||||
local mapping = realearn.Mapping {
|
||||
id = `f{human_i}`,
|
||||
feedback_enabled = false,
|
||||
source = realearn.Source.MidiControlChangeValue {
|
||||
channel = 0,
|
||||
controller_number = 48 + i,
|
||||
character = "Range",
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = i,
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
-- Clip launch buttons
|
||||
local feedback_value_table = realearn.FeedbackValueTable.FromTextToDiscrete {
|
||||
value = {
|
||||
-- Off
|
||||
["playtime.slot_state.empty"] = 0,
|
||||
-- Red
|
||||
["playtime.slot_state.armed"] = 0,
|
||||
-- Yellow
|
||||
["playtime.slot_state.stopped"] = 5,
|
||||
-- Green blinking
|
||||
["playtime.slot_state.scheduled_for_play_start"] = 2,
|
||||
-- Green
|
||||
["playtime.slot_state.playing"] = 1,
|
||||
-- Yellow
|
||||
["playtime.slot_state.paused"] = 5,
|
||||
-- Yellow blinking
|
||||
["playtime.slot_state.scheduled_for_play_stop"] = 6,
|
||||
-- Yellow blinking
|
||||
["playtime.slot_state.scheduled_for_play_restart"] = 6,
|
||||
-- Red blinking
|
||||
["playtime.slot_state.scheduled_for_record_start"] = 4,
|
||||
-- Red
|
||||
["playtime.slot_state.recording"] = 3,
|
||||
-- Yellow blinking
|
||||
["playtime.slot_state.scheduled_for_record_stop"] = 6,
|
||||
},
|
||||
}
|
||||
for col = 0, 7 do
|
||||
local human_col = col + 1
|
||||
for row = 0, 7 do
|
||||
local human_row = row + 1
|
||||
local key_number_offset = (7 - row) * 8
|
||||
local id = `col{human_col}/row{human_row}/pad`
|
||||
local mapping = realearn.Mapping {
|
||||
id = id,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = key_number_offset + col,
|
||||
},
|
||||
glue = {
|
||||
feedback_value_table = feedback_value_table,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Multi",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
end
|
||||
|
||||
-- Clip stop buttons
|
||||
for col = 0, 7 do
|
||||
local human_col = col + 1
|
||||
local id = `col{human_col}/stop`
|
||||
local mapping = realearn.Mapping {
|
||||
id = id,
|
||||
activation_condition = no_shift_activation_condition,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = 64 + col,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
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,
|
||||
activation_condition = no_shift_activation_condition,
|
||||
source = realearn.Source.MidiNoteVelocity {
|
||||
channel = 0,
|
||||
key_number = 82 + row,
|
||||
},
|
||||
target = realearn.Target.Virtual {
|
||||
id = id,
|
||||
character = "Button",
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
parameters = parameters,
|
||||
mappings = mappings,
|
||||
custom_data = {
|
||||
grid = {
|
||||
column_count = 8,
|
||||
row_count = 8,
|
||||
},
|
||||
},
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
--!strict
|
||||
|
||||
local midi_script = require("midi_script_source_runtime")
|
||||
|
||||
local module = {}
|
||||
|
||||
type FeedbackEntry = {
|
||||
behavior: number,
|
||||
color: number?,
|
||||
}
|
||||
local dark = 0x91
|
||||
local medium = 0x93
|
||||
local bright = 0x96
|
||||
local pulsing = 0x9A
|
||||
local blinking = 0x9B
|
||||
local red_color = 6
|
||||
local default_color = 3
|
||||
local feedback_table: { [string]: FeedbackEntry? } = {
|
||||
["playtime.slot_state.empty"] = {
|
||||
behavior = dark,
|
||||
color = 0,
|
||||
},
|
||||
["playtime.slot_state.armed"] = {
|
||||
behavior = dark,
|
||||
color = red_color,
|
||||
},
|
||||
["playtime.slot_state.stopped"] = {
|
||||
behavior = dark,
|
||||
},
|
||||
["playtime.slot_state.ignited"] = {
|
||||
behavior = dark,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_play_start"] = {
|
||||
behavior = medium,
|
||||
},
|
||||
["playtime.slot_state.playing"] = {
|
||||
behavior = pulsing,
|
||||
},
|
||||
["playtime.slot_state.paused"] = {
|
||||
behavior = medium,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_play_stop"] = {
|
||||
behavior = blinking,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_play_restart"] = {
|
||||
behavior = blinking,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_record_start"] = {
|
||||
behavior = medium,
|
||||
color = red_color,
|
||||
},
|
||||
["playtime.slot_state.recording"] = {
|
||||
behavior = pulsing,
|
||||
color = red_color,
|
||||
},
|
||||
["playtime.slot_state.scheduled_for_record_stop"] = {
|
||||
behavior = blinking,
|
||||
color = red_color,
|
||||
},
|
||||
}
|
||||
|
||||
--- Although I got the true RGB colors to work, it was impossible to combine them with blinking
|
||||
--- or pulsing behavior. So back to good old color palette.
|
||||
local color_palette: { midi_script.RgbColor } = {
|
||||
{r = 0x00, g= 0x00, b = 0x00},
|
||||
{r = 0x1E, g= 0x1E, b = 0x1E},
|
||||
{r = 0x7F, g= 0x7F, b = 0x7F},
|
||||
{r = 0xFF, g= 0xFF, b = 0xFF},
|
||||
{r = 0xFF, g= 0x4C, b = 0x4C},
|
||||
{r = 0xFF, g= 0x00, b = 0x00},
|
||||
{r = 0x59, g= 0x00, b = 0x00},
|
||||
{r = 0x19, g= 0x00, b = 0x00},
|
||||
{r = 0xFF, g= 0xBD, b = 0x6C},
|
||||
{r = 0xFF, g= 0x54, b = 0x00},
|
||||
{r = 0x59, g= 0x1D, b = 0x00},
|
||||
{r = 0x27, g= 0x1B, b = 0x00},
|
||||
{r = 0xFF, g= 0xFF, b = 0x4C},
|
||||
{r = 0xFF, g= 0xFF, b = 0x00},
|
||||
{r = 0x59, g= 0x59, b = 0x00},
|
||||
{r = 0x19, g= 0x19, b = 0x00},
|
||||
{r = 0x88, g= 0xFF, b = 0x4C},
|
||||
{r = 0x54, g= 0xFF, b = 0x00},
|
||||
{r = 0x1D, g= 0x59, b = 0x00},
|
||||
{r = 0x14, g= 0x2B, b = 0x00},
|
||||
{r = 0x4C, g= 0xFF, b = 0x4C},
|
||||
{r = 0x00, g= 0xFF, b = 0x00},
|
||||
{r = 0x00, g= 0x59, b = 0x00},
|
||||
{r = 0x00, g= 0x19, b = 0x00},
|
||||
{r = 0x4C, g= 0xFF, b = 0x5E},
|
||||
{r = 0x00, g= 0xFF, b = 0x19},
|
||||
{r = 0x00, g= 0x59, b = 0x0D},
|
||||
{r = 0x00, g= 0x19, b = 0x02},
|
||||
{r = 0x4C, g= 0xFF, b = 0x88},
|
||||
{r = 0x00, g= 0xFF, b = 0x55},
|
||||
{r = 0x00, g= 0x59, b = 0x1D},
|
||||
{r = 0x00, g= 0x1F, b = 0x12},
|
||||
{r = 0x4C, g= 0xFF, b = 0xB7},
|
||||
{r = 0x00, g= 0xFF, b = 0x99},
|
||||
{r = 0x00, g= 0x59, b = 0x35},
|
||||
{r = 0x00, g= 0x19, b = 0x12},
|
||||
{r = 0x4C, g= 0xC3, b = 0xFF},
|
||||
{r = 0x00, g= 0xA9, b = 0xFF},
|
||||
{r = 0x00, g= 0x41, b = 0x52},
|
||||
{r = 0x00, g= 0x10, b = 0x19},
|
||||
{r = 0x4C, g= 0x88, b = 0xFF},
|
||||
{r = 0x00, g= 0x55, b = 0xFF},
|
||||
{r = 0x00, g= 0x1D, b = 0x59},
|
||||
{r = 0x00, g= 0x08, b = 0x19},
|
||||
{r = 0x4C, g= 0x4C, b = 0xFF},
|
||||
{r = 0x00, g= 0x00, b = 0xFF},
|
||||
{r = 0x00, g= 0x00, b = 0x59},
|
||||
{r = 0x00, g= 0x00, b = 0x19},
|
||||
{r = 0x87, g= 0x4C, b = 0xFF},
|
||||
{r = 0x54, g= 0x00, b = 0xFF},
|
||||
{r = 0x19, g= 0x00, b = 0x64},
|
||||
{r = 0x0F, g= 0x00, b = 0x30},
|
||||
{r = 0xFF, g= 0x4C, b = 0xFF},
|
||||
{r = 0xFF, g= 0x00, b = 0xFF},
|
||||
{r = 0x59, g= 0x00, b = 0x59},
|
||||
{r = 0x19, g= 0x00, b = 0x19},
|
||||
{r = 0xFF, g= 0x4C, b = 0x87},
|
||||
{r = 0xFF, g= 0x00, b = 0x54},
|
||||
{r = 0x59, g= 0x00, b = 0x1D},
|
||||
{r = 0x22, g= 0x00, b = 0x13},
|
||||
{r = 0xFF, g= 0x15, b = 0x00},
|
||||
{r = 0x99, g= 0x35, b = 0x00},
|
||||
{r = 0x79, g= 0x51, b = 0x00},
|
||||
{r = 0x43, g= 0x64, b = 0x00},
|
||||
{r = 0x03, g= 0x39, b = 0x00},
|
||||
{r = 0x00, g= 0x57, b = 0x35},
|
||||
{r = 0x00, g= 0x54, b = 0x7F},
|
||||
{r = 0x00, g= 0x00, b = 0xFF},
|
||||
{r = 0x00, g= 0x45, b = 0x4F},
|
||||
{r = 0x25, g= 0x00, b = 0xCC},
|
||||
{r = 0x7F, g= 0x7F, b = 0x7F},
|
||||
{r = 0x20, g= 0x20, b = 0x20},
|
||||
{r = 0xFF, g= 0x00, b = 0x00},
|
||||
{r = 0xBD, g= 0xFF, b = 0x2D},
|
||||
{r = 0xAF, g= 0xED, b = 0x06},
|
||||
{r = 0x64, g= 0xFF, b = 0x09},
|
||||
{r = 0x10, g= 0x8B, b = 0x00},
|
||||
{r = 0x00, g= 0xFF, b = 0x87},
|
||||
{r = 0x00, g= 0xA9, b = 0xFF},
|
||||
{r = 0x00, g= 0x2A, b = 0xFF},
|
||||
{r = 0x3F, g= 0x00, b = 0xFF},
|
||||
{r = 0x7A, g= 0x00, b = 0xFF},
|
||||
{r = 0xB2, g= 0x1A, b = 0x7D},
|
||||
{r = 0x40, g= 0x21, b = 0x00},
|
||||
{r = 0xFF, g= 0x4A, b = 0x00},
|
||||
{r = 0x88, g= 0xE1, b = 0x06},
|
||||
{r = 0x72, g= 0xFF, b = 0x15},
|
||||
{r = 0x00, g= 0xFF, b = 0x00},
|
||||
{r = 0x3B, g= 0xFF, b = 0x26},
|
||||
{r = 0x59, g= 0xFF, b = 0x71},
|
||||
{r = 0x38, g= 0xFF, b = 0xCC},
|
||||
{r = 0x5B, g= 0x8A, b = 0xFF},
|
||||
{r = 0x31, g= 0x51, b = 0xC6},
|
||||
{r = 0x87, g= 0x7F, b = 0xE9},
|
||||
{r = 0xD3, g= 0x1D, b = 0xFF},
|
||||
{r = 0xFF, g= 0x00, b = 0x5D},
|
||||
{r = 0xFF, g= 0x7F, b = 0x00},
|
||||
{r = 0xB9, g= 0xB0, b = 0x00},
|
||||
{r = 0x90, g= 0xFF, b = 0x00},
|
||||
{r = 0x83, g= 0x5D, b = 0x07},
|
||||
{r = 0x39, g= 0x2b, b = 0x00},
|
||||
{r = 0x14, g= 0x4C, b = 0x10},
|
||||
{r = 0x0D, g= 0x50, b = 0x38},
|
||||
{r = 0x15, g= 0x15, b = 0x2A},
|
||||
{r = 0x16, g= 0x20, b = 0x5A},
|
||||
{r = 0x69, g= 0x3C, b = 0x1C},
|
||||
{r = 0xA8, g= 0x00, b = 0x0A},
|
||||
{r = 0xDE, g= 0x51, b = 0x3D},
|
||||
{r = 0xD8, g= 0x6A, b = 0x1C},
|
||||
{r = 0xFF, g= 0xE1, b = 0x26},
|
||||
{r = 0x9E, g= 0xE1, b = 0x2F},
|
||||
{r = 0x67, g= 0xB5, b = 0x0F},
|
||||
{r = 0x1E, g= 0x1E, b = 0x30},
|
||||
{r = 0xDC, g= 0xFF, b = 0x6B},
|
||||
{r = 0x80, g= 0xFF, b = 0xBD},
|
||||
{r = 0x9A, g= 0x99, b = 0xFF},
|
||||
{r = 0x8E, g= 0x66, b = 0xFF},
|
||||
{r = 0x40, g= 0x40, b = 0x40},
|
||||
{r = 0x75, g= 0x75, b = 0x75},
|
||||
{r = 0xE0, g= 0xFF, b = 0xFF},
|
||||
{r = 0xA0, g= 0x00, b = 0x00},
|
||||
{r = 0x35, g= 0x00, b = 0x00},
|
||||
{r = 0x1A, g= 0xD0, b = 0x00},
|
||||
{r = 0x07, g= 0x42, b = 0x00},
|
||||
{r = 0xB9, g= 0xB0, b = 0x00},
|
||||
{r = 0x3F, g= 0x31, b = 0x00},
|
||||
{r = 0xB3, g= 0x5F, b = 0x00},
|
||||
{r = 0x4B, g= 0x15, b = 0x02},
|
||||
{r = 0x40, g= 0x40, b = 0x40},
|
||||
{r = 0x75, g= 0x75, b = 0x75},
|
||||
{r = 0xE0, g= 0xFF, b = 0xFF},
|
||||
{r = 0xA0, g= 0x00, b = 0x00},
|
||||
{r = 0x35, g= 0x00, b = 0x00},
|
||||
{r = 0x1A, g= 0xD0, b = 0x00},
|
||||
{r = 0x07, g= 0x42, b = 0x00},
|
||||
{r = 0xB9, g= 0xB0, b = 0x00},
|
||||
{r = 0x3F, g= 0x31, b = 0x00},
|
||||
{r = 0xB3, g= 0x5F, b = 0x00},
|
||||
{r = 0x4B, g= 0x15, b = 0x02},
|
||||
}
|
||||
|
||||
--- 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
|
||||
|
||||
function module.pad_script(pad_index: number, y: midi_script.InputValue, context: midi_script.Context): midi_script.Output
|
||||
local address = pad_index
|
||||
local off_color = 0x00
|
||||
local off_output = {
|
||||
address = address,
|
||||
messages = {
|
||||
{ bright, pad_index, off_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, just as with APC Key 25 mk1
|
||||
local color = math.floor(y * 127)
|
||||
return {
|
||||
address = address,
|
||||
messages = {
|
||||
{ bright, pad_index, color },
|
||||
},
|
||||
}
|
||||
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 = feedback_table[y]
|
||||
if entry == nil then
|
||||
-- Unknown texts switch the LED off
|
||||
return off_output
|
||||
end
|
||||
local explicit_color = entry.color
|
||||
if explicit_color ~= nil then
|
||||
-- Switch LED to explicit color
|
||||
return {
|
||||
address = address,
|
||||
messages = {
|
||||
{ entry.behavior, pad_index, explicit_color },
|
||||
},
|
||||
}
|
||||
end
|
||||
-- No explicit color means we should use the event color (which is set in the "Glue" section)
|
||||
local event_color = context.feedback_event.color
|
||||
if event_color == nil then
|
||||
-- Event has default color / no specific color
|
||||
return {
|
||||
address = address,
|
||||
messages = {
|
||||
{ entry.behavior, pad_index, default_color },
|
||||
},
|
||||
}
|
||||
end
|
||||
-- Find color available on the controller that's closest to the desired RGB color
|
||||
local closest_color = find_closest_color_in_palette(event_color, color_palette)
|
||||
return {
|
||||
address = address,
|
||||
messages = {
|
||||
{ entry.behavior, pad_index, closest_color },
|
||||
},
|
||||
}
|
||||
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
|
||||
Reference in New Issue
Block a user