Go Back   Cockos Incorporated Forums > REAPER Forums > ReaScript, JSFX, REAPER Plug-in Extensions, Developer Forum

Reply
 
Thread Tools Display Modes
Old 03-27-2023, 02:35 AM   #1
IXix
Human being with feelings
 
Join Date: Jan 2007
Location: mcr:uk
Posts: 3,891
Default Script: Link and automate chain bypass to last touched FX (CPU saver)

EDIT: This script is largely redundant thanks to the new "Force auto-bypass on silence" options. Hurrah!

This script automatically bypasses all FX on a track when there are no items active on that track. It links the bypass of the last touched plugin to the bypass of all other plugins in the chain and adds automation, leaving a second of breathing space to avoid problems with plugins waking up slowly.

I call it "Link and automate chain bypass to last touched FX.lua". Catchy title no?

This is by far the most useful script I've ever made.

Code:
function pairsByKeys (t, f)
    local a = {}
    for n in pairs(t) do table.insert(a, n) end
        table.sort(a, f)
        local i = 0                -- iterator variable
        local iter = function ()   -- iterator function
        i = i + 1
        if a[i] == nil then return nil
            else return a[i], t[a[i]]
        end
    end
    return iter
end

function sortpoints(a,b)
    return a.pos < b.pos
end

pProj = 0

-- Toggling plugin bypass at item edges can sometimes cause glitches
-- so envelope points are offset by dOffset seconds for wriggle room
dOffset = 1

-- Get info on last touched FX. We don't care about param number.
retval, tracknumber, fxnumber, paramnumber = reaper.GetLastTouchedFX()
if retval == false then
    reaper.ReaScriptError("!No last touched FX.") -- Terminate
end

iTrack = tracknumber & 0x0000FFFF -- low word is track index -- 0 = master track, 1 = track 1, etc.
iItem = tracknumber & 0xFFFF0000  -- high word is item index -- 1 = first item on track, etc
if iItem > 0 then
    -- TakeFX
    -- Probably not much point handling these since they're only active in the scope of the item
    reaper.ReaScriptError("!This script does not work with Item FX.") -- Terminate

    iPlugin = fxnumber & 0x0000FFFF -- low word == FX index 
    iTake   = fxnumber & 0xFFFF0000 -- high word == take number
else
    -- TrackFX
    iPlugin = fxnumber & 0x00000FFF     -- low 24 bits of fxnumber == FX index
    bExtFX  = fxnumber & 0x0000F000 > 0 -- if next 8 bits are 01, FX is record FX
end

-- Get track of last touched FX
if iTrack == 0 then
  pTrack = reaper.GetMasterTrack()
else
  pTrack = reaper.GetTrack(0, iTrack - 1)
end

nItems = reaper.CountTrackMediaItems(pTrack)
if nItems == 0 then
    reaper.ReaScriptError("!No items on track.") -- Terminate
end

nPlugins = reaper.TrackFX_GetCount(pTrack)
if nPlugins < 1 then
    reaper.ReaScriptError("!Script requires one or more Track FX to work.") -- Terminate
end

-- Find index of bypass on last touched FX
nParams = reaper.TrackFX_GetNumParams(pTrack, iPlugin)
iLeader = nParams - 3;

for ip = 0, nPlugins-1, 1 do
    if ip ~= iPlugin then
        nParams = reaper.TrackFX_GetNumParams(pTrack, ip)
        iFollower = nParams - 3;
        
        sParam = string.format('param.%s.plink.active', iFollower)
        bResult = reaper.TrackFX_SetNamedConfigParm(pTrack, ip, sParam, "1")

        sParam = string.format('param.%s.plink.effect', iFollower)
        bResult = reaper.TrackFX_SetNamedConfigParm(pTrack, ip, sParam, tostring(iPlugin))

        sParam = string.format('param.%s.plink.param', iFollower)
        bResult = reaper.TrackFX_SetNamedConfigParm(pTrack, ip, sParam, tostring(iLeader))
    end
end

