@Raul, the earlier 1.3 version of this tool uses the process slicer. It is still available to download on the tools page
http://www.renoise.com/tools/set-track-to-active-columns
You can see the ProcessSlicer() calls from line 196 onwards in the following copy of main(). There is a require “Process_slicer” on line 1, as the ProcessSlicer() class is in another .lua file in the tool
main() from 1.3
Click to view contents
require "Process_slicer"
require "note_column_functions"
require "effects_column_functions"
------------------------------------------
--Set up Preferences file
------------------------------------------
--create preferences xml
options = renoise.Document.create {
include_fx = true,
leave_one = false
}
--assign options-object to .preferences so renoise knows to load and update it with the tool
renoise.tool().preferences = options
------------------------------------------
--------------------------------------------
--dialog global
local my_dialog
--------------------------------------------------------------
--GUI
--------------------------------------------------------------
function invoke_gui()
--1 dialog at a time
if (my_dialog and my_dialog.visible) then -- only allows one dialog instance
my_dialog:close()
return
end
--viewbuilder object
local vb = renoise.ViewBuilder()
---------------------------------------
--executes when re-order button pressed
----------------------------------------
local function re_order_button()
if vb.views["popup"].value == 1 then
process_single_track_in_pattern()
elseif vb.views["popup"].value == 2 then
-- process_selection_in_pattern()
main_selection_in_pattern()
elseif vb.views["popup"].value == 3 then
process_whole_track()
end
end
--dialog content
local dialog_content = vb:column {
margin = 8,
vb:vertical_aligner {
mode = "center",
spacing = 8,
vb:column {
style = "group",
margin = 7,
vb:text {
text = "Range:"
},
vb:popup {
value = 1,
id = "popup",
width = 120,
items = {
"All Tracks in Song",
"Track in Song",
},
},
vb:text {
text = "Include Effects Columns:"
},
vb:checkbox{
active = true,
value = options.include_fx.value, --`not` preserve columns as GUI label is left align
notifier = function()
--set bool for including fx scan or not
options.include_fx.value = not options.include_fx.value
--set `Min of 1 Effects Column:` checkbox to be active or not
vb.views["min"].active = options.include_fx.value
end
},
vb:text {
text = "Min of 1 Effects Column:"
},
vb:checkbox{
id = "min",
active = options.include_fx.value,
value = options.leave_one.value, --`not` preserve columns as GUI label is left align
notifier = function()options.leave_one.value = not options.leave_one.value
end
},
},--column
vb:horizontal_aligner {
mode = "center",
margin = 4,
vb:button {
text = "Apply",
id = "button",
height = 24,
width = 110,
notifier = function()
--process Single Track note columns only
if vb.views["popup"].value == 2 then
process_single_track()
if options.include_fx.value then
--all tracks fx
process_single_track_fx()
end
return --job done
end
--process All Tracks
if vb.views["popup"].value == 1 then
--all tracks notes
process()
--and all track fx
if options.include_fx.value then
--all tracks fx
process_fx()
end
end
end,
},
}
}
}
------------------------------------------------------
--key Handler
local function my_keyhandler_func(dialog, key)
--scrolling the range popup with the up and down keys
--[[ if key.name == "down" then
if vb.views["popup"].value < 3 then
vb.views["popup"].value = vb.views["popup"].value + 1
else
return
end
elseif key.name == "up" then
if vb.views["popup"].value > 1 then
vb.views["popup"].value = vb.views["popup"].value - 1
else
return
end
--executing re-order button with return key
elseif key.name == "return" then
re_order_button()--]]
--closing the dialog with escape
if not (key.modifiers == "" and key.name == "esc") then
return key
else
dialog:close()
end
end
my_dialog = renoise.app():show_custom_dialog("To Active Columns", dialog_content, my_keyhandler_func)
end
--------------------------------
--Keybindings
--------------------------------
renoise.tool():add_keybinding {
name = "Global:Tools:Set Track Widths To Active Columns",
invoke = function() invoke_gui()
end
}
renoise.tool():add_menu_entry {
name = "Main Menu:Tools:Ledger`s Scripts:Set Track Widths To Active Columns",
invoke = function() invoke_gui()
end
}
----------------------------------------------------------------------------------------
--Process slicers Current track or all tracks --called in Apply button notifier function
----------------------------------------------------------------------------------------
--Current track
function process_single_track()
--single_track = true
local slicer = ProcessSlicer(function() set_track_to_active_columns(true)end)
slicer:start()
end
-----------------------------------------------------------------------
--All tracks
function process()
-- single_track = false
local slicer = ProcessSlicer(function() set_track_to_active_columns(false)end)
slicer:start()
end
-----------------------------------------------------------------------
--EFFECTS
-----------------------------------------------------------------------
--Current track
function process_single_track_fx()
-- single_track = true
local slicer = ProcessSlicer(function() set_track_to_active_fx_columns(true)end)
slicer:start()
end
--All tracks
function process_fx()
-- single_track = false
local slicer = ProcessSlicer(function() set_track_to_active_fx_columns(false)end)
slicer:start()
end
Process Slicer class
Click to view contents
--[[============================================================================
process_slicer.lua
============================================================================]]--
--[[
ProcessSlicer allows you to slice up a function which takes a lot of
processing time into multiple calls via Lua coroutines.
* Example usage:
local slicer = ProcessSlicer(my_process_func, argument1, argumentX)
-- This starts calling 'my_process_func' in idle, passing all arguments
-- you've specified in the ProcessSlicer constructor.
slicer:start()
-- To abort a running sliced process, you can call "stop" at any time
-- from within your processing function of outside of it in the main thread.
-- As soon as your process function returns, the slicer is automatically
-- stopped.
slicer:stop()
-- To give processing time back to Renoise, call 'coroutine.yield()'
-- anywhere in your process function to temporarily yield back to Renoise:
function my_process_func()
for j=1,100 do
-- do something that needs a lot of time, and periodically call
-- "coroutine.yield()" to give processing time back to Renoise. Renoise
-- will switch back to this point of the function as soon as has done
-- "its" job:
coroutine.yield()
end
end
* Drawbacks:
By slicing your processing function, you will also slice any changes that are
done to the Renoise song into multiple undo actions (one action per slice/yield).
Modal dialogs will block the slicer, cause on_idle notifications are not fired then.
It will even block your own process GUI when trying to show it modal.
]]
class "ProcessSlicer"
function ProcessSlicer:__init(process_func, ...)
assert(type(process_func) == "function",
"expected a function as first argument")
self.__process_func = process_func
self.__process_func_args = arg
self.__process_thread = nil
end
--------------------------------------------------------------------------------
-- returns true when the current process currently is running
function ProcessSlicer:running()
return (self.__process_thread ~= nil)
end
--------------------------------------------------------------------------------
-- start a process
function ProcessSlicer:start()
assert(not self:running(), "process already running")
self.__process_thread = coroutine.create(self.__process_func)
renoise.tool().app_idle_observable:add_notifier(
ProcessSlicer.__on_idle, self)
end
--------------------------------------------------------------------------------
-- stop a running process
function ProcessSlicer:stop()
assert(self:running(), "process not running")
renoise.tool().app_idle_observable:remove_notifier(
ProcessSlicer.__on_idle, self)
self.__process_thread = nil
end
--------------------------------------------------------------------------------
-- function that gets called from Renoise to do idle stuff. switches back
-- into the processing function or detaches the thread
function ProcessSlicer:__on_idle()
assert(self.__process_thread ~= nil, "ProcessSlicer internal error: "..
"expected no idle call with no thread running")
-- continue or start the process while its still active
if (coroutine.status(self.__process_thread) == 'suspended') then
local succeeded, error_message = coroutine.resume(
self.__process_thread, unpack(self.__process_func_args))
if (not succeeded) then
-- stop the process on errors
self:stop()
-- and forward the error to the main thread
error(error_message)
end
-- stop when the process function completed
elseif (coroutine.status(self.__process_thread) == 'dead') then
self:stop()
end
end
So basically:
--include the ProcessSlicer class (in this case we store it in Process_slicer.lua)
require "Process_slicer"
--Pass your cpu_heavy_function() into a ProcessSlicer constructor and start the slicer with the :start() method on the object you just created.
function sliced_func()
--constructor
local slicer = ProcessSlicer(function() cpu_heavy_function() end)
--call method to start
slicer:start()
end
packaged up in a function here sliced_func() that can be called elsewhere, or by a menu/ shortcut.
Note: As these functions were placed at the end of the original main.lua, they were defined as global and not local to account for upvalues.