Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release Zuljeah v0.1beta1 #1392

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Various/hemisphera_Zuljeah.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- @description Zuljeah
-- @author Hemisphera
-- @version 0.1beta1
-- @changelog First release
-- @metapackage
-- @provides
-- [main] hemisphera_Zuljeah/Zuljeah_Monitor.lua
-- [main] hemisphera_Zuljeah/Zuljeah_NextSong.lua
-- [main] hemisphera_Zuljeah/Zuljeah_PreviousSong.lua
-- @about
-- # Zuljeah Setlist Player
--
-- Zuljeah is a setlist player for REAPER. You organize your various songs into project regions inside a single REAPER project. Zuljeah then allows you to rearrange the playing order, navigate them and trigger the playback of them.
--
-- ## How do I prepare my project?
--
-- 1. You start out by creating your project. Create one or more regions in your project where each region corresponds to a song on your setlist
-- 2. Create one or more "setlist" tracks. A setlist track is any arbitrary track that contains 'SETLIST' (literally, case sensititve) in the name. This track will be used to define the order of your songs. The first setlist track that is found will be used. Muted tracks will be excluded. This way you can create multiple setlists, mute them all and unmute only the one you want to use
-- 3. Create an "Empty Item" for each song that you want to have in your setlist and place it on your setlist track anywhere inside the project region.
-- 4. As content of this empty item insert a numeric value which specifies the sequence of this song on your setlist. It is important that this item does not contain anything but a numeric value. The setlist will be ordered by the numbers you assign here. These numbers can have gaps.
--
-- ## Alright. And now?
--
-- 1. Assign shortcuts or keys or MIDI notes to Zuljeah actions.
-- 2. Run the "Zuljeah - Monitor" action which starts Zuljeah. This must always be running to use Zuljeah. You can restart this anytime you make substantial changes to your setlist or songs to refresh Zuljeah.
-- 3. Use the "Next Song" and "Previous Song" to navigate between songs.
-- 4. Hit play to start playback of a song. Zuljeah will automatically stop at the end of the region and move to the next song in the list.

153 changes: 153 additions & 0 deletions Various/hemisphera_Zuljeah/Zuljeah_Monitor.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
-- @noindex

local function collectRegions()
local regions = {}
markerCount = reaper.CountProjectMarkers(0)
for i = 0,markerCount-1 do
ok, isRegion, posStart, posEnd, name, id = reaper.EnumProjectMarkers2(0, i)
if (isRegion) then
local r = {}
r["id"] = id
r["name"] = name
r["sequence"] = 0
r["start"] = posStart
r["end"] = posEnd
regions[i] = r
end
end
return regions
end

local function findSetlistTrack()
local trackCount = reaper.GetNumTracks()
for i = 0, trackCount - 1 do
local track = reaper.GetTrack(0, i)
local isMuted = reaper.GetMediaTrackInfo_Value(track, 'B_MUTE')
local ok, trackName = reaper.GetTrackName(track)
if (isMuted == 0) and (string.find(trackName, "SETLIST")) then
return track
end
end
return nil
end

local function buildSetlist(track)
if (track == nil) then
track = findSetlistTrack()
end
if (track == nil) then
return nil
end

local itemCount = reaper.GetTrackNumMediaItems(track)
local regions = collectRegions()
sq = {}
local sortedRegions = {}
for i = 0, itemCount - 1 do
local mi = reaper.GetTrackMediaItem(track, i)
local ok, take = reaper.GetSetMediaItemInfo_String(mi, "P_NOTES", "", false)
local pos = reaper.GetMediaItemInfo_Value(mi, 'D_POSITION')
local seq = tonumber(tostring(take))
for k,v in pairs(regions) do
if (v["start"] <= pos) and (v["end"] >= pos) then
v["mediaitem"] = mi
v["selected"] = reaper.IsMediaItemSelected(mi)
sortedRegions[seq] = v
Copy link
Member

@cfillion cfillion Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the long delay... Tried it out: it crashes on this line (seq is nil) if P_NOTES fails to parse as a number.

The 'Consolas' font is only pre-installed on Windows.

The user-facing actions should have the author prefix and be named as sentence case (eg. "hemisphera_Zuljeah - Next song.lua") to blend well with other actions.

Since all three Lua files share a lot of code, I recommend putting the common code in one file and loading that from the others instead of duplicating. A good place would be the hemisphera_Zuljeah.lua package file (in which case it needs to be marked for installation, but not listed in the action list).

