|
| 1 | +-- @description Copy - Paste the selected or all the FX from the focused FX chain to all the selected items or tracks |
| 2 | +-- @author amagalma |
| 3 | +-- @version 1.00 |
| 4 | +-- @link https://forum.cockos.com/showthread.php?t=290715 |
| 5 | +-- @screenshot https://i.ibb.co/SQRQDhw/Show.gif |
| 6 | +-- @donation https://www.paypal.me/amagalma |
| 7 | +-- @about |
| 8 | +-- Copies and pastes the selected (or all the FX in chain, if there is not a selection) from the focused FX chain to all the selected items or tracks. |
| 9 | +-- |
| 10 | +-- - Requires js_ReaScriptAPI and ReaImGui |
| 11 | + |
| 12 | + |
| 13 | +if not reaper.ImGui_GetBuiltinPath then |
| 14 | + return reaper.MB('ReaImGui is not installed or too old.', 'Error!', 0) |
| 15 | +end |
| 16 | +package.path = reaper.ImGui_GetBuiltinPath() .. '/?.lua' |
| 17 | +local ImGui = require 'imgui' '0.9.0.2' |
| 18 | + |
| 19 | + |
| 20 | + |
| 21 | +local function GetSelectedListItems( windowTitle ) |
| 22 | + local FX_win = reaper.JS_Window_Find( windowTitle, true ) |
| 23 | + if not FX_win then return end |
| 24 | + local sel_FX = {} |
| 25 | + local list = reaper.JS_Window_FindChildByID( FX_win, 1076 ) |
| 26 | + local sel_nr, sel_fx = reaper.JS_ListView_ListAllSelItems( list ) |
| 27 | + local fx_cnt = 0 |
| 28 | + if sel_nr ~= 0 then |
| 29 | + for i in sel_fx:gmatch("%d+") do |
| 30 | + sel_FX[fx_cnt+1] = tonumber(i) |
| 31 | + fx_cnt = fx_cnt + 1 |
| 32 | + end |
| 33 | + else -- if none selected, consider as all selected |
| 34 | + fx_cnt = reaper.JS_ListView_GetItemCount( list ) |
| 35 | + for i = 1, fx_cnt do |
| 36 | + sel_FX[i] = i-1 |
| 37 | + end |
| 38 | + end |
| 39 | + return sel_FX, fx_cnt |
| 40 | +end |
| 41 | + |
| 42 | + |
| 43 | +local function GetInfo() |
| 44 | + local retval, trackidx, itemidx, takeidx = reaper.GetTouchedOrFocusedFX( 1 ) |
| 45 | + if retval == false then return end |
| 46 | + local what, sel_FX, fx_cnt |
| 47 | + local isMaster = trackidx == -1 |
| 48 | + local track = isMaster and reaper.GetMasterTrack( 0 ) or reaper.GetTrack( 0, trackidx ) |
| 49 | + local GUIDS = {} |
| 50 | + if takeidx ~= -1 then -- Item |
| 51 | + local item = reaper.GetTrackMediaItem( track, itemidx ) |
| 52 | + local take = reaper.GetTake( item, takeidx ) |
| 53 | + local name = reaper.GetTakeName( take ) |
| 54 | + local search = string.format("FX: Item%s", name == "" and "" or (' "' .. name .. '"')) |
| 55 | + sel_FX, fx_cnt = GetSelectedListItems( search ) |
| 56 | + for i = 1, fx_cnt do |
| 57 | + GUIDS[reaper.TakeFX_GetFXGUID( take, sel_FX[i] )] = true |
| 58 | + end |
| 59 | + return {what = "Take", obj = take, fx = GUIDS, cnt = fx_cnt} |
| 60 | + else |
| 61 | + if isMaster then |
| 62 | + sel_FX, fx_cnt = GetSelectedListItems( "FX: Master Track" ) |
| 63 | + else -- normal Track |
| 64 | + local _, name = reaper.GetSetMediaTrackInfo_String( track, "P_NAME", "", false ) |
| 65 | + local search = string.format("FX: Track %i%s", trackidx+1, name == "" and "" or (' "' .. name .. '"')) |
| 66 | + sel_FX, fx_cnt = GetSelectedListItems( search ) |
| 67 | + for i = 1, fx_cnt do |
| 68 | + GUIDS[reaper.TrackFX_GetFXGUID( track, sel_FX[i] )] = true |
| 69 | + end |
| 70 | + return {what = "Track", obj = track, fx = GUIDS} |
| 71 | + end |
| 72 | + end |
| 73 | +end |
| 74 | + |
| 75 | + |
| 76 | +local data = GetInfo() |
| 77 | +if not data then |
| 78 | + return reaper.MB( "Could not get the focused FX chain.\nPlease, click it to focus and run again.", |
| 79 | + "Aborting..", 0 ) |
| 80 | +end |
| 81 | + |
| 82 | + |
| 83 | +local open, visible -- Needed up here for the NotValid() to be able to affect the ImGui window |
| 84 | + |
| 85 | + |
| 86 | +local function Undo( func, descchange, flags ) |
| 87 | + reaper.Undo_BeginBlock2( 0 ) |
| 88 | + reaper.PreventUIRefresh( 1 ) |
| 89 | + func() |
| 90 | + reaper.PreventUIRefresh( -1 ) |
| 91 | + reaper.Undo_EndBlock2( 0 , descchange, flags ) |
| 92 | +end |
| 93 | + |
| 94 | + |
| 95 | +local function NotValid(ptr) |
| 96 | + if not reaper.ValidatePtr2( 0, data.obj, ptr ) then |
| 97 | + open = false |
| 98 | + reaper.MB("The source FX are no longer valid!", "Quitting..", 0) |
| 99 | + return true |
| 100 | + end |
| 101 | +end |
| 102 | + |
| 103 | + |
| 104 | +local function FromTrackToItem() |
| 105 | + if NotValid("MediaTrack*") then return end |
| 106 | + for i = 0, reaper.CountSelectedMediaItems(0)-1 do |
| 107 | + local item = reaper.GetSelectedMediaItem( 0, i ) |
| 108 | + local take = reaper.GetActiveTake( item ) |
| 109 | + if not take then return end |
| 110 | + local fx_cnt = reaper.TakeFX_GetCount( take ) - 1 |
| 111 | + local c = 0 |
| 112 | + for j = 0, reaper.TrackFX_GetCount( data.obj ) - 1 do |
| 113 | + local GUID = reaper.TrackFX_GetFXGUID( data.obj, j ) |
| 114 | + if data.fx[GUID] then |
| 115 | + c = c + 1 |
| 116 | + reaper.TrackFX_CopyToTake( data.obj, j, take, fx_cnt + c, false ) |
| 117 | + end |
| 118 | + end |
| 119 | + end |
| 120 | +end |
| 121 | + |
| 122 | + |
| 123 | +local function FromTrackToTrack() |
| 124 | + if NotValid("MediaTrack*") then return end |
| 125 | + for i = 0, reaper.CountSelectedTracks2(0, true)-1 do |
| 126 | + local track = reaper.GetSelectedTrack2( 0, i, true ) |
| 127 | + local fx_cnt = reaper.TrackFX_GetCount( track ) - 1 |
| 128 | + local c = 0 |
| 129 | + for j = 0, reaper.TrackFX_GetCount( data.obj ) - 1 do |
| 130 | + local GUID = reaper.TrackFX_GetFXGUID( data.obj, j ) |
| 131 | + if data.fx[GUID] then |
| 132 | + c = c + 1 |
| 133 | + reaper.TrackFX_CopyToTrack( data.obj, j, track, fx_cnt + c, false ) |
| 134 | + end |
| 135 | + end |
| 136 | + end |
| 137 | +end |
| 138 | + |
| 139 | + |
| 140 | +local function FromItemToItem() |
| 141 | + if NotValid("MediaItem_Take*") then return end |
| 142 | + for i = 0, reaper.CountSelectedMediaItems(0)-1 do |
| 143 | + local item = reaper.GetSelectedMediaItem( 0, i ) |
| 144 | + local take = reaper.GetActiveTake( item ) |
| 145 | + if not take then return end |
| 146 | + local fx_cnt = reaper.TakeFX_GetCount( take ) - 1 |
| 147 | + local c = 0 |
| 148 | + for j = 0, reaper.TakeFX_GetCount( data.obj ) - 1 do |
| 149 | + local GUID = reaper.TakeFX_GetFXGUID( data.obj, j ) |
| 150 | + if data.fx[GUID] then |
| 151 | + c = c + 1 |
| 152 | + reaper.TakeFX_CopyToTake( data.obj, j, take, fx_cnt + c, false ) |
| 153 | + end |
| 154 | + end |
| 155 | + end |
| 156 | +end |
| 157 | + |
| 158 | + |
| 159 | +local function FromItemToTrack() |
| 160 | + if NotValid("MediaItem_Take*") then return end |
| 161 | + for i = 0, reaper.CountSelectedTracks2(0, true)-1 do |
| 162 | + local track = reaper.GetSelectedTrack2( 0, i, true ) |
| 163 | + local fx_cnt = reaper.TrackFX_GetCount( track ) - 1 |
| 164 | + local c = 0 |
| 165 | + for j = 0, reaper.TakeFX_GetCount( data.obj ) - 1 do |
| 166 | + local GUID = reaper.TakeFX_GetFXGUID( data.obj, j ) |
| 167 | + if data.fx[GUID] then |
| 168 | + c = c + 1 |
| 169 | + reaper.TakeFX_CopyToTrack( data.obj, j, track, fx_cnt + c, false ) |
| 170 | + end |
| 171 | + end |
| 172 | + end |
| 173 | +end |
| 174 | + |
| 175 | + |
| 176 | +----------------------------------------------------------------------------------------------------- |
| 177 | + |
| 178 | + |
| 179 | +local ctx = ImGui.CreateContext('Copy/Paste selected FX', ImGui.ConfigFlags_NoSavedSettings) |
| 180 | +local sans_serif = ImGui.CreateFont('sans-serif', 16) |
| 181 | +ImGui.Attach(ctx, sans_serif) |
| 182 | + |
| 183 | + |
| 184 | +local text = 'Choose the items or tracks where\nyou want to paste the selected FX' |
| 185 | +local text2 = "Paste to:" |
| 186 | +local timer, indent = 0, 0 |
| 187 | + |
| 188 | + |
| 189 | +local function myWindow() |
| 190 | + ImGui.Text( ctx, text ) |
| 191 | + ImGui.Spacing( ctx ) |
| 192 | + ImGui.Separator( ctx ) |
| 193 | + if timer ~= 2 then |
| 194 | + timer = timer + 1 |
| 195 | + indent = (ImGui.GetContentRegionAvail(ctx) - ImGui.CalcTextSize( ctx, text2 ))/2 |
| 196 | + end |
| 197 | + ImGui.Indent( ctx, indent ) |
| 198 | + ImGui.Text( ctx, text2 ) |
| 199 | + ImGui.Unindent( ctx, indent ) |
| 200 | + ImGui.Spacing( ctx ) |
| 201 | + if ImGui.Button(ctx, 'Selected Items') then |
| 202 | + if data.what == "Take" then |
| 203 | + Undo( FromItemToItem, "Copy TakeFX to Items", 4 ) |
| 204 | + else |
| 205 | + Undo( FromTrackToItem, "Copy TrackFX to Items", 4 ) |
| 206 | + end |
| 207 | + end |
| 208 | + ImGui.SameLine(ctx) |
| 209 | + if ImGui.Button(ctx, 'Selected Tracks') then |
| 210 | + if data.what == "Take" then |
| 211 | + Undo( FromItemToTrack, "Copy TakeFX to Tracks", 2 ) |
| 212 | + else |
| 213 | + Undo( FromTrackToTrack, "Copy TrackFX to Tracks", 2 ) |
| 214 | + end |
| 215 | + end |
| 216 | + ImGui.Spacing( ctx ) |
| 217 | +end |
| 218 | + |
| 219 | + |
| 220 | +local window_flags = ImGui.WindowFlags_NoCollapse|ImGui.WindowFlags_NoDocking| |
| 221 | + ImGui.WindowFlags_NoResize|ImGui.WindowFlags_NoSavedSettings|ImGui.WindowFlags_TopMost |
| 222 | +local x, y = reaper.GetMousePosition() |
| 223 | +ImGui.SetNextWindowPos( ctx, x, y, 0, 0.5, 0) |
| 224 | +local window_name = 'Paste copied ' .. data.what .. " FX" |
| 225 | + |
| 226 | + |
| 227 | +local function loop() |
| 228 | + ImGui.PushFont(ctx, sans_serif) |
| 229 | + visible, open = ImGui.Begin(ctx, window_name, true, window_flags) |
| 230 | + if visible then |
| 231 | + myWindow() |
| 232 | + ImGui.End(ctx) |
| 233 | + end |
| 234 | + ImGui.PopFont(ctx) |
| 235 | + |
| 236 | + if open then |
| 237 | + reaper.defer(loop) |
| 238 | + end |
| 239 | +end |
| 240 | + |
| 241 | +reaper.defer(loop) |
0 commit comments