Skip to content

Commit

Permalink
Release Snapshooter v1.2 (#1258)
Browse files Browse the repository at this point in the history
Allow snapshot renaming
Added control for custom tween duration
Window now recalls position
  • Loading branch information
tiagolr authored Oct 3, 2023
1 parent a69519a commit dcc67ce
Showing 1 changed file with 112 additions and 60 deletions.
172 changes: 112 additions & 60 deletions Envelopes/tilr_Snapshooter.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
-- @description Snapshooter
-- @author tilr
-- @version 1.1
-- @version 1.2
-- @changelog
-- Transitions are now written to time selection when available
-- Added transition controls to preserve edges, invert transition, curve points tension and shape
-- Allow snapshot renaming
-- Added control for custom tween duration
-- Window now recalls position
-- @provides
-- tilr_Snapshooter/rtk.lua
-- [main] tilr_Snapshooter/tilr_Snapshooter apply snap 1.lua
Expand Down Expand Up @@ -52,6 +53,8 @@ function logtable(table, indent)
end

globals = {
win_x = null,
win_y = null,
ui_checkbox_seltracks = false,
ui_checkbox_volume = true,
ui_checkbox_pan = true,
Expand All @@ -63,11 +66,16 @@ globals = {
invert_transition = false,
points_shape = 0,
points_tension = 0,
tween_custom_duration = 1000,
tween = 'none',
ease = 'linear'
}

-- init globals from project config
local exists, win_x = reaper.GetProjExtState(0, 'snapshooter', 'win_x')
if exists ~= 0 then globals.win_x = tonumber(win_x) end
local exists, win_y = reaper.GetProjExtState(0, 'snapshooter', 'win_y')
if exists ~= 0 then globals.win_y = tonumber(win_y) end
local exists, seltracks = reaper.GetProjExtState(0, 'snapshooter', 'ui_checkbox_seltracks')
if exists ~= 0 then globals.ui_checkbox_seltracks = seltracks == 'true' end
local exists, volume = reaper.GetProjExtState(0, 'snapshooter', 'ui_checkbox_volume')
Expand All @@ -88,6 +96,8 @@ local exists, ease = reaper.GetProjExtState(0, 'snapshooter', 'ui_ease_menu')
if exists ~= 0 then globals.ease = ease end
local exists, shape = reaper.GetProjExtState(0, 'snapshooter', 'points_shape')
if exists ~= 0 then globals.points_shape = tonumber(shape) end
local exists, duration = reaper.GetProjExtState(0, 'snapshooter', 'tween_custom_duration')
if exists ~= 0 then globals.tween_custom_duration = tonumber(duration) end
local exists, tension = reaper.GetProjExtState(0, 'snapshooter', 'points_tension')
if exists ~= 0 then globals.points_tension = tonumber(tension) end
local exists, invert = reaper.GetProjExtState(0, 'snapshooter', 'invert_transition')
Expand Down Expand Up @@ -170,33 +180,6 @@ function difference (s1, s2)
return diff
end

-- stringify/parse snapshot for mem storage
function stringify(snap)
local entries = {}
for i, line in ipairs(snap) do
for j, col in ipairs(line) do
line[j] = tostring(col)
end
table.insert(entries, table.concat(line, ','))
end
return table.concat(entries, '\n')
end

function parse(snapstr)
function splitline(line)
local split = {}
for word in string.gmatch(line, '([^,]+)') do
table.insert(split, word)
end
return split
end
local lines = {}
for line in string.gmatch(snapstr, '([^\n]+)') do
table.insert(lines, splitline(line))
end
return lines
end

function insertSendEnvelopePoint(track, key, cnt, value, type, shape, tension)
local count = 0
local cursor = reaper.GetCursorPosition()
Expand Down Expand Up @@ -238,7 +221,7 @@ function showTrackEnvelopes(track, envtype)
end
end

-- apply snapshot to params or write to timeline
-- apply snapshot to params or write diff points to cursor
function applydiff(diff, write, tween)
local points_shape = globals.invert_transition and globals.points_shape or 0
local points_tension = globals.invert_transition and globals.points_tension or 0
Expand Down Expand Up @@ -385,6 +368,7 @@ function applydiff(diff, write, tween)
end
end

-- Writes current state points to beggining of time selection
function clearEnvelopesAndAddStartingPoint(diff, starttime, endtime)
if globals.preserve_edges then
_starttime = starttime + 0.000000001
Expand Down Expand Up @@ -485,13 +469,6 @@ function clearEnvelopesAndAddStartingPoint(diff, starttime, endtime)
end
end

function savesnap(slot)
slot = slot or 1
snap = makesnap()
reaper.SetProjExtState(0, 'snapshooter', 'snap'..slot, stringify(snap))
reaper.SetProjExtState(0, 'snapshooter', 'snapdate'..slot, os.date('%c'))
end

function applysnap(slot, write)
reaper.Undo_BeginBlock()
reaper.PreventUIRefresh(1)
Expand Down Expand Up @@ -534,6 +511,7 @@ function applysnap(slot, write)
if (globals.tween == '1/2bar') then duration = duration / 2 end
if (globals.tween == '2bar') then duration = duration * 2 end
if (globals.tween == '4bar') then duration = duration * 4 end
if (globals.tween == 'custom') then duration = globals.tween_custom_duration / 1000 end
ease_fn = tween_fns.linear
if (globals.ease == 'easein') then ease_fn = tween_fns.ease_in end
if (globals.ease == 'easeout') then ease_fn = tween_fns.ease_out end
Expand All @@ -551,11 +529,56 @@ function applysnap(slot, write)
reaper.Undo_EndBlock('apply snapshot', 0)
end

function savesnap(slot)
slot = slot or 1
snap = makesnap()
isNewsnap = reaper.GetProjExtState(0, 'snapshooter', 'snap'..slot) == 0
if isNewsnap then
reaper.SetProjExtState(0, 'snapshooter', 'snapname'..slot, os.date('%c'))
else
_, snapname = reaper.GetProjExtState(0, 'snapshooter', 'snapname'..slot)
_, snapdate = reaper.GetProjExtState(0, 'snapshooter', 'snapdate'..slot)
if (snapname == snapdate or snapname == '') then
reaper.SetProjExtState(0, 'snapshooter', 'snapname'..slot, os.date('%c'))
end
end
reaper.SetProjExtState(0, 'snapshooter', 'snap'..slot, stringify(snap))
reaper.SetProjExtState(0, 'snapshooter', 'snapdate'..slot, os.date('%c'))
end

function clearsnap(slot)
reaper.SetProjExtState(0, 'snapshooter', 'snap'..slot, '')
reaper.SetProjExtState(0, 'snapshooter', 'snapname'..slot, '')
reaper.SetProjExtState(0, 'snapshooter', 'snapdate'..slot, '')
end

-- stringify/parse snapshot for mem storage
function stringify(snap)
local entries = {}
for i, line in ipairs(snap) do
for j, col in ipairs(line) do
line[j] = tostring(col)
end
table.insert(entries, table.concat(line, ','))
end
return table.concat(entries, '\n')
end

function parse(snapstr)
function splitline(line)
local split = {}
for word in string.gmatch(line, '([^,]+)') do
table.insert(split, word)
end
return split
end
local lines = {}
for line in string.gmatch(snapstr, '([^\n]+)') do
table.insert(lines, splitline(line))
end
return lines
end

--------------------------------------------------------------------------------
-- Tween
--------------------------------------------------------------------------------
Expand Down Expand Up @@ -627,34 +650,54 @@ function ui_refresh_buttons()
for i, row in ipairs(ui_snaprows) do
hassnap = reaper.GetProjExtState(0, 'snapshooter', 'snap'..i) ~= 0
row.children[1][1]:attr('color', hassnap and 0x99BD13 or 0x999999) -- savebtn
row.children[2][1]:attr('color', hassnap and 0xffffff or 0x777777) -- savetxt
row.children[2][1]:attr('textcolor', hassnap and 0xffffff or 0x777777) -- savetxt
row.children[3][1]:attr('color', hassnap and 0x999999 or 0x555555) -- applybtn
row.children[4][1]:attr('color', hassnap and 0xffffff or 0x777777) -- applytxt
row.children[5][1]:attr('color', hassnap and 0x999999 or 0x555555) -- writebtn
row.children[6][1]:attr('color', hassnap and 0xffffff or 0x777777) -- writetxt
row.children[7][1]:attr('color', hassnap and 0x999999 or 0x555555) -- delbtn
row.children[8][1]:attr('color', hassnap and 0xffffff or 0x777777) -- deltxt

status, datestr = reaper.GetProjExtState(0, 'snapshooter', 'snapdate'..i)
row.children[2][1]:attr('text', hassnap and ' '..datestr or ' Empty')
_, snapname = reaper.GetProjExtState(0, 'snapshooter', 'snapname'..i)
row.children[2][1]:attr('value', hassnap and snapname or 'Empty')
end
end

function ui_start()
-- package.path = reaper.GetResourcePath() .. '/Scripts/rtk/1/?.lua'
-- local rtk = require('rtk')
local sep = package.config:sub(1, 1)
local script_folder = debug.getinfo(1).source:match("@?(.*[\\|/])")
local rtk = dofile(script_folder .. 'tilr_Snapshooter' .. sep .. 'rtk.lua')
local window = rtk.Window{w=470, h=550}
local window = rtk.Window{ w=470, h=553, title='Snapshooter'}
window.onmove = function (self)
reaper.SetProjExtState(0, 'snapshooter', 'win_x', self.x)
reaper.SetProjExtState(0, 'snapshooter', 'win_y', self.y)
end
window:open{align='center'}
local box = window:add(rtk.VBox{margin=10})
box:add(rtk.Heading{'Snapshooter', bmargin=10})
if globals.win_x and globals.win_y then
window:attr('x', globals.win_x)
window:attr('y', globals.win_y)
end

local box = rtk.VBox{padding=10, tpadding=50}
local vp = rtk.Viewport{box, smoothscroll=true, scrollbar_size=3}
window:add(vp)

local toolbar = box:add(rtk.HBox({ tmargin=-40, bmargin=10 }))
toolbar:add(rtk.Heading{'Snapshooter'})
toolbar:add(rtk.Box.FLEXSPACE)

-- box:add(rtk.Heading{'Snapshooter', tmargin=-30, bmargin=10})

for i = 1, 12 do
local row = box:add(rtk.HBox{bmargin=5})
local button = row:add(rtk.Button{circular=true})
local text = row:add(rtk.Text{string.format("%02d",i)..' Empty', w=230, textalign='center', lmargin=10})
local inputtext = row:add(rtk.Entry{value='Empty', w=230, bmargin=-5, tmargin=-2, lmargin=10, lpadding=0, bg=0x333333, border_hover='#333333', border_focused='#333333'})
inputtext.onchange = function (self)
hassnap = reaper.GetProjExtState(0, 'snapshooter', 'snap'..i) ~= 0
if hassnap then
reaper.SetProjExtState(0, 'snapshooter', 'snapname'..i, self.value)
end
end
button.onclick = function ()
savesnap(i)
ui_refresh_buttons()
Expand Down Expand Up @@ -790,18 +833,14 @@ function ui_start()
{ 'None', id='none' },
{ '1/4 Bar', id='1/4bar' },
{ '1/2 Bar', id='1/2bar' },
{ '1 Bar', id='1bar'},
{ '2 Bar', id='2bar'},
{ '4 Bar', id='4bar'},
{ '1 Bar', id='1bar' },
{ '2 Bar', id='2bar' },
{ '4 Bar', id='4bar' },
{ 'Custom', id='custom' }
}
})
tween_menu:select(globals.tween)
tween_menu.onchange = function (self)
reaper.SetProjExtState(0, 'snapshooter', 'ui_tween_menu', self.selected)
globals.tween = self.selected
end