Is this behavior normal when starting playback after moving the cursor a bit before the regions?
Take

Docker #0 may be anywhere (top/bottom/left/right/floating), so it would be best to persist in which docker the user wants to put the window.

end
end
end
return sortedRegions
end

local function getSelectedIndex(setlist)
totalItems = 0
for k,v in pairs(setlist) do
if (v["selected"]) then
selectedIndex = k
end
totalItems = totalItems + 1
end
return selectedIndex, totalItems
end

local function setSelectedIndex(setlist, newIndex)
selectedIndex, totalItems = getSelectedIndex(setlist)

if (newIndex < 1) then
newIndex = totalItems
end
if (newIndex > totalItems) then
newIndex = 1
end

if (selectedIndex ~= nil) then
reaper.SetMediaItemSelected(setlist[selectedIndex]["mediaitem"], false)
end
reaper.SetMediaItemSelected(setlist[newIndex]["mediaitem"], true)
reaper.SetEditCurPos(setlist[newIndex]["start"], true, false)
end

local function modifySelectedIndex(setlist, delta)
selectedIndex, totalItems = getSelectedIndex(setlist)
if (selectedIndex == nil) then
setSelectedIndex(setlist, 1)
else
setSelectedIndex(setlist, selectedIndex + delta)
end
end

--====--

gfx.init("Zuljeah - integrated", 770, 200, 1)
fontSize = 30
gfx.setfont(1, "Consolas", fontSize)

lastActiveRegion = 0

local function render()
gfx.y = -fontSize
local playPos = reaper.GetPlayPosition()

local setlistTrack = findSetlistTrack()
if (setlistTrack == nil) then
reaper.ShowConsoleMsg("No setlist track found.\n")
return
end

local activeRegion = 0
local setlist = buildSetlist(setlistTrack)
for k,v in pairs(setlist) do
gfx.x = 0
gfx.y = gfx.y + fontSize

local isCurrent = (playPos >= v["start"]) and (playPos <= v["end"])
if (isCurrent) then
activeRegion = k
end

line = ""
if (isCurrent) then
line = line .. ">>"
else
line = line .. " "
end
line = line .. " " .. v["name"]
if (v["selected"]) then
line = "o " .. line
else
line = " " .. line
end
gfx.drawstr(line .. "\n")
end

if (lastActiveRegion ~= activeRegion) then
if (activeRegion == 0) then
reaper.OnStopButton()
setSelectedIndex(setlist, lastActiveRegion + 1)
end
lastActiveRegion = activeRegion
end

reaper.defer(render)
end
render()
107 changes: 107 additions & 0 deletions Various/hemisphera_Zuljeah/Zuljeah_NextSong.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
-- @noindex

local function collectRegions()
local regions = {}
markerCount = reaper.CountProjectMarkers(0)
for i = 0,markerCount-1 do
ok, isRegion, posStart, posEnd, name, id = reaper.EnumProjectMarkers2(0, i)
if (isRegion) then
local r = {}
r["id"] = id
r["name"] = name
r["sequence"] = 0
r["start"] = posStart
r["end"] = posEnd
regions[i] = r
end
end
return regions
end

local function findSetlistTrack()
local trackCount = reaper.GetNumTracks()
for i = 0, trackCount - 1 do
local track = reaper.GetTrack(0, i)
local isMuted = reaper.GetMediaTrackInfo_Value(track, 'B_MUTE')
local ok, trackName = reaper.GetTrackName(track)
if (isMuted == 0) and (string.find(trackName, "SETLIST")) then
return track
end
end
return nil
end

local function buildSetlist(track)
if (track == nil) then
track = findSetlistTrack()
end
if (track == nil) then
return nil
end

local itemCount = reaper.GetTrackNumMediaItems(track)
local regions = collectRegions()
sq = {}
local sortedRegions = {}
for i = 0, itemCount - 1 do
local mi = reaper.GetTrackMediaItem(track, i)
local ok, take = reaper.GetSetMediaItemInfo_String(mi, "P_NOTES", "", false)
local pos = reaper.GetMediaItemInfo_Value(mi, 'D_POSITION')
local seq = tonumber(tostring(take))
for k,v in pairs(regions) do
if (v["start"] <= pos) and (v["end"] >= pos) then
v["mediaitem"] = mi
v["selected"] = reaper.IsMediaItemSelected(mi)
sortedRegions[seq] = v
end
end
end
return sortedRegions
end

