So, I recorded a video showing how my concept works.
At the end, I demonstrate one of the main issues—the dependency on focus. For everything to work properly, you need to make sure that the track is selected and the FX is focused. This is the basic principle behind ReaLearn’s mapping approach. You can remove the FX Focused condition, but then you can end up with situations where two identical plugins on the same track are controlled simultaneously.
Below I’m sharing the code—maybe it will help you or anyone else improve their Electra One experience when integrating with REAPER.
I only guided the logic; all the code and syntax was written by the neural network.
-- Electra One: Output device ID
local midi_output_id = 34
-- Configuration table for presets and parameters
local fx_presets = {
{ match = "SSL E Channel Strip", bank = 0x00, slot = 0x00, page = 0x00,
params = {
{ name = "Input Select", id = 3, index = 1 },
{ name = "Line Gain", id = 1, index = 2 },
{ name = "Mic Gain", id = 2, index = 3 },
{ name = "Pad", id = 4, index = 0 },
{ name = "COMP Ratio", id = 28, index = 7 },
{ name = "COMP Thresh", id = 29, index = 8 },
{ name = "COMP Release", id = 30, index = 9 },
{ name = "COMP Attack", id = 31, index = 10 },
{ name = "Stereo Link", id = 37, index = 11 },
{ name = "EXP Thresh", id = 34, index = 13 },
{ name = "EXP Range", id = 35, index = 14 },
{ name = "EXP Release", id = 36, index = 15 },
{ name = "EXP Attack", id = 38, index = 16 },
{ name = "DYN In", id = 25, index = 0 },
{ name = "COMP In", id = 27, index = 0 },
{ name = "EXP In", id = 26, index = 0 },
{ name = "HP Freq", id = 40, index = 20 },
{ name = "LP Freq", id = 41, index = 21 },
{ name = "HF Gain", id = 13, index = 24 },
{ name = "HF Freq", id = 17, index = 25 },
{ name = "HF Bell", id = 18, index = 26 },
{ name = "HMF Gain", id = 12, index = 27 },
{ name = "HMF Freq", id = 15, index = 28 },
{ name = "HMF Q", id = 16, index = 29 },
{ name = "LMF Gain", id = 10, index = 30 },
{ name = "LMF Freq", id = 11, index = 31 },
{ name = "LMF Q", id = 14, index = 32 },
{ name = "LF Gain", id = 6, index = 33 },
{ name = "LF Freq", id = 7, index = 34 },
{ name = "LF Bell", id = 8, index = 35 },
{ name = "EQ In", id = 19, index = 0 },
{ name = "EQ Pre Dyn", id = 20, index = 0 },
{ name = "EQ Type", id = 9, index = 39 },
{ name = "Level", id = 22, index = 41 },
{ name = "Output", id = 23, index = 42 },
{ name = "Power", id = 24, index = 43 },
}
},
{ match = "API Vision Channel Strip", bank = 0x00, slot = 0x00, page = 0x01,
params = {
{ name = "Mic Gain", id = 42, index = 2 },
{ name = "Line Gain", id = 43, index = 3 },
{ name = "Pad", id = 46, index = 4 },
{ name = "Cut Filter", id = 89, index = 6 }, -- Assuming Cut Filter as HP (Low Cut)
{ name = "215 LP Filter", id = 90, index = 7 },
{ name = "215 HP Filter", id = 89, index = 8 }, -- Remapped to Low Cut for HP
{ name = "235 Thresh", id = 86, index = 11 },
{ name = "235 Depth", id = 87, index = 12 },
{ name = "235 Attack", id = 83, index = 13 },
{ name = "235 Release", id = 88, index = 14 },
{ name = "235 R/H", id = 88, index = 14 },
{ name = "235 G/E", id = 84, index = 16 },
{ name = "235 On", id = 81, index = 17 },
{ name = "225 Thresh", id = 45, index = 18 },
{ name = "225 Ratio", id = 49, index = 19 },
{ name = "225 Attack", id = 51, index = 20 },
{ name = "225 Release", id = 50, index = 21 },
{ name = "225 Knee", id = 52, index = 22 },
{ name = "225 Type", id = 53, index = 23 },
{ name = "225 On", id = 79, index = 24 },
{ name = "550 HF Freq", id = 63, index = 25 },
{ name = "550 HF Gain", id = 59, index = 26 },
{ name = "550 HF Filter", id = 65, index = 27 },
{ name = "550 HMF Freq", id = 62, index = 28 },
{ name = "550 HMF Gain", id = 58, index = 29 },
{ name = "550 LMF Freq", id = 61, index = 30 },
{ name = "550 LMF Gain", id = 57, index = 31 },
{ name = "550 LF Freq", id = 60, index = 32 },
{ name = "550 LF Gain", id = 56, index = 33 },
{ name = "550 LF Filter", id = 64, index = 34 },
{ name = "560 16kHz", id = 77, index = 35 },
{ name = "560 8kHz", id = 76, index = 36 },
{ name = "560 4kHz", id = 75, index = 37 },
{ name = "560 2kHz", id = 74, index = 38 },
{ name = "560 1kHz", id = 73, index = 39 },
{ name = "560 500Hz", id = 72, index = 40 },
{ name = "560 250Hz", id = 71, index = 41 },
{ name = "560 125Hz", id = 69, index = 42 },
{ name = "560 63Hz", id = 70, index = 43 },
{ name = "560 31Hz", id = 68, index = 44 },
{ name = "EQ Predyn", id = 78, index = 45 },
{ name = "EQ Type", id = 55, index = 47 },
{ name = "EQ On", id = 66, index = 48 },
{ name = "Level", id = 44, index = 49 },
{ name = "Power", id = 85, index = 51 }
}
},
{ match = "TrackComp", bank = 0x00, slot = 0x01, page = 0x09 },
}
-- Default preset
local default_preset = { bank = 0x00, slot = 0x06, page = 0x01 }
-- Cache for storing last parameter values
local last_values = {}
-- States
local last_fx_name = ""
local last_sent_key = ""
local pending_preset = nil
local page_send_time = nil
local last_run_time = 0
local min_interval = 0.02 -- Reduced interval for better responsiveness
-- Send SysEx
local function send_sysex(sysex)
if midi_output_id >= reaper.GetNumMIDIOutputs() then
return
end
local msg = ""
for i = 1, #sysex do
msg = msg .. string.char(sysex[i])
end
reaper.SendMIDIMessageToHardware(midi_output_id, msg)
end
-- Trigger ReaLearn feedback update
local function send_realean_feedback()
local cmd_id = reaper.NamedCommandLookup("_REALEARN_SEND_ALL_FEEDBACK")
if cmd_id ~= 0 then
reaper.Main_OnCommand(cmd_id, 0)
end
end
-- Start sending preset
local function trigger_preset(preset)
local key = preset.bank .. "-" .. preset.slot .. "-" .. preset.page
if key == last_sent_key then return end
last_sent_key = key
pending_preset = preset
page_send_time = reaper.time_precise() + 0.2 -- Reduced delay to 200 ms
send_sysex({0xF0, 0x00, 0x21, 0x45, 0x09, 0x08, preset.bank, preset.slot, 0xF7})
end
-- Send text to E1 controller
local function send_control_text(control_id, text)
local msb = math.floor(control_id / 128)
local lsb = control_id % 128
local numeric_value_id = 0x00
local text_bytes = {}
for i = 1, #text do
text_bytes[i] = string.byte(text, i)
end
local sysex = {0xF0, 0x00, 0x21, 0x45, 0x14, 0x0E, lsb, msb, numeric_value_id}
for _, byte in ipairs(text_bytes) do
table.insert(sysex, byte)
end
table.insert(sysex, 0xF7)
send_sysex(sysex)
end
-- Main loop
function main()
local current_time = reaper.time_precise()
if current_time - last_run_time < min_interval then
reaper.defer(main)
return
end
last_run_time = current_time
-- Send page with delay
if pending_preset and current_time >= page_send_time then
send_sysex({0xF0, 0x00, 0x21, 0x45, 0x09, 0x0A, pending_preset.page, 0xF7})
send_realean_feedback() -- Moved here after SysEx send
pending_preset = nil
end
-- Check for no tracks
if reaper.CountTracks(0) == 0 then
if last_fx_name ~= "None" then
last_fx_name = "None"
trigger_preset(default_preset)
end
reaper.defer(main)
return
end
-- Get focused FX
local retval, tracknum, _, fxnum = reaper.GetFocusedFX()
if retval == 1 and tracknum >= 0 then
local track = reaper.CSurf_TrackFromID(tracknum, false)
if track then
local fx_index = fxnum >= 0x1000000 and (fxnum - 0x1000000) or fxnum
if fx_index >= 0 and fx_index < reaper.TrackFX_GetCount(track) then
local _, fx_name = reaper.TrackFX_GetFXName(track, fx_index, "")
-- reaper.ShowConsoleMsg("Focused FX: " .. fx_name .. "\n") -- Debug output, remove -- to enable
if fx_name ~= last_fx_name then
last_fx_name = fx_name
local matched = false
for _, preset in ipairs(fx_presets) do
if string.find(fx_name, preset.match, 1, true) then
trigger_preset(preset)
matched = true
break
end
end
if not matched then
trigger_preset(default_preset)
end
end
-- Update parameters for matched plugin
for _, preset in ipairs(fx_presets) do
if string.find(fx_name, preset.match, 1, true) and preset.params then
local param_count = reaper.TrackFX_GetNumParams(track, fx_index)
for _, param in ipairs(preset.params) do
local _, formatted_value = reaper.TrackFX_GetFormattedParamValue(track, fx_index, param.index - 1) -- Offset -1
local control_id = param.id
if last_values[control_id] ~= formatted_value then
send_control_text(control_id, formatted_value or "N/A")
last_values[control_id] = formatted_value
end
end
break
end
end
else
if last_fx_name ~= "None" then
last_fx_name = "None"
trigger_preset(default_preset)
end
end
end
else
if last_fx_name ~= "None" then
last_fx_name = "None"
trigger_preset(default_preset)
end
end
reaper.defer(main)
end
main()