So, how would I go about writing “available plugins” into a textfile, from where the textfile can be read to auto-generate keybindings?
That was not anything i could extract from your question here, you were asking about keeping “button states” in which i provided you an answer for.
If you want witch-craft, you could even store complete functions inside your text files that are recalled back when loaded.
Here is a snippet from the pitch device for samples examplewhere i store a function inside an array that can be called whenever the specific programmed device contains all magical values in the title:
function set_pitch_device_notifiers()
--all available pitch-devices are getting a notifier
--We are generating dynamic functions that are given a notifier.
--This is pretty much how you can deal with dynamic functionality
--No need for _G['funcname_ref'] but it is practically the same.
--Moving and erasing or adding tracks is covered by this function.
local song = renoise.song()
--Let's clean up first to prevent the notifier array from getting too clunky
for x = 1, #song.tracks do
if song.tracks[x].devices_observable:has_notifier(set_pitch_device_notifiers) then
song.tracks[x].devices_observable:remove_notifier(set_pitch_device_notifiers)
end
for y = 1, #song.tracks[x].devices do
local func_ref = 't'..tostring(x)..'d'..tostring(y)
if device_notifiers[func_ref] ~= nil then
--We'll start by removing the old notifier
if song.tracks[x].devices[y].parameters[1].value_observable:has_notifier(device_notifiers[func_ref]) then
song.tracks[x].devices[y].parameters[1].value_observable:remove_notifier(device_notifiers[func_ref])
end
end
end
end
--then we simply swat the array
device_notifiers = {}
--populating the new notifier functions
for x = 1, #song.tracks do
--Cover DSP changes
if not song.tracks[x].devices_observable:has_notifier(set_pitch_device_notifiers) then
song.tracks[x].devices_observable:add_notifier(set_pitch_device_notifiers)
end
for y = 1, #song.tracks[x].devices do
local func_ref = 't'..tostring(x)..'d'..tostring(y)
if string.find(string.lower(song.tracks[x].devices[y].display_name), "pitch device") ~= nil then
--Putting functions inside an array this way rather than creating a global name-space variable
--has a few advantages:
--all functions are referenced from the same spot and the table can be simply erased ;)
device_notifiers[func_ref] = function()
change_from_tool = true
triggered_track = x
triggered_device = y
get_pitch_device_properties(song.tracks[x].devices[y])
set_pitchbend_slider (x,y, selected_instrument, sample_transpose_mode, pitch_range )
change_from_tool = false
end
if not song.tracks[x].devices[y].parameters[1].value_observable:has_notifier(device_notifiers[func_ref]) then
song.tracks[x].devices[y].parameters[1].value_observable:add_notifier(device_notifiers[func_ref])
end
end
end
end
set_pitch_device_notifier()
end
#song.tracksx could as well be #song.instruments, not sure which plugins you refer to (VSTI or VST fx).
But if you store those kind of functions inside an xml file (perhaps using the serialize function to prevent illegal characters inside the function from being gobbled up by the XML parser), you can store very plugin specific parameters and also store keybindings along since you are saving actual Lua code to the file. If you need all plugin parameters, even those that are not available through the parameter list accessible by Lua, you may want to save the chunk of the plugin itself using the active_preset_data :
oprint(renoise.song().tracks[1].devices[2].active_preset_data) → vst fx plugins
oprint(renoise.song().instruments[1].plugin_properties.plugin_device.active_preset_data) → vst instrument plugins
The only issue with the raw chunk-data is that you still cannot really change the contents for those specific parameters unless you know what you are actually doing and there is no guarantee all parameters are available inside the chunk as well.
Another thing is: it may cost a lot of time to grab and set the chunk data as it is CDATA encoded.