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:
@@ -0,0 +1 @@
|
||||
../../api/luau/.stylua.toml
|
||||
@@ -0,0 +1 @@
|
||||
../../api/luau/.vscode
|
||||
@@ -0,0 +1 @@
|
||||
../../api/luau/README.md
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
--- name: APC Key 25 mk2 - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Akai
|
||||
--- device_name: APC Key 25 mk2
|
||||
--- description: |
|
||||
--- This main preset turns the APC Key 25 mk2 into a capable device for controlling Playtime. Hold Shift
|
||||
--- to access advanced functions.
|
||||
--- used_schemes: [akai/apc-key-25-mk2]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
|
||||
local commons = require("akai/apc-lib/playtime-commons")
|
||||
|
||||
return commons.create_compartment {
|
||||
use_column_stop_buttons = true,
|
||||
stop_column_if_slot_empty = true,
|
||||
mute_track_button_id = "row3/play",
|
||||
arm_track_button_id = "row4/play",
|
||||
column_count = 8,
|
||||
row_count = 5,
|
||||
up_button_id = "col1/stop",
|
||||
down_button_id = "col2/stop",
|
||||
left_button_id = "col3/stop",
|
||||
right_button_id = "col4/stop",
|
||||
volume_button_id = "col5/stop",
|
||||
pan_button_id = "col6/stop",
|
||||
send_button_id = "col7/stop",
|
||||
device_button_id = "col8/stop",
|
||||
stop_all_clips_button_id = "stop-all-clips",
|
||||
stop_all_clips_need_shift = false,
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
--- name: APC Key 25 - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Akai
|
||||
--- device_name: APC Key 25
|
||||
--- description: |
|
||||
--- This main preset turns the APC Key 25 mk1 into a capable device for controlling Playtime. Hold Shift or Sustain
|
||||
--- to access advanced functions.
|
||||
--- used_schemes: [akai/apc-key-25]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
local commons = require("akai/apc-lib/playtime-commons")
|
||||
|
||||
return commons.create_compartment {
|
||||
use_column_stop_buttons = true,
|
||||
stop_column_if_slot_empty = true,
|
||||
mute_track_button_id = "row4/play",
|
||||
arm_track_button_id = "row3/play",
|
||||
column_count = 8,
|
||||
row_count = 5,
|
||||
up_button_id = "col1/stop",
|
||||
down_button_id = "col2/stop",
|
||||
left_button_id = "col3/stop",
|
||||
right_button_id = "col4/stop",
|
||||
volume_button_id = "col5/stop",
|
||||
pan_button_id = "col6/stop",
|
||||
send_button_id = "col7/stop",
|
||||
device_button_id = "col8/stop",
|
||||
stop_all_clips_button_id = "stop-all-clips",
|
||||
stop_all_clips_need_shift = false,
|
||||
}
|
||||
+886
@@ -0,0 +1,886 @@
|
||||
--!strict
|
||||
|
||||
local util = require("util")
|
||||
local realearn = require("realearn")
|
||||
local playtime_util = require("playtime_util")
|
||||
|
||||
local module = {}
|
||||
|
||||
export type ApcPlaytimePresetConfig = {
|
||||
mute_track_button_id: string,
|
||||
arm_track_button_id: string,
|
||||
use_column_stop_buttons: boolean,
|
||||
stop_column_if_slot_empty: boolean,
|
||||
column_count: number,
|
||||
row_count: number,
|
||||
up_button_id: string,
|
||||
down_button_id: string,
|
||||
left_button_id: string,
|
||||
right_button_id: string,
|
||||
volume_button_id: string,
|
||||
pan_button_id: string,
|
||||
send_button_id: string,
|
||||
device_button_id: string,
|
||||
stop_all_clips_button_id: string,
|
||||
stop_all_clips_need_shift: boolean,
|
||||
}
|
||||
|
||||
function module.create_compartment(config: ApcPlaytimePresetConfig): realearn.Compartment
|
||||
-- Aliases
|
||||
|
||||
local partial_mapping = util.partial_mapping
|
||||
local turbo = playtime_util.turbo
|
||||
local scroll_vertically = playtime_util.scroll_vertically
|
||||
local scroll_horizontally = playtime_util.scroll_horizontally
|
||||
|
||||
-- Types
|
||||
|
||||
type Mode = {
|
||||
index: number,
|
||||
label: string,
|
||||
}
|
||||
|
||||
type ModeMap = { [string]: Mode }
|
||||
|
||||
type ParameterMap = { [string]: realearn.Parameter }
|
||||
|
||||
type GroupMap = { [string]: realearn.Group }
|
||||
|
||||
-- Constants
|
||||
|
||||
local column_mode_count = 100
|
||||
local knob_mode_count = 100
|
||||
|
||||
-- Column modes
|
||||
|
||||
local column_modes: ModeMap = {
|
||||
stop = {
|
||||
index = 0,
|
||||
label = "Stop clip",
|
||||
},
|
||||
solo = {
|
||||
index = 1,
|
||||
label = "Solo",
|
||||
},
|
||||
record_arm = {
|
||||
index = 2,
|
||||
label = "Record arm",
|
||||
},
|
||||
mute = {
|
||||
index = 3,
|
||||
label = "Mute",
|
||||
},
|
||||
select = {
|
||||
index = 4,
|
||||
label = "Track select",
|
||||
},
|
||||
}
|
||||
local sorted_column_modes = util.sorted_by_index(column_modes)
|
||||
|
||||
-- Knob modes
|
||||
local knob_modes: ModeMap = {
|
||||
volume = {
|
||||
index = 0,
|
||||
label = "Volume",
|
||||
},
|
||||
pan = {
|
||||
index = 1,
|
||||
label = "Pan",
|
||||
},
|
||||
sends = {
|
||||
index = 2,
|
||||
label = "Sends",
|
||||
},
|
||||
device = {
|
||||
index = 3,
|
||||
label = "Device",
|
||||
},
|
||||
}
|
||||
local sorted_knob_modes = util.sorted_by_index(knob_modes)
|
||||
|
||||
-- Parameters
|
||||
|
||||
local params: ParameterMap = {
|
||||
shift = {
|
||||
index = 0,
|
||||
name = "Shift modifier",
|
||||
},
|
||||
column_mode = {
|
||||
index = 1,
|
||||
name = "Column mode",
|
||||
value_count = column_mode_count,
|
||||
value_labels = util.extract_labels(sorted_column_modes),
|
||||
},
|
||||
knob_mode = {
|
||||
index = 2,
|
||||
name = "Knob mode",
|
||||
value_count = knob_mode_count,
|
||||
value_labels = util.extract_labels(sorted_knob_modes),
|
||||
},
|
||||
send = {
|
||||
index = 3,
|
||||
name = "Send",
|
||||
value_count = 2,
|
||||
},
|
||||
sustain = {
|
||||
index = 4,
|
||||
name = "Sustain modifier",
|
||||
},
|
||||
}
|
||||
|
||||
-- Domain functions
|
||||
|
||||
local function create_index_expression(variable_name: string, index: number): string
|
||||
return `{variable_name} + {index}`
|
||||
end
|
||||
|
||||
local function create_col_expression(col: number): string
|
||||
return create_index_expression("control_unit_column_index", col)
|
||||
end
|
||||
|
||||
local function create_row_expression(row: number): string
|
||||
return create_index_expression("control_unit_row_index", row)
|
||||
end
|
||||
|
||||
local function create_slot_selector(col: number, row: number): realearn.PlaytimeSlotDescriptor
|
||||
return realearn.PlaytimeSlotDescriptor.Dynamic {
|
||||
column_expression = create_col_expression(col),
|
||||
row_expression = create_row_expression(row),
|
||||
}
|
||||
end
|
||||
|
||||
local function create_column_selector(col: number): realearn.PlaytimeColumnDescriptor
|
||||
return realearn.PlaytimeColumnDescriptor.Dynamic {
|
||||
expression = create_col_expression(col),
|
||||
}
|
||||
end
|
||||
|
||||
local function create_row_selector(row: number): realearn.PlaytimeRowDescriptor
|
||||
return realearn.PlaytimeRowDescriptor.Dynamic {
|
||||
expression = create_row_expression(row),
|
||||
}
|
||||
end
|
||||
|
||||
local function knob(index: number)
|
||||
return partial_mapping {
|
||||
source = realearn.Source.Virtual {
|
||||
character = "Multi",
|
||||
id = index,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function button(id: string)
|
||||
return partial_mapping {
|
||||
source = realearn.Source.Virtual {
|
||||
character = "Button",
|
||||
id = id,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function column_stop_button(col: number)
|
||||
return button(`col{col + 1}/stop`)
|
||||
end
|
||||
|
||||
local function row_play_button(row: number)
|
||||
return button(`row{row + 1}/play`)
|
||||
end
|
||||
|
||||
local function slot_button(col: number, row: number)
|
||||
return partial_mapping {
|
||||
source = realearn.Source.Virtual {
|
||||
character = "Multi",
|
||||
id = `col{col + 1}/row{row + 1}/pad`,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function shift_pressed(on: boolean)
|
||||
return partial_mapping {
|
||||
activation_condition = realearn.ActivationCondition.Modifier {
|
||||
modifiers = {
|
||||
{
|
||||
parameter = params.shift.index,
|
||||
on = on,
|
||||
},
|
||||
{
|
||||
parameter = params.sustain.index,
|
||||
on = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function sustain_pressed(on: boolean)
|
||||
return partial_mapping {
|
||||
activation_condition = realearn.ActivationCondition.Modifier {
|
||||
modifiers = {
|
||||
{
|
||||
parameter = params.sustain.index,
|
||||
on = on,
|
||||
},
|
||||
{
|
||||
parameter = params.shift.index,
|
||||
on = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function shift_or_sustain_pressed()
|
||||
return partial_mapping {
|
||||
activation_condition = realearn.ActivationCondition.Expression {
|
||||
condition = `p[{params.shift.index}] || p[{params.sustain.index}]`,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function shift_and_sustain_pressed()
|
||||
return partial_mapping {
|
||||
activation_condition = realearn.ActivationCondition.Modifier {
|
||||
modifiers = {
|
||||
{
|
||||
parameter = params.sustain.index,
|
||||
on = true,
|
||||
},
|
||||
{
|
||||
parameter = params.shift.index,
|
||||
on = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function fire_max(millis: number)
|
||||
return partial_mapping {
|
||||
glue = {
|
||||
fire_mode = realearn.FireMode.Normal {
|
||||
press_duration_interval = { 0, millis },
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function fire_after_timeout(millis: number)
|
||||
return partial_mapping {
|
||||
glue = {
|
||||
fire_mode = realearn.FireMode.AfterTimeout {
|
||||
timeout = millis,
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function fire_on_single_press(max_duration: number)
|
||||
return partial_mapping {
|
||||
glue = {
|
||||
fire_mode = realearn.FireMode.OnSinglePress {
|
||||
max_duration = max_duration,
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function fire_on_double_press()
|
||||
return partial_mapping {
|
||||
glue = {
|
||||
fire_mode = realearn.FireMode.OnDoublePress(),
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local no_mod = shift_pressed(false)
|
||||
local shift = shift_pressed(true)
|
||||
local sustain = sustain_pressed(true)
|
||||
local shift_or_sustain = shift_or_sustain_pressed()
|
||||
local short_press = fire_max(200)
|
||||
local long_press = fire_after_timeout(1000)
|
||||
local single_press = fire_on_single_press(200)
|
||||
local double_press = fire_on_double_press()
|
||||
|
||||
local function clip_matrix_action(action: realearn.PlaytimeMatrixAction)
|
||||
return partial_mapping {
|
||||
target = realearn.Target.PlaytimeMatrixAction {
|
||||
action = action,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function clip_column_action(col: number, action: realearn.PlaytimeColumnAction)
|
||||
return partial_mapping {
|
||||
target = realearn.Target.PlaytimeColumnAction {
|
||||
column = create_column_selector(col),
|
||||
action = action,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function clip_row_action(row: number, action: realearn.PlaytimeRowAction)
|
||||
return partial_mapping {
|
||||
target = realearn.Target.PlaytimeRowAction {
|
||||
row = create_row_selector(row),
|
||||
action = action,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function clip_column_track(col: number): realearn.TrackDescriptor
|
||||
return realearn.TrackDescriptor.FromClipColumn {
|
||||
column = create_column_selector(col),
|
||||
context = "Playback",
|
||||
}
|
||||
end
|
||||
|
||||
local function column_track_target(col: number, track_target_kind: realearn.TargetKind, exclusive: boolean?)
|
||||
return partial_mapping {
|
||||
target = {
|
||||
kind = track_target_kind,
|
||||
track = clip_column_track(col),
|
||||
exclusivity = if exclusive then "WithinFolderOnOnly" else nil,
|
||||
} :: any,
|
||||
}
|
||||
end
|
||||
|
||||
local function route_target(col: number, route_target_kind: realearn.TargetKind)
|
||||
return partial_mapping {
|
||||
target = {
|
||||
kind = route_target_kind,
|
||||
route = realearn.RouteDescriptor.Dynamic {
|
||||
track = clip_column_track(col),
|
||||
expression = `p[{params.send.index}]`,
|
||||
},
|
||||
} :: any,
|
||||
}
|
||||
end
|
||||
|
||||
local function clip_transport_action(col: number, row: number, action: realearn.PlaytimeSlotTransportAction)
|
||||
return partial_mapping {
|
||||
target = realearn.Target.PlaytimeSlotTransportAction {
|
||||
slot = create_slot_selector(col, row),
|
||||
action = action,
|
||||
stop_column_if_slot_empty = config.stop_column_if_slot_empty,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function clip_management_action(col: number, row: number, action: realearn.PlaytimeSlotManagementAction)
|
||||
return partial_mapping {
|
||||
target = realearn.Target.PlaytimeSlotManagementAction {
|
||||
slot = create_slot_selector(col, row),
|
||||
action = action,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function transport_action(action: realearn.TransportAction)
|
||||
return partial_mapping {
|
||||
target = realearn.Target.TransportAction {
|
||||
action = action,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function reaper_action(command: realearn.ReaperCommand)
|
||||
return partial_mapping {
|
||||
target = realearn.Target.ReaperAction {
|
||||
command = command,
|
||||
invocation = "Trigger",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function toggle()
|
||||
return partial_mapping {
|
||||
glue = {
|
||||
absolute_mode = "ToggleButton",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function pick_up()
|
||||
return partial_mapping {
|
||||
glue = {
|
||||
takeover_mode = "PickUpTolerant",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function incremental()
|
||||
return partial_mapping {
|
||||
glue = {
|
||||
absolute_mode = "IncrementalButton",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function wrap()
|
||||
return partial_mapping {
|
||||
glue = {
|
||||
wrap = true,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function control_disabled()
|
||||
return partial_mapping {
|
||||
control_enabled = false,
|
||||
visible_in_projection = false,
|
||||
}
|
||||
end
|
||||
|
||||
local function feedback_disabled()
|
||||
return partial_mapping {
|
||||
feedback_enabled = false,
|
||||
}
|
||||
end
|
||||
|
||||
local function set_param(index: number)
|
||||
return partial_mapping {
|
||||
target = realearn.Target.CompartmentParameterValue {
|
||||
parameter = realearn.CompartmentParameterDescriptor.ById {
|
||||
index = index,
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function name(n: string)
|
||||
return partial_mapping {
|
||||
name = n,
|
||||
}
|
||||
end
|
||||
|
||||
local function group(g: realearn.Group)
|
||||
return partial_mapping {
|
||||
group = g.id,
|
||||
}
|
||||
end
|
||||
|
||||
local function set_mode(mode: Mode, mode_count: number, mode_param_index: number)
|
||||
local target_value = mode.index / (mode_count - 1)
|
||||
return partial_mapping {
|
||||
name = mode.label,
|
||||
glue = {
|
||||
target_interval = { target_value, target_value },
|
||||
out_of_range_behavior = "Min",
|
||||
},
|
||||
target = realearn.Target.CompartmentParameterValue {
|
||||
parameter = realearn.CompartmentParameterDescriptor.ById {
|
||||
index = mode_param_index,
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function set_column_mode(mode: Mode)
|
||||
return set_mode(mode, column_mode_count, params.column_mode.index)
|
||||
end
|
||||
|
||||
local function set_knob_mode(mode: Mode)
|
||||
return set_mode(mode, knob_mode_count, params.knob_mode.index)
|
||||
end
|
||||
|
||||
local function column_mode_is(column_mode: Mode)
|
||||
return realearn.ActivationCondition.Bank {
|
||||
parameter = params.column_mode.index,
|
||||
bank_index = column_mode.index,
|
||||
}
|
||||
end
|
||||
|
||||
local function knob_mode_is(knob_mode)
|
||||
return realearn.ActivationCondition.Bank {
|
||||
parameter = params.knob_mode.index,
|
||||
bank_index = knob_mode.index,
|
||||
}
|
||||
end
|
||||
|
||||
-- Groups
|
||||
|
||||
local groups: GroupMap = {
|
||||
slot_modes = {
|
||||
name = "Slot modes",
|
||||
},
|
||||
column_modes = {
|
||||
name = "Column modes",
|
||||
},
|
||||
record_settings = {
|
||||
name = "Record settings",
|
||||
},
|
||||
knob_modes = {
|
||||
name = "Knob modes",
|
||||
},
|
||||
slot_feedback = {
|
||||
name = "Slot feedback",
|
||||
},
|
||||
slot_play = {
|
||||
name = "Slot play",
|
||||
},
|
||||
slot_clear = {
|
||||
name = "Slot clear",
|
||||
},
|
||||
slot_quantize = {
|
||||
name = "Slot quantize",
|
||||
},
|
||||
slot_copy_or_paste = {
|
||||
name = "Slot copy or paste",
|
||||
},
|
||||
slot_double = {
|
||||
name = "Slot double section",
|
||||
},
|
||||
slot_halve = {
|
||||
name = "Slot halve section",
|
||||
},
|
||||
column_stop = {
|
||||
name = "Column stop",
|
||||
activation_condition = column_mode_is(column_modes.stop),
|
||||
},
|
||||
row_play_scene = {
|
||||
name = "Row play scene",
|
||||
},
|
||||
row_build_scene = {
|
||||
name = "Row build scene",
|
||||
},
|
||||
row_copy_or_paste_scene = {
|
||||
name = "Row copy or paste scene",
|
||||
},
|
||||
row_clear_scene = {
|
||||
name = "Row clear scene",
|
||||
},
|
||||
column_solo = {
|
||||
name = "Column solo",
|
||||
activation_condition = column_mode_is(column_modes.solo),
|
||||
},
|
||||
column_record_arm = {
|
||||
name = "Column record arm",
|
||||
activation_condition = column_mode_is(column_modes.record_arm),
|
||||
},
|
||||
column_mute = {
|
||||
name = "Column mute",
|
||||
activation_condition = column_mode_is(column_modes.mute),
|
||||
},
|
||||
column_select = {
|
||||
name = "Column select",
|
||||
activation_condition = column_mode_is(column_modes.select),
|
||||
},
|
||||
knob_volume = {
|
||||
name = "Knob volume",
|
||||
activation_condition = knob_mode_is(knob_modes.volume),
|
||||
},
|
||||
knob_pan = {
|
||||
name = "Knob pan",
|
||||
activation_condition = knob_mode_is(knob_modes.pan),
|
||||
},
|
||||
knob_sends = {
|
||||
name = "Knob sends",
|
||||
activation_condition = knob_mode_is(knob_modes.sends),
|
||||
},
|
||||
knob_device = {
|
||||
name = "Knob device",
|
||||
activation_condition = knob_mode_is(knob_modes.device),
|
||||
},
|
||||
}
|
||||
util.set_keys_as_ids(groups)
|
||||
|
||||
-- Mappings
|
||||
|
||||
local mappings = {
|
||||
name("Play/stop") + no_mod + button("play") + toggle() + clip_matrix_action("PlayIgnitedOrEnterSilenceMode"),
|
||||
name("Shift modifier") + button("shift") + set_param(params.shift.index),
|
||||
name("Sustain modifier") + button("sustain") + set_param(params.sustain.index),
|
||||
name("Undo") + shift + button("play") + clip_matrix_action("Undo"),
|
||||
name("Record") + no_mod + button("record") + clip_matrix_action("SmartRecord"),
|
||||
name("Redo") + shift + button("record") + clip_matrix_action("Redo"),
|
||||
name("Build scene") + sustain + button("record") + clip_matrix_action("BuildScene"),
|
||||
name("Switch send")
|
||||
+ group(groups.knob_sends)
|
||||
+ feedback_disabled()
|
||||
+ shift
|
||||
+ button("col7/stop")
|
||||
+ incremental()
|
||||
+ wrap()
|
||||
+ set_param(params.send.index),
|
||||
name("Column stop mode")
|
||||
+ group(groups.column_modes)
|
||||
+ shift
|
||||
+ button("row1/play")
|
||||
+ set_column_mode(column_modes.stop),
|
||||
name("Column solo mode")
|
||||
+ group(groups.column_modes)
|
||||
+ shift
|
||||
+ button("row2/play")
|
||||
+ set_column_mode(column_modes.solo),
|
||||
name("Column arm mode")
|
||||
+ group(groups.column_modes)
|
||||
+ shift
|
||||
+ button(config.arm_track_button_id)
|
||||
+ set_column_mode(column_modes.record_arm),
|
||||
name("Column mute mode")
|
||||
+ group(groups.column_modes)
|
||||
+ shift
|
||||
+ button(config.mute_track_button_id)
|
||||
+ set_column_mode(column_modes.mute),
|
||||
name("Column select mode")
|
||||
+ group(groups.column_modes)
|
||||
+ shift
|
||||
+ button("row5/play")
|
||||
+ set_column_mode(column_modes.select),
|
||||
}
|
||||
|
||||
if config.stop_all_clips_need_shift then
|
||||
-- We have a device that has a "Stop all clips" button but it needs shift to be pressed
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Stop all clips") + shift + button(config.stop_all_clips_button_id) + clip_matrix_action("Stop")
|
||||
)
|
||||
else
|
||||
-- We have a device with a dedicated "Stop all clips" button
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Stop all clips") + no_mod + button(config.stop_all_clips_button_id) + clip_matrix_action("Stop")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Click") + shift + button(config.stop_all_clips_button_id) + reaper_action(40364)
|
||||
)
|
||||
end
|
||||
|
||||
if config.use_column_stop_buttons then
|
||||
-- Scrolling
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Scroll up") + shift_or_sustain + button(config.up_button_id) + turbo() + scroll_vertically(-1)
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Scroll down") + shift_or_sustain + button(config.down_button_id) + turbo() + scroll_vertically(1)
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Scroll left") + shift_or_sustain + button(config.left_button_id) + turbo() + scroll_horizontally(-1)
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Scroll right") + shift_or_sustain + button(config.right_button_id) + turbo() + scroll_horizontally(1)
|
||||
)
|
||||
-- Modes
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Knob volume mode")
|
||||
+ group(groups.knob_modes)
|
||||
+ shift
|
||||
+ button(config.volume_button_id)
|
||||
+ set_knob_mode(knob_modes.volume)
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Knob pan mode")
|
||||
+ group(groups.knob_modes)
|
||||
+ shift
|
||||
+ button(config.pan_button_id)
|
||||
+ set_knob_mode(knob_modes.pan)
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Knob send mode")
|
||||
+ group(groups.knob_modes)
|
||||
+ shift
|
||||
+ button(config.send_button_id)
|
||||
+ set_knob_mode(knob_modes.sends)
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Knob device mode")
|
||||
+ group(groups.knob_modes)
|
||||
+ shift
|
||||
+ button(config.device_button_id)
|
||||
+ set_knob_mode(knob_modes.device)
|
||||
)
|
||||
end
|
||||
|
||||
-- For each column
|
||||
for col = 0, config.column_count - 1 do
|
||||
-- Column stop button functions
|
||||
if config.use_column_stop_buttons then
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Stop column")
|
||||
+ group(groups.column_stop)
|
||||
+ no_mod
|
||||
+ column_stop_button(col)
|
||||
+ clip_column_action(col, "Stop")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Solo track")
|
||||
+ group(groups.column_solo)
|
||||
+ no_mod
|
||||
+ toggle()
|
||||
+ column_stop_button(col)
|
||||
+ column_track_target(col, "TrackSoloState")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Arm track")
|
||||
+ group(groups.column_record_arm)
|
||||
+ no_mod
|
||||
+ toggle()
|
||||
+ column_stop_button(col)
|
||||
+ clip_column_action(col, "ArmStateExclusive")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Mute track")
|
||||
+ group(groups.column_mute)
|
||||
+ no_mod
|
||||
+ toggle()
|
||||
+ column_stop_button(col)
|
||||
+ column_track_target(col, "TrackMuteState")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Select track")
|
||||
+ group(groups.column_select)
|
||||
+ no_mod
|
||||
+ toggle()
|
||||
+ column_stop_button(col)
|
||||
+ column_track_target(col, "TrackSelectionState", true)
|
||||
)
|
||||
end
|
||||
-- Knob functions
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Track volume") + group(groups.knob_volume) + knob(col) + pick_up() + column_track_target(col, "TrackVolume")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Track pan") + group(groups.knob_pan) + knob(col) + pick_up() + column_track_target(col, "TrackPan")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Track send volume") + group(groups.knob_sends) + knob(col) + pick_up() + route_target(col, "RouteVolume")
|
||||
)
|
||||
end
|
||||
|
||||
-- For each row
|
||||
for row = 0, config.row_count - 1 do
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Play scene")
|
||||
+ group(groups.row_play_scene)
|
||||
+ feedback_disabled()
|
||||
+ no_mod
|
||||
+ row_play_button(row)
|
||||
+ clip_row_action(row, "PlayScene")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Copy or paste")
|
||||
+ group(groups.row_copy_or_paste_scene)
|
||||
+ sustain
|
||||
+ short_press
|
||||
+ row_play_button(row)
|
||||
+ clip_row_action(row, "CopyOrPasteScene")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Long = Clear")
|
||||
+ group(groups.row_clear_scene)
|
||||
+ feedback_disabled()
|
||||
+ sustain
|
||||
+ long_press
|
||||
+ row_play_button(row)
|
||||
+ clip_row_action(row, "ClearScene")
|
||||
)
|
||||
end
|
||||
|
||||
-- For each slot
|
||||
for col = 0, config.column_count - 1 do
|
||||
for row = 0, config.row_count - 1 do
|
||||
-- Feedback
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Slot feedback")
|
||||
+ group(groups.slot_feedback)
|
||||
+ control_disabled()
|
||||
+ slot_button(col, row)
|
||||
+ playtime_util.slot_state_text_feedback()
|
||||
+ clip_transport_action(col, row, "Trigger")
|
||||
)
|
||||
-- Control
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Slot trigger")
|
||||
+ group(groups.slot_play)
|
||||
+ feedback_disabled()
|
||||
+ no_mod
|
||||
+ slot_button(col, row)
|
||||
+ clip_transport_action(col, row, "Trigger")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Copy or paste")
|
||||
+ group(groups.slot_copy_or_paste)
|
||||
+ feedback_disabled()
|
||||
+ sustain
|
||||
+ single_press
|
||||
+ slot_button(col, row)
|
||||
+ toggle()
|
||||
+ clip_management_action(col, row, "CopyOrPasteClip")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Long = Delete")
|
||||
+ group(groups.slot_clear)
|
||||
+ feedback_disabled()
|
||||
+ sustain
|
||||
+ long_press
|
||||
+ slot_button(col, row)
|
||||
+ clip_management_action(col, row, "ClearSlot")
|
||||
)
|
||||
table.insert(
|
||||
mappings,
|
||||
name("2x = Edit")
|
||||
+ group(groups.slot_quantize)
|
||||
+ feedback_disabled()
|
||||
+ sustain
|
||||
+ double_press
|
||||
+ slot_button(col, row)
|
||||
+ toggle()
|
||||
+ clip_management_action(col, row, "EditClip")
|
||||
)
|
||||
--table.insert(mappings, name("Overdub clip") + group(groups.slot_play) + feedback_disabled() + shift + single_press + slot_button(col, row) + toggle() + clip_transport_action(col, row, "RecordStop", false))
|
||||
--table.insert(mappings, name("2x = Double section") + group(groups.slot_double) + feedback_disabled() + shift + double_press + slot_button(col, row) + adjust_clip_section_length_action(col, row, 2))
|
||||
--table.insert(mappings, name("1x = Halve section") + group(groups.slot_double) + feedback_disabled() + shift + single_press + slot_button(col, row) + adjust_clip_section_length_action(col, row, 0.5))
|
||||
table.insert(
|
||||
mappings,
|
||||
name("Fill slot")
|
||||
+ group(groups.slot_quantize)
|
||||
+ feedback_disabled()
|
||||
+ shift
|
||||
+ single_press
|
||||
+ slot_button(col, row)
|
||||
+ clip_management_action(col, row, "FillSlotWithSelectedItem")
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
parameters = util.sorted_by_index(params),
|
||||
groups = util.to_array(groups),
|
||||
mappings = mappings :: any,
|
||||
custom_data = {
|
||||
playtime = {
|
||||
control_unit = {
|
||||
column_count = config.column_count,
|
||||
row_count = config.row_count,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
return module
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
--- name: APC mini mk2 - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Akai
|
||||
--- device_name: APC mini mk2
|
||||
--- description: |
|
||||
--- This main preset turns the APC mini mk2 into a capable device for controlling Playtime. Hold Shift
|
||||
--- to access advanced functions.
|
||||
--- used_schemes: [akai/apc-mini-mk2]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
|
||||
local commons = require("akai/apc-lib/playtime-commons")
|
||||
|
||||
return commons.create_compartment {
|
||||
use_column_stop_buttons = true,
|
||||
stop_column_if_slot_empty = true,
|
||||
mute_track_button_id = "row3/play",
|
||||
arm_track_button_id = "row4/play",
|
||||
column_count = 8,
|
||||
row_count = 8,
|
||||
up_button_id = "col5/stop",
|
||||
down_button_id = "col6/stop",
|
||||
left_button_id = "col7/stop",
|
||||
right_button_id = "col8/stop",
|
||||
volume_button_id = "col1/stop",
|
||||
pan_button_id = "col2/stop",
|
||||
send_button_id = "col3/stop",
|
||||
device_button_id = "col4/stop",
|
||||
stop_all_clips_button_id = "row8/play",
|
||||
stop_all_clips_need_shift = true,
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
--- name: APC mini - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Akai
|
||||
--- device_name: APC mini
|
||||
--- description: |
|
||||
--- This main preset turns the APC mini into a capable device for controlling Playtime. Hold Shift
|
||||
--- to access advanced functions.
|
||||
--- used_schemes: [akai/apc-mini]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
|
||||
local commons = require("akai/apc-lib/playtime-commons")
|
||||
|
||||
return commons.create_compartment {
|
||||
use_column_stop_buttons = true,
|
||||
stop_column_if_slot_empty = true,
|
||||
mute_track_button_id = "row4/play",
|
||||
arm_track_button_id = "row3/play",
|
||||
column_count = 8,
|
||||
row_count = 8,
|
||||
up_button_id = "col1/stop",
|
||||
down_button_id = "col2/stop",
|
||||
left_button_id = "col3/stop",
|
||||
right_button_id = "col4/stop",
|
||||
volume_button_id = "col5/stop",
|
||||
pan_button_id = "col6/stop",
|
||||
send_button_id = "col7/stop",
|
||||
device_button_id = "col8/stop",
|
||||
stop_all_clips_button_id = "row8/play",
|
||||
stop_all_clips_need_shift = true,
|
||||
}
|
||||
+1174
File diff suppressed because it is too large
Load Diff
+75
@@ -0,0 +1,75 @@
|
||||
--- name: Generic grid controller - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Generic
|
||||
--- device_name: Generic
|
||||
--- description: |
|
||||
--- This main preset turns any grid-like controller into a device for doing very basic clip launching via Playtime.
|
||||
---
|
||||
--- This generic preset is intended to be used as fallback if no device-specific preset exists. It lacks features because it can't take advantage
|
||||
--- of device-specific features.
|
||||
--- used_schemes: [grid]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
-- Configuration
|
||||
|
||||
local stop_column_if_slot_empty = true
|
||||
|
||||
-- Requires
|
||||
|
||||
local realearn = require("realearn")
|
||||
|
||||
-- Constants
|
||||
|
||||
-- TODO This should be parameterized from the controller preset. One more reason to allow script parameterization :)
|
||||
|
||||
local column_count = 4
|
||||
local row_count = 4
|
||||
|
||||
-- Mappings
|
||||
|
||||
local mappings = {}
|
||||
|
||||
-- For each slot
|
||||
for col = 0, column_count - 1 do
|
||||
for row = 0, row_count - 1 do
|
||||
local mapping = realearn.Mapping {
|
||||
name = `Trigger slot {col + 1}/{row + 1}`,
|
||||
source = realearn.Source.Virtual {
|
||||
character = "Multi",
|
||||
id = `col{col + 1}/row{row + 1}/pad`,
|
||||
},
|
||||
glue = {
|
||||
feedback = realearn.Feedback.Text {
|
||||
text_expression = "{{ target.slot_state.id }}",
|
||||
color = realearn.VirtualColor {
|
||||
prop = "target.slot.color",
|
||||
},
|
||||
},
|
||||
},
|
||||
target = realearn.Target.PlaytimeSlotTransportAction {
|
||||
slot = realearn.PlaytimeSlotDescriptor.Dynamic {
|
||||
column_expression = `control_unit_column_index + {col}`,
|
||||
row_expression = `control_unit_row_index + {row}`,
|
||||
},
|
||||
action = "Trigger",
|
||||
stop_column_if_slot_empty = stop_column_if_slot_empty,
|
||||
},
|
||||
}
|
||||
table.insert(mappings, mapping)
|
||||
end
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
mappings = mappings :: any,
|
||||
custom_data = {
|
||||
playtime = {
|
||||
control_unit = {
|
||||
column_count = column_count,
|
||||
row_count = row_count,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
--- name: Numbered - FX parameters
|
||||
--- realearn_version: 2.16.0-pre.9
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Generic
|
||||
--- device_name: Generic
|
||||
--- description: |
|
||||
--- This main preset is built for controllers that offer a set of knobs and buttons. Each knob will control one parameter of
|
||||
--- the unit FX (which by default resolves to the currently focused FX), starting with parameter 1. Each button will switch
|
||||
--- to the next bank of parameters. If the controller has `bank-left` and `bank-right` buttons, they can be used to switch
|
||||
--- between pages, making it possible to control even more parameters.
|
||||
---
|
||||
--- At the moment, this preset assumes that the controller has exactly 16 knobs and buttons. In future, this will be variable.
|
||||
--- used_schemes: [numbered]
|
||||
|
||||
--!strict
|
||||
|
||||
local realearn = require("realearn")
|
||||
|
||||
--- Configuration
|
||||
|
||||
local multi_count = 16
|
||||
local button_count = 16
|
||||
|
||||
-- Code
|
||||
|
||||
local page_count = 16
|
||||
local bank_count = button_count
|
||||
|
||||
local parameters: { realearn.Parameter } = {
|
||||
{
|
||||
index = 0,
|
||||
name = "Page",
|
||||
value_count = page_count,
|
||||
},
|
||||
{
|
||||
index = 1,
|
||||
name = "Bank",
|
||||
value_count = bank_count,
|
||||
},
|
||||
}
|
||||
|
||||
local mappings: { realearn.Mapping } = {
|
||||
-- Side button left => Decrease page
|
||||
realearn.Mapping {
|
||||
name = "Page -",
|
||||
source = realearn.Source.Virtual {
|
||||
character = "Button",
|
||||
id = "bank-left",
|
||||
},
|
||||
glue = {
|
||||
absolute_mode = "IncrementalButton",
|
||||
reverse = true,
|
||||
},
|
||||
target = realearn.Target.CompartmentParameterValue {
|
||||
parameter = realearn.CompartmentParameterDescriptor.ById {
|
||||
index = 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
-- Side button right => Increase page
|
||||
realearn.Mapping {
|
||||
name = "Page +",
|
||||
source = realearn.Source.Virtual {
|
||||
character = "Button",
|
||||
id = "bank-right",
|
||||
},
|
||||
glue = {
|
||||
absolute_mode = "IncrementalButton",
|
||||
reverse = false,
|
||||
},
|
||||
target = realearn.Target.CompartmentParameterValue {
|
||||
parameter = realearn.CompartmentParameterDescriptor.ById {
|
||||
index = 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
-- Encoder movement => Set FX parameter value
|
||||
for i = 0, multi_count - 1 do
|
||||
local m = realearn.Mapping {
|
||||
name = `Set parameter value {i + 1}`,
|
||||
source = realearn.Source.Virtual {
|
||||
character = "Multi",
|
||||
id = i,
|
||||
},
|
||||
target = realearn.Target.FxParameterValue {
|
||||
parameter = realearn.FxParameterDescriptor.Dynamic {
|
||||
fx = realearn.FxDescriptor.Instance {},
|
||||
expression = `p[0] * {page_count} * {bank_count} + p[1] * {bank_count} + {i}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
table.insert(mappings, m)
|
||||
end
|
||||
|
||||
-- Encoder push => Select bank
|
||||
for i = 0, button_count - 1 do
|
||||
local normalized_bank = i / (button_count - 1)
|
||||
local m = realearn.Mapping {
|
||||
name = `Switch to bank {i + 1}`,
|
||||
source = realearn.Source.Virtual {
|
||||
character = "Button",
|
||||
id = i,
|
||||
},
|
||||
glue = {
|
||||
out_of_range_behavior = "Min",
|
||||
target_interval = { normalized_bank, normalized_bank },
|
||||
},
|
||||
target = realearn.Target.CompartmentParameterValue {
|
||||
parameter = realearn.CompartmentParameterDescriptor.ById {
|
||||
index = 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
table.insert(mappings, m)
|
||||
end
|
||||
|
||||
return realearn.Compartment {
|
||||
parameters = parameters,
|
||||
mappings = mappings,
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
local mappings = {}
|
||||
|
||||
for i = 0, 7 do
|
||||
local m = {
|
||||
name = tostring(i + 1),
|
||||
source = {
|
||||
kind = "Key",
|
||||
keystroke = {
|
||||
modifiers = 1,
|
||||
key = 49 + i,
|
||||
},
|
||||
},
|
||||
glue = {
|
||||
button_filter = "PressOnly",
|
||||
},
|
||||
target = {
|
||||
kind = "SendMidi",
|
||||
message = "B0 00 0" .. i,
|
||||
},
|
||||
}
|
||||
table.insert(mappings, m)
|
||||
end
|
||||
|
||||
return {
|
||||
kind = "MainCompartment",
|
||||
value = {
|
||||
mappings = mappings,
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../api/luau/midi_script_source_runtime.luau
|
||||
+1062
File diff suppressed because it is too large
Load Diff
+48
@@ -0,0 +1,48 @@
|
||||
--- name: Launchpad Mini mk3 - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad Mini mk3
|
||||
--- description: |
|
||||
--- This main preset turns the Launchpad Mini mk3 into a capable device for controlling Playtime.
|
||||
--- used_schemes: [novation/launchpad-mini-mk3/live]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
local commons = require("novation/launchpad-lib/playtime-commons")
|
||||
|
||||
return commons.create_compartment {
|
||||
stop_column_if_slot_empty = true,
|
||||
has_dedicated_column_action_buttons = false,
|
||||
has_shift_button = false,
|
||||
has_delete_button = false,
|
||||
has_quantize_button = false,
|
||||
has_fixed_length_button = false,
|
||||
has_duplicate_button = false,
|
||||
has_play_button = false,
|
||||
has_record_button = false,
|
||||
|
||||
stop_clip_button_id = nil,
|
||||
stop_clip_needs_mixer = false,
|
||||
mute_button_id = nil,
|
||||
mute_needs_mixer = false,
|
||||
solo_button_id = nil,
|
||||
solo_needs_mixer = false,
|
||||
record_arm_button_id = nil,
|
||||
record_arm_needs_mixer = false,
|
||||
track_select_button_id = nil,
|
||||
|
||||
undo_button_id = nil,
|
||||
undo_needs_shift = true,
|
||||
redo_button_id = nil,
|
||||
redo_needs_shift = true,
|
||||
click_button_id = nil,
|
||||
click_needs_shift = true,
|
||||
double_button_id = nil,
|
||||
double_needs_shift = true,
|
||||
tap_button_id = nil,
|
||||
tap_needs_shift = true,
|
||||
use_last_row_play_button_for_column_modes = true,
|
||||
use_mk1_colors = false,
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
--- name: Launchpad mk1 - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad mk1
|
||||
--- description: |
|
||||
--- This main preset turns the Launchpad mk1 into a capable device for controlling Playtime.
|
||||
---
|
||||
--- The following features are not yet supported: vol, pan, snd A, snd B
|
||||
--- used_schemes: [novation/launchpad-mk1]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
local commons = require("novation/launchpad-lib/playtime-commons")
|
||||
|
||||
return commons.create_compartment {
|
||||
stop_column_if_slot_empty = true,
|
||||
has_dedicated_column_action_buttons = false,
|
||||
has_shift_button = false,
|
||||
has_delete_button = false,
|
||||
has_quantize_button = false,
|
||||
has_fixed_length_button = false,
|
||||
has_duplicate_button = false,
|
||||
has_play_button = false,
|
||||
has_record_button = false,
|
||||
|
||||
stop_clip_button_id = "row5/play",
|
||||
stop_clip_needs_mixer = true,
|
||||
mute_button_id = "row6/play",
|
||||
mute_needs_mixer = true,
|
||||
solo_button_id = "row7/play",
|
||||
solo_needs_mixer = true,
|
||||
record_arm_button_id = "row8/play",
|
||||
record_arm_needs_mixer = true,
|
||||
track_select_button_id = nil,
|
||||
|
||||
undo_button_id = nil,
|
||||
undo_needs_shift = false,
|
||||
redo_button_id = nil,
|
||||
redo_needs_shift = false,
|
||||
click_button_id = nil,
|
||||
click_needs_shift = false,
|
||||
double_button_id = nil,
|
||||
double_needs_shift = false,
|
||||
tap_button_id = nil,
|
||||
tap_needs_shift = false,
|
||||
use_last_row_play_button_for_column_modes = false,
|
||||
mixer_button_id = "mixer",
|
||||
use_mk1_colors = true,
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
--- name: Launchpad Pro mk2 - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad Pro mk2
|
||||
--- description: |
|
||||
--- This main preset turns the Launchpad Pro mk2 into a capable device for controlling Playtime.
|
||||
---
|
||||
--- The following features are not yet supported: Note, Device, User, Volume, Pan, Sends, Record,
|
||||
--- Record Quantise.
|
||||
--- used_schemes: [novation/launchpad-pro-mk2/live]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
local commons = require("novation/launchpad-lib/playtime-commons")
|
||||
|
||||
return commons.create_compartment {
|
||||
stop_column_if_slot_empty = true,
|
||||
has_dedicated_column_action_buttons = false,
|
||||
has_shift_button = true,
|
||||
has_delete_button = true,
|
||||
has_quantize_button = true,
|
||||
has_fixed_length_button = false,
|
||||
has_duplicate_button = true,
|
||||
has_play_button = false,
|
||||
has_record_button = true,
|
||||
|
||||
stop_clip_button_id = "stop-clip",
|
||||
stop_clip_needs_mixer = false,
|
||||
mute_button_id = "mute",
|
||||
mute_needs_mixer = false,
|
||||
solo_button_id = "solo",
|
||||
solo_needs_mixer = false,
|
||||
record_arm_button_id = "record-arm",
|
||||
record_arm_needs_mixer = false,
|
||||
track_select_button_id = "track-select",
|
||||
|
||||
undo_button_id = "undo",
|
||||
undo_needs_shift = false,
|
||||
redo_button_id = "undo",
|
||||
redo_needs_shift = true,
|
||||
click_button_id = "click",
|
||||
click_needs_shift = false,
|
||||
double_button_id = "double",
|
||||
double_needs_shift = false,
|
||||
tap_button_id = nil,
|
||||
tap_needs_shift = false,
|
||||
use_last_row_play_button_for_column_modes = false,
|
||||
mixer_button_id = nil,
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
--- name: Launchpad Pro mk3 - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad Pro mk3
|
||||
--- description: |
|
||||
--- This main preset turns the Launchpad Pro mk3 into a capable device for controlling Playtime.
|
||||
---
|
||||
--- The following features are not yet supported: Note, Chord, Custom, Sequencer, Projects, Volume, Pan, Sends, Device,
|
||||
--- Swing, Tempo, Capture MIDI, Patterns, Steps, Pattern Settings, Velocity, Probability,
|
||||
--- Mutation, Micro Step, Print to Clip.
|
||||
--- used_schemes: [novation/launchpad-pro-mk3/live]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
local commons = require("novation/launchpad-lib/playtime-commons")
|
||||
|
||||
return commons.create_compartment {
|
||||
stop_column_if_slot_empty = true,
|
||||
has_dedicated_column_action_buttons = true,
|
||||
|
||||
has_shift_button = true,
|
||||
has_delete_button = true,
|
||||
has_quantize_button = true,
|
||||
has_fixed_length_button = true,
|
||||
has_duplicate_button = true,
|
||||
has_play_button = true,
|
||||
has_record_button = true,
|
||||
|
||||
stop_clip_button_id = "stop-clip",
|
||||
stop_clip_needs_mixer = false,
|
||||
mute_button_id = "mute",
|
||||
mute_needs_mixer = false,
|
||||
solo_button_id = "solo",
|
||||
solo_needs_mixer = false,
|
||||
record_arm_button_id = "record-arm",
|
||||
record_arm_needs_mixer = false,
|
||||
track_select_button_id = "track-select",
|
||||
|
||||
undo_button_id = "record-arm",
|
||||
undo_needs_shift = true,
|
||||
redo_button_id = "mute",
|
||||
redo_needs_shift = true,
|
||||
click_button_id = "solo",
|
||||
click_needs_shift = true,
|
||||
double_button_id = "duplicate",
|
||||
double_needs_shift = true,
|
||||
tap_button_id = "sends",
|
||||
tap_needs_shift = true,
|
||||
use_last_row_play_button_for_column_modes = false,
|
||||
mixer_button_id = nil,
|
||||
use_mk1_colors = false,
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
--- name: Launchpad X - Playtime
|
||||
--- realearn_version: 2.16.0-pre.8
|
||||
--- author: helgoboss
|
||||
--- device_manufacturer: Novation
|
||||
--- device_name: Launchpad X
|
||||
--- description: |
|
||||
--- This main preset turns the Launchpad X into a capable device for controlling Playtime.
|
||||
---
|
||||
--- The following features are not yet supported: Note, Custom, Volume, Pan, Send A, Send B, Capture MIDI
|
||||
--- used_schemes: [novation/launchpad-x/live]
|
||||
--- required_features: [playtime]
|
||||
|
||||
--!strict
|
||||
|
||||
local commons = require("novation/launchpad-lib/playtime-commons")
|
||||
|
||||
return commons.create_compartment {
|
||||
stop_column_if_slot_empty = true,
|
||||
has_dedicated_column_action_buttons = false,
|
||||
has_shift_button = false,
|
||||
has_delete_button = false,
|
||||
has_quantize_button = false,
|
||||
has_fixed_length_button = false,
|
||||
has_duplicate_button = false,
|
||||
has_play_button = false,
|
||||
has_record_button = true,
|
||||
|
||||
stop_clip_button_id = "row5/play",
|
||||
stop_clip_needs_mixer = true,
|
||||
mute_button_id = "row6/play",
|
||||
mute_needs_mixer = true,
|
||||
solo_button_id = "row7/play",
|
||||
solo_needs_mixer = true,
|
||||
record_arm_button_id = "row8/play",
|
||||
record_arm_needs_mixer = true,
|
||||
track_select_button_id = nil,
|
||||
|
||||
undo_button_id = nil,
|
||||
undo_needs_shift = false,
|
||||
redo_button_id = nil,
|
||||
redo_needs_shift = false,
|
||||
click_button_id = nil,
|
||||
click_needs_shift = false,
|
||||
double_button_id = nil,
|
||||
double_needs_shift = false,
|
||||
tap_button_id = nil,
|
||||
tap_needs_shift = false,
|
||||
use_last_row_play_button_for_column_modes = false,
|
||||
mixer_button_id = "session",
|
||||
use_mk1_colors = false,
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
--!strict
|
||||
|
||||
type ArbitraryTable = { [any]: any }
|
||||
|
||||
--- Returns a new table that is the result of merging t2 into t1.
|
||||
---
|
||||
--- Values in t2 have precedence.
|
||||
---
|
||||
--- The result will be mergeable as well. This is good for "modifier chaining".
|
||||
---
|
||||
--- Typing not ideal because of https://github.com/luau-lang/luau/issues/392#issuecomment-1050344334
|
||||
function merged<A, B>(t1: A, t2: B): A & B
|
||||
if type(t1) ~= "table" or type(t2) ~= "table" then
|
||||
return t1 :: any
|
||||
end
|
||||
local result = table.clone(t1)
|
||||
for key, new_value in pairs(t2 :: ArbitraryTable) do
|
||||
local old_value = result[key]
|
||||
if old_value and type(old_value) == "table" and type(new_value) == "table" then
|
||||
-- Merge table value as well
|
||||
result[key] = merged(old_value, new_value) :: any
|
||||
else
|
||||
-- Simple use new value
|
||||
result[key] = new_value
|
||||
end
|
||||
end
|
||||
return result :: any
|
||||
end
|
||||
|
||||
type PartialImpl = {
|
||||
new: (part: ArbitraryTable) -> Partial,
|
||||
__index: PartialImpl,
|
||||
__add: (self: Partial, part: Partial) -> Partial,
|
||||
}
|
||||
|
||||
export type Partial = typeof(setmetatable({}, {} :: PartialImpl))
|
||||
|
||||
local Partial: PartialImpl = {} :: PartialImpl
|
||||
Partial.__index = Partial
|
||||
|
||||
function Partial.new(part)
|
||||
return setmetatable(part, Partial)
|
||||
end
|
||||
|
||||
function Partial:__add(part)
|
||||
return Partial.new(merged(self, part) :: any)
|
||||
end
|
||||
|
||||
return {
|
||||
Partial = Partial,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../api/luau/playtime.luau
|
||||
@@ -0,0 +1,59 @@
|
||||
--!strict
|
||||
|
||||
local realearn = require("realearn")
|
||||
local util = require("util")
|
||||
|
||||
local playtime_util = {}
|
||||
|
||||
--- A partial mapping that enables turbo mode for buttons.
|
||||
function playtime_util.turbo()
|
||||
return util.partial_mapping {
|
||||
glue = {
|
||||
fire_mode = realearn.FireMode.AfterTimeoutKeepFiring {
|
||||
timeout = 0,
|
||||
rate = 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function scroll(axis: realearn.Axis, amount: number)
|
||||
local abs_amount = math.abs(amount)
|
||||
return util.partial_mapping {
|
||||
glue = {
|
||||
absolute_mode = "IncrementalButton",
|
||||
step_factor_interval = { abs_amount, abs_amount },
|
||||
reverse = amount < 0,
|
||||
--- This makes the LED switch off when no scrolling is possible in the given direction
|
||||
feedback = realearn.Feedback.Numeric {
|
||||
transformation = "x = y < 1",
|
||||
},
|
||||
},
|
||||
target = realearn.Target.PlaytimeControlUnitScroll {
|
||||
axis = axis,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
function playtime_util.scroll_horizontally(amount: number)
|
||||
return scroll("X", amount)
|
||||
end
|
||||
|
||||
function playtime_util.scroll_vertically(amount: number)
|
||||
return scroll("Y", amount)
|
||||
end
|
||||
|
||||
function playtime_util.slot_state_text_feedback()
|
||||
return util.partial_mapping {
|
||||
glue = {
|
||||
feedback = realearn.Feedback.Text {
|
||||
text_expression = "{{ target.slot_state.id }}",
|
||||
color = realearn.VirtualColor {
|
||||
prop = "target.slot.color",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
return playtime_util
|
||||
@@ -0,0 +1 @@
|
||||
../../api/luau/preset_runtime.luau
|
||||
@@ -0,0 +1 @@
|
||||
../../api/luau/realearn.luau
|
||||
@@ -0,0 +1,56 @@
|
||||
--!strict
|
||||
|
||||
local realearn = require("realearn")
|
||||
local partial = require("partial")
|
||||
|
||||
local util = {}
|
||||
|
||||
--- Creates a partial mapping.
|
||||
---
|
||||
--- A partial mapping can be conveniently mixed with other partial mappings by using the + operator.
|
||||
--- This enables a very intuitive but still flexible way of writing mappings.
|
||||
---
|
||||
--- Ideally, we would return Partial & Mapping but Luau has still issues with that. In particular, it
|
||||
--- can't resolve the + operator when used in an intersection.
|
||||
function util.partial_mapping(m: realearn.Mapping): partial.Partial
|
||||
return partial.Partial.new(m)
|
||||
end
|
||||
|
||||
--- Returns the values of the given key-value table as array.
|
||||
function util.to_array<V>(t: { [string]: V }): { V }
|
||||
local array = {}
|
||||
for _, v in t do
|
||||
table.insert(array, v)
|
||||
end
|
||||
return array
|
||||
end
|
||||
|
||||
--- Returns the values of the given key-value table as array, sorted by the values'
|
||||
--- `index` key.
|
||||
function util.sorted_by_index<V>(t: { [string]: V }): { V }
|
||||
local sorted = util.to_array(t)
|
||||
local compare_index = function(left: any, right: any): boolean
|
||||
return left.index < right.index
|
||||
end
|
||||
table.sort(sorted, compare_index)
|
||||
return sorted
|
||||
end
|
||||
|
||||
--- Takes a key-value table and adds a new attribute `id` to each value that
|
||||
--- corresponds to the key.
|
||||
function util.set_keys_as_ids(t: { [string]: { [string]: any } })
|
||||
for key, value in pairs(t) do
|
||||
value.id = key
|
||||
end
|
||||
end
|
||||
|
||||
--- Puts each `label` property value of the given array of tables into a new array.
|
||||
function util.extract_labels(array: { { [string]: any } }): { string }
|
||||
local labels = {}
|
||||
for _, element in ipairs(array) do
|
||||
table.insert(labels, element.label)
|
||||
end
|
||||
return labels
|
||||
end
|
||||
|
||||
return util
|
||||
Reference in New Issue
Block a user