local function getSelectedIndex(setlist)
totalItems = 0
for k,v in pairs(setlist) do
if (v["selected"]) then
selectedIndex = k
end
totalItems = totalItems + 1
end
return selectedIndex, totalItems
end

local function setSelectedIndex(setlist, newIndex)
selectedIndex, totalItems = getSelectedIndex(setlist)

if (newIndex < 1) then
newIndex = totalItems
end
if (newIndex > totalItems) then
newIndex = 1
end

if (selectedIndex ~= nil) then
reaper.SetMediaItemSelected(setlist[selectedIndex]["mediaitem"], false)
end
reaper.SetMediaItemSelected(setlist[newIndex]["mediaitem"], true)
reaper.SetEditCurPos(setlist[newIndex]["start"], true, false)
end

local function modifySelectedIndex(setlist, delta)
selectedIndex, totalItems = getSelectedIndex(setlist)
if (selectedIndex == nil) then
setSelectedIndex(setlist, 1)
else
setSelectedIndex(setlist, selectedIndex + delta)
end
end

--====--

setlistTrack = findSetlistTrack()
if (setlistTrack == nil) then
return
end
setlist = buildSetlist(setlistTrack)
modifySelectedIndex(setlist, 1)
reaper.UpdateArrange()
107 changes: 107 additions & 0 deletions Various/hemisphera_Zuljeah/Zuljeah_PreviousSong.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
-- @noindex

local function collectRegions()
local regions = {}
markerCount = reaper.CountProjectMarkers(0)
for i = 0,markerCount-1 do
ok, isRegion, posStart, posEnd, name, id = reaper.EnumProjectMarkers2(0, i)
if (isRegion) then
local r = {}
r["id"] = id
r["name"] = name
r["sequence"] = 0
r["start"] = posStart
r["end"] = posEnd
regions[i] = r
end
end
return regions
end

local function findSetlistTrack()
local trackCount = reaper.GetNumTracks()
for i = 0, trackCount - 1 do
local track = reaper.GetTrack(0, i)
local isMuted = reaper.GetMediaTrackInfo_Value(track, 'B_MUTE')
local ok, trackName = reaper.GetTrackName(track)
if (isMuted == 0) and (string.find(trackName, "SETLIST")) then
return track
end
end
return nil
end

local function buildSetlist(track)
if (track == nil) then
track = findSetlistTrack()
end
if (track == nil) then
return nil
end

local itemCount = reaper.GetTrackNumMediaItems(track)
local regions = collectRegions()
sq = {}
local sortedRegions = {}
for i = 0, itemCount - 1 do
local mi = reaper.GetTrackMediaItem(track, i)
local ok, take = reaper.GetSetMediaItemInfo_String(mi, "P_NOTES", "", false)
local pos = reaper.GetMediaItemInfo_Value(mi, 'D_POSITION')
local seq = tonumber(tostring(take))
for k,v in pairs(regions) do
if (v["start"] <= pos) and (v["end"] >= pos) then
v["mediaitem"] = mi
v["selected"] = reaper.IsMediaItemSelected(mi)
sortedRegions[seq] = v
end
end
end
return sortedRegions
end

local function getSelectedIndex(setlist)
totalItems = 0
for k,v in pairs(setlist) do
if (v["selected"]) then
selectedIndex = k
end
totalItems = totalItems + 1
end
return selectedIndex, totalItems
end

local function setSelectedIndex(setlist, newIndex)
selectedIndex, totalItems = getSelectedIndex(setlist)

if (newIndex < 1) then
newIndex = totalItems
end
if (newIndex > totalItems) then
newIndex = 1
end

if (selectedIndex ~= nil) then
reaper.SetMediaItemSelected(setlist[selectedIndex]["mediaitem"], false)
end
reaper.SetMediaItemSelected(setlist[newIndex]["mediaitem"], true)
reaper.SetEditCurPos(setlist[newIndex]["start"], true, false)
end

local function modifySelectedIndex(setlist, delta)
selectedIndex, totalItems = getSelectedIndex(setlist)
if (selectedIndex == nil) then
setSelectedIndex(setlist, 1)
else
setSelectedIndex(setlist, selectedIndex + delta)
end
end

--====--

setlistTrack = findSetlistTrack()
if (setlistTrack == nil) then
return
end
setlist = buildSetlist(setlistTrack)
modifySelectedIndex(setlist, -1)
reaper.UpdateArrange()