Skip to content

Commit

Permalink
Release Song switcher (for live use) v1.7 (#1441)
Browse files Browse the repository at this point in the history
run action markers from take markers in the current song's primary track (REAPER v6+)
  • Loading branch information
cfillion authored Oct 13, 2024
1 parent 3fcb287 commit 599dd8d
Showing 1 changed file with 84 additions and 21 deletions.
105 changes: 84 additions & 21 deletions Various/cfillion_Song switcher.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
-- @description Song switcher (for live use)
-- @author cfillion
-- @version 1.6.1
-- @changelog
-- disable keyboard input when the filter text box or a context menu has focus
-- fix the toolbar buttons showing 1 frame early, before the filter text box is hidden
-- @version 1.7
-- @changelog run action markers from take markers in the current song's folder track (REAPER v6+)
-- @provides
-- [main] cfillion_Song switcher/cfillion_Song switcher - Send signal.lua > cfillion_Song switcher/cfillion_Song switcher - Switch to next song.lua
-- [main] cfillion_Song switcher/cfillion_Song switcher - Send signal.lua > cfillion_Song switcher/cfillion_Song switcher - Switch to previous song.lua
Expand Down Expand Up @@ -48,6 +46,8 @@
-- - cfillion_Song switcher - Switch to queued song.lua
-- - cfillion_Song switcher - Reset data.lua
--
-- Take markers starting with a `!` within the current song's folder track are treated as action markers.
--
-- A web browser interface is also installed as **song_switcher.html** for
-- remote use (this feature requires REAPER v5.30+ and ReaPack v1.1+).
-- Note that the timecode displayed in the web interface always starts at 00:00 for convenience.
Expand All @@ -59,7 +59,7 @@ if not reaper.ImGui_GetBuiltinPath then
return reaper.MB('ReaImGui is not installed or too old.', SCRIPT_NAME, 0)
end
package.path = reaper.ImGui_GetBuiltinPath() .. '/?.lua'
local ImGui = require 'imgui' '0.9'
local ImGui = require 'imgui' '0.9.3'

local EXT_SECTION = 'cfillion_song_switcher'
local EXT_SWITCH_MODE = 'onswitch'
Expand All @@ -78,6 +78,7 @@ local scrollTo, setDock
-- initialized in reset()
local currentIndex, nextIndex, invalid, filterPrompt
local signals = {}
local prevPlayPos

local fonts = {
small = ImGui.CreateFont('sans-serif', 13),
Expand Down Expand Up @@ -194,7 +195,7 @@ end
local function updateState()
local song = songs[currentIndex] or {name='', startTime=0, endTime=0}

local state = string.format("%d\t%d\t%s\t%f\t%f\t%s",
local state = ("%d\t%d\t%s\t%f\t%f\t%s"):format(
currentIndex, #songs, song.name, song.startTime, song.endTime,
tostring(invalid)
)
Expand Down Expand Up @@ -352,6 +353,7 @@ local function reset()
filterPrompt, invalid = false, false
currentIndex, nextIndex, scrollTo = 0, 0, 0
highlightTime = ImGui.GetTime(ctx)
prevPlayPos = nil

-- clear previous pending external commands
for signal, _ in pairs(signals) do
Expand Down Expand Up @@ -383,6 +385,68 @@ local function execRemoteActions()
end
end

local function getParentProject(track)
local search = reaper.GetMediaTrackInfo_Value(track, 'P_PROJECT')

if reaper.JS_Window_HandleFromAddress then
return reaper.JS_Window_HandleFromAddress(search)
end

for i = 0, math.huge do
local project = reaper.EnumProjects(i)
if not project then break end

local master = reaper.GetMasterTrack(project)
if search == reaper.GetMediaTrackInfo_Value(master, 'P_PROJECT') then
return project
end
end
end

local function execTakeMarkers()
if not reaper.GetNumTakeMarkers then return end -- REAPER v5

local song = songs[currentIndex]
local track = song and song.tracks[1]
local valid, numItems = pcall(reaper.GetTrackNumMediaItems, track)
if not valid then return end -- validates track across all tabs

local proj = getParentProject(track)
if reaper.GetPlayStateEx(proj) & 3 ~= 1 then return end -- not playing or paused

local playPos = reaper.GetPlayPositionEx(proj)
if playPos == prevPlayPos then return end

local minPos, maxPos = playPos, playPos
if prevPlayPos and playPos > prevPlayPos and (playPos - prevPlayPos) < 0.1 then
minPos = prevPlayPos
end
prevPlayPos = playPos

for ii = 0, numItems - 1 do
local item = reaper.GetTrackMediaItem(track, ii)
local mute = reaper.GetMediaItemInfo_Value(item, 'B_MUTE')
local pos = reaper.GetMediaItemInfo_Value(item, 'D_POSITION')
local len = reaper.GetMediaItemInfo_Value(item, 'D_LENGTH')
local take = reaper.GetActiveTake(item)

if take and mute == 0 and pos <= minPos and pos + len > maxPos then
local offs = reaper.GetMediaItemTakeInfo_Value(take, 'D_STARTOFFS')

for mi = 0, reaper.GetNumTakeMarkers(take) - 1 do
local time, name = reaper.GetTakeMarker(take, mi)
time = time + pos - offs
if time >= minPos and time <= maxPos and name:sub(1, 1) == '!' then
for action in name:sub(2):gmatch('%S+') do
local action = reaper.NamedCommandLookup(action)
if action ~= 0 then reaper.Main_OnCommandEx(action, 0, proj) end
end
end
end
end
end
end

function drawName(song)
local name = song and song.name or 'No song selected'

Expand Down Expand Up @@ -600,27 +664,25 @@ local function navButtons()
end

local function keyInput(input)
if not ImGui.IsWindowFocused(ctx,
ImGui.FocusedFlags_ChildWindows | ImGui.FocusedFlags_NoPopupHierarchy) or
ImGui.IsAnyItemActive(ctx) then return end
if ImGui.IsAnyItemActive(ctx) then return end

if ImGui.IsKeyPressed(ctx, ImGui.Key_UpArrow) or
ImGui.IsKeyPressed(ctx, ImGui.Key_LeftArrow) then
if ImGui.Shortcut(ctx, ImGui.Key_UpArrow, ImGui.InputFlags_Repeat) or
ImGui.Shortcut(ctx, ImGui.Key_LeftArrow, ImGui.InputFlags_Repeat) then
setNextIndex(nextIndex - 1)
elseif ImGui.IsKeyPressed(ctx, ImGui.Key_DownArrow) or
ImGui.IsKeyPressed(ctx, ImGui.Key_RightArrow) then
elseif ImGui.Shortcut(ctx, ImGui.Key_DownArrow, ImGui.InputFlags_Repeat) or
ImGui.Shortcut(ctx, ImGui.Key_RightArrow, ImGui.InputFlags_Repeat) then
setNextIndex(nextIndex + 1)
elseif ImGui.IsKeyPressed(ctx, ImGui.Key_PageUp, false) or
ImGui.IsKeyPressed(ctx, ImGui.Key_KeypadSubtract, false) then
elseif ImGui.Shortcut(ctx, ImGui.Key_PageUp) or
ImGui.Shortcut(ctx, ImGui.Key_KeypadSubtract) then
trySetCurrentIndex(currentIndex - 1)
elseif ImGui.IsKeyPressed(ctx, ImGui.Key_PageDown, false) or
ImGui.IsKeyPressed(ctx, ImGui.Key_KeypadAdd, false) then
elseif ImGui.Shortcut(ctx, ImGui.Key_PageDown) or
ImGui.Shortcut(ctx, ImGui.Key_KeypadAdd) then
trySetCurrentIndex(currentIndex + 1)
elseif ImGui.IsKeyPressed(ctx, ImGui.Key_Insert, false) or
ImGui.IsKeyPressed(ctx, ImGui.Key_NumLock, false) then
elseif ImGui.Shortcut(ctx, ImGui.Key_Insert) or
ImGui.Shortcut(ctx, ImGui.Key_NumLock) then
reset()
elseif ImGui.IsKeyPressed(ctx, ImGui.Key_Enter, false) or
ImGui.IsKeyPressed(ctx, ImGui.Key_KeypadEnter, false) then
elseif ImGui.Shortcut(ctx, ImGui.Key_Enter) or
ImGui.Shortcut(ctx, ImGui.Key_KeypadEnter) then
if nextIndex == currentIndex then
filterPrompt = true
else
Expand Down Expand Up @@ -703,6 +765,7 @@ end

local function loop()
execRemoteActions()
execTakeMarkers()

ImGui.PushFont(ctx, fonts.small)
ImGui.SetNextWindowSize(ctx, 500, 300, setDock and ImGui.Cond_Always or ImGui.Cond_FirstUseEver)
Expand Down

0 comments on commit 599dd8d

Please sign in to comment.