-- Reaper scripted "Big Clock" window with extra functions -- - Normal time display as in the usual "Big Clock" -- - Time since last project marker -- - Time since last region start -- Author: Ludwig Frühschütz -- Source: https://www.eleton-audio.de -- Git: https://files.eleton-audio.de/gitea/Ludwig/Reaper-Scripts.git -- License: GPL v3.0 -- Requires: Reaper 5 or 6 -- Basic gui stuff by forum user "spk77": https://forum.cockos.com/showthread.php?t=161557 -- Some adjustable settings (startup settings that can be changed in GUI): local timeSinceProject = true -- EITHER this one is true OR one of the next two local timeSinceMarker = false local timeSinceRegion = false local timeUnitHMS = true -- EITHER this one is true OR one of the next one local timeUnitMeasures = false -- Nothing to adjust here anymore... local script_path local mouse local Menu local gui = {} -- contains some settings for the GUI local quit = false local sws_present = false -- Send a message to the console function msg(m) reaper.ShowConsoleMsg(tostring(m) .. "\n") end -- Round number function round(num, numDecimalPlaces) local mult = 10^(numDecimalPlaces or 0) return math.floor(num * mult + 0.5) / mult end -- Store state to project variables function storeExtState() if timeSinceProject then reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceProject', '1') else reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceProject', '0') end if timeSinceMarker then reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceMarker', '1') else reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceMarker', '0') end if timeSinceRegion then reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceRegion', '1') else reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceRegion', '0') end if timeUnitMeasures then reaper.SetProjExtState(0, 'bigclock_extra', 'timeUnit', 'measures') else reaper.SetProjExtState(0, 'bigclock_extra', 'timeUnit', 'hms') end reaper.SetProjExtState(0, 'bigclock_extra', 'dockState', gfx.dock(-1)) end --------------------------- -- functions for graphics... --------------------------- -- Returns current script's path function get_script_path() local info = debug.getinfo(1,'S'); local script_path = info.source:match[[^@?(.*[\/])[^\/]-$]] return script_path end -- gui init stuff, including rightclick-menu function gui_init() -- Get "script path" script_path = get_script_path() --msg(script_path) -- Modify "package.path" package.path = package.path .. ";" .. script_path .. "?.lua" --msg(package.path) -- Import files ("classes", functions etc.)-- require "class" -- import "base class" mouse = require "mouse" Menu = require "menu class" -- Create "right click" menu -- -- Create a "Menu" instance rc_menu = Menu("rc_menu") -- Add menu items to "rc_menu" -- ">" at the start spawns a submenu, | at the end creates a spacer rc_menu:add_item({label = "Time since project start", toggleable = true, selected = timeSinceProject}) rc_menu:add_item({label = "Time since previous marker", toggleable = true, selected = timeSinceMarker}) rc_menu:add_item({label = "Time since previous region|", toggleable = true, selected = timeSinceRegion}) rc_menu:add_item({label = "HH:MM:SS.xxx", toggleable = true, selected = timeUnitHMS}) rc_menu:add_item({label = "Measures.Beats.xx|", toggleable = true, selected = timeUnitMeasures}) rc_menu:add_item({label = "Quit"}) -- Let's add a command to all created items: rc_menu.items[1].command = function() if rc_menu.items[1].selected then timeSinceProject = true timeSinceMarker = false timeSinceRegion = false rc_menu.items[2].selected = false rc_menu.items[3].selected = false else --dont let user disable option but force selection of other option rc_menu.items[1].selected = true; end storeExtState() end rc_menu.items[2].command = function() if rc_menu.items[2].selected then timeSinceProject = false timeSinceMarker = true timeSinceRegion = false rc_menu.items[1].selected = false rc_menu.items[3].selected = false else --dont let user disable option but force selection of other option rc_menu.items[2].selected = true; end storeExtState() end rc_menu.items[3].command = function() if rc_menu.items[3].selected then timeSinceProject = false timeSinceMarker = false timeSinceRegion = true rc_menu.items[1].selected = false rc_menu.items[2].selected = false else --dont let user disable option but force selection of other option rc_menu.items[3].selected = true; end storeExtState() end rc_menu.items[4].command = function() if rc_menu.items[4].selected then timeUnitHMS = true timeUnitMeasures = false rc_menu.items[5].selected = false else --dont let user disable option but force selection of other option rc_menu.items[5].selected = true; end storeExtState() end rc_menu.items[5].command = function() if rc_menu.items[5].selected then timeUnitHMS = false timeUnitMeasures = true rc_menu.items[4].selected = false else --dont let user disable option but force selection of other option rc_menu.items[4].selected = true; end storeExtState() end rc_menu.items[6].command = function() quit = true end end -- Draw GUI -- function drawGui() local retval = 0 local val local timeRaw local timeDisplay local timeDisplayHours local timeDisplayMinutes local timeDisplaySeconds local timeMeasures local timeBeats local markerId local regionId local isRegion local markerRegionPos local regionEnd local markerRegionName local markerRegionNumber -- get time depending on play state if reaper.GetAllProjectPlayStates(0) == 0 then -- 0: stopped timeRaw = reaper.GetCursorPosition() else -- 1: play; 2: pause; 4: record (and their bitwise |) timeRaw = reaper.GetPlayPosition() end -- calculate time since previous marker or region markerId, regionId = reaper.GetLastMarkerAndCurRegion(0, timeRaw) if timeSinceMarker then retval, isRegion, markerRegionPos, regionEnd, markerRegionName, markerRegionNumber = reaper.EnumProjectMarkers(markerId) elseif timeSinceRegion then retval, isRegion, markerRegionPos, regionEnd, markerRegionName, markerRegionNumber = reaper.EnumProjectMarkers(regionId) end if retval > 0 then timeRaw = timeRaw - markerRegionPos end -- Construct display string if timeUnitMeasures then -- measures and beats: e.g. 07.3.99 timeBeats, timeMeasures, _, _, _ = reaper.TimeMap2_timeToBeats(0, timeRaw) timeDisplay = timeMeasures+1 .. '.' .. round(timeBeats+1, 2) else -- minutes and seconds: MM:SS.xxx (e.g. 27:08.321) timeDisplayHours = math.floor(timeRaw/3600) timeRaw = timeRaw%3600 timeDisplayMinutes = math.floor(timeRaw/60) timeDisplaySeconds = round(timeRaw%60, 3) if timeDisplayHours > 0 then timeDisplay = timeDisplayHours .. ":" if timeDisplayMinutes < 10 then timeDisplay = timeDisplay .. "0" end else timeDisplay = "" end timeDisplay = timeDisplay .. timeDisplayMinutes .. ":" if timeDisplaySeconds < 10 then timeDisplay = timeDisplay .. "0" end timeDisplay = timeDisplay .. timeDisplaySeconds end gfx.clear = 3355443 -- background is dark grey -- If window is docked, use different scaling if gfx.dock(-1) > 0 then -- landscape format if gfx.w > gfx.h then gfx.x = 20 gfx.y = ( gfx.h-select(2, gfx.measurestr(timeDisplay)) ) / 2 gui.settings.font_size = gfx.h / 2 gfx.setfont(1,"Arial", gui.settings.font_size) -- check if string fits into window, if not decrease fontsize a little, check again and loop while gfx.measurestr(timeDisplay) > (gfx.w - 2*gfx.x) do gui.settings.font_size = gui.settings.font_size / 1.1 gfx.setfont(1,"Arial", gui.settings.font_size) end gfx.printf(timeDisplay) -- portrait format else gfx.x = gfx.w/15 gfx.y = ( gfx.h-select(2, gfx.measurestr(timeDisplay)) ) / 2 gui.settings.font_size = gfx.w / 3 gfx.setfont(1,"Arial", gui.settings.font_size) gfx.printf(timeDisplay) end else -- window is not docked -- landscape format if gfx.w > gfx.h then gfx.x = gfx.w/20 gfx.y = ( gfx.h-select(2, gfx.measurestr(timeDisplay)) ) / 2 gui.settings.font_size = gfx.w / 6 gfx.setfont(1,"Arial", gui.settings.font_size) -- check if string fits into window, if not decrease fontsize a little, check again and loop while gfx.measurestr(timeDisplay) > (gfx.w - 2*gfx.x) do gui.settings.font_size = gui.settings.font_size / 1.1 gfx.setfont(1,"Arial", gui.settings.font_size) end gfx.printf(timeDisplay) -- portrait format else gfx.x = gfx.w/20 gfx.y = ( gfx.h-select(2, gfx.measurestr(timeDisplay)) ) / 2 gui.settings.font_size = gfx.w / 3 gfx.setfont(1,"Arial", gui.settings.font_size) gfx.printf(timeDisplay) end end end -- Called on script termination. Store stuff... function onExit() storeExtState() end -- Main init function function init() -- check for SWS extensions if reaper.NamedCommandLookup('_SWS_ABOUT') > 0 then sws_present = true end -- load settings from rpp local retval local val retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'timeSinceProject') if retval > 0 then if tonumber(val) > 0 then timeSinceProject = true else timeSinceProject = false end end retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'timeSinceMarker') if retval > 0 then if tonumber(val) > 0 then timeSinceMarker = true else timeSinceMarker = false end end retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'timeSinceRegion') if retval > 0 then if tonumber(val) > 0 then timeSinceRegion = true else timeSinceRegion = false end end retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'timeUnit') if retval > 0 then if val == 'measures' then timeUnitMeasures = true timeUnitHMS = false else timeUnitMeasures = false timeUnitHMS = true end end -- init stuff... gui_init() -- Add stuff to "gui" table gui.settings = {} -- Add "settings" table to "gui" table gui.settings.font_size = 50 -- font size gui.settings.docker_id = 0 -- try 0, 1, 257, 513, 1027 etc. -- Initialize gfx window -- gfx.init("Big Clock Extra", 350, 100, gui.settings.docker_id) gfx.setfont(1,"Arial", gui.settings.font_size) gfx.clear = 3355443 -- background is dark grey -- Restore docked state retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'dockState') if retval > 0 then gfx.dock(val) end end -- Main loop -- function mainloop() -- mouseclicks? local RMB_state = mouse.cap(mouse.RB) local LMB_state = mouse.cap(mouse.LB) local mx = gfx.mouse_x local my = gfx.mouse_y if not mouse.last_RMB_state and gfx.mouse_cap&2 == 2 then -- right click pressed down -> show "right click menu" at mouse cursor rc_menu:show(mx, my) if sws_present then reaper.Main_OnCommand( reaper.NamedCommandLookup('_BR_FOCUS_ARRANGE_WND'), 0 ) -- focus arranger end end if not mouse.last_LMB_state and gfx.mouse_cap&1 == 1 then -- left click pressed down if sws_present then reaper.Main_OnCommand( reaper.NamedCommandLookup('_BR_FOCUS_ARRANGE_WND'), 0 ) -- focus arranger end end mouse.last_RMB_state = RMB_state -- store current right mouse button state mouse.last_LMB_state = LMB_state -- store current left mouse button state drawGui() gfx.update() if gfx.getchar() >= 0 and not quit then reaper.defer(mainloop) end end -- START HERE... reaper.atexit(onExit) init() mainloop()