Last active
December 7, 2024 08:57
-
-
Save jtackaberry/da3e192c560fd2b02b93709360ffe5b5 to your computer and use it in GitHub Desktop.
Reaper script to search for tracks by name
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
* ReaScript Name: Select Track By Name - Next | |
* Description: Advance to the next track matched by the main "Select Track by Name" script. | |
* Author: Jason Tackaberry (tack) | |
* Licence: Public Domain | |
* Extensions: SWS/S&M 2.8.0 | |
* Version: 1.0 | |
--]] | |
function focusTrack(track, multiselect) | |
if multiselect == "1" or multiselect == true then | |
reaper.SetTrackSelected(track, true) | |
else | |
reaper.SetOnlyTrackSelected(track) | |
end | |
reaper.SetMixerScroll(track) | |
-- Track: Set first selected track as last touched track. | |
reaper.Main_OnCommandEx(40914, 0, 0) | |
-- Track: Vertical scroll selected tracks into view. | |
reaper.Main_OnCommandEx(40913, 0, 0) | |
end | |
function focusNext(matches, current) | |
local useNext = false | |
for match, score in string.gmatch(matches, "(%S+)/(%S+)") do | |
if useNext or not current then | |
focusTrack(reaper.GetTrack(0, match), false) | |
return match | |
elseif current == match then | |
useNext = true | |
end | |
end | |
return nil | |
end | |
function main() | |
matches = reaper.GetExtState("select_track_by_name", "matches") | |
current = reaper.GetExtState("select_track_by_name", "current") | |
if matches then | |
next = focusNext(matches, current) | |
if not next then | |
-- wrap back to beginning of results | |
next = focusNext(matches, nil) | |
end | |
reaper.SetExtState("select_track_by_name", "current", next or "", false) | |
end | |
end | |
reaper.defer(main) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
* ReaScript Name: Select Track By Name | |
* Description: Select track name by SublimeText-esque substring match. Use the companion "Next" script | |
to advance to next match. | |
* Author: Jason Tackaberry (tack) | |
* Licence: Public Domain | |
* Extensions: SWS/S&M 2.8.0 | |
* Version: 1.0 | |
--]] | |
INSTRUMENT_TRACKS_ONLY = true | |
function focusTrack(track, multiselect) | |
if multiselect == "1" or multiselect == true then | |
reaper.SetTrackSelected(track, true) | |
else | |
reaper.SetOnlyTrackSelected(track) | |
end | |
reaper.SetMixerScroll(track) | |
-- Track: Set first selected track as last touched track. | |
reaper.Main_OnCommandEx(40914, 0, 0) | |
-- Track: Vertical scroll selected tracks into view. | |
reaper.Main_OnCommandEx(40913, 0, 0) | |
end | |
function isInstrumentTrack(track) | |
-- Iterate over all FX and look for FX names prefixed with | |
-- "VSTi:". We can't use TrackFX_GetInstrument because it skips | |
-- offlined instruments. | |
for fxIdx = 0, reaper.TrackFX_GetCount(track) - 1 do | |
r, name = reaper.TrackFX_GetFXName(track, fxIdx, "") | |
if string.sub(name, 0, 5) == "VSTi:" then | |
return true | |
end | |
end | |
return false | |
end | |
function getScore(track, term) | |
local termPos = 1 | |
local lastMatchPos = 0 | |
local score = 0 | |
local match = false | |
local instrument = isInstrumentTrack(track) | |
local enabled = reaper.GetMediaTrackInfo_Value(track, "I_FXEN") > 0 | |
if term:sub(1, 1) == '/' then | |
if not enabled then | |
return 0 | |
end | |
term = term:sub(2, #term) | |
end | |
if not instrument and INSTRUMENT_TRACKS_ONLY then | |
return 0 | |
end | |
local termCh = term:sub(termPos, termPos):lower() | |
local name, flags = reaper.GetTrackState(track) | |
visible = reaper.GetMediaTrackInfo_Value(track, "B_SHOWINTCP") | |
if visible == 0 then | |
return 0 | |
end | |
for namePos = 1, #name do | |
local nameCh = name:sub(namePos, namePos):lower() | |
if nameCh == termCh then | |
if lastMatchPos > 0 then | |
local distance = namePos - lastMatchPos | |
score = score + (100 - distance) | |
end | |
if termPos == #term then | |
-- We have matched all characters in the term | |
match = true | |
break | |
else | |
lastMatchPos = namePos | |
termPos = termPos + 1 | |
termCh = term:sub(termPos, termPos):lower() | |
end | |
end | |
end | |
if not match then | |
return 0 | |
else | |
-- Add 0.1 if this is an instrument track. | |
if instrument then | |
score = score + 0.1 | |
end | |
-- Add another 0.1 if the track is enabled | |
if reaper.GetMediaTrackInfo_Value(track, "I_FXEN") > 0 then | |
score = score + 0.1 | |
end | |
-- reaper.ShowConsoleMsg(name .. " -- " .. score .. "\n") | |
return score | |
end | |
end | |
function main() | |
r, term = reaper.GetUserInputs("Select track", 1, "Track name", "") | |
if #term == 0 or not term then | |
return | |
end | |
local matches = nil | |
local bestScore = 0 | |
local bestTrack = nil | |
local bestTrackIdx = nil | |
for trackIdx = 0, reaper.CountTracks(0) - 1 do | |
track = reaper.GetTrack(0, trackIdx) | |
score = getScore(track, term) | |
if score > 0 then | |
if score > bestScore then | |
bestScore = score | |
bestTrack = track | |
bestTrackIdx = trackIdx | |
end | |
local result = trackIdx .. "/" .. score | |
if matches then | |
matches = matches .. " " .. result | |
else | |
matches = result | |
end | |
end | |
end | |
if bestTrack then | |
focusTrack(bestTrack, false) | |
end | |
reaper.SetExtState("select_track_by_name", "matches", matches or "", false) | |
reaper.SetExtState("select_track_by_name", "current", bestTrackIdx or "", false) | |
end | |
reaper.defer(main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment