Collection of custom reaper scripts
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

393 lines
14 KiB

  1. -- Reaper scripted "Big Clock" window with extra functions
  2. -- - Normal time display as in the usual "Big Clock"
  3. -- - Time since last project marker
  4. -- - Time since last region start
  5. -- Author: Ludwig Frühschütz
  6. -- Source: https://www.eleton-audio.de
  7. -- Git: https://files.eleton-audio.de/gitea/Ludwig/Reaper-Scripts.git
  8. -- License: GPL v3.0
  9. -- Requires: Reaper 5 or 6
  10. -- Basic gui stuff by forum user "spk77": https://forum.cockos.com/showthread.php?t=161557
  11. -- Some adjustable settings (startup settings that can be changed in GUI):
  12. local timeSinceProject = true -- EITHER this one is true OR one of the next two
  13. local timeSinceMarker = false
  14. local timeSinceRegion = false
  15. local timeUnitHMS = true -- EITHER this one is true OR one of the next one
  16. local timeUnitMeasures = false
  17. -- Nothing to adjust here anymore...
  18. local script_path
  19. local mouse
  20. local Menu
  21. local gui = {} -- contains some settings for the GUI
  22. local quit = false
  23. local sws_present = false
  24. -- Send a message to the console
  25. function msg(m)
  26. reaper.ShowConsoleMsg(tostring(m) .. "\n")
  27. end
  28. -- Round number
  29. function round(num, numDecimalPlaces)
  30. local mult = 10^(numDecimalPlaces or 0)
  31. return math.floor(num * mult + 0.5) / mult
  32. end
  33. -- Store state to project variables
  34. function storeExtState()
  35. if timeSinceProject then
  36. reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceProject', '1')
  37. else
  38. reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceProject', '0')
  39. end
  40. if timeSinceMarker then
  41. reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceMarker', '1')
  42. else
  43. reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceMarker', '0')
  44. end
  45. if timeSinceRegion then
  46. reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceRegion', '1')
  47. else
  48. reaper.SetProjExtState(0, 'bigclock_extra', 'timeSinceRegion', '0')
  49. end
  50. if timeUnitMeasures then
  51. reaper.SetProjExtState(0, 'bigclock_extra', 'timeUnit', 'measures')
  52. else
  53. reaper.SetProjExtState(0, 'bigclock_extra', 'timeUnit', 'hms')
  54. end
  55. reaper.SetProjExtState(0, 'bigclock_extra', 'dockState', gfx.dock(-1))
  56. end
  57. ---------------------------
  58. -- functions for graphics...
  59. ---------------------------
  60. -- Returns current script's path
  61. function get_script_path()
  62. local info = debug.getinfo(1,'S');
  63. local script_path = info.source:match[[^@?(.*[\/])[^\/]-$]]
  64. return script_path
  65. end
  66. -- gui init stuff, including rightclick-menu
  67. function gui_init()
  68. -- Get "script path"
  69. script_path = get_script_path()
  70. --msg(script_path)
  71. -- Modify "package.path"
  72. package.path = package.path .. ";" .. script_path .. "?.lua"
  73. --msg(package.path)
  74. -- Import files ("classes", functions etc.)--
  75. require "class" -- import "base class"
  76. mouse = require "mouse"
  77. Menu = require "menu class"
  78. -- Create "right click" menu --
  79. -- Create a "Menu" instance
  80. rc_menu = Menu("rc_menu")
  81. -- Add menu items to "rc_menu"
  82. -- ">" at the start spawns a submenu, | at the end creates a spacer
  83. rc_menu:add_item({label = "Time since project start",
  84. toggleable = true,
  85. selected = timeSinceProject})
  86. rc_menu:add_item({label = "Time since previous marker",
  87. toggleable = true,
  88. selected = timeSinceMarker})
  89. rc_menu:add_item({label = "Time since previous region|",
  90. toggleable = true,
  91. selected = timeSinceRegion})
  92. rc_menu:add_item({label = "HH:MM:SS.xxx",
  93. toggleable = true,
  94. selected = timeUnitHMS})
  95. rc_menu:add_item({label = "Measures.Beats.xx|",
  96. toggleable = true,
  97. selected = timeUnitMeasures})
  98. rc_menu:add_item({label = "Quit"})
  99. -- Let's add a command to all created items:
  100. rc_menu.items[1].command = function()
  101. if rc_menu.items[1].selected then
  102. timeSinceProject = true
  103. timeSinceMarker = false
  104. timeSinceRegion = false
  105. rc_menu.items[2].selected = false
  106. rc_menu.items[3].selected = false
  107. else
  108. --dont let user disable option but force selection of other option
  109. rc_menu.items[1].selected = true;
  110. end
  111. storeExtState()
  112. end
  113. rc_menu.items[2].command = function()
  114. if rc_menu.items[2].selected then
  115. timeSinceProject = false
  116. timeSinceMarker = true
  117. timeSinceRegion = false
  118. rc_menu.items[1].selected = false
  119. rc_menu.items[3].selected = false
  120. else
  121. --dont let user disable option but force selection of other option
  122. rc_menu.items[2].selected = true;
  123. end
  124. storeExtState()
  125. end
  126. rc_menu.items[3].command = function()
  127. if rc_menu.items[3].selected then
  128. timeSinceProject = false
  129. timeSinceMarker = false
  130. timeSinceRegion = true
  131. rc_menu.items[1].selected = false
  132. rc_menu.items[2].selected = false
  133. else
  134. --dont let user disable option but force selection of other option
  135. rc_menu.items[3].selected = true;
  136. end
  137. storeExtState()
  138. end
  139. rc_menu.items[4].command = function()
  140. if rc_menu.items[4].selected then
  141. timeUnitHMS = true
  142. timeUnitMeasures = false
  143. rc_menu.items[5].selected = false
  144. else
  145. --dont let user disable option but force selection of other option
  146. rc_menu.items[5].selected = true;
  147. end
  148. storeExtState()
  149. end
  150. rc_menu.items[5].command = function()
  151. if rc_menu.items[5].selected then
  152. timeUnitHMS = false
  153. timeUnitMeasures = true
  154. rc_menu.items[4].selected = false
  155. else
  156. --dont let user disable option but force selection of other option
  157. rc_menu.items[4].selected = true;
  158. end
  159. storeExtState()
  160. end
  161. rc_menu.items[6].command = function() quit = true end
  162. end
  163. -- Draw GUI --
  164. function drawGui()
  165. local retval = 0
  166. local val
  167. local timeRaw
  168. local timeDisplay
  169. local timeDisplayHours
  170. local timeDisplayMinutes
  171. local timeDisplaySeconds
  172. local timeMeasures
  173. local timeBeats
  174. local markerId
  175. local regionId
  176. local isRegion
  177. local markerRegionPos
  178. local regionEnd
  179. local markerRegionName
  180. local markerRegionNumber
  181. -- get time depending on play state
  182. if reaper.GetAllProjectPlayStates(0) == 0 then -- 0: stopped
  183. timeRaw = reaper.GetCursorPosition()
  184. else -- 1: play; 2: pause; 4: record (and their bitwise |)
  185. timeRaw = reaper.GetPlayPosition()
  186. end
  187. -- calculate time since previous marker or region
  188. markerId, regionId = reaper.GetLastMarkerAndCurRegion(0, timeRaw)
  189. if timeSinceMarker then
  190. retval, isRegion, markerRegionPos, regionEnd, markerRegionName, markerRegionNumber = reaper.EnumProjectMarkers(markerId)
  191. elseif timeSinceRegion then
  192. retval, isRegion, markerRegionPos, regionEnd, markerRegionName, markerRegionNumber = reaper.EnumProjectMarkers(regionId)
  193. end
  194. if retval > 0 then timeRaw = timeRaw - markerRegionPos end
  195. -- Construct display string
  196. if timeUnitMeasures then
  197. -- measures and beats: e.g. 07.3.99
  198. timeBeats, timeMeasures, _, _, _ = reaper.TimeMap2_timeToBeats(0, timeRaw)
  199. timeDisplay = timeMeasures+1 .. '.' .. round(timeBeats+1, 2)
  200. else
  201. -- minutes and seconds: MM:SS.xxx (e.g. 27:08.321)
  202. timeDisplayHours = math.floor(timeRaw/3600)
  203. timeRaw = timeRaw%3600
  204. timeDisplayMinutes = math.floor(timeRaw/60)
  205. timeDisplaySeconds = round(timeRaw%60, 3)
  206. if timeDisplayHours > 0 then
  207. timeDisplay = timeDisplayHours .. ":"
  208. if timeDisplayMinutes < 10 then
  209. timeDisplay = timeDisplay .. "0"
  210. end
  211. else
  212. timeDisplay = ""
  213. end
  214. timeDisplay = timeDisplay .. timeDisplayMinutes .. ":"
  215. if timeDisplaySeconds < 10 then
  216. timeDisplay = timeDisplay .. "0"
  217. end
  218. timeDisplay = timeDisplay .. timeDisplaySeconds
  219. end
  220. gfx.clear = 3355443 -- background is dark grey
  221. -- If window is docked, use different scaling
  222. if gfx.dock(-1) > 0 then
  223. -- landscape format
  224. if gfx.w > gfx.h then
  225. gfx.x = 20
  226. gfx.y = ( gfx.h-select(2, gfx.measurestr(timeDisplay)) ) / 2
  227. gui.settings.font_size = gfx.h / 2
  228. gfx.setfont(1,"Arial", gui.settings.font_size)
  229. -- check if string fits into window, if not decrease fontsize a little, check again and loop
  230. while gfx.measurestr(timeDisplay) > (gfx.w - 2*gfx.x) do
  231. gui.settings.font_size = gui.settings.font_size / 1.1
  232. gfx.setfont(1,"Arial", gui.settings.font_size)
  233. end
  234. gfx.printf(timeDisplay)
  235. -- portrait format
  236. else
  237. gfx.x = gfx.w/15
  238. gfx.y = ( gfx.h-select(2, gfx.measurestr(timeDisplay)) ) / 2
  239. gui.settings.font_size = gfx.w / 3
  240. gfx.setfont(1,"Arial", gui.settings.font_size)
  241. gfx.printf(timeDisplay)
  242. end
  243. else -- window is not docked
  244. -- landscape format
  245. if gfx.w > gfx.h then
  246. gfx.x = gfx.w/20
  247. gfx.y = ( gfx.h-select(2, gfx.measurestr(timeDisplay)) ) / 2
  248. gui.settings.font_size = gfx.w / 6
  249. gfx.setfont(1,"Arial", gui.settings.font_size)
  250. -- check if string fits into window, if not decrease fontsize a little, check again and loop
  251. while gfx.measurestr(timeDisplay) > (gfx.w - 2*gfx.x) do
  252. gui.settings.font_size = gui.settings.font_size / 1.1
  253. gfx.setfont(1,"Arial", gui.settings.font_size)
  254. end
  255. gfx.printf(timeDisplay)
  256. -- portrait format
  257. else
  258. gfx.x = gfx.w/20
  259. gfx.y = ( gfx.h-select(2, gfx.measurestr(timeDisplay)) ) / 2
  260. gui.settings.font_size = gfx.w / 3
  261. gfx.setfont(1,"Arial", gui.settings.font_size)
  262. gfx.printf(timeDisplay)
  263. end
  264. end
  265. end
  266. -- Called on script termination. Store stuff...
  267. function onExit()
  268. storeExtState()
  269. end
  270. -- Main init function
  271. function init()
  272. -- check for SWS extensions
  273. if reaper.NamedCommandLookup('_SWS_ABOUT') > 0 then sws_present = true end
  274. -- load settings from rpp
  275. local retval
  276. local val
  277. retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'timeSinceProject')
  278. if retval > 0 then
  279. if tonumber(val) > 0 then
  280. timeSinceProject = true
  281. else
  282. timeSinceProject = false
  283. end
  284. end
  285. retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'timeSinceMarker')
  286. if retval > 0 then
  287. if tonumber(val) > 0 then
  288. timeSinceMarker = true
  289. else
  290. timeSinceMarker = false
  291. end
  292. end
  293. retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'timeSinceRegion')
  294. if retval > 0 then
  295. if tonumber(val) > 0 then
  296. timeSinceRegion = true
  297. else
  298. timeSinceRegion = false
  299. end
  300. end
  301. retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'timeUnit')
  302. if retval > 0 then
  303. if val == 'measures' then
  304. timeUnitMeasures = true
  305. timeUnitHMS = false
  306. else
  307. timeUnitMeasures = false
  308. timeUnitHMS = true
  309. end
  310. end
  311. -- init stuff...
  312. gui_init()
  313. -- Add stuff to "gui" table
  314. gui.settings = {} -- Add "settings" table to "gui" table
  315. gui.settings.font_size = 50 -- font size
  316. gui.settings.docker_id = 0 -- try 0, 1, 257, 513, 1027 etc.
  317. -- Initialize gfx window --
  318. gfx.init("Big Clock Extra", 350, 100, gui.settings.docker_id)
  319. gfx.setfont(1,"Arial", gui.settings.font_size)
  320. gfx.clear = 3355443 -- background is dark grey
  321. -- Restore docked state
  322. retval, val = reaper.GetProjExtState(0, 'bigclock_extra', 'dockState')
  323. if retval > 0 then
  324. gfx.dock(val)
  325. end
  326. end
  327. -- Main loop --
  328. function mainloop()
  329. -- mouseclicks?
  330. local RMB_state = mouse.cap(mouse.RB)
  331. local LMB_state = mouse.cap(mouse.LB)
  332. local mx = gfx.mouse_x
  333. local my = gfx.mouse_y
  334. if not mouse.last_RMB_state and gfx.mouse_cap&2 == 2 then
  335. -- right click pressed down -> show "right click menu" at mouse cursor
  336. rc_menu:show(mx, my)
  337. if sws_present then
  338. reaper.Main_OnCommand( reaper.NamedCommandLookup('_BR_FOCUS_ARRANGE_WND'), 0 ) -- focus arranger
  339. end
  340. end
  341. if not mouse.last_LMB_state and gfx.mouse_cap&1 == 1 then
  342. -- left click pressed down
  343. if sws_present then
  344. reaper.Main_OnCommand( reaper.NamedCommandLookup('_BR_FOCUS_ARRANGE_WND'), 0 ) -- focus arranger
  345. end
  346. end
  347. mouse.last_RMB_state = RMB_state -- store current right mouse button state
  348. mouse.last_LMB_state = LMB_state -- store current left mouse button state
  349. drawGui()
  350. gfx.update()
  351. if gfx.getchar() >= 0 and not quit then reaper.defer(mainloop) end
  352. end
  353. -- START HERE...
  354. reaper.atexit(onExit)
  355. init()
  356. mainloop()