Skip to content

Instantly share code, notes, and snippets.

@jtackaberry
Last active December 20, 2016 15:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jtackaberry/da3e192c560fd2b02b93709360ffe5b5 to your computer and use it in GitHub Desktop.
Save jtackaberry/da3e192c560fd2b02b93709360ffe5b5 to your computer and use it in GitHub Desktop.
Reaper script to search for tracks by name
--[[
* 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)
--[[
* 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