row:add(rtk.Text{'Ease', lmargin=20, rmargin=5, tmargin=5})
row:add(rtk.Text{'Ease', lmargin=10, rmargin=5, tmargin=5})
local ease_menu = row:add(rtk.OptionMenu{
menu = {
{ 'Linear', id='linear' },
Expand All @@ -816,6 +855,23 @@ function ui_start()
globals.ease = self.selected
end

local duration_text = row:add(rtk.Text{'Duration', lmargin=10, tmargin=5})
local duration_entry = row:add(rtk.Entry{value=globals.tween_custom_duration, lmargin=10, w=70})

duration_entry.onchange = function (self)
local value = tonumber(self.value) or 0
globals.tween_custom_duration = value
reaper.SetProjExtState(0, 'snapshooter', 'tween_custom_duration', value)
end

tween_menu.onchange = function (self)
reaper.SetProjExtState(0, 'snapshooter', 'ui_tween_menu', self.selected)
globals.tween = self.selected
duration_text:attr('visible', self.selected == 'custom')
duration_entry:attr('visible', self.selected == 'custom')
end
tween_menu:select(globals.tween)

ui_refresh_buttons()
end

Expand All @@ -827,7 +883,3 @@ return {
savesnap = savesnap,
applysnap = applysnap
}
-- interactive dev:
-- reaper.showConsoleMsg(¨chars¨) // show console output
-- reaper.NamedCommandLookup('_RSad7acd2e5dbd41ab15aa68ffdb01c4c5fc82c446',0)
-- reaper.Main_OnCommand(55863, 0)

0 comments on commit dcc67ce

Please sign in to comment.