I've also attached the full code at the bottom of this message in case that helps, but, to summarize,
I have a lua plugin (MIDI2LR by rsjaffe) that receives MIDI messages from an application through a LrSocket. Everything has been working, but I've tried to add copy/paste develop settings without success.
In the following code, the copy settings is:
function copySettings()
local photo = LrApplication.activeCatalog():getTargetPhoto()
settings = photo:getDevelopSettings()
end
and the paste settings is:
function pasteSettings()
applySettings(settings)
end
function applySettings(set) --still experimental
if LrApplicationView.getCurrentModuleName() ~= 'develop' then
LrApplicationView.switchToModule('develop')
end
for x,v in pairs(set) do
-- SERVER:send(string.format('%s %d\n', x, develop_lerp_to_midi(v)))
-- PARAM_OBSERVER[x] = v
LrDevelopController.setValue(x,v)
end
end
The functions do get called, but nothing happens to the target photo. I've also been unable to attach a debugger (tried ZeroBrane studio as described in Debugging Adobe Lightroom plugins with ZeroBrane Studio - ZeroBranebut can't get Lightroom to load mobdebug.lrmodule.
Can someone point out where I'm going wrong?
Thanks.
require 'strict.lua' -- catch some incorrect variable names
require 'Develop_Params.lua' -- global table of develop params we need to observe
local LrApplication = import 'LrApplication'
local LrApplicationView = import 'LrApplicationView'
local LrDevelopController = import 'LrDevelopController'
local LrFunctionContext = import 'LrFunctionContext'
local LrSelection = import 'LrSelection'
local LrShell = import 'LrShell'
local LrSocket = import 'LrSocket'
local LrTasks = import 'LrTasks'
local LrUndo = import 'LrUndo'
-- File-local consts
local RECEIVE_PORT = 58763
local SEND_PORT = 58764
local PICKUP_THRESHOLD = 4
-- File-local vars
local CopyUUID
local settings
local LAST_PARAM = ''
local PARAM_OBSERVER = {}
local PICKUP_ENABLED = true
local SERVER = {}
--File-local function declarations, advance declared to allow it to be in scope for all calls.
--When defining pre-declared function, DO NOT USE local KEYWORD again, as it will define yet another local function.
--These declaration are intended to get around some Lua gotcha's.
local applySettings
local copySettings
local develop_lerp_to_midi
local midi_lerp_to_develop
local pasteSettings
local processMessage
local sendChangedParams
local startServer
local updateParam
local ACTIONS = {
['DecrementLastDevelopParameter'] = function () LrDevelopController.decrement(LAST_PARAM) end,
['VirtualCopy'] = function () LrApplication.activeCatalog():createVirtualCopies() end,
['ToggleScreenTwo'] = LrApplicationView.toggleSecondaryDisplay,
['CopySettings'] = copySettings,
['PasteSettings'] = pasteSettings,
}
local TOOL_ALIASES = {
['Loupe'] = 'loupe',
['CropOverlay'] = 'crop',
['SpotRemoval'] = 'dust',
['RedEye'] = 'redeye',
['GraduatedFilter'] = 'gradient',
['RadialFilter'] = 'circularGradient',
['AdjustmentBrush'] = 'localized',
}
local SETTINGS = {
['Pickup'] = function(enabled) PICKUP_ENABLED = (enabled == 1) end,
}
function copySettings()
local photo = LrApplication.activeCatalog():getTargetPhoto()
settings = photo:getDevelopSettings()
end
function pasteSettings()
applySettings(settings)
end
function midi_lerp_to_develop(param, midi_value)
-- map midi range to develop parameter range
local min,max = LrDevelopController.getRange(param)
-- if(param == 'Temperature') then
-- min = 3000
-- max = 9000
-- end
local result = midi_value/127 * (max-min) + min
return result
end
function develop_lerp_to_midi(param)
-- map develop parameter range to midi range
local min, max = LrDevelopController.getRange(param)
-- if(param == 'Temperature') then
-- min = 3000
-- max = 9000
-- end
local result = (LrDevelopController.getValue(param)-min)/(max-min) * 127
return result
end
function updateParam(param, midi_value)
-- this function does a 'pickup' type of check
-- that is, it will ensure the develop parameter is close
-- to what the inputted command value is before updating it
if LrApplicationView.getCurrentModuleName() ~= 'develop' then
LrApplicationView.switchToModule('develop')
end
if((not PICKUP_ENABLED) or (math.abs(midi_value - develop_lerp_to_midi(param)) <= PICKUP_THRESHOLD)) then
PARAM_OBSERVER[param] = midi_lerp_to_develop(param, midi_value)
LrDevelopController.setValue(param, midi_lerp_to_develop(param, midi_value))
LAST_PARAM = param
end
end
function applySettings(set) --still experimental
if LrApplicationView.getCurrentModuleName() ~= 'develop' then
LrApplicationView.switchToModule('develop')
end
for x,v in pairs(set) do
-- SERVER:send(string.format('%s %d\n', x, develop_lerp_to_midi(v)))
-- PARAM_OBSERVER[x] = v
LrDevelopController.setValue(x,v)
end
end
-- message processor
function processMessage(message)
if type(message) == 'string' then
-- messages are in the format 'param value'
local _, _, param, value = string.find( message, '(%S+)%s(%d+)' )
if(ACTIONS[param] ~= nil) then -- perform a one time action
if(tonumber(value) == 127) then ACTIONS[param]() end
elseif(param:find('Reset') == 1) then -- perform a reset other than those explicitly coded in ACTIONS array
if(tonumber(value) == 127) then LrDevelopController.resetToDefault(param:sub(6)) end
elseif(param:find('SwToM') == 1) then -- perform a switch to module
if(tonumber(value) == 127) then LrApplicationView.switchToModule(param:sub(6)) end
elseif(param:find('ShoVw') == 1) then -- change application's view mode
if(tonumber(value) == 127) then LrApplicationView.showView(param:sub(6)) end
elseif(param:find('ShoScndVw') == 1) then -- change application's view mode
if(tonumber(value) == 127) then LrApplicationView.showSecondaryView(param:sub(10)) end
elseif(TOOL_ALIASES[param] ~= nil) then -- switch to desired tool
if(tonumber(value) == 127) then
if(LrDevelopController.getSelectedTool() == TOOL_ALIASES[param]) then -- toggle between the tool/loupe
LrDevelopController.selectTool('loupe')
else
LrDevelopController.selectTool(TOOL_ALIASES[param])
end
end
elseif(SETTINGS[param] ~= nil) then
SETTINGS[param](tonumber(value))
else -- otherwise update a develop parameter
updateParam(param, tonumber(value))
end
end
end
-- send changed parameters to MIDI2LR
function sendChangedParams( observer )
for _, param in ipairs(DEVELOP_PARAMS) do
if(observer[param] ~= LrDevelopController.getValue(param)) then
SERVER:send(string.format('%s %d\n', param, develop_lerp_to_midi(param)))
observer[param] = LrDevelopController.getValue(param)
LAST_PARAM = param
end
end
end
function startServer(context)
SERVER = LrSocket.bind {
functionContext = context,
plugin = _PLUGIN,
port = SEND_PORT,
mode = 'send',
onClosed = function( socket ) -- this callback never seems to get called...
-- MIDI2LR closed connection, allow for reconnection
-- socket:reconnect()
end,
onError = function( socket, err )
socket:reconnect()
end,
}
end
-- Main task
LrTasks.startAsyncTask( function()
LrFunctionContext.callWithContext( 'socket_remote', function( context )
LrDevelopController.revealAdjustedControls( true ) -- reveal affected parameter in panel track
-- add an observer for develop param changes
LrDevelopController.addAdjustmentChangeObserver( context, PARAM_OBSERVER, sendChangedParams )
local client = LrSocket.bind {
functionContext = context,
plugin = _PLUGIN,
port = RECEIVE_PORT,
mode = 'receive',
onMessage = function(socket, message)
processMessage(message)
end,
onClosed = function( socket )
-- MIDI2LR closed connection, allow for reconnection
socket:reconnect()
-- calling SERVER:reconnect causes LR to hang for some reason...
SERVER:close()
startServer(context)
end,
onError = function(socket, err)
if err == 'timeout' then -- reconnect if timed out
socket:reconnect()
end
end
}
startServer(context)
while true do
LrTasks.sleep( 1/2 )
end
client:close()
SERVER:close()
end )
end )
LrTasks.startAsyncTask( function()
if(WIN_ENV) then
LrShell.openFilesInApp({_PLUGIN.path..'/Info.lua'}, _PLUGIN.path..'/MIDI2LR.exe')
else
LrShell.openFilesInApp({_PLUGIN.path..'/Info.lua'}, _PLUGIN.path..'/MIDI2LR.app') -- On Mac it seems like the files argument has to include an existing file
end
end)