-- Calculate envelope points
tPoints = {}
nItems = reaper.CountTrackMediaItems(pTrack)
for idx = 0, nItems-1, 1 do
    pItem = reaper.GetMediaItem(pProj, idx)

    -- Ignore muted items
    bMuted = reaper.GetMediaItemInfo_Value(pItem, 'B_MUTE_ACTUAL')
    if bMuted == 1.0 then
        goto skip
    end

    dStart = reaper.GetMediaItemInfo_Value(pItem, 'D_POSITION')
    dEnd = dStart + reaper.GetMediaItemInfo_Value(pItem, 'D_LENGTH')

    -- Offset start but don't go below zero
    if dStart > dOffset then
        dStart = dStart - dOffset
    else
        dStart = 0
    end
    dEnd = dEnd + dOffset

    -- Multiple item edges may occur at the same point in time, so sum the edge occurences.
    -- Left edges add one, right edges subtract one.

    -- Left edge
    vs = tPoints[dStart]
    if vs == nil then
        tPoints[dStart] = 1
    else
        tPoints[dStart] = vs + 1
    end

    -- Right edge
    ve = tPoints[dEnd]
    if ve == nil then
        tPoints[dEnd] = -1
    else
        tPoints[dEnd] = ve - 1
    end

    ::skip::
end

tSortedPoints = {}
for k, v in pairsByKeys(tPoints) do
    table.insert(tSortedPoints, {pos = k, val = v})
end
table.sort(tSortedPoints, sortpoints)

tFinalPoints = {}
prevDepth = 0
depth = 0
action = 1 -- 1 = open, 0 = close
count = 0

for k, v in ipairs(tSortedPoints) do
    keep = false
    count = count + 1
    
    -- Insert point at start of envelope if necessary
    if count == 1 and v.pos > 0 then
        table.insert(tFinalPoints, {pos = 0, val = 0})
    end
    
    depth = depth + v.val
    if count == 1 or depth == action then
        keep = true
    end

    if keep == true then
        table.insert(tFinalPoints, {pos = v.pos, val = action})
        action = action ~ 1
    end
end

-- Populate envelope

reaper.Undo_BeginBlock2(pProj)

-- Get the envelope and clear existing points
pEnv = reaper.GetFXEnvelope(pTrack, iPlugin, iLeader, 0)
if reaper.ValidatePtr2(pProj, pEnv, "TrackEnvelope*") then --Clear envelope
    state = ""
    ret, state = reaper.GetEnvelopeStateChunk(pEnv, state, false)
    state = string.gsub(state, "PT[^>]+>", "PT 0 1 1>")
    reaper.SetEnvelopeStateChunk(pEnv, state, false)
else
    pEnv = reaper.GetFXEnvelope(pTrack, iPlugin, iLeader, 1) -- Create envelope
end

reaper.TrackList_AdjustWindows(false)

-- Now insert the envelope points
for k, v in pairs(tFinalPoints) do
    value =  v.val ~ 1 -- values are backwards for bypass so invert
    reaper.InsertEnvelopePointEx(pEnv, -1, v.pos, value, 1, 0, false, true)
end

reaper.Envelope_SortPoints(pEnv)
reaper.TrackList_AdjustWindows(false)
reaper.UpdateArrange()

reaper.Undo_EndBlock2(pProj, "Link and automate chain bypass to last touched FX", 1|2)

Last edited by IXix; 04-29-2023 at 03:47 AM.
IXix is offline   Reply With Quote
Old 03-28-2023, 02:52 PM   #2
Suzuki
Human being with feelings
 
Suzuki's Avatar
 
Join Date: Jul 2022
Location: Japan
Posts: 814
Default

Oh this looks useful! Thanks for sharing it

Could you add it to your ReaPack?
Suzuki is offline   Reply With Quote
Old 03-29-2023, 03:13 AM   #3
IXix
Human being with feelings
 
Join Date: Jan 2007
Location: mcr:uk
Posts: 3,891
Default

Quote:
Originally Posted by Suzuki View Post
Could you add it to your ReaPack?
Er, maybe. I don't do much scripting so my repo is all JSFX. I'll have to look at how to structure it to incorporate scripts, or maybe just add it to the ReaTeam stuff.
IXix is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 06:05 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.