diff --git a/FX/80icio_Floating FX bypass toggle.lua b/FX/80icio_Floating FX bypass toggle.lua index 2a877d3a9..81c03306a 100644 --- a/FX/80icio_Floating FX bypass toggle.lua +++ b/FX/80icio_Floating FX bypass toggle.lua @@ -1,6 +1,7 @@ -- @description Floating FX bypass toggle -- @author 80icio --- @version 1.0 +-- @version 1.1 +-- @changelog - Added toggle wet knob function -- @about -- This script let you toggle any visible floating FX bypass. -- track FX, take FX, input FX or fx chain focused FX @@ -8,6 +9,7 @@ -- -- Thanks to BirdBird & Tycho + r = reaper dofile(r.GetResourcePath() .. '/Scripts/ReaTeam Extensions/API/imgui.lua') ('0.8.1') @@ -29,12 +31,15 @@ TakeFX = str_to_bool(r.GetExtState(scriptname,'TakeFX') ) or false ChainFXwndw = str_to_bool(r.GetExtState(scriptname,'ChainFXwndw') ) or false +WetToggle = str_to_bool(r.GetExtState(scriptname,'WetToggle') ) or false + function exit() r.SetExtState(scriptname,'trkFX',tostring(trkFX),true) r.SetExtState(scriptname,'TakeFX',tostring(TakeFX),true) r.SetExtState(scriptname,'InpFX',tostring(InpFX),true) r.SetExtState(scriptname,'ChainFXwndw',tostring(ChainFXwndw),true) + r.SetExtState(scriptname,'WetToggle',tostring(WetToggle),true) end @@ -42,10 +47,10 @@ end ctx = r.ImGui_CreateContext(scriptname) function TFB_GUI() - r.ImGui_SetNextWindowSize( ctx, 220, 105) + r.ImGui_SetNextWindowSize( ctx, 300, 80) local visible, open = r.ImGui_Begin(ctx, scriptname, true, r.ImGui_WindowFlags_NoResize() | r.ImGui_WindowFlags_NoScrollbar()) if visible then - r.ImGui_BeginTable(ctx, 'options', 2, r.ImGui_TableFlags_SizingStretchProp() ) + r.ImGui_BeginTable(ctx, 'options', 3, r.ImGui_TableFlags_SizingStretchProp() ) r.ImGui_TableNextColumn(ctx) _, trkFX = r.ImGui_Checkbox( ctx, 'Track FX', trkFX ) @@ -55,10 +60,16 @@ function TFB_GUI() _, TakeFX = r.ImGui_Checkbox( ctx, 'Take FX ', TakeFX ) _, ChainFXwndw = r.ImGui_Checkbox( ctx, 'Chain FX', ChainFXwndw ) - r.ImGui_EndTable(ctx) + r.ImGui_TableNextColumn(ctx) + + _, WetToggle = r.ImGui_Checkbox( ctx, 'Toggle Wet', WetToggle ) toggleBttn = r.ImGui_Button(ctx,'TOGGLE', -1) + r.ImGui_EndTable(ctx) + + + r.ImGui_End(ctx) end @@ -67,7 +78,7 @@ function TFB_GUI() end ---------------------------------------END GUI-------------------------- if toggleBttn and (trkFX or InpFX or TakeFX or ChainFXwndw) then -reaper.ClearConsole() + r.Undo_BeginBlock2(0) for i = -1, r.CountTracks(0) - 1 do @@ -84,13 +95,29 @@ reaper.ClearConsole() if trkFX then if r.TrackFX_GetFloatingWindow( track, index ) then - r.TrackFX_SetEnabled(track, index, not r.TrackFX_GetEnabled(track, index)) + if WetToggle then + local wetparam = r.TrackFX_GetParamFromIdent( track, index, ":wet" ) + local wetparam_value = r.TrackFX_GetParam(track, index, wetparam) + local wetparam_value = math.floor(wetparam_value + 0.5) + r.TrackFX_SetParam(track, index, wetparam, math.abs(wetparam_value -1) ) + else + r.TrackFX_SetEnabled(track, index, not r.TrackFX_GetEnabled(track, index)) + end end end if ChainFXwndw then if index == chainfltngfx then - r.TrackFX_SetEnabled(track, chainfltngfx, not r.TrackFX_GetEnabled(track, chainfltngfx)) + + if WetToggle then + local wetparam = r.TrackFX_GetParamFromIdent( track, chainfltngfx, ":wet" ) + local wetparam_value = r.TrackFX_GetParam(track, chainfltngfx, wetparam) + local wetparam_value = math.floor(wetparam_value + 0.5) + r.TrackFX_SetParam(track, chainfltngfx, wetparam, math.abs(wetparam_value -1) ) + else + r.TrackFX_SetEnabled(track, chainfltngfx, not r.TrackFX_GetEnabled(track, chainfltngfx)) + end + end end --end @@ -102,13 +129,27 @@ reaper.ClearConsole() if InpFX then if r.TrackFX_GetFloatingWindow(track, index + 0x1000000) then - r.TrackFX_SetEnabled(track, index + 0x1000000, not r.TrackFX_GetEnabled(track, index + 0x1000000 )) + if WetToggle then + local wetparam = r.TrackFX_GetParamFromIdent( track, index + 0x1000000, ":wet" ) + local wetparam_value = r.TrackFX_GetParam(track, index + 0x1000000, wetparam) + local wetparam_value = math.floor(wetparam_value + 0.5) + r.TrackFX_SetParam(track, index + 0x1000000, wetparam, math.abs(wetparam_value -1) ) + else + r.TrackFX_SetEnabled(track, index + 0x1000000, not r.TrackFX_GetEnabled(track, index + 0x1000000 )) + end end end if ChainFXwndw then if index == INchainfltngfx then - r.TrackFX_SetEnabled(track, INchainfltngfx + 0x1000000, not r.TrackFX_GetEnabled(track, INchainfltngfx + 0x1000000)) + if WetToggle then + local wetparam = r.TrackFX_GetParamFromIdent( track, INchainfltngfx + 0x1000000, ":wet" ) + local wetparam_value = r.TrackFX_GetParam(track, INchainfltngfx + 0x1000000, wetparam) + local wetparam_value = math.floor(wetparam_value + 0.5) + r.TrackFX_SetParam(track, INchainfltngfx + 0x1000000, wetparam, math.abs(wetparam_value -1) ) + else + r.TrackFX_SetEnabled(track, INchainfltngfx + 0x1000000, not r.TrackFX_GetEnabled(track, INchainfltngfx + 0x1000000)) + end end end end @@ -122,12 +163,26 @@ reaper.ClearConsole() for j = 0, r.TakeFX_GetCount(take) - 1 do if r.TakeFX_GetFloatingWindow(take, j) then - r.TakeFX_SetEnabled(take, j, not r.TakeFX_GetEnabled(take, j)) + if WetToggle then + local wetparam = r.TakeFX_GetParamFromIdent( take, j, ":wet" ) + local wetparam_value = r.TakeFX_GetParam(take, j, wetparam) + local wetparam_value = math.floor(wetparam_value + 0.5) + r.TakeFX_SetParam(take, j, wetparam, math.abs(wetparam_value -1) ) + else + r.TakeFX_SetEnabled(take, j, not r.TakeFX_GetEnabled(take, j)) + end end if ChainFXwndw then if j == TKchainfltngfx then - r.TakeFX_SetEnabled(take, TKchainfltngfx, not r.TakeFX_GetEnabled(take, TKchainfltngfx)) + if WetToggle then + local wetparam = r.TakeFX_GetParamFromIdent( take, TKchainfltngfx, ":wet" ) + local wetparam_value = r.TakeFX_GetParam(take, TKchainfltngfx, wetparam) + local wetparam_value = math.floor(wetparam_value + 0.5) + r.TakeFX_SetParam(take, TKchainfltngfx, wetparam, math.abs(wetparam_value -1) ) + else + r.TakeFX_SetEnabled(take, TKchainfltngfx, not r.TakeFX_GetEnabled(take, TKchainfltngfx)) + end end end diff --git a/Items Editing/80icio_Perfect Timing! - Audio Quantizer.lua b/Items Editing/80icio_Perfect Timing! - Audio Quantizer.lua index ed18f1431..15d2a057a 100644 --- a/Items Editing/80icio_Perfect Timing! - Audio Quantizer.lua +++ b/Items Editing/80icio_Perfect Timing! - Audio Quantizer.lua @@ -1,9 +1,9 @@ -- @description Perfect Timing! - Audio Quantizer -- @author 80icio --- @version 0.26 +-- @version 0.27 -- @changelog --- - Unlocked 1 bar, 1/2 bar Grid and relative triplets Divisions --- - Changed layout for Visualizer zoom bounds reference on tracks on Main window +-- - Fixed graphic issue between mouse position indication vertical line and arrange window trigger lines +-- - Fixed arrange window trigger lines behaviour when tracks disappear in folder -- @link Forum thread https://forum.cockos.com/showthread.php?t=288964 -- @about -- # PERFECT TIMING! @@ -1789,6 +1789,8 @@ end function MW_drawlines() + local mouse_vert_line_on_arrange_window = false + if check_itm == true and open and quantize_state == false then local itemsn = r.CountSelectedMediaItems(0) @@ -1808,24 +1810,42 @@ function MW_drawlines() local sel_tr = sel_tracks_table[trck] local media_move_check = r.GetMediaItemInfo_Value(item, "D_LENGTH" ) - r.GetMediaItemInfo_Value(item, "D_POSITION") + local track_H = r.GetMediaTrackInfo_Value(sel_tr, "I_TCPH") if media_move_check == first_sel_item_length - first_sel_item_start and sel_tr == r.GetMediaItemTrack(item) then + local MWzoom = r.GetHZoomLevel() local _, scrollpos, pageSize, min, max, trackPos = r.JS_Window_GetScrollInfo( trackview, "h" ) local _, scrollpos_v = r.JS_Window_GetScrollInfo( trackview, "v" ) local _, width = r.JS_Window_GetClientSize( trackview ) local track_y = r.GetMediaTrackInfo_Value(sel_tr, "I_TCPY") local track_H = r.GetMediaTrackInfo_Value(sel_tr, "I_TCPH") + --local track_FL_n = r.GetMediaTrackInfo_Value(sel_tr, "I_NUMFIXEDLANES") - local media_H = r.GetMediaItemInfo_Value(item, "I_LASTH" ) + if r.GetToggleCommandState(43194) then + local window, _, _ = r.BR_GetMouseCursorContext() + if window == "arrange" then + mouse_vert_line_on_arrange_window = true + else + mouse_vert_line_on_arrange_window = false + end + end + + local media_H = 0 + + if track_H ~= 0 then + media_H = r.GetMediaItemInfo_Value(item, "I_LASTH" ) + end + local media_Y = r.GetMediaItemInfo_Value(item, "I_LASTY" ) + --local media_FL_y = r.GetMediaItemInfo_Value(item, "F_FREEMODE_Y") -- local media_FL_h = r.GetMediaItemInfo_Value(item, "F_FREEMODE_H") movescreen[trck] =h_zoom_center + zoom_bounds_L + zoom_bounds_Total + r.CSurf_TrackToID( sel_tr, false ) + MWzoom + scrollpos + track_H + media_H + scrollpos_v + itemsn + track_y*2 - if MW_lines_ON and (Gtolerance_slider or r.GetPlayState() ~= 0 or visualizer_rv or Offset or Detect_rv2 or Detect_rv or Visualizer_mode_rv or + if MW_lines_ON and (Gtolerance_slider or r.GetPlayState() ~= 0 or mouse_vert_line_on_arrange_window or visualizer_rv or Offset or Detect_rv2 or Detect_rv or Visualizer_mode_rv or color_button or Sensitivity_slider or Change_grid or movescreen[trck] ~= movescreen_prev[trck]) then --or set_grid_from_script or get_grid_from_proj if visualizer then diff --git a/Items Editing/amagalma_Smart Crossfade.lua b/Items Editing/amagalma_Smart Crossfade.lua index 8639b6ac9..3c82eba14 100644 --- a/Items Editing/amagalma_Smart Crossfade.lua +++ b/Items Editing/amagalma_Smart Crossfade.lua @@ -1,8 +1,8 @@ -- @description Smart Crossfade -- @author amagalma --- @version 1.70 +-- @version 1.73 -- @changelog --- - support for fixed item lanes +-- - set default for User Setting for maximum gap allowance between two selected items that will crossfade (fill-in gaps) to the default split crossfade length -- @link https://forum.cockos.com/showthread.php?t=195490 -- @donation https://www.paypal.me/amagalma -- @about @@ -32,6 +32,10 @@ local remove_timesel = 1 -- Set to 1 if you want to remove the time selection (e -- Razor Edit area settings -- local remove_RE_area = 1 -- Set to 1 if you want to remove the Razor Edit area (else keep it) -- -- +-- Maximum gap between two selected items (not in time selection or RE area) that can crossfade -- +-- (set it to -1, if you want it to be equal to the default split crossfade length) -- +local maximum_gap = -1 -- in ms. -- + -- --------------------------------------------------------------------------------------------------- @@ -54,6 +58,8 @@ if xfadeshape < 0 or xfadeshape > 7 then xfadeshape = tonumber(({reaper.get_config_var_string( "defxfadeshape" )})[2]) or 7 end +maximum_gap = maximum_gap > -1 and maximum_gap/1000 or xfadetime + -- Razor Edit -------------------------------- @@ -65,6 +71,7 @@ if track_cnt == 0 then return reaper.defer(function() end) end local tracks_with_RE, tr = {}, 0 local began_block = false +local reenableTrim = false for t = 0, track_cnt - 1 do local track = reaper.GetTrack(0, t) @@ -101,6 +108,10 @@ for t = 0, track_cnt - 1 do reaper.Undo_BeginBlock() reaper.PreventUIRefresh( 1 ) began_block = true + if reaper.GetToggleCommandState( 41117 ) == 1 then + reaper.Main_OnCommand(41117, 0) -- Trim content behind media items when editing + reenableTrim = true + end end tr = tr + 1 tracks_with_RE[tr] = track @@ -126,6 +137,9 @@ if began_block then reaper.PreventUIRefresh( -1 ) reaper.UpdateArrange() reaper.Undo_EndBlock( "Smart crossfade items in RE area", (remove_RE_area and 1 or 0)|4 ) + if reenableTrim then + reaper.Main_OnCommand(41117, 0) -- Trim content behind media items when editing + end return end @@ -238,10 +252,11 @@ for tr = 1, #categorized_item do -- tracks local second_end = second_start + reaper.GetMediaItemInfo_Value(item, "D_LENGTH") local first_start = reaper.GetMediaItemInfo_Value(prev_item, "D_POSITION") local first_end = first_start + reaper.GetMediaItemInfo_Value(prev_item, "D_LENGTH") - if first_end < second_start - xfadetime and (sel_start == sel_end or (first_end < sel_start and second_start > sel_end)) then + if first_end < second_start - maximum_gap and (sel_start == sel_end or (first_end < sel_start and second_start > sel_end)) then -- items do not touch and there is no time selection covering parts of both items -- do nothing - Msg("not touch - gap greater than xfadetime") + Msg("not touch - gap greater than maximum_gap") + Msg(string.format("Gap is %f seconds", second_start - first_end)) --[[elseif geq( second_start - first_end, xfadetime) then --leq( first_start, second_start) and geq( first_end, second_end) then -- one item encloses the other @@ -265,7 +280,7 @@ for tr = 1, #categorized_item do -- tracks reaper.SetMediaItemSelected(item, false) change = true end - elseif geq(second_start, first_end) and leq(second_start, first_end + xfadetime) + elseif geq(second_start, first_end) and leq(second_start, first_end + maximum_gap) then -- items are adjacent (or there is a gap smaller or equal to the crossfade time) Msg("adjacent - gap: " .. second_start - first_end) if not CrossfadeOK(item, prev_item, second_start, first_end) then @@ -276,7 +291,7 @@ for tr = 1, #categorized_item do -- tracks end reaper.SetMediaItemSelected(prev_item, true) reaper.ApplyNudge(0, 1, 3, 1, second_start, 0, 0) - FadeOut(prev_item, xfadetime) + FadeOut(prev_item, maximum_gap) reaper.SetMediaItemSelected(prev_item, false) if groupid ~= 0 then reaper.SetMediaItemInfo_Value( prev_item, "I_GROUPID", groupid ) @@ -287,9 +302,9 @@ for tr = 1, #categorized_item do -- tracks reaper.SetMediaItemInfo_Value( item, "I_GROUPID", 0 ) end reaper.SetMediaItemSelected(item, true) - reaper.ApplyNudge(0, 1, 1, 1, second_start - xfadetime, 0, 0) - Msg(xfadetime) - FadeIn(item, xfadetime) + reaper.ApplyNudge(0, 1, 1, 1, second_start - maximum_gap, 0, 0) + Msg(maximum_gap) + FadeIn(item, maximum_gap) reaper.SetMediaItemSelected(item, false) if groupid ~= 0 then reaper.SetMediaItemInfo_Value( item, "I_GROUPID", groupid ) diff --git a/Items Editing/az_Fade tool (work on context of mouse, razor or time selection).lua b/Items Editing/az_Fade tool (work on context of mouse, razor or time selection).lua index 39f0824fc..5f431addc 100644 --- a/Items Editing/az_Fade tool (work on context of mouse, razor or time selection).lua +++ b/Items Editing/az_Fade tool (work on context of mouse, razor or time selection).lua @@ -1,10 +1,17 @@ --- @description Fade tool (work on context of mouse, razor or time selection) +-- @description Fade tool (works on context of mouse, razor or time selection) -- @author AZ --- @version 1.3 +-- @version 2.2.1 -- @changelog --- - Creating envelope transitions via razor --- - Ability to mirror item fade in envelopes using razor and mouse placement --- @link Author's page https://forum.cockos.com/member.php?u=135221 +-- - ignore snapping for certain cases +-- - apply font resizing immediately +-- - fixed regression with mouse fade on selected item +-- @provides +-- az_Fade tool (work on context of mouse, razor or time selection)/az_Options window for az_Fade tool.lua +-- [main] az_Fade tool (work on context of mouse, razor or time selection)/az_Open options for az_Fade tool.lua +-- @link +-- Author's page https://forum.cockos.com/member.php?u=135221 +-- Forum thread https://forum.cockos.com/showthread.php?t=293335 +-- @donation Donate via PayPal https://www.paypal.me/AZsound -- @about -- # Fade tool -- @@ -13,7 +20,7 @@ -- - Use top/bottom mouse placement on the item to define what fade you need, in or out. -- -- There is two ways that you can change in the options. --- +-- -- Default is: top / bottom -> fadein / fadeout -- -- Another is: top / bottom -> close edge fade / far edge fade @@ -36,232 +43,207 @@ -- -- - Locking respected by default. -- --- - Script warns if non-midi items have too short source for creating crossfade and offer to loop source. +-- - Script warns if non-midi items have too short source for creating crossfade and offer to loop the source. -- -- - Last batch values can be saved in project (by default) --- --- --- P.S. There is experimental support for fixed media lanes being in pre-release versions. - ---------------------- -Options = { ---TO MODIFY THESE OPTIONS CALL THE OPTIONS WINDOW. ---JUST PLACE MOUSE ON TRANSPORT OR MIXER PANEL AND PRESS SHORTCUT FOR THIS SCRIPT. - -RespectLocking = "yes", --Global locking and locked items (yes/no) -moveEditCursor = "yes", -- moves cursor after fade set -curOffset = 1, -- offset between fade and edit cursor in sec -LRdefine = 2, -- 1 = cross define, based on close and far edge, - -- 2 = top item half: fadein, bottom: fadeout -DefaultFadeIn = 30, -- Fade-in in ms -DefaultFadeOut = 30, -- Fade-out in ms -DefaultCrossFade = 30, -- Default crossfade in ms -DefCrossType = 1, -- 1 = pre-crossfade, 2 = centered crossfade -RespectExistingFades = "yes", --For default batch fades/crossfades -RespectLockingBatch = "yes", --For default batch fades/crossfades +--[[ +TO MODIFY SCRIPT OPTIONS +OPEN THE OPTIONS WINDOW BY RUNNING THE SCRIPT WITH MOUSE ON TRANSPORT OR MIXER PANEL +]] -SaveLastBatchPrj = "yes", --Use and save last batch settings in project -} --------------------- --------------------- +--------------------------------- +--------Options functions-------- +--Start load file +ExtStateName = 'AZ_FadeTool' -function msg(value) - reaper.ShowConsoleMsg(tostring(value)..'\n') +function GetExtStates(OptionsTable) + for i, option in ipairs(OptionsTable) do + if option[3] ~= nil then + local state = reaper.GetExtState(ExtStateName, option[2]) + + if state ~= "" then + local wrong + local stateType = type(option[3]) + if stateType == 'number' then state = tonumber(state) end + if stateType == 'boolean' then + if state == 'true' then state = true else state = false end + end + if stateType == 'string' then + wrong = true + + for i = 1, #option[4] do + local var = option[4][i] + if state == var then + wrong = false + break + end + end + + end + + if wrong == true then + reaper.SetExtState(ExtStateName, option[2], tostring(option[3]), true) + else + OptionsTable[i][3] = state + end + else + reaper.SetExtState(ExtStateName, option[2], tostring(option[3]), true) + end + + end + end end ---------------------------------- ---------Options functions-------- -ScriptName = 'AZ_FadeTool' +--------------------- -function GetExtStates() - local respLock = reaper.GetExtState(ScriptName, 'respLock') - if respLock:match('y') == 'y' or respLock == 'true' then - Options.RespectLocking = 'yes' - elseif respLock:match('n') == 'n' or respLock == 'false' then - Options.RespectLocking = 'no' +function SetExtStates(OptionsTable) + for i, option in ipairs(OptionsTable) do + if option[3] ~= nil then + reaper.SetExtState(ExtStateName, option[2], tostring(option[3]), true) + end end +end - local moveEcur = reaper.GetExtState(ScriptName, 'moveEcur') - if moveEcur:match('y') == 'y' or moveEcur == 'true' then - Options.moveEditCursor = 'yes' - elseif moveEcur:match('n') == 'n' or moveEcur == 'false' then - Options.moveEditCursor = 'no' - end +--------------------- + +function OptionsDefaults(NamedTable) + local text - local curOffs = reaper.GetExtState(ScriptName, 'curOffs') - if curOffs:match('%d+')~= nil then - Options.curOffset = tonumber(curOffs) - end + text = 'Behaviour' + table.insert(NamedTable, {text, 'Separator', nil}) - local lrDef = reaper.GetExtState(ScriptName, 'lrDef') - if tonumber(lrDef)==1 or tonumber(lrDef)==2 then - Options.LRdefine = tonumber(lrDef) - end + text = 'Respect snap for mouse on items' + table.insert(NamedTable, {text, 'RespSnapItems', true }) - ----------- + text = 'Respect snap for mouse on envelopes' + table.insert(NamedTable, {text, 'RespSnapEnvs', true }) - local defFin = reaper.GetExtState(ScriptName, 'defFin') - if defFin:match('%d+')~= nil then - Options.DefaultFadeIn = tonumber(defFin) - end + text = 'Ignore locking for mouse editing' + table.insert(NamedTable, {text, 'IgnoreLockingMouse', false }) - local defFout = reaper.GetExtState(ScriptName, 'defFout') - if defFout:match('%d+')~= nil then - Options.DefaultFadeOut = tonumber(defFout) - end + text = 'Move edit cursor after setting a fade/crossfade' + table.insert(NamedTable, {text, 'moveEditCursor', true }) - local defCrossF = reaper.GetExtState(ScriptName, 'defCrossF') - if defCrossF:match('%d+')~= nil then - Options.DefaultCrossFade = tonumber(defCrossF) - end + text = 'Offset between fade and edit cursor in seconds' + table.insert(NamedTable, {text, 'curOffset', 1, "%.2f"}) - local defCrossT = reaper.GetExtState(ScriptName, 'defCrossType') - if tonumber(defCrossT)==1 or tonumber(defCrossT)==2 then - Options.DefCrossType = tonumber(defCrossT) - end + text = 'Extend item edge to fill razor or time selection' + table.insert(NamedTable, {text, 'MoveItemEdgeRazor', false }) - local respExistF = reaper.GetExtState(ScriptName, 'RespectExistingFades') - if respExistF:match('y') == 'y' or respExistF == 'true' then - Options.RespectExistingFades = 'yes' - elseif respExistF:match('n') == 'n' or respExistF == 'false' then - Options.RespectExistingFades = 'no' - end + text = 'Mouse top / bottom placement on item is used for' + table.insert(NamedTable, {text, 'LRdefine', 'Left/Right fade', { + 'Left/Right fade', + 'Closest/Farthest fade' } }) - local respLockB = reaper.GetExtState(ScriptName, 'RespectLockingBatch') - if respLockB == 'y' or respLockB == 'yes' or respLockB == 'true' then - Options.RespectLockingBatch = 'yes' - elseif respLockB:match('n') == 'n' or respLockB == 'false' then - Options.RespectLockingBatch = 'no' - end + text = 'Use default shape instead of linear when cutting envelopes' + table.insert(NamedTable, {text, 'CutShapeUseDef', false }) - local saveBatchPrj = reaper.GetExtState(ScriptName, 'SaveLastBatchPrj') - if saveBatchPrj == 'y' or saveBatchPrj == 'yes' or saveBatchPrj == 'true' then - Options.SaveLastBatchPrj = 'yes' - elseif saveBatchPrj:match('n') == 'n' or saveBatchPrj == 'false' then - Options.SaveLastBatchPrj = 'no' - end - SetGlobVariables() -end ------------------- - -function SetGlobVariables() + text = 'Batch fades/crossfades defaults' + table.insert(NamedTable, {text, 'Separator', nil}) - if Options.RespectLocking:match('y') == 'y' or Options.RespectLocking == 'true' then - RespectLocking = true - elseif Options.RespectLocking:match('n') == 'n' or Options.RespectLocking == 'false' then - RespectLocking = false - end + text = 'Fade-in' + table.insert(NamedTable, {text, 'DefaultFadeIn', 30, "%.0f"}) + + text = 'Fade-out' + table.insert(NamedTable, {text, 'DefaultFadeOut', 30, "%.0f"}) + + text = 'Crossfade' + table.insert(NamedTable, {text, 'DefaultCrossFade', 30, "%.0f"}) - if Options.moveEditCursor:match('y') == 'y' or Options.moveEditCursor == 'true' then - moveEditCursor = true - elseif Options.moveEditCursor:match('n') == 'n' or Options.moveEditCursor == 'false' then - moveEditCursor = false - end - curOffset = Options.curOffset - LRdefine = Options.LRdefine + text = 'Values' + table.insert(NamedTable, {text, 'ValueType', 'milliseconds', { + 'milliseconds', + 'frames' } }) - ----------- + text = 'Crossfade type' + table.insert(NamedTable, {text, 'DefCrossType', 'Left', { + 'Left', + 'Centered' } }) + + text = 'Respect existing fades' + table.insert(NamedTable, {text, 'RespectExistingFades', true}) + + text = 'Respect locking' + table.insert(NamedTable, {text, 'RespectLockingBatch', true}) + + text = 'Other options' + table.insert(NamedTable, {text, 'Separator', nil}) + + text = 'Save and use last batch settings in project' + table.insert(NamedTable, {text, 'SaveLastBatchPrj', true}) + +end - DefaultFadeIn = Options.DefaultFadeIn - DefaultFadeOut = Options.DefaultFadeOut - DefaultCrossFade = Options.DefaultCrossFade - DefCrossType = Options.DefCrossType - if Options.RespectExistingFades:match('y') == 'y' or Options.RespectExistingFades == 'true' then - RespectExistingFades = 'yes' - elseif Options.RespectExistingFades:match('n') == 'n' or Options.RespectExistingFades == 'false' then - RespectExistingFades = 'no' - end +------------------------- - if Options.RespectLockingBatch:match('y') == 'y' or Options.RespectLockingBatch == 'true' then - RespectLockingBatch = 'yes' - elseif Options.RespectLockingBatch:match('n') == 'n' or Options.RespectLockingBatch == 'false' then - RespectLockingBatch = 'no' - end - - if Options.SaveLastBatchPrj:match('y') == 'y' or Options.SaveLastBatchPrj == 'true' then - SaveLastBatchPrj = 'yes' - elseif Options.SaveLastBatchPrj:match('n') == 'n' or Options.SaveLastBatchPrj == 'false' then - SaveLastBatchPrj = 'no' +function SetOptGlobals(NamedTable, OptionsTable) + for i = 1, #OptionsTable do + local name = OptionsTable[i][2] + NamedTable[name] = OptionsTable[i][3] end end ------------------ -function SetExtStates(str) - local retval = {} - for s in str:gmatch('[^,]+')do - table.insert(retval, s) - end +------------------------- +--End load file +------------------------- + +function msg(value) + reaper.ShowConsoleMsg(tostring(value)..'\n') +end + +------------------------------ +function BatchDefaults(NamedTable) + local text - reaper.SetExtState(ScriptName, 'respLock',retval[1], true) - reaper.SetExtState(ScriptName, 'moveEcur',retval[2], true) - reaper.SetExtState(ScriptName, 'curOffs',retval[3], true) - reaper.SetExtState(ScriptName, 'lrDef',retval[4], true) + text = 'Fade-in' + table.insert(NamedTable, {text, 'DefaultFadeIn', 30, "%.0f"}) - reaper.SetExtState(ScriptName, 'defFin',retval[5], true) - reaper.SetExtState(ScriptName, 'defFout',retval[6], true) - reaper.SetExtState(ScriptName, 'defCrossF',retval[7], true) - reaper.SetExtState(ScriptName, 'defCrossType',retval[8], true) + text = 'Fade-out' + table.insert(NamedTable, {text, 'DefaultFadeOut', 30, "%.0f"}) - reaper.SetExtState(ScriptName, 'RespectExistingFades',retval[9], true) - reaper.SetExtState(ScriptName, 'RespectLockingBatch',retval[10], true) + text = 'Crossfade' + table.insert(NamedTable, {text, 'DefaultCrossFade', 30, "%.0f"}) + + text = 'Values' + table.insert(NamedTable, {text, 'ValueType', 'milliseconds', { + 'milliseconds', + 'frames' } }) + + + text = 'Crossfade type' + table.insert(NamedTable, {text, 'DefCrossType', 'Left', { + 'Left', + 'Centered' } }) + + text = 'Respect existing fades' + table.insert(NamedTable, {text, 'RespectExistingFades', true}) + + text = 'Respect locking' + table.insert(NamedTable, {text, 'RespectLockingBatch', true}) - reaper.SetExtState(ScriptName, 'SaveLastBatchPrj',retval[11], true) end -function OptionsWindow() - - GetExtStates() - - local title = 'Fade tool - global options' - local describe = - 'Respect locking (y/n)'..','.. - - 'Move E-cursor after fade set (y/n)'..','.. - 'Distance from cursor to fade in sec'..','.. - 'L/R define type for mouse (1/2)'..','.. - - 'Batch: Fade-in in ms'..','.. - 'Batch: Fade-out in ms'..','.. - 'Batch: Crossfade in ms'..','.. - 'Crossfade type 1=pre 2=centered'..','.. - 'Batch: Respect existing fades(y/n)'..','.. - 'Batch: Respect locking (y/n)'..','.. - 'Save last batch settings in project' - local values = - tostring(Options.RespectLocking)..','.. - tostring(Options.moveEditCursor)..','.. - tostring(Options.curOffset)..','.. - tostring(Options.LRdefine)..','.. - tostring(Options.DefaultFadeIn)..','.. - tostring(Options.DefaultFadeOut)..','.. - tostring(Options.DefaultCrossFade)..','.. - tostring(Options.DefCrossType)..','.. - tostring(Options.RespectExistingFades)..','.. - tostring(Options.RespectLockingBatch)..','.. - tostring(Options.SaveLastBatchPrj) - local done, str = reaper.GetUserInputs(title, 11, describe, values) - - if done then SetExtStates(str) end -end +------------------------- function HelloMessage() - local text = 'Hello friend! It seems you updated script "az_Fade tool".' - ..'\n\n'..'Now you can edit envelope transitions via razor.' - ..'\n'..'Also try to mirror item fade on envelope.' - ..'\n\n'..'As previos you have easy access to the options.' - ..'\n'..'Just press assigned hotkey when mouse placed on transport or mixer area.' + local text = 'Hello friend! It seems you have updated script "az_Fade tool".' + ..'\n\n'..'The script now has new GUI and more thoughtfull behavior.' + ..'\n'..'Explore all of them including envelope editing, look here: ' + ..'\n'..'https://forum.cockos.com/showthread.php?t=293335' + ..'\n\n'..'Please, check the options if they were changed.' + ..'\n'..'Just press assigned hotkey when mouse placed on the transport or mixer area.' ..'\n'..'Have fun!)' reaper.ShowMessageBox(text,'Fade tool - Hello!',0) end --------------------------------- ---------Work functions----------- +-----------Work functions-------- --------------------------------- function RazorEditSelectionExists() @@ -286,15 +268,25 @@ end ----------------------- -function GetEnvelopePointsInRange(envelopeTrack, areaStart, areaEnd) - local envelopePoints = {Start = {}, End = {}} - --local prevTime, lastTime - - for i = 1, reaper.CountEnvelopePoints(envelopeTrack) do - local retval, time, value, shape, tension, selected = reaper.GetEnvelopePoint(envelopeTrack, i - 1) - +function GetEnvelopePointsInRange(envelope, time1, time2) + local areaStart = math.min(time1, time2) + local areaEnd = math.max(time1, time2) + local envelopePoints = {Start = {}, End = {} } + local oldtime + local oldvalue + local pointsNum = reaper.CountEnvelopePoints(envelope) + + for i = 1, pointsNum do + local retval, time, value, shape, tension, selected = reaper.GetEnvelopePoint(envelope, i- 1) + local increase = 1 + + if oldtime == time and oldvalue == value then + reaper.DeleteEnvelopePointEx( envelope, -1, i-1) + increase = 0 + end + if time >= areaStart and time <= areaEnd then --point is in range - envelopePoints[#envelopePoints + 1] = { + envelopePoints[#envelopePoints + increase] = { id = i-1 , time = time, value = value, @@ -303,21 +295,19 @@ function GetEnvelopePointsInRange(envelopeTrack, areaStart, areaEnd) selected = selected } - --if time - areaStart < 0.02 then prevTime = time end - if time - areaStart < 0.02 then --and prevTime then --need fade in - envelopePoints.Start[#envelopePoints.Start+1] = i-1 - --prevTime = time + if time - areaStart < 0.02 then --for env RE edge points removing + envelopePoints.Start[#envelopePoints.Start + increase] = i-1 end - if areaEnd - time < 0.02 then --need fade out maybe - envelopePoints.End[#envelopePoints.End+1] = i-1 - --lastTime = time + if areaEnd - time < 0.02 then --for env RE edge points removing + envelopePoints.End[#envelopePoints.End + increase] = i-1 end + end end - --if lastTime ~= areaEnd then envelopePoints.End = {} end - + reaper.Envelope_SortPointsEx( envelope, -1 ) + return envelopePoints end @@ -343,7 +333,8 @@ function GetItemsInRange(track, areaStart, areaEnd, areaTop, areaBottom) --retur end --check if item is in area bounds - if itemEndPos > areaStart and pos < areaEnd and (iLock==0 or RespectLocking == false) then + if itemEndPos > areaStart and pos < areaEnd + and (iLock == 0 or RespectLockingBatch == false) then if areaBottom and itemTop then if itemTop < areaBottom - 0.001 and itemBottom > areaTop + 0.001 then @@ -384,6 +375,7 @@ function ParseAreaPerLane(RawTable, itemH) --one level metatable local GUID = RawTable[i][3] local areaTop = tonumber(RawTable[i][4]) local areaBottom = tonumber(RawTable[i][5]) + local isEnvelope = GUID ~= '""' if not isEnvelope then areaWidth = math.floor(((areaBottom - areaTop)/itemH)+0.5) -- how many lanes include @@ -432,6 +424,9 @@ end function GetRazorEdits() --returns areaMap, needBatch ONLYenvelopes = true + RAZORenvelopes = false + MouseDistDiff = nil + local mouseTime = reaper.BR_PositionAtMouseCursor( false ) local NeedPerLane = true local trackCount = reaper.CountTracks(0) local areaMap = {} @@ -440,7 +435,7 @@ function GetRazorEdits() --returns areaMap, needBatch for i = -1, trackCount - 1 do local track if i == -1 then - track = reaper.GetMasterTrack( 0 ) + track = reaper.GetMasterTrack( 0 ) else track = reaper.GetTrack(0, i) end @@ -479,8 +474,7 @@ function GetRazorEdits() --returns areaMap, needBatch if NeedPerLane == true then TRareaTable = AreaParsed else TRareaTable = TRstr end --FILL AREA DATA - local i = 1 - + local i = 1 while i <= #TRareaTable do --area data local areaStart = tonumber(TRareaTable[i][1]) @@ -488,17 +482,17 @@ function GetRazorEdits() --returns areaMap, needBatch local GUID = TRareaTable[i][3] local areaTop = tonumber(TRareaTable[i][4]) local areaBottom = tonumber(TRareaTable[i][5]) - local isEnvelope = GUID ~= '""' - + local isEnvelope = GUID ~= '""' --get item/envelope data local items = {} local envelopeName, envelope local envelopePoints + if isEnvelope == true then RAZORenvelopes = true end + if not isEnvelope then ONLYenvelopes = false - --reaper.ShowConsoleMsg(areaTop.." "..areaBottom.."\n\n") local TOneedBatch items, TOneedBatch = GetItemsInRange(track, areaStart, areaEnd, areaTop, areaBottom) if needBatch ~= true then needBatch = TOneedBatch end @@ -508,6 +502,18 @@ function GetRazorEdits() --returns areaMap, needBatch envelopeName = envName envelopePoints = GetEnvelopePointsInRange(envelope, areaStart, areaEnd) + + if mouseTime > 0 then + local distDiff + if math.abs(mouseTime - areaStart) < math.abs(mouseTime - areaEnd) then + distDiff = mouseTime - areaStart + else + distDiff = areaEnd - mouseTime + end + if MouseDistDiff == nil or math.abs(distDiff) < math.abs(MouseDistDiff) then + MouseDistDiff = distDiff + end + end end local areaData = { @@ -559,7 +565,9 @@ function GetRazorEdits() --returns areaMap, needBatch local envelopeName, envelope local envelopePoints - if not isEnvelope then + if isEnvelope == true then RAZORenvelopes = true end + + if not isEnvelope then ONLYenvelopes = false local TOneedBatch items, TOneedBatch = GetItemsInRange(track, areaStart, areaEnd) @@ -570,6 +578,18 @@ function GetRazorEdits() --returns areaMap, needBatch envelopeName = envName envelopePoints = GetEnvelopePointsInRange(envelope, areaStart, areaEnd) + + if mouseTime > 0 then + local distDiff + if math.abs(mouseTime - areaStart) < math.abs(mouseTime - areaEnd) then + distDiff = mouseTime - areaStart + else + distDiff = areaEnd - mouseTime + end + if MouseDistDiff == nil or math.abs(distDiff) < math.abs(MouseDistDiff) then + MouseDistDiff = distDiff + end + end end local areaData = { @@ -601,8 +621,18 @@ end -------------------------------- function SetCrossfade(Litem,Ritem,areaData) - local Ltake = reaper.GetActiveTake(Litem) - local Rtake = reaper.GetActiveTake(Ritem) + if not Litem and not Ritem then return end + local Ltake + local Rtake + if Litem then Ltake = reaper.GetActiveTake(Litem) end + if Ritem then Rtake = reaper.GetActiveTake(Ritem) end + + local LiPos + local LiLock + local RiPos + local RiEnd + local RiLock + local LiNewEnd local RiNewStart @@ -610,270 +640,377 @@ function SetCrossfade(Litem,Ritem,areaData) local rightISmidi -----------Left item edges--------------- - local LiPos = reaper.GetMediaItemInfo_Value( Litem, "D_POSITION" ) - local LiLock = reaper.GetMediaItemInfo_Value( Litem, "C_LOCK" ) - - if LiLock == 1 and RespectLocking == false then - table.insert(LOCKEDitems, Litem) - reaper.SetMediaItemInfo_Value( Litem, "C_LOCK", 0 ) - end - - if reaper.GetMediaItemInfo_Value( Litem, "B_LOOPSRC" ) == 0 then - local tOffs = reaper.GetMediaItemTakeInfo_Value( Ltake, "D_STARTOFFS" ) - local source = reaper.GetMediaItemTake_Source( Ltake ) - local srcLength, lengthIsQN = reaper.GetMediaSourceLength( source ) - local srcEnd = LiPos - tOffs + srcLength - if lengthIsQN == true then --if take is midi - srcEnd = areaData.areaEnd - leftISmidi = true + if Litem then + LiPos = reaper.GetMediaItemInfo_Value( Litem, "D_POSITION" ) + LiEnd = LiPos + reaper.GetMediaItemInfo_Value( Litem, "D_LENGTH" ) + LiLock = reaper.GetMediaItemInfo_Value( Litem, "C_LOCK" ) + + if LiLock == 1 + and (Opt.IgnoreLockingMouse == true or RespectLockingBatch == false) then + table.insert(LOCKEDitems, Litem) + reaper.SetMediaItemInfo_Value( Litem, "C_LOCK", 0 ) + end + + if ((edgesLock == 1 and globalLock == 1) or LiLock == 1) + and Opt.IgnoreLockingMouse == false + then + areaData.areaEnd = LiEnd + end + + if reaper.GetMediaItemInfo_Value( Litem, "B_LOOPSRC" ) == 0 then + local tOffs = reaper.GetMediaItemTakeInfo_Value( Ltake, "D_STARTOFFS" ) + local source = reaper.GetMediaItemTake_Source( Ltake ) + local srcLength, lengthIsQN = reaper.GetMediaSourceLength( source ) + local srcEnd = LiPos - tOffs + srcLength + if lengthIsQN == true then --if take is midi + srcEnd = areaData.areaEnd + leftISmidi = true + end + LiNewEnd = math.min(srcEnd,areaData.areaEnd) + reaper.BR_SetItemEdges(Litem, LiPos, LiNewEnd) + else + LiNewEnd = areaData.areaEnd + reaper.BR_SetItemEdges(Litem, LiPos, LiNewEnd) end - LiNewEnd = math.min(srcEnd,areaData.areaEnd) - reaper.BR_SetItemEdges(Litem, LiPos, LiNewEnd) - else - LiNewEnd = areaData.areaEnd - reaper.BR_SetItemEdges(Litem, LiPos, LiNewEnd) end -----------Right item edges------------ - local RiPos = reaper.GetMediaItemInfo_Value( Ritem, "D_POSITION" ) - local RiEnd = RiPos + reaper.GetMediaItemInfo_Value( Ritem, "D_LENGTH" ) - local RiLock = reaper.GetMediaItemInfo_Value( Ritem, "C_LOCK" ) - - if RiLock == 1 and RespectLocking == false then - table.insert(LOCKEDitems, Ritem) - reaper.SetMediaItemInfo_Value( Ritem, "C_LOCK", 0 ) - end - - if reaper.GetMediaItemInfo_Value( Ritem, "B_LOOPSRC" ) == 0 then - local tOffs = reaper.GetMediaItemTakeInfo_Value( Rtake, "D_STARTOFFS" ) - local source = reaper.GetMediaItemTake_Source( Rtake ) - local srcLength, lengthIsQN = reaper.GetMediaSourceLength( source ) - local srcStart = RiPos - tOffs - if lengthIsQN == true then --if take is midi - srcStart = areaData.areaStart - rightISmidi = true - end - RiNewStart = math.max(srcStart, areaData.areaStart) - reaper.BR_SetItemEdges(Ritem, RiNewStart, RiEnd) - else - RiNewStart = areaData.areaStart - reaper.BR_SetItemEdges(Ritem, RiNewStart, RiEnd) + if Ritem then + RiPos = reaper.GetMediaItemInfo_Value( Ritem, "D_POSITION" ) + RiEnd = RiPos + reaper.GetMediaItemInfo_Value( Ritem, "D_LENGTH" ) + RiLock = reaper.GetMediaItemInfo_Value( Ritem, "C_LOCK" ) + + if RiLock == 1 + and (Opt.IgnoreLockingMouse == true or RespectLockingBatch == false) then + table.insert(LOCKEDitems, Ritem) + reaper.SetMediaItemInfo_Value( Ritem, "C_LOCK", 0 ) + end + + if ((edgesLock == 1 and globalLock == 1) or RiLock == 1) + and Opt.IgnoreLockingMouse == false + then + areaData.areaStart = RiPos + end + + if reaper.GetMediaItemInfo_Value( Ritem, "B_LOOPSRC" ) == 0 then + local tOffs = reaper.GetMediaItemTakeInfo_Value( Rtake, "D_STARTOFFS" ) + local source = reaper.GetMediaItemTake_Source( Rtake ) + local srcLength, lengthIsQN = reaper.GetMediaSourceLength( source ) + local srcStart = RiPos - tOffs + if lengthIsQN == true then --if take is midi + srcStart = areaData.areaStart + rightISmidi = true + end + RiNewStart = math.max(srcStart, areaData.areaStart) + reaper.BR_SetItemEdges(Ritem, RiNewStart, RiEnd) + else + RiNewStart = areaData.areaStart + reaper.BR_SetItemEdges(Ritem, RiNewStart, RiEnd) + end end --------------------------------------- - -----------Chech is a gap between items-------- + -----------Check is a gap between items-------- if LiNewEnd and RiNewStart and LiNewEnd <= RiNewStart then - local SourceMsg = - "There is items pair with don't crossing sources.\n\nDo you want to loop their sources?" - .."\nIf no there will be just longest fades if possible." - if reaper.ShowMessageBox(SourceMsg,"Fade tool",4) == 7 then - SetFade(Litem, "out", LiNewEnd - areaData.areaStart ) - SetFade(Ritem, "in", areaData.areaEnd - RiNewStart) - else - reaper.SetMediaItemInfo_Value( Litem, "B_LOOPSRC", 1 ) - reaper.SetMediaItemInfo_Value( Ritem, "B_LOOPSRC", 1 ) - reaper.BR_SetItemEdges(Litem, LiPos, areaData.areaEnd) - reaper.BR_SetItemEdges(Ritem, areaData.areaStart, RiEnd) + if not GAPanswer then + local SourceMsg + if RunBatch == true then + SourceMsg = + "There is items pair(s) with don't crossing sources.\n" + .."\nThey will not be crossfaded" + GAPanswer = reaper.ShowMessageBox(SourceMsg,"Fade tool",0) + else + SourceMsg = + "There is items pair(s) with don't crossing sources.\n\nDo you want to loop their sources?" + .."\nIf no there will be just fades on the rests if possible." + GAPanswer = reaper.ShowMessageBox(SourceMsg,"Fade tool",4) + end + end + + if RunBatch ~= true then + if GAPanswer == 7 then + SetFade(Litem, "out", LiNewEnd - areaData.areaStart ) + SetFade(Ritem, "in", areaData.areaEnd - RiNewStart) + else + reaper.SetMediaItemInfo_Value( Litem, "B_LOOPSRC", 1 ) + reaper.SetMediaItemInfo_Value( Ritem, "B_LOOPSRC", 1 ) + reaper.BR_SetItemEdges(Litem, LiPos, areaData.areaEnd) + reaper.BR_SetItemEdges(Ritem, areaData.areaStart, RiEnd) + end end end ---------Is new fade------- check for Midi items and items on different lanes/tracks - if LiNewEnd and RiNewStart and LiNewEnd > RiNewStart then + if (LiNewEnd and RiNewStart and LiNewEnd > RiNewStart) + or MoveItemEdgeRazor == true + then + local parOut = "D_FADEOUTLEN" + local parIn = "D_FADEINLEN" + local parOutA = "D_FADEOUTLEN_AUTO" + local parInA = "D_FADEINLEN_AUTO" - if reaper.GetMediaItemInfo_Value( Litem, "D_FADEOUTLEN_AUTO") == 0 then - reaper.SetMediaItemInfo_Value( Litem, "D_FADEOUTLEN", LiNewEnd - RiNewStart ) + if not LiNewEnd then LiNewEnd = areaData.areaEnd end + if not RiNewStart then RiNewStart = areaData.areaStart end + + if Litem then + if MoveItemEdgeRazor == true then + reaper.SetMediaItemInfo_Value( Litem, parOutA, 0) + reaper.SetMediaItemInfo_Value( Litem, parOut, LiNewEnd - RiNewStart ) + else + reaper.SetMediaItemInfo_Value( Litem, parOutA, LiNewEnd - RiNewStart ) + end + + local fEdge = RiNewStart + local InitInFade = reaper.GetMediaItemInfo_Value( Litem, "D_FADEINLEN" ) + local i_autoFin = reaper.GetMediaItemInfo_Value(Litem, "D_FADEINLEN_AUTO") + if i_autoFin ~= 0 then InitInFade = i_autoFin end + + if InitInFade ~= 0 then + if fEdge < LiPos + InitInFade then + if i_autoFin ~= 0 then + local shape = reaper.GetMediaItemInfo_Value(Litem, "C_FADEINSHAPE") + reaper.SetMediaItemInfo_Value(Litem, "D_FADEINLEN_AUTO", 0) + reaper.SetMediaItemInfo_Value(Litem, "C_FADEINSHAPE", shape ) + end + reaper.SetMediaItemInfo_Value( Litem, "D_FADEINLEN", fEdge - LiPos ) + end + end + + end - if reaper.GetMediaItemInfo_Value( Ritem, "D_FADEINLEN_AUTO") == 0 then - reaper.SetMediaItemInfo_Value( Ritem, "D_FADEINLEN", LiNewEnd - RiNewStart ) + if Ritem then + if MoveItemEdgeRazor == true then + reaper.SetMediaItemInfo_Value( Ritem, parInA, 0) + reaper.SetMediaItemInfo_Value( Ritem, parIn, LiNewEnd - RiNewStart ) + else + reaper.SetMediaItemInfo_Value( Ritem, parInA, LiNewEnd - RiNewStart ) + end + + local fEdge = LiNewEnd + local InitOutFade = reaper.GetMediaItemInfo_Value( Ritem, "D_FADEOUTLEN" ) + local i_autoFout = reaper.GetMediaItemInfo_Value(Ritem, "D_FADEOUTLEN_AUTO") + if i_autoFout ~= 0 then InitOutFade = i_autoFout end + + if InitOutFade ~= 0 then + if fEdge > RiEnd - InitOutFade then + if i_autoFout ~= 0 then + local shape = reaper.GetMediaItemInfo_Value(Ritem, "C_FADEOUTSHAPE") + reaper.SetMediaItemInfo_Value(Ritem, "D_FADEOUTLEN_AUTO", 0) + reaper.SetMediaItemInfo_Value(Ritem, "C_FADEOUTSHAPE", shape ) + end + reaper.SetMediaItemInfo_Value( Ritem, "D_FADEOUTLEN", RiEnd - fEdge ) + end + end end end + + return math.min(RiNewStart,LiNewEnd) end ----------------------- ----------------------- -function GetSetPrjBatchDefaults(get_set) +function GetSetBatchExtStates(DefT, getset) -- set == true, get == false local Mtrack = reaper.GetMasterTrack(0) - if get_set == "set" then - local parname - - parname = "P_EXT:"..'AZ_Ftool_Batch '..'DefaultFadeIn' - local retval, stringNeedBig = reaper.GetSetMediaTrackInfo_String - ( Mtrack, parname, tostring(DefaultFadeIn), true ) - - parname = "P_EXT:"..'AZ_Ftool_Batch '..'DefaultFadeOut' - local retval, stringNeedBig = reaper.GetSetMediaTrackInfo_String - ( Mtrack, parname, tostring(DefaultFadeOut), true ) + local parname = "P_EXT:"..'AZ_Ftool_Batch ' + if getset == true then - parname = "P_EXT:"..'AZ_Ftool_Batch '..'DefaultCrossFade' - local retval, stringNeedBig = reaper.GetSetMediaTrackInfo_String - ( Mtrack, parname, tostring(DefaultCrossFade), true ) - - parname = "P_EXT:"..'AZ_Ftool_Batch '..'DefCrossType' - local retval, stringNeedBig = reaper.GetSetMediaTrackInfo_String - ( Mtrack, parname, tostring(DefCrossType), true ) - - parname = "P_EXT:"..'AZ_Ftool_Batch '..'RespectExistingFades' - local retval, stringNeedBig = reaper.GetSetMediaTrackInfo_String - ( Mtrack, parname, tostring(RespectExistingFades), true ) - - parname = "P_EXT:"..'AZ_Ftool_Batch '..'RespectLockingBatch' - local retval, stringNeedBig = reaper.GetSetMediaTrackInfo_String - ( Mtrack, parname, tostring(RespectLockingBatch), true ) + for i, option in pairs(DefT) do + if option[3] ~= nil then + local ret, str = reaper.GetSetMediaTrackInfo_String + ( Mtrack, parname..option[2], tostring(option[3]), getset ) + end + end - elseif get_set == "get" then - - local parname - parname = "P_EXT:"..'AZ_Ftool_Batch '..'DefaultFadeIn' - local retval, string = reaper.GetSetMediaTrackInfo_String( Mtrack, parname, '', false ) - if string ~= "" and retval ~= false then DefaultFadeIn = tonumber(string) end - - parname = "P_EXT:"..'AZ_Ftool_Batch '..'DefaultFadeOut' - local retval, string = reaper.GetSetMediaTrackInfo_String( Mtrack, parname, '', false ) - if string ~= "" and retval ~= false then DefaultFadeOut = tonumber(string) end - - parname = "P_EXT:"..'AZ_Ftool_Batch '..'DefaultCrossFade' - local retval, string = reaper.GetSetMediaTrackInfo_String( Mtrack, parname, '', false ) - if string ~= "" and retval ~= false then DefaultCrossFade = tonumber(string) end + elseif getset == false then - parname = "P_EXT:"..'AZ_Ftool_Batch '..'DefCrossType' - local retval, string = reaper.GetSetMediaTrackInfo_String( Mtrack, parname, '', false ) - if string ~= "" and retval ~= false then DefCrossType = tonumber(string) end - - parname = "P_EXT:"..'AZ_Ftool_Batch '..'RespectExistingFades' - local retval, string = reaper.GetSetMediaTrackInfo_String( Mtrack, parname, '', false ) - if string ~= "" and retval ~= false then RespectExistingFades = string end + for i, option in pairs(DefT) do + if option[3] ~= nil then + local state + local ret, str = reaper.GetSetMediaTrackInfo_String + ( Mtrack, parname..option[2], '', getset ) + if str ~= "" and ret ~= false and Opt.SaveLastBatchPrj == true then + state = str + local stateType = type(option[3]) + if stateType == 'number' then state = tonumber(str) end + if stateType == 'boolean' then + if str == 'true' then state = true else state = false end + end + DefT[i][3] = state + else + DefT[i][3] = Opt[option[2]] + end + end + + end -- for - parname = "P_EXT:"..'AZ_Ftool_Batch '..'RespectLockingBatch' - local retval, string = reaper.GetSetMediaTrackInfo_String( Mtrack, parname, '', false ) - if string ~= "" and retval ~= false then RespectLockingBatch = string end - end + end --if getset == false end ----------------------- ----------------------- -function BatchFades(razorEdits) +function BatchFadesWindow(razorEdits) + BDefaults = {} + BatchDefaults(BDefaults) + GetSetBatchExtStates(BDefaults, false) + RunBatch = true + RazorEditsBatch = razorEdits + --look for additional file + local script_path = get_script_path() + local file = script_path .. 'az_Fade tool (work on context of mouse, razor or time selection)/' + ..'az_Options window for az_Fade tool.lua' + dofile(file) + OptionsWindow(BDefaults, 'Batch fades/crossfades - Fade Tool') - if SaveLastBatchPrj == "yes" then GetSetPrjBatchDefaults("get") end +end + +----------------------- +----------------------- + +function BatchFades() + reaper.Undo_BeginBlock2( 0 ) + reaper.PreventUIRefresh( 1 ) + local AnyEdit + BOpt = {} + SetOptGlobals(BOpt, BDefaults) + GetSetBatchExtStates(BDefaults, true) + + if BOpt.RespectLockingBatch == false then + RespectLockingBatch = false + reaper.Main_OnCommandEx(40596,0,0) --Locking: Clear item edges locking mode + if start_TS ~= end_TS then + RazorEditsBatch = GetTSandItems(start_TS, end_TS) + else + RazorEditsBatch = GetRazorEdits() + end + end - local done, str = reaper.GetUserInputs - ( "Batch fades/crossfades", 6, "Fade-in ms,Fade-out ms,Crossfade ms,Pre-crossfade or centered,Respect existing fades y/n,RespectLocking y/n", - tostring(DefaultFadeIn)..','..tostring(DefaultFadeOut)..','..tostring(DefaultCrossFade)..','..tostring(DefCrossType).. - ','..tostring(RespectExistingFades)..','..tostring(RespectLockingBatch)) - if done == true then - - local ret = {} - for s in str:gmatch('[^,]+')do - table.insert(ret, s) - end - DefaultFadeIn = tonumber(ret[1]) - DefaultFadeOut = tonumber(ret[2]) - DefaultCrossFade = tonumber(ret[3]) - DefCrossType = tonumber(ret[4]) - RespectExistingFades = ret[5] - RespectLockingBatch = ret[6] - - if SaveLastBatchPrj == "yes" then GetSetPrjBatchDefaults("set") end + local typeValueCoeff + if BOpt.ValueType == "milliseconds" then + typeValueCoeff = 0.001 + elseif BOpt.ValueType == "frames" then + typeValueCoeff = reaper.parse_timestr_pos( "00:00:00:01", 5 ) + end + + for i = 1, #RazorEditsBatch do + local areaData = RazorEditsBatch[i] + if not areaData.isEnvelope then + local items = areaData.items - if RespectLockingBatch:match('n') == 'n' then - RespectLocking = false - reaper.Main_OnCommandEx(40596,0,0) --Locking: Clear item edges locking mode - if start_TS ~= end_TS then - razorEdits = GetTSandItems(start_TS, end_TS) - else - razorEdits = GetRazorEdits() - end - else RespectLocking = true - end + local PrevEnd + local PrevFout - for i = 1, #razorEdits do - local areaData = razorEdits[i] - if not areaData.isEnvelope then - local items = areaData.items - - local PrevEnd - local PrevFout - local AnyEdit - - for i, item in pairs(items) do - local iPos = reaper.GetMediaItemInfo_Value( item, "D_POSITION" ) - local iEnd = iPos + reaper.GetMediaItemInfo_Value( item, "D_LENGTH" ) - local iFin = reaper.GetMediaItemInfo_Value( item, "D_FADEINLEN" ) - local iFout = reaper.GetMediaItemInfo_Value( item, "D_FADEOUTLEN" ) + for i, item in pairs(items) do + local iPos = reaper.GetMediaItemInfo_Value( item, "D_POSITION" ) + local iEnd = iPos + reaper.GetMediaItemInfo_Value( item, "D_LENGTH" ) + local iFin = reaper.GetMediaItemInfo_Value( item, "D_FADEINLEN" ) + local iFout = reaper.GetMediaItemInfo_Value( item, "D_FADEOUTLEN" ) + + if BOpt.RespectExistingFades == false then iFin = 0 iFout = 0 end + + if iPos >= areaData.areaStart then + if PrevEnd and math.abs(iPos - PrevEnd) < 0.0002 then + local crossStart, crossEnd + if BOpt.DefCrossType == 'Left' then + crossStart = iPos - BOpt.DefaultCrossFade * typeValueCoeff + crossEnd = iPos + elseif BOpt.DefCrossType == 'Centered' then + crossStart = iPos - BOpt.DefaultCrossFade/2 * typeValueCoeff + crossEnd = iPos + BOpt.DefaultCrossFade/2 * typeValueCoeff + end - if RespectExistingFades:match('n') == 'n' then iFin = 0 iFout = 0 end + local crossData = { + areaStart = crossStart, + areaEnd = crossEnd, + } - if iPos >= areaData.areaStart then - if iPos == PrevEnd then - local crossStart, crossEnd - if DefCrossType == 1 then - crossStart = iPos - DefaultCrossFade/1000 - crossEnd = iPos - else - crossStart = iPos - DefaultCrossFade/2000 - crossEnd = iPos + DefaultCrossFade/2000 - end - - local crossData = { - areaStart = crossStart, - areaEnd = crossEnd, - } - - if PrevFout == 0 and iFin == 0 then - SetCrossfade(items[i-1],item, crossData) - AnyEdit = 1 - elseif PrevFout == 0 then - SetFade(items[i-1], "out", DefaultFadeOut/1000) - AnyEdit = 1 - elseif iFin == 0 then - SetFade(item, "in", DefaultFadeIn/1000) - AnyEdit = 1 - end - - elseif not PrevEnd or iPos > PrevEnd then - if iFin == 0 then - SetFade(item, "in", DefaultFadeIn/1000) - AnyEdit = 1 - end - if PrevFout == 0 then - SetFade(items[i-1], "out", DefaultFadeOut/1000) - AnyEdit = 1 - end - end - - if i == #items and iFout == 0 and iEnd <= areaData.areaEnd then --fadeout for the last item - SetFade(item, "out", DefaultFadeOut/1000) - AnyEdit = 1 - end + if PrevFout == 0 and iFin == 0 then + SetCrossfade(items[i-1],item, crossData) + AnyEdit = 1 + elseif PrevFout == 0 then + SetFade(items[i-1], "out", BOpt.DefaultFadeOut * typeValueCoeff) + AnyEdit = 1 + elseif iFin == 0 then + SetFade(item, "in", BOpt.DefaultFadeIn * typeValueCoeff) + AnyEdit = 1 end - PrevEnd = iEnd - PrevFout = iFout - end -- for in pairs(items) - - if AnyEdit == nil then - reaper.defer(function()end) - else - reaper.Main_OnCommandEx(42406, 0, 0) --Clear RE area - if start_TS ~= end_TS then - reaper.Main_OnCommandEx(40020, 0, 0) - --Time selection: Remove (unselect) time selection and loop points + elseif not PrevEnd or iPos > PrevEnd then + if iFin == 0 then + SetFade(item, "in", BOpt.DefaultFadeIn * typeValueCoeff) + AnyEdit = 1 + end + if PrevFout == 0 then + SetFade(items[i-1], "out", BOpt.DefaultFadeOut * typeValueCoeff) + AnyEdit = 1 end end - end --if not area is Envelope - end -- end for cycle #razorEdits - end --if done + + if i == #items and iFout == 0 and iEnd <= areaData.areaEnd then + --fadeout for the last item + SetFade(item, "out", BOpt.DefaultFadeOut * typeValueCoeff) + AnyEdit = 1 + end + end + + PrevEnd = iEnd + PrevFout = iFout + end -- for in pairs(items) + + end --if not area is Envelope + end -- end for cycle #razorEdits + + + if AnyEdit == nil then + reaper.defer(function()end) + else + reaper.Main_OnCommandEx(42406, 0, 0) --Clear RE area + if start_TS ~= end_TS then + reaper.Main_OnCommandEx(40020, 0, 0) + --Time selection: Remove (unselect) time selection and loop points + end + UndoString = 'FadeTool - Batch fades/crossfades' + end + + TheRestAfterBatch() + end +----------------------- +function TheRestAfterBatch() + if AutoCrosState == 0 then + reaper.Main_OnCommandEx(40041,0,0) --Options: Toggle auto-crossfade on/off + end + reaper.PreventUIRefresh(-1) + RestoreLockedItems() + + if globalLock == 1 then + reaper.Main_OnCommandEx(40569,0,0) --Locking: Enable locking + end + if trimContBehItems == 1 then + reaper.Main_OnCommandEx(41120,0,0) --Options: Enable trim content behind media items when editing + end + + if UndoString ~= nil then + if sTime then MoveEditCursor(sTime) end + reaper.Undo_EndBlock2( 0, UndoString, -1 ) + reaper.UpdateArrange() + else reaper.defer(function()end) + end +end ----------------------- ----------------------- function MouseOverWhat(razorEdits) + if not reaper.APIExists("JS_Mouse_GetCursor") then + reaper.ShowMessageBox('Missing API!\nInstall js_ReaScriptAPI from ReaPack', 'Fade tool',0) + return + end local currentcur = reaper.JS_Mouse_GetCursor() local fadeid = {105,184, 529} for id=202, 204 do @@ -932,88 +1069,104 @@ end ----------------------- ----------------------- -function CutEnv(env,pointsNumber,pointsTable,areaStart,areaEnd) - local cutShape = 5 +function CutEnv(env,pointsTable,areaEdge1,areaEdge2) + local areaStart = math.min(areaEdge1,areaEdge2) + local areaEnd = math.max(areaEdge1,areaEdge2) + local pointsNumber = #pointsTable + if pointsNumber == 0 then return false end + + local cutShape = 0 + if Opt.CutShapeUseDef == true then cutShape = tonumber(ExtractDefPointShape(env)) end + local inBordRet, inBordVal, _,_,_ = reaper.Envelope_Evaluate( env, areaStart, 192000, 1 ) - local inBordRet, outBordVal, _,_,_ = reaper.Envelope_Evaluate( env, areaEnd, 192000, 1 ) + local outBordRet, outBordVal, _,_,_ = reaper.Envelope_Evaluate( env, areaEnd, 192000, 1 ) local PrevPindex = pointsTable[1]["id"] -1 - local InnerPindex = pointsTable[pointsNumber]["id"] + local InnerPindex = pointsTable[pointsNumber]["id"] local _, prevPtime, prevPvalue, prevPshape, prevPtension, _ = reaper.GetEnvelopePointEx(env, -1, PrevPindex) - local _, _, _, inPshape, inPtension, _ = + local _, inPtime, _, inPshape, inPtension, _ = reaper.GetEnvelopePointEx(env, -1, InnerPindex) local _, afterPtime, afterPvalue, afterPshape, afterPtension, _ = - reaper.GetEnvelopePointEx(env, -1, pointsTable[#pointsTable]["id"]+1) - - local i = pointsNumber - while i > 0 do - local point = pointsTable[i] - local id = point.id - local time = point.time - local value = point.value - local shape = point.shape - local tension = point.tension - local selected = point.selected - - reaper.DeleteEnvelopePointEx( env, -1, id ) - i=i-1 - end + reaper.GetEnvelopePointEx(env, -1, pointsTable[pointsNumber]["id"]+1) + + reaper.DeleteEnvelopePointRangeEx( env, -1, areaStart, areaEnd ) reaper.InsertEnvelopePointEx( env, -1, areaStart, inBordVal, cutShape, 0, false, true ) if inPtension ~= 0 then local right_tenscoeff= math.sqrt((afterPtime - pointsTable[pointsNumber]["time"])/(afterPtime - areaEnd)) inPtension = inPtension/right_tenscoeff end - reaper.InsertEnvelopePointEx( env, -1, areaEnd, outBordVal, inPshape, inPtension, false, true ) + + if inPtime ~= areaEnd then + reaper.InsertEnvelopePointEx( env, -1, areaEnd, outBordVal, inPshape, inPtension, false, true ) + end + if prevPtension ~=0 then local left_tenscoeff = math.sqrt((pointsTable[1]["time"] - prevPtime)/(areaStart - prevPtime)) reaper.SetEnvelopePointEx(env,-1,PrevPindex, prevPtime, prevPvalue, prevPshape, prevPtension/left_tenscoeff, true, true) end -end - ------------------------ ------------------------ - -function InOutEnv(env) - if #envPoints.End > 1 and MouseOverRazor == true then - local iE = #envPoints.End - while iE > 1 do - reaper.DeleteEnvelopePointEx( env, -1, envPoints.End[iE] ) - iE = iE-1 - end - end - if #envPoints.Start > 1 and MouseOverRazor == true then - local iS = #envPoints.Start - while iS > 1 do - reaper.DeleteEnvelopePointEx( env, -1, envPoints.Start[iS] ) - iS = iS-1 - end - end - - --reaper.InsertEnvelopePointEx( env, -1, areaStart, inBordVal, prevPshape, prevPtension, false, true ) - --reaper.InsertEnvelopePointEx( env, -1, areaEnd, outBordVal, inPshape, inPtension, false, true ) + return true end + ----------------------- ----------------------- function GetFadeMouse(item) + local mpos = reaper.BR_PositionAtMouseCursor(false) local ipos = reaper.GetMediaItemInfo_Value( item,'D_POSITION' ) local iend = ipos + reaper.GetMediaItemInfo_Value( item, 'D_LENGTH' ) local finlen = reaper.GetMediaItemInfo_Value( item,'D_FADEINLEN' ) local foutlen = reaper.GetMediaItemInfo_Value( item, 'D_FADEOUTLEN' ) + local finlenAuto = reaper.GetMediaItemInfo_Value( item,'D_FADEINLEN_AUTO' ) + local foutlenAuto = reaper.GetMediaItemInfo_Value( item, 'D_FADEOUTLEN_AUTO' ) local finshape = reaper.GetMediaItemInfo_Value( item,'C_FADEINSHAPE' ) local foutshape = reaper.GetMediaItemInfo_Value( item,'C_FADEOUTSHAPE' ) - local window, segment, details = reaper.BR_GetMouseCursorContext() - local mouse_pos = reaper.BR_GetMouseCursorContext_Position() - - if MouseCUR:match('left')=='left' then + + if finlenAuto > 0 then finlen = finlenAuto end + if foutlenAuto > 0 then foutlen = foutlenAuto end + + local fadeType + if mpos < iend - foutlen then + if MouseCUR:match('left') then + fadeType = "L" + elseif MouseCUR:match('right') then + fadeType = "R" + local leftItem = FindXfadedNeigbourItem(item, ipos, iend, -1) + + local lipos = reaper.GetMediaItemInfo_Value( leftItem,'D_POSITION' ) + iend = lipos + reaper.GetMediaItemInfo_Value( leftItem, 'D_LENGTH' ) + foutlen = reaper.GetMediaItemInfo_Value( leftItem, 'D_FADEOUTLEN' ) + foutlenAuto = reaper.GetMediaItemInfo_Value( leftItem, 'D_FADEOUTLEN_AUTO' ) + foutshape = reaper.GetMediaItemInfo_Value( leftItem,'C_FADEOUTSHAPE' ) + end + elseif mpos >= iend - foutlen then + if MouseCUR:match('right') then + fadeType = "R" + elseif MouseCUR:match('left') then + fadeType = "L" + local rightItem = FindXfadedNeigbourItem(item, ipos, iend, 1) + + ipos = reaper.GetMediaItemInfo_Value( rightItem,'D_POSITION' ) + finlen = reaper.GetMediaItemInfo_Value( item,'D_FADEINLEN' ) + finlenAuto = reaper.GetMediaItemInfo_Value( item,'D_FADEINLEN_AUTO' ) + finshape = reaper.GetMediaItemInfo_Value( item,'C_FADEINSHAPE' ) + end + end + + if finlenAuto > 0 then finlen = finlenAuto end + if foutlenAuto > 0 then foutlen = foutlenAuto end + + if finshape == 7 then finshape = 1 end + if foutshape == 7 then foutshape = 1 end + + if fadeType == "L" then local fT = {0,3,4,3,4,2,2} finshape = fT[finshape+1] return finshape, ipos, ipos + finlen - elseif MouseCUR:match('right')=='right' then + elseif fadeType == "R" then local fT = {0,4,3,4,3,2,2} foutshape = fT[foutshape+1] return foutshape, iend - foutlen, iend @@ -1023,9 +1176,10 @@ end ------------------- function ItemFadeToEnv(env,pointsNumber,pointsTable,areaStart,areaEnd) - local item_mouse = reaper.BR_GetMouseCursorContext_Item() + local x, y = reaper.GetMousePosition() + local item_mouse = reaper.GetItemFromPoint(x,y, true) local fShape, fStart, fEnd = GetFadeMouse(item_mouse) - + if areaStart < fEnd and fStart < areaEnd then local _, infVal, _,_,_ = reaper.Envelope_Evaluate( env, areaStart, 192000, 1 ) local _, outfVal, _,_,_ = reaper.Envelope_Evaluate( env, areaEnd, 192000, 1 ) @@ -1046,16 +1200,11 @@ function ItemFadeToEnv(env,pointsNumber,pointsTable,areaStart,areaEnd) reaper.GetEnvelopePointEx(env, -1, InnerPindex) reaper.DeleteEnvelopePointRangeEx( env, -1, Start, End+0.0001) - --[[ - if infVal ~= prevPvalue then - reaper.InsertEnvelopePointEx( env, -1, Start, startVal, 0, 0, false, true ) - end - if outfVal ~= nextPvalue then - reaper.InsertEnvelopePointEx( env, -1, End, endVal, 2, 0, false, true ) - end - ]] + reaper.InsertEnvelopePointEx(env,-1,fStart, infVal, fShape, 0, false, true ) reaper.InsertEnvelopePointEx(env,-1,fEnd, outfVal, inPshape, inPtension, false, true ) + + UndoString = 'Fade tool - item fade to envelope' return fStart end end @@ -1074,9 +1223,9 @@ function FadeEnvelope(areaData) local envPoints = areaData.envelopePoints local GUID = areaData.GUID local pointsNumber = #envPoints - + --msg(pointsNumber) local inBordRet, inBordVal, _,_,_ = reaper.Envelope_Evaluate( env, areaStart, 192000, 1 ) - local inBordRet, outBordVal, _,_,_ = reaper.Envelope_Evaluate( env, areaEnd, 192000, 1 ) + local outBordRet, outBordVal, _,_,_ = reaper.Envelope_Evaluate( env, areaEnd, 192000, 1 ) --here we need to get shape from neighbour points --get points index by time or earlier then time @@ -1088,73 +1237,118 @@ function FadeEnvelope(areaData) local _, _, _, inPshape, inPtension, _ = reaper.GetEnvelopePointEx(env, -1, InnerPindex) - --msg(pointsNumber) - --msg(inPtension) - UndoString = 'Edit envelope - Fade tool' + if MouseCUR:match('item fade')=='item fade' then fadestart = ItemFadeToEnv(env,pointsNumber,envPoints,areaStart,areaEnd) goto continue end - if pointsNumber > 2 and - (MouseCUR ~= 'razor env' or (#envPoints.Start==0 and #envPoints.End==0) ) then - CutEnv(env,pointsNumber,envPoints,areaStart,areaEnd) - end + if MouseCUR == 'razor env' then + if pointsNumber > 2 then + + if #envPoints.End > 1 or #envPoints.Start > 1 then + if #envPoints.End > 1 then + local iE = #envPoints.End + while iE > 0 do + reaper.DeleteEnvelopePointEx( env, -1, envPoints.End[iE] ) + iE = iE-1 + end + + DONTremoveRazor = true + end + + if #envPoints.Start > 1 then + local iS = #envPoints.Start + while iS > 0 do + reaper.DeleteEnvelopePointEx( env, -1, envPoints.Start[iS] ) + iS = iS-1 + end + + DONTremoveRazor = true + end + + if #envPoints.End > 1 then + reaper.InsertEnvelopePointEx( env, -1, areaEnd, outBordVal, inPshape, inPtension, false, true ) + end + if #envPoints.Start > 1 then + reaper.InsertEnvelopePointEx( env, -1, areaStart, inBordVal, prevPshape, prevPtension, false, true ) + end + + UndoString = 'Fade tool - remove inner edge points' + + elseif #envPoints.Start <= 1 and #envPoints.End <= 1 then + CutEnv(env,envPoints,areaStart,areaEnd) + UndoString = 'Fade tool - Cut envelope segment' + end + + end --if pointsNumber > 2 + + if pointsNumber <= 2 then + CutEnv(env,envPoints,areaStart,areaEnd) + UndoString = 'Fade tool - Cut envelope segment' + end + end --if MouseCUR == 'razor env' - if pointsNumber <= 2 and pointsNumber > 0 and MouseCUR ~= 'razor env' then - StretchEnv(env,pointsNumber,envPoints,areaStart,areaEnd) - elseif pointsNumber <= 2 and pointsNumber > 0 and MouseCUR == 'razor env' then - CutEnv(env,pointsNumber,envPoints,areaStart,areaEnd) - end - if pointsNumber > 2 and MouseCUR == 'razor env' then - - if #envPoints.End > 1 or #envPoints.Start > 1 then - if #envPoints.End > 1 then - local iE = #envPoints.End - while iE > 0 do - reaper.DeleteEnvelopePointEx( env, -1, envPoints.End[iE] ) - iE = iE-1 + if MouseCUR ~= 'razor env' then + + if pointsNumber > 2 then + if MouseDistDiff then + local newStart = areaStart + MouseDistDiff + local newEnd = areaEnd - MouseDistDiff + fadestart = math.min(areaStart, newStart) + + if MouseDistDiff < 0 then + areaStart = envPoints[2]['time'] + areaEnd = envPoints[#envPoints -1]['time'] end - reaper.InsertEnvelopePointEx( env, -1, areaEnd, outBordVal, inPshape, inPtension, false, true ) - DONTremoveRazor = true - end - - if #envPoints.Start > 1 then - local iS = #envPoints.Start - while iS > 0 do - reaper.DeleteEnvelopePointEx( env, -1, envPoints.Start[iS] ) - iS = iS-1 + + envPoints = GetEnvelopePointsInRange(env, areaEnd,newEnd) + local ret = CutEnv(env,envPoints,areaEnd,newEnd) + reaper.Envelope_SortPointsEx( env, -1 ) + + envPoints = GetEnvelopePointsInRange(env, areaStart, newStart) + local ret2 = CutEnv(env,envPoints,areaStart,newStart) + + if ret == true or ret2 == true then + UndoString = 'Fade tool - Smooth env RE area edges with mouse' + else DONTremoveRazor = true end - reaper.InsertEnvelopePointEx( env, -1, areaStart, inBordVal, prevPshape, prevPtension, false, true ) - --local tenscoeff = math.sqrt((envPoints[1]["time"] - prevPtime)/(areaStart - prevPtime)) - --reaper.SetEnvelopePointEx(env,-1,PrevPindex, prevPtime, prevPvalue, 5, prevPtension/tenscoeff, true, true) - DONTremoveRazor = true - end - - elseif #envPoints.End == 1 or #envPoints.Start == 1 then - if #envPoints.End == 1 then - reaper.DeleteEnvelopePointEx( env, -1, envPoints.End[1] ) - DONTremoveRazor = true - end - if #envPoints.Start == 1 then - reaper.DeleteEnvelopePointEx( env, -1, envPoints.Start[1] ) + else DONTremoveRazor = true end + goto continue end - - end - - if pointsNumber == 0 then - local _, _, outPvalue, outPshape, outPtension, _ = - reaper.GetEnvelopePointEx(env,-1,PrevPindex+1) - reaper.SetEnvelopePointEx(env,-1,PrevPindex, areaStart, prevPvalue, prevPshape, prevPtension, true, true) - reaper.SetEnvelopePointEx(env,-1,PrevPindex+1, areaEnd, outPvalue, outPshape, outPtension, true, true) - end + + if pointsNumber <= 2 and pointsNumber > 0 then + StretchEnv(env,pointsNumber,envPoints,areaStart,areaEnd) + UndoString = 'Fade tool - Expand envelope points' + elseif pointsNumber == 0 then + local _, _, outPvalue, outPshape, outPtension, _ = + reaper.GetEnvelopePointEx(env,-1,PrevPindex+1) + reaper.SetEnvelopePointEx(env,-1,PrevPindex, areaStart, prevPvalue, prevPshape, prevPtension, true, true) + reaper.SetEnvelopePointEx(env,-1,PrevPindex+1, areaEnd, outPvalue, outPshape, outPtension, true, true) + UndoString = 'Fade tool - Collapse envelope points' + end + --[[ Replaced by Smooth env RE area edges + if pointsNumber > 2 then + CutEnv(env,envPoints,areaStart,areaEnd) + UndoString = 'Fade tool - Cut envelope segment' + end + ]] + end --if MouseCUR ~= 'razor env' ::continue:: reaper.Envelope_SortPointsEx( env, -1 ) + + if UndoString then --Deselect all env points: + local pcount = reaper.CountEnvelopePointsEx(env, -1) + for i = 0, pcount -1 do + reaper.SetEnvelopePointEx(env, -1, i, nil, nil, nil, nil, false, true) + end + end + return fadestart end @@ -1167,112 +1361,130 @@ function FadeRazorEdits(razorEdits, needBatch) --get areaMap table and batch fla local fadeStartEdge reaper.PreventUIRefresh(1) - local state = reaper.GetToggleCommandState(40041) --Options: Toggle auto-crossfade on/off - if state == 0 then + AutoCrosState = reaper.GetToggleCommandState(40041) --Options: Toggle auto-crossfade on/off + if AutoCrosState == 0 then reaper.Main_OnCommandEx(40041,0,0) end if needBatch == true then - if fulliLock == 0 or RespectLocking == false then - UndoString = 'Batch fades/crossfades' - BatchFades(razorEdits) + if fulliLock == 0 then + BatchFadesWindow(razorEdits) end else - - if fulliLock == 0 or RespectLocking == false then - local i = #razorEdits - while i > 0 do - local areaData = razorEdits[i] - if not areaData.isEnvelope then - local items = areaData.items - local Litem - local Ritem + local i = #razorEdits + while i > 0 do + local areaData = razorEdits[i] + + if not areaData.isEnvelope + and (RAZORenvelopes == false + or (window == 'arrange' and (segment == 'track' or segment == 'empty'))) + then + local items = areaData.items + local Litem + local Ritem + + + if #items == 0 or #items > 2 then + reaper.defer(function()end) + end + + ---------Define L/R items-------- + if #items == 2 then + local item_1 = items[1] + local item_2 = items[2] + local iPos_1 = reaper.GetMediaItemInfo_Value(item_1, "D_POSITION") + local itemEndPos_1 = iPos_1+reaper.GetMediaItemInfo_Value(item_1, "D_LENGTH") + local iPos_2 = reaper.GetMediaItemInfo_Value(item_2, "D_POSITION") + local itemEndPos_2 = iPos_2+reaper.GetMediaItemInfo_Value(item_2, "D_LENGTH") - - if #items == 0 or #items > 2 then - reaper.defer(function()end) + if iPos_1 <= areaData.areaStart and itemEndPos_1 > areaData.areaStart and iPos_1 <= iPos_2 + and iPos_2 < areaData.areaEnd and itemEndPos_2 >= areaData.areaEnd + and itemEndPos_1 <= itemEndPos_2 + then + Litem = item_1 + Ritem = item_2 end - - ---------Define L/R items-------- - if #items == 2 then - local item_1 = items[1] - local item_2 = items[2] - local iPos_1 = reaper.GetMediaItemInfo_Value(item_1, "D_POSITION") - local itemEndPos_1 = iPos_1+reaper.GetMediaItemInfo_Value(item_1, "D_LENGTH") - local iPos_2 = reaper.GetMediaItemInfo_Value(item_2, "D_POSITION") - local itemEndPos_2 = iPos_2+reaper.GetMediaItemInfo_Value(item_2, "D_LENGTH") - - if iPos_1 <= areaData.areaStart and itemEndPos_1 > areaData.areaStart and iPos_1 <= iPos_2 and - iPos_2 < areaData.areaEnd and itemEndPos_2 >= areaData.areaEnd and itemEndPos_1 <= itemEndPos_2 then - Litem = item_1 - Ritem = item_2 - end - - elseif #items == 1 then - item_1 = items[1] - local iPos_1 = reaper.GetMediaItemInfo_Value(item_1, "D_POSITION") - local itemEndPos_1 = iPos_1+reaper.GetMediaItemInfo_Value(item_1, "D_LENGTH") - if (iPos_1 <= areaData.areaStart and itemEndPos_1 < areaData.areaEnd) or - (iPos_1 < areaData.areaStart and itemEndPos_1 <= areaData.areaEnd)then - Litem = item_1 - elseif (iPos_1 >= areaData.areaStart and itemEndPos_1 > areaData.areaEnd) or - (iPos_1 > areaData.areaStart and itemEndPos_1 >= areaData.areaEnd)then - Ritem = item_1 - end - end - - -------------1st case------------- - if #items == 2 and Litem and Ritem and (edgesLock == 0 or RespectLocking == false) then - UndoString = "Set crossfade in RE area" - --create crossfade - table.insert(fadeStartT, areaData.areaStart) - SetCrossfade(Litem,Ritem,areaData) - + elseif #items == 1 then + + item_1 = items[1] + local iPos_1 = reaper.GetMediaItemInfo_Value(item_1, "D_POSITION") + local itemEndPos_1 = iPos_1+reaper.GetMediaItemInfo_Value(item_1, "D_LENGTH") + if (iPos_1 <= areaData.areaStart and itemEndPos_1 < areaData.areaEnd) or + (iPos_1 < areaData.areaStart and itemEndPos_1 <= areaData.areaEnd)then + Litem = item_1 + elseif (iPos_1 >= areaData.areaStart and itemEndPos_1 > areaData.areaEnd) or + (iPos_1 > areaData.areaStart and itemEndPos_1 >= areaData.areaEnd)then + Ritem = item_1 end - -------End of the 1st case----------- + end + + -------------1st case------------- + if #items == 2 and Litem and Ritem + and (fulliLock == 0 and edgesLock == 0) then + UndoString = "FadeTool - razor area" + --create crossfade + table.insert(fadeStartT, areaData.areaStart) + SetCrossfade(Litem,Ritem,areaData) + end + -------End of the 1st case----------- + + + -------Other 3 cases----------- + if fulliLock == 1 then return end + + if #items == 1 then + UndoString = "FadeTool - razor area" + MoveItemEdgeRazor = Opt.MoveItemEdgeRazor - -------Other 3 cases----------- - if #items == 1 then - UndoString = "Set fade by RE area" - if Ritem and Litem == nil then --fade IN - local item = items[1] - local iPos = reaper.GetMediaItemInfo_Value( item, "D_POSITION" ) + if Ritem and Litem == nil then --fade IN + local item = items[1] + local iPos = reaper.GetMediaItemInfo_Value( item, "D_POSITION" ) + if Opt.MoveItemEdgeRazor == true then + table.insert(fadeStartT, areaData.areaStart) + SetCrossfade(_,item,areaData) + else fadeStartEdge = SetFade(item, "in", areaData.areaEnd - iPos) if fadeStartEdge ~= nil then table.insert(fadeStartT, fadeStartEdge) end - - elseif Litem and Ritem == nil then -- fade OUT - local item = items[1] - local iPos = reaper.GetMediaItemInfo_Value( item, "D_POSITION" ) - local iEnd = iPos + reaper.GetMediaItemInfo_Value( item, "D_LENGTH" ) + end + elseif Litem and Ritem == nil then -- fade OUT + local item = items[1] + local iPos = reaper.GetMediaItemInfo_Value( item, "D_POSITION" ) + local iEnd = iPos + reaper.GetMediaItemInfo_Value( item, "D_LENGTH" ) + if Opt.MoveItemEdgeRazor == true then + table.insert(fadeStartT, areaData.areaStart) + SetCrossfade(item,_,areaData) + else fadeStartEdge = SetFade(item, "out", iEnd - areaData.areaStart) if fadeStartEdge ~= nil then table.insert(fadeStartT, fadeStartEdge) end - - elseif Litem == nil and Ritem == nil then -- fades on the rests - local item = items[1] - local iPos = reaper.GetMediaItemInfo_Value( item, "D_POSITION" ) - local iEnd = iPos + reaper.GetMediaItemInfo_Value( item, "D_LENGTH" ) - fadeStartEdge = SetFade(item, "in", areaData.areaStart - iPos) - SetFade(item, "out", iEnd - areaData.areaEnd) - if fadeStartEdge ~= nil then table.insert(fadeStartT, fadeStartEdge) end - - end - end --other 3 cases - - else -- if not area is envelope - if ONLYenvelopes == true then - if RespectLocking == false or envLock == 0 then - if not MouseCUR then MouseCUR = MouseOverWhat(razorEdits) end - table.insert(fadeStartT, FadeEnvelope(areaData)) end + + elseif Litem == nil and Ritem == nil then -- fades on the rests + local item = items[1] + local iPos = reaper.GetMediaItemInfo_Value( item, "D_POSITION" ) + local iEnd = iPos + reaper.GetMediaItemInfo_Value( item, "D_LENGTH" ) + fadeStartEdge = SetFade(item, "in", areaData.areaStart - iPos) + SetFade(item, "out", iEnd - areaData.areaEnd) + if fadeStartEdge ~= nil then table.insert(fadeStartT, fadeStartEdge) end + + end + end --other 3 cases + + elseif areaData.isEnvelope then -- if area is envelope + if (window == 'arrange' and segment == 'envelope') + or ONLYenvelopes == true + then + if envLock == 0 then + if not MouseCUR then MouseCUR = MouseOverWhat(razorEdits)end + table.insert(fadeStartT, FadeEnvelope(areaData)) end - end -- if not area is envelope - i = i-1 - end -- end cycle through areas - end --if not locking + end + end -- if not area is envelope + i = i-1 + end -- end cycle through areas - if #fadeStartT == 0 and UndoString ~= 'Batch fades/crossfades' then UndoString = nil + if #fadeStartT == 0 and UndoString ~= 'FadeTool - Batch fades/crossfades' then UndoString = nil else if DONTremoveRazor ~= true then reaper.Main_OnCommandEx(42406, 0, 0) --Clear RE area @@ -1285,7 +1497,7 @@ function FadeRazorEdits(razorEdits, needBatch) --get areaMap table and batch fla end --else (if no Batch) - if state == 0 then + if AutoCrosState == 0 and not RunBatch then reaper.Main_OnCommandEx(40041,0,0) --Options: Toggle auto-crossfade on/off end reaper.PreventUIRefresh(-1) @@ -1310,7 +1522,8 @@ function SaveSelItemsByTracks(startTime, endTime) --- time optional local iEnd = iPos + reaper.GetMediaItemInfo_Value( item, "D_LENGTH" ) local iLock = reaper.GetMediaItemInfo_Value(item, "C_LOCK") - if iPos < endTime and iEnd > startTime and (iLock==0 or RespectLocking == false) then + if iPos < endTime and iEnd > startTime + and (iLock==0 or RespectLockingBatch == false) then if iPos >= startTime and iEnd <= endTime then needBatch = true end else item = nil @@ -1352,7 +1565,7 @@ function GetTSandItems(start_TS, end_TS) --returns areaMap and needBatch local itemEndPos_2 = iPos_2+reaper.GetMediaItemInfo_Value(item_2, "D_LENGTH") local iLock_2 = reaper.GetMediaItemInfo_Value(item_2, "C_LOCK") - if (iLock_1 == 0 and iLock_2 == 0) or RespectLocking == false then + if iLock_1 == 0 and iLock_2 == 0 then if iPos_1 <= start_TS and itemEndPos_1 > start_TS and iPos_1 < iPos_2 and iPos_2 < end_TS and itemEndPos_2 >= end_TS and itemEndPos_1 < itemEndPos_2 then items[1] = item_1 @@ -1484,16 +1697,23 @@ function SetFade(item, f_type, f_size, shape ) -- returns fadeStartEdge local InitOutFade = reaper.GetMediaItemInfo_Value( item, "D_FADEOUTLEN" ) local iPos = reaper.GetMediaItemInfo_Value( item, "D_POSITION" ) local iLock = reaper.GetMediaItemInfo_Value( item, "C_LOCK" ) + local i_autoFin = reaper.GetMediaItemInfo_Value(item, "D_FADEINLEN_AUTO") - if (iLock == 0 and fadesLock == 0) or RespectLocking == false then + if (iLock == 0 and fadesLock == 0) or Opt.IgnoreLockingMouse == true + or RespectLockingBatch == false then if InitOutFade ~= 0 then local iEnd = iPos + reaper.GetMediaItemInfo_Value( item, "D_LENGTH" ) local fEdge = iPos + f_size if fEdge > iEnd - InitOutFade then - reaper.SetMediaItemInfo_Value( item, "D_FADEOUTLEN", iEnd - fEdge ) + reaper.SetMediaItemInfo_Value( item, "D_FADEOUTLEN", iEnd - fEdge ) end end + if i_autoFin ~= 0 then + shape = reaper.GetMediaItemInfo_Value(item, "C_FADEINSHAPE") + reaper.SetMediaItemInfo_Value(item, "D_FADEINLEN_AUTO", 0) + reaper.SetMediaItemInfo_Value(item, "C_FADEINSHAPE", shape ) + end reaper.SetMediaItemInfo_Value( item, "D_FADEINLEN", f_size ) return iPos end @@ -1505,14 +1725,28 @@ function SetFade(item, f_type, f_size, shape ) -- returns fadeStartEdge local iEnd = iPos + reaper.GetMediaItemInfo_Value( item, "D_LENGTH" ) local fEdge = iEnd - f_size local iLock = reaper.GetMediaItemInfo_Value( item, "C_LOCK" ) + local i_autoFout = reaper.GetMediaItemInfo_Value(item, "D_FADEOUTLEN_AUTO") + local i_autoFin = reaper.GetMediaItemInfo_Value(item, "D_FADEINLEN_AUTO") + if i_autoFin ~= 0 then InitInFade = i_autoFin end - if (iLock == 0 and fadesLock == 0) or RespectLocking == false then + if (iLock == 0 and fadesLock == 0) or Opt.IgnoreLockingMouse == true + or RespectLockingBatch == false then if InitInFade ~= 0 then if fEdge < iPos + InitInFade then + if i_autoFin ~= 0 then + shape = reaper.GetMediaItemInfo_Value(item, "C_FADEINSHAPE") + reaper.SetMediaItemInfo_Value(item, "D_FADEINLEN_AUTO", 0) + reaper.SetMediaItemInfo_Value(item, "C_FADEINSHAPE", shape ) + end reaper.SetMediaItemInfo_Value( item, "D_FADEINLEN", fEdge - iPos ) end end + if i_autoFout ~= 0 then + shape = reaper.GetMediaItemInfo_Value(item, "C_FADEOUTSHAPE") + reaper.SetMediaItemInfo_Value(item, "D_FADEOUTLEN_AUTO", 0) + reaper.SetMediaItemInfo_Value(item, "C_FADEOUTSHAPE", shape ) + end reaper.SetMediaItemInfo_Value( item, "D_FADEOUTLEN", f_size ) return fEdge end @@ -1543,65 +1777,445 @@ end ---------------------------- -function WhatFade(half, i_pos, i_end, mPos) - local f_type, f_size +function FindXfadedNeigbourItem(item, i_pos, i_end, step) --step decreasing or increasing item number + local i_numb = reaper.GetMediaItemInfo_Value(item, "IP_ITEMNUMBER") + local i_Y = reaper.GetMediaItemInfo_Value(item, "F_FREEMODE_Y") + local i_H = reaper.GetMediaItemInfo_Value(item, "F_FREEMODE_H") + local nextItem = item + local track = reaper.GetMediaItemTrack(item) + i_numb = i_numb + step + while nextItem do + nextItem = reaper.GetTrackMediaItem(track, i_numb) + if nextItem then + local ni_pos = reaper.GetMediaItemInfo_Value(nextItem, "D_POSITION") + local ni_end = ni_pos + reaper.GetMediaItemInfo_Value(nextItem, "D_LENGTH") + local ni_Y = reaper.GetMediaItemInfo_Value(nextItem, "F_FREEMODE_Y") + local ni_H = reaper.GetMediaItemInfo_Value(nextItem, "F_FREEMODE_H") + + if ((ni_end > i_pos and ni_end < i_end) or (ni_pos > i_pos and ni_pos < i_end)) + and ( ni_Y < i_Y + i_H and ni_Y + ni_H > i_Y ) + then + return nextItem + end + + if ni_end < i_pos or ni_pos > i_end then break end + end + i_numb = i_numb + step + end +end + +---------------------------- + +function WhatFade(half, leftF, rightF, mPos) + local f_type - if LRdefine == 1 then --cross define + if Opt.LRdefine == 'Closest/Farthest fade' then --cross define if half == "top" then - if (i_end - mPos) <= (mPos - i_pos) then + if (rightF - mPos) <= (mPos - leftF) then f_type = "out" - f_size = i_end - mPos else f_type = "in" - f_size = mPos - i_pos end elseif half == "bottom" then - if (i_end - mPos) > (mPos - i_pos) then + if (rightF - mPos) > (mPos - leftF) then f_type = "out" - f_size = i_end - mPos else f_type = "in" - f_size = mPos - i_pos end end - elseif LRdefine == 2 then + elseif Opt.LRdefine == 'Left/Right fade' then if half == "top" then f_type = "in" - f_size = mPos - i_pos elseif half == "bottom" then f_type = "out" - f_size = i_end - mPos end end - return f_type, f_size + + return f_type +end + +---------------------------- + +function SortSelItems(Items, ref_item, ref_leftItem, ref_rightItem, reverseFlag) + local ret + local ref_i_pos, ref_i_end, ref_fLeftpos, ref_fRightpos + ref_i_pos = reaper.GetMediaItemInfo_Value(ref_item, "D_POSITION") + ref_i_end = ref_i_pos + reaper.GetMediaItemInfo_Value(ref_item, "D_LENGTH") + local ref_i_autoFin = reaper.GetMediaItemInfo_Value(ref_item, "D_FADEINLEN_AUTO") + local ref_i_autoFout = reaper.GetMediaItemInfo_Value(ref_item, "D_FADEOUTLEN_AUTO") + local ref_i_Fin = reaper.GetMediaItemInfo_Value(ref_item, "D_FADEINLEN") + local ref_i_Fout = reaper.GetMediaItemInfo_Value(ref_item, "D_FADEOUTLEN") + + if ref_i_autoFin ~= 0 then + ref_fLeftpos = ref_i_pos + ref_i_autoFin + else ref_fLeftpos = ref_i_pos + ref_i_Fin + end + if ref_i_autoFout ~= 0 then + ref_fRightpos = ref_i_end - ref_i_autoFout + else ref_fRightpos = ref_i_end - ref_i_Fout + end + + for i=0, reaper.CountSelectedMediaItems(0) - 1 do + local item = reaper.GetSelectedMediaItem(0,i) + local itemLock = reaper.GetMediaItemInfo_Value( item, "C_LOCK" ) + + if itemLock == 0 or (Opt.IgnoreLockingMouse == true and item == ref_item) then + local i_pos = reaper.GetMediaItemInfo_Value(item, "D_POSITION") + local i_end = i_pos + reaper.GetMediaItemInfo_Value(item, "D_LENGTH") + + local fLeftpos, fRightpos + local leftItem, rightItem + local leftIlock, rightIlock + + local i_autoFin = reaper.GetMediaItemInfo_Value(item, "D_FADEINLEN_AUTO") + local i_autoFout = reaper.GetMediaItemInfo_Value(item, "D_FADEOUTLEN_AUTO") + local i_Fin = reaper.GetMediaItemInfo_Value(item, "D_FADEINLEN") + local i_Fout = reaper.GetMediaItemInfo_Value(item, "D_FADEOUTLEN") + + if i_autoFin ~= 0 then fLeftpos = i_pos + i_autoFin else fLeftpos = i_pos + i_Fin end + if i_autoFout ~= 0 then fRightpos = i_end - i_autoFout else fRightpos = i_end - i_Fout end + + if i_autoFin ~= 0 then + leftItem = FindXfadedNeigbourItem(item, i_pos, i_end, -1) + if leftItem then leftIlock = reaper.GetMediaItemInfo_Value( leftItem, "C_LOCK" ) end + end + + if i_autoFout ~= 0 then + rightItem = FindXfadedNeigbourItem(item, i_pos, i_end, 1) + if rightItem then rightIlock = reaper.GetMediaItemInfo_Value( rightItem, "C_LOCK" ) end + end + + if leftIlock == 1 and Opt.IgnoreLockingMouse == false then leftItem = nil end + if rightIlock == 1 and Opt.IgnoreLockingMouse == false then rightItem = nil end + + local ItemData = {} + ItemData.item = item + ItemData.i_pos = i_pos + ItemData.i_end = i_end + ItemData.fLeftpos = fLeftpos + ItemData.fRightpos = fRightpos + ItemData.leftItem = leftItem + ItemData.rightItem = rightItem + + ---Sort items--- + if math.abs(i_pos - ref_i_pos) < 0.0002 and math.abs(fLeftpos - ref_fLeftpos) < 0.0002 + and leftItem and ref_leftItem then + if reverseFlag == true then table.insert(Items.Vert.R, ItemData) ret = true + else table.insert(Items.Vert.L, ItemData) ret = true + end + end + + if math.abs(i_end - ref_i_end) < 0.0002 and math.abs(fRightpos - ref_fRightpos) < 0.0002 + and rightItem and ref_rightItem then + if reverseFlag == true then table.insert(Items.Vert.L, ItemData) ret = true + else table.insert(Items.Vert.R, ItemData) ret = true + end + end + + if not ref_leftItem and not leftItem then + table.insert(Items.Horiz.L, ItemData) ret = true + end + + if not ref_rightItem and not rightItem then + table.insert(Items.Horiz.R, ItemData) ret = true + end + + end + end + return ret end ---------------------------- -function FadeToMouse(item, itemHalf) - if itemHalf == 'header' then return nil end +function FadeToMouse(item, itemHalf) --returns table of fades start position + local ilock = reaper.GetMediaItemInfo_Value( item, "C_LOCK" ) + if ilock == 1 and Opt.IgnoreLockingMouse == false then return end + + local mPos = reaper.BR_PositionAtMouseCursor(false) + local mPosSnapped - local mPos = reaper.SnapToGrid(0,reaper.BR_GetMouseCursorContext_Position()) + if Opt.RespSnapItems == true then mPosSnapped = reaper.SnapToGrid(0,mPos) + else mPosSnapped = mPos + end + + local Items = {Vert = {L = {}, R ={} }, + Horiz = {L = {}, R ={} } + } + local fadeStartT = {} local fadeStartEdge local i_pos = reaper.GetMediaItemInfo_Value(item, "D_POSITION") local i_end = i_pos + reaper.GetMediaItemInfo_Value(item, "D_LENGTH") - local f_type = "in" - f_type, f_size = WhatFade(itemHalf, i_pos, i_end, mPos) - - if reaper.IsMediaItemSelected(item) == false then - fadeStartEdge = SetFade(item, f_type, f_size) -- item, "in"/"out" f_type, f_size, (shape) + + local fLeftpos, fRightpos + + local i_autoFin = reaper.GetMediaItemInfo_Value(item, "D_FADEINLEN_AUTO") + local i_autoFout = reaper.GetMediaItemInfo_Value(item, "D_FADEOUTLEN_AUTO") + local i_Fin = reaper.GetMediaItemInfo_Value(item, "D_FADEINLEN") + local i_Fout = reaper.GetMediaItemInfo_Value(item, "D_FADEOUTLEN") + + local f_type, f_size + local leftItem, rightItem + local leftIlock, rightIlock + + if i_autoFin ~= 0 then fLeftpos = i_pos + i_autoFin else fLeftpos = i_pos + i_Fin end + if i_autoFout ~= 0 then fRightpos = i_end - i_autoFout else fRightpos = i_end - i_Fout end + + if i_autoFin ~= 0 then + leftItem = FindXfadedNeigbourItem(item, i_pos, i_end, -1) + if leftItem then + leftIlock = reaper.GetMediaItemInfo_Value( leftItem, "C_LOCK" ) + end + end + + if i_autoFout ~= 0 then + rightItem = FindXfadedNeigbourItem(item, i_pos, i_end, 1) + if rightItem then + rightIlock = reaper.GetMediaItemInfo_Value( rightItem, "C_LOCK" ) + end + end + + local reverse + if leftItem and mPos < fLeftpos then + f_type = WhatFade(itemHalf, i_pos, fLeftpos, mPos) + reverse = false + if f_type == 'out' then + rightItem = item + item = leftItem + reverse = true + end + elseif rightItem and mPos > fRightpos then + f_type = WhatFade(itemHalf, fRightpos, i_end, mPos) + reverse = false + if f_type == 'in' then + leftItem = item + item = rightItem + reverse = true + end else - for i=0, reaper.CountSelectedMediaItems(0) - 1 do - local item = reaper.GetSelectedMediaItem(0,i) - fadeStartEdge = SetFade(item, f_type, f_size) -- item, "in"/"out" f_type, f_size, (shape) + f_type = WhatFade(itemHalf, fLeftpos, fRightpos, mPos) + end + + if Grouping == 1 then + SaveSelItems() + local group = reaper.GetMediaItemInfo_Value( item, "I_GROUPID" ) + if reaper.IsMediaItemSelected(item) == false and group ~= 0 then + reaper.SetMediaItemSelected(item, true) + end + reaper.Main_OnCommandEx(40034,0,0) --Item grouping: Select all items in groups + end + + local LeftSelFlag, RightSelFlag + local sortSuccess + if reaper.IsMediaItemSelected(item) == true then + sortSuccess = SortSelItems(Items, item, leftItem, rightItem, false) + else + if leftItem and reaper.IsMediaItemSelected(leftItem) == true + and (f_type == 'in' or itemHalf == 'header') then + LeftSelFlag = true + sortSuccess = SortSelItems(Items, leftItem, nil, item, true) + end + if rightItem and reaper.IsMediaItemSelected(rightItem) == true and reverse ~= false + and (f_type == 'out' or itemHalf == 'header') then + RightSelFlag = true + sortSuccess = SortSelItems(Items, rightItem, item, nil, true) + end + if sortSuccess ~= true then + local ItemData = {} + ItemData.item = item + ItemData.i_pos = i_pos + ItemData.i_end = i_end + ItemData.fLeftpos = fLeftpos + ItemData.fRightpos = fRightpos + ItemData.leftItem = leftItem + ItemData.rightItem = rightItem + + table.insert(Items.Vert.L, ItemData) + table.insert(Items.Vert.R, ItemData) + table.insert(Items.Horiz.L, ItemData) + table.insert(Items.Horiz.R, ItemData) + end + end + + + if mPos > i_pos and mPos < fLeftpos then + if mPosSnapped > fLeftpos or mPosSnapped < i_pos then + mPosSnapped = mPos + end + elseif mPos > fRightpos and mPos < i_end then + if mPosSnapped < fRightpos or mPosSnapped > i_end then + mPosSnapped = mPos + end + end + + if itemHalf == 'header' then --MOVE CLOSEST XFADE TO MOUSE + if Opt.IgnoreLockingMouse == false and (edgesLock == 1 and globalLock == 1) then + return + end + + if leftIlock == 1 and Opt.IgnoreLockingMouse == false then leftItem = nil end + if rightIlock == 1 and Opt.IgnoreLockingMouse == false then rightItem = nil end + + local areaData = {areaStart, areaEnd} + local ret + if leftItem and rightItem then + if mPos - fLeftpos < fRightpos - mPos then -- left xfade + areaData.areaStart = mPosSnapped - i_autoFin/2 + areaData.areaEnd = mPosSnapped + i_autoFin/2 + for i, itemData in ipairs(Items.Vert.L) do + local Litem, Ritem + if LeftSelFlag == true then + Litem = itemData.item + Ritem = itemData.rightItem + elseif RightSelFlag == true then + Litem = leftItem + Ritem = item + else + Litem = itemData.leftItem + Ritem = itemData.item + end + ret = SetCrossfade(Litem, Ritem, areaData) + end + else -- right xfade + areaData.areaStart = mPosSnapped - i_autoFout/2 + areaData.areaEnd = mPosSnapped + i_autoFout/2 + for i, itemData in ipairs(Items.Vert.R) do + local Litem, Ritem + if RightSelFlag == true then + Litem = itemData.leftItem + Ritem = itemData.item + else + Litem = itemData.item + Ritem = itemData.rightItem + end + ret = SetCrossfade(Litem, Ritem, areaData) + end + end + elseif leftItem then --left xfade + areaData.areaStart = mPosSnapped - i_autoFin/2 + areaData.areaEnd = mPosSnapped + i_autoFin/2 + for i, itemData in ipairs(Items.Vert.L) do + local Litem, Ritem + if LeftSelFlag == true then + Litem = itemData.item + Ritem = itemData.rightItem + elseif RightSelFlag == true then + Litem = leftItem + Ritem = item + else + Litem = itemData.leftItem + Ritem = itemData.item + end + ret = SetCrossfade(Litem, Ritem, areaData) + end + elseif rightItem then -- right xfade + areaData.areaStart = mPosSnapped - i_autoFout/2 + areaData.areaEnd = mPosSnapped + i_autoFout/2 + for i, itemData in ipairs(Items.Vert.R) do + local Litem, Ritem + if LeftSelFlag == true then + Litem = item + Ritem = rightItem + elseif RightSelFlag == true then + Litem = itemData.leftItem + Ritem = itemData.item + else + Litem = itemData.item + Ritem = itemData.rightItem + end + ret = SetCrossfade(Litem, Ritem, areaData) + end + end + + if ret then + UndoString = 'FadeTool - move closest xfade to mouse' + table.insert(fadeStartT, ret) + end + return fadeStartT + end + ------------------------ + + if f_type == 'in' then + if mPosSnapped <= i_pos + 0.0002 then mPosSnapped = mPos end + f_size = mPosSnapped - i_pos + elseif f_type == 'out' then + if mPosSnapped >= i_end - 0.0002 then mPosSnapped = mPos end + f_size = i_end - mPosSnapped + end + + if f_type == 'in' and leftItem then --change crossfade + if Opt.IgnoreLockingMouse == false + and ((edgesLock == 1 and globalLock == 1) or leftIlock == 1) then + return + end + + local areaData + if reverse == true then --msg('right item selected, move Left edge of xfade') + areaData = {areaStart = mPosSnapped, areaEnd = i_end} + elseif reverse == false then --msg('move Left edge') + areaData = {areaStart = mPosSnapped, areaEnd = fLeftpos} + else + areaData = {areaStart = i_pos, areaEnd = mPosSnapped} + end + + for i, itemData in ipairs(Items.Vert.L) do + local Litem, Ritem + if LeftSelFlag == true then + Litem = itemData.item + Ritem = itemData.rightItem + elseif RightSelFlag == true then + Litem = leftItem + Ritem = item + else + Litem = itemData.leftItem + Ritem = itemData.item + end + fadeStartEdge = SetCrossfade(Litem, Ritem, areaData) + end + + elseif f_type == 'out' and rightItem then --change cdrossfade + if Opt.IgnoreLockingMouse == false + and ((edgesLock == 1 and globalLock == 1) or rightIlock == 1) then + return + end + + local areaData + if reverse == true then + areaData = {areaStart = i_pos, areaEnd = mPosSnapped} + elseif reverse == false then --msg('right item selected, move Right edge of xfade') + areaData = {areaStart = fRightpos, areaEnd = mPosSnapped} + else + areaData = {areaStart = mPosSnapped, areaEnd = i_end} + end + + for i, itemData in ipairs(Items.Vert.R) do + local Litem, Ritem + if RightSelFlag == true then + Litem = itemData.leftItem + Ritem = itemData.item + else + Litem = itemData.item + Ritem = itemData.rightItem + end + fadeStartEdge = SetCrossfade(Litem, Ritem, areaData) + end + + else --change fade + if f_type == "in" then + for i, itemData in ipairs(Items.Horiz.L) do + fadeStartEdge = SetFade(itemData.item, f_type, f_size) -- item, "in"/"out" f_type, f_size, (shape) + end + elseif f_type == "out" then + for i, itemData in ipairs(Items.Horiz.R) do + fadeStartEdge = SetFade(itemData.item, f_type, f_size) -- item, "in"/"out" f_type, f_size, (shape) + end end end if fadeStartEdge ~= nil then table.insert(fadeStartT, fadeStartEdge) - UndoString = 'Set fade to mouse' + UndoString = 'FadeTool - mouse' end return fadeStartT @@ -1612,8 +2226,8 @@ end function MoveEditCursor(timeTable) if #timeTable > 0 then local fadeStartEdge = math.min(table.unpack(timeTable)) - if moveEditCursor == true then - reaper.SetEditCurPos2(0, fadeStartEdge - curOffset, false, false) + if Opt.moveEditCursor == true then + reaper.SetEditCurPos2(0, fadeStartEdge - Opt.curOffset, false, false) else reaper.SetEditCurPos2(0, EcurInit, false, false) end @@ -1623,100 +2237,335 @@ end ----------------------------------------- function RestoreLockedItems() - if RespectLocking == false and #LOCKEDitems > 0 then - for i=1, #LOCKEDitems do - local item = LOCKEDitems[i] - reaper.SetMediaItemInfo_Value( item, "C_LOCK", 1 ) + for i=1, #LOCKEDitems do + local item = LOCKEDitems[i] + reaper.SetMediaItemInfo_Value( item, "C_LOCK", 1 ) + end +end + +----------------------------------------- + +function UnselectEnvPoints(env, autoitem_idx, IDtable, timeStart, timeEnd) --env and autoitem are necessary + + if timeStart and timeEnd then + elseif IDtable then + else + local pCount = reaper.CountEnvelopePointsEx( env, autoitem_idx ) -1 + for id = 0, pCount do + local ret, time, value, shape, tension, sel = reaper.GetEnvelopePointEx( env, autoitem_idx, id ) + reaper.SetEnvelopePointEx + (env, autoitem_idx, id, time, value, shape, tension, false, true ) end end + end + +----------------------------------------- + +function ExtractDefPointShape(env) + local ret, chunk = reaper.GetEnvelopeStateChunk(env, '', false) + for line in chunk:gmatch("[^\n]+") do + if line:match('DEFSHAPE') then + local value = line:match('%d') + return value + end + end +end + +----------------------------------------- + +function MovePoint(env, time, item, pointPos) +--pointPos should be 'left' or 'right' relative to time + if pointPos == 'right' then pointPos = 1 else pointPos = 0 end + local itemPos = 0 + local takeRate = 1 + local timeSnap + + if Opt.RespSnapEnvs == true then + timeSnap = reaper.SnapToGrid(0, time) + else timeSnap = time + end + + if item then + itemPos = reaper.GetMediaItemInfo_Value(item, 'D_POSITION') + local envTake = reaper.GetActiveTake(item) + takeRate = reaper.GetMediaItemTakeInfo_Value(envTake, 'D_PLAYRATE') + + timeSnap = (timeSnap - itemPos ) * takeRate + time = (time - itemPos) * takeRate + end + + local point_idx = reaper.GetEnvelopePointByTimeEx( env, -1, time ) + pointPos + local _, pTime, pValue, pShape, pTension, pSelected = + reaper.GetEnvelopePointEx(env, -1, point_idx) + + if (pTime > timeSnap and pTime < time) or (pTime < timeSnap and pTime > time) then + timeSnap = time + end + + local pointsNum = reaper.CountEnvelopePointsEx( env, -1 ) -1 + + if point_idx > -1 and point_idx <= pointsNum then + UnselectEnvPoints(env, -1) + reaper.SetEnvelopePointEx( env, -1, point_idx, timeSnap, pValue, pShape, pTension, true, false ) + UndoString = 'FadeTool - move point' + else + UnselectEnvPoints(env, -1) + _, pValue, _, _, _ = reaper.Envelope_Evaluate( env, timeSnap, 192000, 1 ) + --pShape = GetPrefs('defenvs') >> 16 + pShape = tonumber(ExtractDefPointShape(env)) + reaper.InsertEnvelopePointEx( env, -1, timeSnap, pValue, pShape, 0, true, false ) + UndoString = 'FadeTool - create point' + end + + return (timeSnap + itemPos)/takeRate +end + +----------------------------------------- + +function PointToMouse(env) --return time table with single point value + local envItem = reaper.GetEnvelopeInfo_Value(env, 'P_ITEM') + local retTimeT = {} + + if envItem ~= 0 then + if Opt.IgnoreLockingMouse == false and takeEnvLock == 1 then return end + + local item_mouse, itemHalf = GetTopBottomItemHalf() + + if envItem == item_mouse and itemHalf ~= 'header' then + local mouse_time = reaper.BR_GetMouseCursorContext_Position() + + if itemHalf == 'top' then + retTime = MovePoint(env, mouse_time, envItem, 'left') + elseif itemHalf == 'bottom' then + retTime = MovePoint(env, mouse_time, envItem, 'right') + end + + end + + else -- if not envItem + if Opt.IgnoreLockingMouse == false and envLock == 1 then return end + local x,y = reaper.GetMousePosition() + + local OScoeff = 1 + if reaper.GetOS():match("^Win") == nil then OScoeff = -1 end + + local envYpos = reaper.GetEnvelopeInfo_Value( env, 'I_TCPY' ) + local envTrack = reaper.GetEnvelopeInfo_Value( env, 'P_TRACK' ) + local envTrackH = reaper.GetMediaTrackInfo_Value( envTrack, 'I_TCPH' ) + + local envHbig = reaper.GetEnvelopeInfo_Value( env, 'I_TCPH' ) + local envH = reaper.GetEnvelopeInfo_Value( env, 'I_TCPH_USED' ) + local envPad = envHbig - envH + local mouseEnv + local testEnv + + local track + local testTrack + + if envYpos < envTrackH then --env in media lane + track, _ = reaper.GetThingFromPoint(x,y) + local test_y = math.floor( y + (envTrackH/2 - envPad)*OScoeff ) + testTrack, _ = reaper.GetThingFromPoint(x, test_y ) + else --env in separate lane + local test_y = math.floor( y + (envH/2 + envPad)*OScoeff ) + + local track, info = reaper.GetThingFromPoint(x,y) + if info:match('envelope') == 'envelope' then + mouseEnv = reaper.GetTrackEnvelope(track, info:match('%d+')) + end + + track, info = reaper.GetThingFromPoint(x, test_y ) + if info:match('envelope') == 'envelope' then + testEnv = reaper.GetTrackEnvelope(track, info:match('%d+')) + end + end + + + local mouse_time = reaper.BR_GetMouseCursorContext_Position() + + if (env == mouseEnv and env == testEnv) + or (envTrack == track and envTrack == testTrack) then --top half + --msg('top') + retTime = MovePoint(env, mouse_time, nil, 'left') + elseif env == mouseEnv + or envTrack == track then -- bottom half + --msg('bottom') + retTime = MovePoint(env, mouse_time, nil, 'right') + end --top/bottom + + end -- if not envItem + +table.insert(retTimeT,retTime) +return retTimeT +end + +---------------------------------------- + +function GetPrefs(key) -- key need to be string as in Reaper ini file + local iniPath = reaper.get_ini_file() + local value + + for line in io.lines(iniPath) do + if line:match(key) then + value = tonumber(line:gsub(key..'=',''):format("%.5f")) + end + + if value then return value end + end +end + ---------------------------------------- ---------------------------------------- function Main() -GetExtStates() EcurInit = reaper.GetCursorPosition() LOCKEDitems = {} edgesLock = reaper.GetToggleCommandState(40597) --Locking: Toggle item edges locking mode fadesLock = reaper.GetToggleCommandState(40600) --Locking: Toggle item fade/volume handles locking mode envLock = reaper.GetToggleCommandState(40585) --Locking: Toggle track envelope locking mode +takeEnvLock = reaper.GetToggleCommandState(41851) --Locking: Toggle take envelope locking mode fulliLock = reaper.GetToggleCommandState(40576) --Locking: Toggle full item locking mode -if RespectLocking ~= true and (edgesLock == 1 or fadesLock == 1) then - reaper.Main_OnCommandEx(40596,0,0) --Locking: Clear item edges locking mode - reaper.Main_OnCommandEx(40599,0,0) --Locking: Clear item fade/volume handles locking mode - reaper.Main_OnCommandEx(40584,0,0) --Locking: Clear track envelope locking mode - reaper.Main_OnCommandEx(40575,0,0) --Locking: Clear full item locking mode +globalLock = reaper.GetToggleCommandState(1135) --Options: Toggle locking + +trimContBehItems = reaper.GetToggleCommandState(41117) --Options: Trim content behind media items when editing +Grouping = reaper.GetToggleCommandState(1156) --Options: Toggle item grouping and track media/razor edit grouping + +if trimContBehItems == 1 then + reaper.Main_OnCommandEx(41121,0,0) --Options: Disable trim content behind media items when editing +end + +if Opt.IgnoreLockingMouse == true then + reaper.Main_OnCommandEx(40570,0,0) --Locking: Disable locking end if RazorEditSelectionExists() == true then reaper.Undo_BeginBlock2( 0 ) reaper.PreventUIRefresh( 1 ) sTime = FadeRazorEdits(GetRazorEdits()) - MoveEditCursor(sTime) - RestoreLockedItems() - if UndoString then - reaper.Undo_EndBlock2( 0, UndoString, -1 ) - reaper.UpdateArrange() - else reaper.defer(function()end) end + if not RunBatch then RestoreLockedItems() end + if UndoString then return UndoString end else + local env = reaper.GetSelectedEnvelope(0) start_TS, end_TS = reaper.GetSet_LoopTimeRange2( 0, false, false, 0, 0, 0 ) - if start_TS ~= end_TS and reaper.CountSelectedMediaItems(0)> 0 then + if env then + reaper.Undo_BeginBlock2( 0 ) + reaper.PreventUIRefresh( 1 ) + sTime = PointToMouse(env) + end + if UndoString then return UndoString end + + if start_TS ~= end_TS and reaper.CountSelectedMediaItems(0)> 0 and UndoString == nil then reaper.Undo_BeginBlock2( 0 ) reaper.PreventUIRefresh( 1 ) sTime = FadeRazorEdits(GetTSandItems(start_TS, end_TS)) - MoveEditCursor(sTime) - RestoreLockedItems() - if UndoString == 'Batch fades/crossfades' then - reaper.Undo_EndBlock2( 0, UndoString, -1 ) - else - reaper.Undo_EndBlock2( 0, "Fades in Time selection", -1 ) + if not RunBatch then RestoreLockedItems() end + if UndoString ~= 'FadeTool - Batch fades/crossfades' then + UndoString = "FadeTool - time selection" end - reaper.UpdateArrange() - else + return UndoString + + end + if UndoString == nil then local item_mouse, itemHalf = GetTopBottomItemHalf() - if item_mouse and ((RespectLocking == true and fadesLock == 0) or RespectLocking ~= true) then + if item_mouse then reaper.Undo_BeginBlock2( 0 ) reaper.PreventUIRefresh( 1 ) sTime = FadeToMouse(item_mouse, itemHalf) - if sTime then MoveEditCursor(sTime) end - if UndoString then - reaper.Undo_EndBlock2( 0, UndoString, -1 ) - reaper.UpdateArrange() - else reaper.defer(function()end) end + if Grouping == 1 then RestoreSelItems() end + RestoreLockedItems() + if UndoString then return UndoString end else - reaper.defer(function()end) - end - end -end - - -if edgesLock == 1 then - reaper.Main_OnCommandEx(40595,0,0) --Locking: Set item edges locking mode -end -if fadesLock == 1 then - reaper.Main_OnCommandEx(40598,0,0) --Locking: Set item fade/volume handles locking mode -end -if envLock == 1 then - reaper.Main_OnCommandEx(40583,0,0) --Locking: Set track envelope locking mode -end -if fulliLock == 1 then - reaper.Main_OnCommandEx(40574,0,0) --Locking: Set full item locking mode + local x,y = reaper.GetMousePosition() + local track, info = reaper.GetThingFromPoint(x, y) + + if info:match('envelope') == 'envelope' then + local env = reaper.GetTrackEnvelope(track, info:match('%d+')) + reaper.Undo_BeginBlock2( 0 ) + reaper.PreventUIRefresh( 1 ) + sTime = PointToMouse(env) + if UndoString then return UndoString end + end -- if match envelope + + end --if item_mouse + end -- if not UndoString + end end --end of Main() --------------------------- +function get_script_path() + local info = debug.getinfo(1,'S'); + local script_path = info.source:match[[^@?(.*[\/])[^\/]-$]] + --script_path = script_path:gsub('[^/\\]*[/\\]*$','') --one level up + return script_path +end + --------------------------- -----------START----------- -if reaper.GetExtState(ScriptName,'version') ~= '1.3' then - reaper.SetExtState(ScriptName,'version', '1.3', true) - HelloMessage() +CurVers = 2.21 +version = tonumber( reaper.GetExtState(ExtStateName, "version") ) +if version ~= CurVers then + if not version or version < 2.0 then + HelloMessage() + else reaper.ShowMessageBox('The script was updated to version '..CurVers ,'Fade tool',0) + end + reaper.SetExtState(ExtStateName, "version", CurVers, true) + reaper.defer(function()end) +else + if reaper.APIExists( 'BR_GetMouseCursorContext' ) ~= true then + reaper.ShowMessageBox('Please, install SWS extension!', 'No SWS extension', 0) + return + end + + window, segment, details = reaper.BR_GetMouseCursorContext() + --msg(window) msg(segment) msg(details) + local tcpActIDstr = reaper.GetExtState(ExtStateName, 'TCPaction') + local tcpActID + + OptDefaults = {} + OptionsDefaults(OptDefaults) + GetExtStates(OptDefaults) + + if window == 'transport' or window == 'mcp' then + --look for additional file + local script_path = get_script_path() + local file = script_path .. 'az_Fade tool (work on context of mouse, razor or time selection)/' + ..'az_Options window for az_Fade tool.lua' + dofile(file) + --ExternalOpen = true + OptionsWindow(OptDefaults, 'Fade Tool Options') + elseif window == 'tcp' and tcpActIDstr ~= '' then + if tcpActIDstr:gsub('%d+', '') == '' then + tcpActID = tonumber(tcpActIDstr) + elseif tcpActIDstr ~= '' then + tcpActID = tonumber(reaper.NamedCommandLookup(tcpActIDstr)) + end + reaper.Main_OnCommandEx(tcpActID,0,0) + else + Opt = {} + SetOptGlobals(Opt, OptDefaults) + UndoString = Main() + + if globalLock == 1 and not RunBatch then + reaper.Main_OnCommandEx(40569,0,0) --Locking: Enable locking + end + if trimContBehItems == 1 and not RunBatch then + reaper.Main_OnCommandEx(41120,0,0) --Options: Enable trim content behind media items when editing + end + + if UndoString ~= nil then --msg(UndoString) + if sTime then MoveEditCursor(sTime) end + reaper.Undo_EndBlock2( 0, UndoString, -1 ) + reaper.UpdateArrange() + else + reaper.defer(function()end) + end + end + end -window, segment, details = reaper.BR_GetMouseCursorContext() -if window == 'transport' or window == 'mcp' then - OptionsWindow() -else Main() end diff --git a/Items Editing/az_Fade tool (work on context of mouse, razor or time selection)/az_Open options for az_Fade tool.lua b/Items Editing/az_Fade tool (work on context of mouse, razor or time selection)/az_Open options for az_Fade tool.lua new file mode 100644 index 000000000..4c5aac5d7 --- /dev/null +++ b/Items Editing/az_Fade tool (work on context of mouse, razor or time selection)/az_Open options for az_Fade tool.lua @@ -0,0 +1,45 @@ +-- @noindex + +ScriptName = 'az_Fade tool (work on context of mouse, razor or time selection)' +GUIName = 'az_Options window for az_Fade tool' + +function msg(s) reaper.ShowConsoleMsg(tostring(s)..'\n') end + +function get_script_path() + local info = debug.getinfo(1,'S'); + local script_path = info.source:match[[^@?(.*[\/])[^\/]-$]] + --script_path = script_path:gsub('[^/\\]*[/\\]*$','') --one level up + return script_path +end + +local gui_path = get_script_path() + +local gui_file = gui_path .. GUIName ..'.lua' +dofile(gui_file) +-------- + +local script_path = get_script_path() +script_path = script_path:gsub('[^/\\]*[/\\]*$','') --one level up + +local file = script_path .. ScriptName ..'.lua' +local scriptPart = '' +local add + +for line in io.lines(file) do + if line:match('--Start load file') then add = true end + if add == true then scriptPart = scriptPart ..line ..'\n' end + if line:match('--End load file') then break end +end +--msg(scriptPart) +local func = load(scriptPart) + +if func then + func() + + OptDefaults = {} + OptionsDefaults(OptDefaults) + GetExtStates(OptDefaults) + + ExternalOpen = true + OptionsWindow(OptDefaults, 'Fade Tool Options') +end diff --git a/Items Editing/az_Fade tool (work on context of mouse, razor or time selection)/az_Options window for az_Fade tool.lua b/Items Editing/az_Fade tool (work on context of mouse, razor or time selection)/az_Options window for az_Fade tool.lua new file mode 100644 index 000000000..0b3e6e89f --- /dev/null +++ b/Items Editing/az_Fade tool (work on context of mouse, razor or time selection)/az_Options window for az_Fade tool.lua @@ -0,0 +1,312 @@ +-- @noindex + +-------------------------- +function rgbToHex(rgba) -- passing a table with percentage like {100, 50, 20, 90} + local hexadecimal = '0X' + + for key, value in pairs(rgba) do + local hex = '' + if value > 100 or value < 0 then return error('Color must be a percantage value\n between 0 and 100') end + value = (255/100)*value + while(value > 0)do + local index = math.floor(math.fmod(value, 16) + 1) + value = math.floor(value / 16) + hex = string.sub('0123456789ABCDEF', index, index) .. hex + end + + if(string.len(hex) == 0)then + hex = '00' + + elseif(string.len(hex) == 1)then + hex = '0' .. hex + end + + hexadecimal = hexadecimal .. hex + end + + return hexadecimal +end +------------------------ + +function OptionsWindow(OptTable, windowName) + local imgui_path = reaper.GetResourcePath() .. '/Scripts/ReaTeam Extensions/API/imgui.lua' + if not reaper.file_exists(imgui_path) then + reaper.ShowMessageBox('Please, install ReaImGui from Reapack!', 'No Imgui library', 0) + return + end + dofile(imgui_path) '0.8.7.6' + + local fontSize = 17 + local ctx, font, fontSep + local H = fontSize + local W = fontSize + local loopcnt = 0 + local _, imgui_version_num, _ = reaper.ImGui_GetVersion() + + local tcpActIDstr = reaper.GetExtState(ExtStateName, 'TCPaction') + local tcpActName = '' + local section + + local savedFontSize = tonumber(reaper.GetExtState(ExtStateName, 'FontSize')) + if type(savedFontSize) == 'number' then fontSize = savedFontSize end + if not savedFontSize then savedFontSize = fontSize end + + if tcpActIDstr ~= '' and tcpActIDstr:gsub('%d+', '') == '' then + section = reaper.SectionFromUniqueID( tonumber(tcpActIDstr) ) + tcpActName = reaper.kbd_getTextFromCmd( tonumber(tcpActIDstr), section ) + elseif tcpActIDstr ~= '' then + section = reaper.SectionFromUniqueID( tonumber(reaper.NamedCommandLookup(tcpActIDstr)) ) + tcpActName = reaper.kbd_getTextFromCmd + ( tonumber(reaper.NamedCommandLookup(tcpActIDstr)), section ) + end + + local esc + local enter + local space + local escMouse + local enterMouse + local spaceMouse + + local gui_colors = { + White = rgbToHex({90,90,90,100}), + Green = rgbToHex({52,85,52,100}), + Red = rgbToHex({90,10,10,100}), + Blue = rgbToHex({10,30,40,100}), + TitleBg = rgbToHex({30,20,30,100}), + Background = rgbToHex({11,14,14,95}), + Text = rgbToHex({92,92,81.5,100}), + activeText = rgbToHex({50,95,80,100}), + ComboBox = { + Default = rgbToHex({20,25,30,100}), + Hovered = rgbToHex({35,40,45,80}), + Active = rgbToHex({42,42,37,100}), + }, + --[[ + Input = { + Background = rgbToHex({50,50,50,100}), + Hover = rgbToHex({10,10,90,100}), + Text = rgbToHex({90,90,80,100}), + Label = rgbToHex({90,80,90,100}), + },]] + Button = { + Default = rgbToHex({25,30,30,100}), + Hovered = rgbToHex({35,40,45,100}), + Active = rgbToHex({42,42,37,100}), + } + } + ------ + + local fontName + ctx = reaper.ImGui_CreateContext(windowName) -- Add VERSION TODO + if reaper.GetOS():match("^Win") == nil then + reaper.ImGui_SetConfigVar(ctx, reaper.ImGui_ConfigVar_ViewportsNoDecoration(), 0) + fontName = 'sans-serif' + else + fontName = 'Calibri' + end + + -------------- + function frame() + reaper.ImGui_PushFont(ctx, font) + + for i, v in ipairs(OptTable) do + local option = v + + if type(option[3]) == 'boolean' then + local _, newval = reaper.ImGui_Checkbox(ctx, option[1], option[3]) + option[3] = newval + end + + if type(option[3]) == 'number' then + reaper.ImGui_PushItemWidth(ctx, fontSize*3 ) + local _, newval = + reaper.ImGui_InputDouble(ctx, option[1], option[3], nil, nil, option[4]) + + option[3] = newval + end + + if type(option[3]) == 'string' then + local choice + for k = 1, #option[4] do + if option[4][k] == option[3] then choice = k end + end + + reaper.ImGui_Text(ctx, option[1]) + reaper.ImGui_SameLine(ctx, nil, nil) + + reaper.ImGui_PushItemWidth(ctx, fontSize*10.3 ) + + if reaper.ImGui_BeginCombo(ctx, '##'..i, option[3], nil) then + for k,f in ipairs(option[4]) do + local is_selected = choice == k + if reaper.ImGui_Selectable(ctx, option[4][k], is_selected) then + choice = k + end + + -- Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if is_selected then + reaper.ImGui_SetItemDefaultFocus(ctx) + end + end + reaper.ImGui_EndCombo(ctx) + end + + option[3] = option[4][choice] + end + + if type(option[3]) == 'nil' then + reaper.ImGui_PushFont(ctx, fontSep) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.White) + + if i ~= 1 then reaper.ImGui_Text(ctx, '' ) end + reaper.ImGui_SeparatorText( ctx, option[1] ) + + reaper.ImGui_PopStyleColor(ctx, 1) + reaper.ImGui_PopFont(ctx) + end + + OptTable[i] = option + end -- for + + if RunBatch == nil then + reaper.ImGui_Text(ctx, '' ) --space + reaper.ImGui_PushItemWidth(ctx, fontSize*5 ) + _, tcpActIDstr = reaper.ImGui_InputText + (ctx,'TCP context action (paste command ID):\n'..tcpActName, tcpActIDstr) + + _, savedFontSize = reaper.ImGui_InputInt + (ctx, 'Font size for the window (default is 17)', savedFontSize) + end + + reaper.ImGui_Text(ctx, '' ) --space before buttons + reaper.ImGui_Text(ctx, '' ) --space before buttons + + --Esc button + reaper.ImGui_SameLine(ctx, fontSize*2, fontSize) + if esc == true then + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Button(), gui_colors.Button.Active) + end + escMouse = reaper.ImGui_Button(ctx, 'Esc', nil, nil ) + if esc == true then reaper.ImGui_PopStyleColor(ctx, 1) end + + --Save button + reaper.ImGui_SameLine(ctx, nil, fontSize) + if enter == true then + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Button(), gui_colors.Button.Active) + end + local enterName = 'Save & Quit - Enter' + if RunBatch ~= nil then enterName = 'Run - Enter' end + enterMouse = reaper.ImGui_Button(ctx, enterName, nil, nil) + if enter == true then reaper.ImGui_PopStyleColor(ctx, 1) end + + --Apply button + if ExternalOpen == true then + reaper.ImGui_SameLine(ctx, nil, fontSize) + if space == true then + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Button(), gui_colors.Button.Active) + end + spaceMouse = reaper.ImGui_Button(ctx, 'Apply - Space', nil, nil) + if space == true then reaper.ImGui_PopStyleColor(ctx, 1) end + end + + --About button + reaper.ImGui_SameLine(ctx, fontSize*25, nil) + if reaper.ImGui_Button(ctx, 'About - forum page', nil, nil) then + local doc = 'https://forum.cockos.com/showthread.php?t=293335' + if reaper.CF_ShellExecute then + reaper.CF_ShellExecute(doc) + else + reaper.MB(doc, 'Fade Tool forum page', 0) + end + end + + reaper.ImGui_PopFont(ctx) + end + + -------------- + function loop() + if not font or savedFontSize ~= fontSize then + reaper.SetExtState(ExtStateName, 'FontSize', savedFontSize, true) + fontSize = savedFontSize + if font then reaper.ImGui_Detach(ctx, font) end + if fontSep then reaper.ImGui_Detach(ctx, fontSep) end + font = reaper.ImGui_CreateFont(fontName, fontSize, reaper.ImGui_FontFlags_None()) -- Create the fonts you need + fontSep = reaper.ImGui_CreateFont(fontName, fontSize-2, reaper.ImGui_FontFlags_Italic()) + reaper.ImGui_Attach(ctx, font) + reaper.ImGui_Attach(ctx, fontSep) + end + + esc = reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Escape()) + enter = reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Enter()) + space = reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Space()) + + reaper.ImGui_PushFont(ctx, font) + + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_WindowBg(), gui_colors.Background) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_TitleBgActive(), gui_colors.TitleBg) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.Text) + + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Button(), gui_colors.Button.Default) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_ButtonHovered(), gui_colors.Button.Hovered) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_ButtonActive(), gui_colors.Button.Active) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_CheckMark(), gui_colors.Green) + + --Combo box and check box background + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_FrameBg(), gui_colors.ComboBox.Default) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_FrameBgHovered(), gui_colors.ComboBox.Hovered) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_FrameBgActive(), gui_colors.ComboBox.Active) + --Combo box drop down list + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Header(), gui_colors.ComboBox.Default) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_HeaderHovered(), gui_colors.ComboBox.Hovered) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_HeaderActive(), gui_colors.ComboBox.Active) + + local window_flags = reaper.ImGui_WindowFlags_MenuBar() + reaper.ImGui_SetNextWindowSize(ctx, W, H, reaper.ImGui_Cond_Once()) -- Set the size of the windows. Use in the 4th argument reaper.ImGui_Cond_FirstUseEver() to just apply at the first user run, so ImGUI remembers user resize s2 + + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.White) + local visible, open = reaper.ImGui_Begin(ctx, windowName, true, window_flags) + reaper.ImGui_PopStyleColor(ctx, 1) + + if visible then + frame() + reaper.ImGui_SetWindowSize(ctx, 0, 0, nil ) + --if loopcnt == 0 then reaper.ImGui_SetWindowSize(ctx, 0, 0, nil ) end + reaper.ImGui_End(ctx) + end + + reaper.ImGui_PopStyleColor(ctx, 13) + reaper.ImGui_PopFont(ctx) + + esc = escMouse or reaper.ImGui_IsKeyReleased(ctx, reaper.ImGui_Key_Escape()) + enter = enterMouse or reaper.ImGui_IsKeyReleased(ctx, reaper.ImGui_Key_Enter()) + + if ExternalOpen == true then + space = spaceMouse or reaper.ImGui_IsKeyReleased(ctx, reaper.ImGui_Key_Space()) + if space == true then + SetExtStates(OptTable) + reaper.SetExtState(ExtStateName, 'TCPaction', tcpActIDstr, true) + end + end + + if open and esc ~= true and enter ~= true then + reaper.defer(loop) + elseif enter == true then + if RunBatch == true then + reaper.ImGui_DestroyContext(ctx) + BatchFades() + else + SetExtStates(OptTable) + reaper.SetExtState(ExtStateName, 'TCPaction', tcpActIDstr, true) + reaper.ImGui_DestroyContext(ctx) + end + else + if RunBatch == true then TheRestAfterBatch() end + reaper.ImGui_DestroyContext(ctx) + end + + loopcnt = loopcnt+1 + end + ----------------- + loop(ctx, font) +end + diff --git a/Items Editing/az_Smart split items by mouse cursor.lua b/Items Editing/az_Smart split items by mouse cursor.lua index 48710e9e8..07e382849 100644 --- a/Items Editing/az_Smart split items by mouse cursor.lua +++ b/Items Editing/az_Smart split items by mouse cursor.lua @@ -1,9 +1,12 @@ -- @description Smart split items using mouse cursor context (also edit cursor, razor area and time selection) -- @author AZ --- @version 3.30 +-- @version 3.40 -- @changelog --- - fixed bug for media edit groups when smaller items didn't obey the grouping --- - fixed bug when stretch markers were produce incorrect take time offset +-- - New option for splitting unselected item under mouse at time selection +-- - Collapsed and resizeable options window +-- - fixed bug for splitting at time selection if there is one selected item in the project +-- - fixed bug when media editing group of selected tracks obeys any unselected track +-- - fixed bug for take envelopes -- @provides [main] az_Smart split items by mouse cursor/az_Open options for az_Smart split items by mouse cursor.lua -- @link Forum thread https://forum.cockos.com/showthread.php?t=259751 -- @donation Donate via PayPal https://www.paypal.me/AZsound @@ -16,7 +19,7 @@ -- -- There are a lot of options. To open options window place mouse on the transport panel or mixer panel and press assigned shortcut. -- --- By design it should be assigned to keyboard shortcut, not to a mouse modifier. +-- By design it should be assigned to a keyboard shortcut, not to a mouse modifier. --[[ TO MODIFY SCRIPT OPTIONS @@ -79,6 +82,9 @@ function OptionsDefaults() text = 'Use time selection for split selected items' table.insert(OptDefaults, {text, 'UseTSselItems', true}) + text = 'Allow split unselected items under mouse at time selection' + table.insert(OptDefaults, {text, 'AllowTSunsel', false}) + text = 'Use time selection at all' table.insert(OptDefaults, {text, 'UseTSall', true}) @@ -110,7 +116,7 @@ function OptionsDefaults() table.insert(OptDefaults, {text, 'eCurPriority', false}) -- Edit cursor have piority against mouse on selected item. - text = 'Move Edit Cursor with Offset is useful for immediate listening in context' + text = 'Move Edit Cursor with Offset (useful for immediate listening in context)' table.insert(OptDefaults, {text, 'Separator', nil}) text = 'Move cursor after split if mouse is over item and not recording' @@ -197,6 +203,10 @@ function OptionsWindow() local tcpActName = '' local section + local savedFontSize = tonumber(reaper.GetExtState(ExtStateName, 'FontSize')) + if type(savedFontSize) == 'number' then fontSize = savedFontSize end + if not savedFontSize then savedFontSize = fontSize end + if tcpActIDstr ~= '' and tcpActIDstr:gsub('%d+', '') == '' then section = reaper.SectionFromUniqueID( tonumber(tcpActIDstr) ) tcpActName = reaper.kbd_getTextFromCmd( tonumber(tcpActIDstr), section ) @@ -240,66 +250,83 @@ function OptionsWindow() Active = rgbToHex({42,42,37,100}), } } + --------- + + local fontName + ctx = reaper.ImGui_CreateContext('Smart Split Options') -- Add VERSION TODO + if reaper.GetOS():match("^Win") == nil then + reaper.ImGui_SetConfigVar(ctx, reaper.ImGui_ConfigVar_ViewportsNoDecoration(), 0) + fontName = 'sans-serif' + else + fontName = 'Calibri' + end + -------------- function frame() reaper.ImGui_PushFont(ctx, font) + local Headers = {} for i, v in ipairs(OptDefaults) do local option = v - if type(option[3]) == 'boolean' then - local _, newval = reaper.ImGui_Checkbox(ctx, option[1], option[3]) - option[3] = newval - end - - if type(option[3]) == 'number' then - reaper.ImGui_PushItemWidth(ctx, fontSize*3 ) - local _, newval = - reaper.ImGui_InputDouble(ctx, option[1], option[3], nil, nil, option[4]) + if type(option[3]) == 'nil' then + reaper.ImGui_PushFont(ctx, fontSep) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.White) + + reaper.ImGui_Text(ctx, '' ) + --reaper.ImGui_SeparatorText( ctx, option[1] ) + local ret = reaper.ImGui_CollapsingHeader(ctx, option[1]) + table.insert(Headers, ret) - option[3] = newval + reaper.ImGui_PopStyleColor(ctx, 1) + reaper.ImGui_PopFont(ctx) end - if type(option[3]) == 'string' then - local choice - for k = 1, #option[4] do - if option[4][k] == option[3] then choice = k end + if Headers[#Headers] == true or #Headers == 0 then + if type(option[3]) == 'boolean' then + local _, newval = reaper.ImGui_Checkbox(ctx, option[1], option[3]) + option[3] = newval end - reaper.ImGui_Text(ctx, option[1]) - reaper.ImGui_SameLine(ctx, nil, nil) - - reaper.ImGui_PushItemWidth(ctx, fontSize*10.3 ) - --reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.activeText) - if reaper.ImGui_BeginCombo(ctx, '##'..i, option[3], nil) then - for k,f in ipairs(option[4]) do - local is_selected = choice == k - if reaper.ImGui_Selectable(ctx, option[4][k], is_selected) then - choice = k - end + if type(option[3]) == 'number' then + reaper.ImGui_PushItemWidth(ctx, fontSize*3 ) + local _, newval = + reaper.ImGui_InputDouble(ctx, option[1], option[3], nil, nil, option[4]) + + option[3] = newval + end - -- Set the initial focus when opening the combo (scrolling + keyboard navigation focus) - if is_selected then - reaper.ImGui_SetItemDefaultFocus(ctx) + if type(option[3]) == 'string' then + local choice + for k = 1, #option[4] do + if option[4][k] == option[3] then choice = k end + end + + reaper.ImGui_Text(ctx, option[1]) + reaper.ImGui_SameLine(ctx, nil, nil) + + reaper.ImGui_PushItemWidth(ctx, fontSize*10.3 ) + --reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.activeText) + if reaper.ImGui_BeginCombo(ctx, '##'..i, option[3], nil) then + for k,f in ipairs(option[4]) do + local is_selected = choice == k + if reaper.ImGui_Selectable(ctx, option[4][k], is_selected) then + choice = k + end + + -- Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if is_selected then + reaper.ImGui_SetItemDefaultFocus(ctx) + end end + reaper.ImGui_EndCombo(ctx) end - reaper.ImGui_EndCombo(ctx) + --reaper.ImGui_PopStyleColor(ctx) + + option[3] = option[4][choice] end - --reaper.ImGui_PopStyleColor(ctx) - - option[3] = option[4][choice] - end + end --if ret - if type(option[3]) == 'nil' then - reaper.ImGui_PushFont(ctx, fontSep) - reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.White) - - reaper.ImGui_Text(ctx, '' ) - reaper.ImGui_SeparatorText( ctx, option[1] ) - - reaper.ImGui_PopStyleColor(ctx, 1) - reaper.ImGui_PopFont(ctx) - end OptDefaults[i] = option end -- for @@ -310,6 +337,9 @@ function OptionsWindow() _, tcpActIDstr = reaper.ImGui_InputText (ctx,'TCP context action (paste command ID):\n'..tcpActName, tcpActIDstr) + _, savedFontSize = reaper.ImGui_InputInt + (ctx, 'Font size for the window (default is 17)', savedFontSize) + reaper.ImGui_Text(ctx, '' ) --space before buttons reaper.ImGui_Text(ctx, '' ) --space before buttons @@ -354,7 +384,18 @@ function OptionsWindow() end -------------- - function loop() + function loop() + if not font or savedFontSize ~= fontSize then + reaper.SetExtState(ExtStateName, 'FontSize', savedFontSize, true) + fontSize = savedFontSize + if font then reaper.ImGui_Detach(ctx, font) end + if fontSep then reaper.ImGui_Detach(ctx, fontSep) end + font = reaper.ImGui_CreateFont(fontName, fontSize, reaper.ImGui_FontFlags_None()) -- Create the fonts you need + fontSep = reaper.ImGui_CreateFont(fontName, fontSize-2, reaper.ImGui_FontFlags_Italic()) + reaper.ImGui_Attach(ctx, font) + reaper.ImGui_Attach(ctx, fontSep) + end + esc = reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Escape()) enter = reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Enter()) space = reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Space()) @@ -387,8 +428,9 @@ function OptionsWindow() reaper.ImGui_PopStyleColor(ctx, 1) if visible then - frame() - if loopcnt == 0 then reaper.ImGui_SetWindowSize(ctx, 0, 0, nil ) end + frame() + reaper.ImGui_SetWindowSize(ctx, 0, 0, nil ) + --if loopcnt == 0 then reaper.ImGui_SetWindowSize(ctx, 0, 0, nil ) end reaper.ImGui_End(ctx) end @@ -418,17 +460,6 @@ function OptionsWindow() loopcnt = loopcnt+1 end ----------------- - local fontName - ctx = reaper.ImGui_CreateContext('Smart Split Options') -- Add VERSION TODO - if reaper.GetOS():match("^Win") == nil then - reaper.ImGui_SetConfigVar(ctx, reaper.ImGui_ConfigVar_ViewportsNoDecoration(), 0) - fontName = 'sans-serif' - else fontName = 'Calibri' - end - font = reaper.ImGui_CreateFont(fontName, fontSize, reaper.ImGui_FontFlags_None()) -- Create the fonts you need - fontSep = reaper.ImGui_CreateFont(fontName, fontSize-2, reaper.ImGui_FontFlags_Italic()) - reaper.ImGui_Attach(ctx, font) - reaper.ImGui_Attach(ctx, fontSep) loop(ctx, font) end @@ -474,8 +505,8 @@ function PixelDistance() local zoom = reaper.GetHZoomLevel() if Mcur_pos ~= nil then - if (math.abs(MouseSnapped - startTS)*zoom <= distance) - or (math.abs(MouseSnapped - endTS)*zoom <= distance) then + if math.abs(MouseSnapped - startTS)*zoom <= distance + or math.abs(MouseSnapped - endTS)*zoom <= distance then return 'close' else return 'far' end @@ -835,13 +866,17 @@ function SplitRazorEdits(razorEdits) local areaData = razorEdits[i] if not areaData.isEnvelope then table.move(areaData.items, 1, #areaData.items, #areaData.grItems+1, areaData.grItems) + --msg('areaData.items') for j,v in pairs(areaData.items) do msg(v) end + --msg('areaData.grItems') for j,v in pairs(areaData.grItems) do msg(v) end local sTime, selItems, itemsToRegroup, newItems = Split_Items_At_Time(areaData.items, areaData.grItems, {areaData.areaStart}, areaData.prevEdge) - table.move(sTime, 1, #sTime, #SplitsT+1, SplitsT) + --table.move(newItems, 1, #newItems, #areaData.items+1, areaData.items) table.move(newItems, 1, #newItems, #areaData.grItems+1, areaData.grItems) razorEdits[i]['itemsToRegroup'] = itemsToRegroup - + --msg(razorEdits[i]['itemsToRegroup']['SplsGrs']) + --msg(itemsToRegroup.SplGrs) + --razorEdits[i]['items'] = areaData.items end i=i-1 end @@ -866,6 +901,8 @@ function SplitRazorEdits(razorEdits) while i > 0 do local areaData = razorEdits[i] if not areaData.isEnvelope then + --msg('areaData.items') for j,v in pairs(areaData.items) do msg(v) end + --msg('areaData.grItems') for j,v in pairs(areaData.grItems) do msg(v) end local sTime, selItems, itemsToRegroup = Split_Items_At_Time(areaData.items, areaData.grItems, {areaData.areaEnd}, areaData.areaStart) table.move(sTime, 1, #sTime, #SplitsT+1, SplitsT) @@ -980,6 +1017,11 @@ function AddGroupInfo(AreasT) AreasT[i] = areaData end -- if not isEnvelope end -- for AreasT + --[[ + for i = 1, #RegroupAreasIDs do --servise msg + local block = RegroupAreasIDs[i] + msg(table.concat(block, ' - ')) + end]] AreasT.RegroupAreasIDs = RegroupAreasIDs return AreasT @@ -1303,7 +1345,8 @@ function AddTrMediaEditingGroup(Items, timeT) local ItemsH = {} local GrTracks = {} local GrIDsLow = 0 - local GrIDsHigh = 0 + local GrIDsHigh = 0 + local SelState = 0 for i, item in ipairs(Items) do local ipos = reaper.GetMediaItemInfo_Value(item, 'D_POSITION') @@ -1321,54 +1364,64 @@ function AddTrMediaEditingGroup(Items, timeT) for i, tr in ipairs(Tracks) do -- collect gr info for tracks with initially captured items local intlow = reaper.GetSetTrackGroupMembership( tr, "MEDIA_EDIT_LEAD", 0, 0 ) - local inthigh = reaper.GetSetTrackGroupMembershipHigh( tr, "MEDIA_EDIT_LEAD", 0, 0 ) + local inthigh = reaper.GetSetTrackGroupMembershipHigh( tr, "MEDIA_EDIT_LEAD", 0, 0 ) + local intSel = reaper.GetMediaTrackInfo_Value(tr, 'I_SELECTED') GrIDsLow = GrIDsLow | intlow - GrIDsHigh = GrIDsHigh | inthigh + GrIDsHigh = GrIDsHigh | inthigh + SelState = SelState | intSel end - for i = 1, reaper.CountTracks(0) do -- collect all group mappings + + for i = 1, reaper.CountTracks(0) do -- collect all matching group mappings local tr = reaper.GetTrack(0, i-1) local intlow = reaper.GetSetTrackGroupMembership( tr, "MEDIA_EDIT_LEAD", 0, 0 ) local inthigh = reaper.GetSetTrackGroupMembershipHigh( tr, "MEDIA_EDIT_LEAD", 0, 0 ) local intlowF = reaper.GetSetTrackGroupMembership( tr, "MEDIA_EDIT_FOLLOW", 0, 0 ) local inthighF = reaper.GetSetTrackGroupMembershipHigh( tr, "MEDIA_EDIT_FOLLOW", 0, 0 ) - if GrIDsLow & intlow ~= 0 or GrIDsLow & intlowF ~= 0 then - GrIDsLow = GrIDsLow | intlow - end + local intSel = reaper.GetMediaTrackInfo_Value(tr, 'I_SELECTED') - if GrIDsHigh & inthigh ~= 0 or GrIDsHigh & inthighF ~= 0 then - GrIDsHigh = GrIDsHigh | inthigh + if GrIDsLow & intlow ~= 0 or GrIDsLow & intlowF ~= 0 or SelState & intSel ~= 0 then + GrIDsLow = GrIDsLow | intlow end - local sel = reaper.IsTrackSelected(tr) - if sel == true and GrSelTrs == 1 then + if GrIDsHigh & inthigh ~= 0 or GrIDsHigh & inthighF ~= 0 or SelState & intSel ~= 0 then + GrIDsHigh = GrIDsHigh | inthigh + end + --[[ + if reaper.IsTrackSelected(tr) == true and GrSelTrs == 1 then + --and FieldMatch(Tracks, tr) == true then GrIDsLow = GrIDsLow | intlow GrIDsHigh = GrIDsHigh | inthigh + msg('addSel') end + ]] end for i = 1, reaper.CountTracks(0) do -- add corresponding tracks to the table local tr = reaper.GetTrack(0, i-1) - if GrAllTrs == 1 then FieldMatch(GrTracks, tr, true) + if GrAllTrs == 1 then + FieldMatch(GrTracks, tr, true) else local intlow = reaper.GetSetTrackGroupMembership( tr, "MEDIA_EDIT_LEAD", 0, 0 ) local inthigh = reaper.GetSetTrackGroupMembershipHigh( tr, "MEDIA_EDIT_LEAD", 0, 0 ) local intlowF = reaper.GetSetTrackGroupMembership( tr, "MEDIA_EDIT_FOLLOW", 0, 0 ) local inthighF = reaper.GetSetTrackGroupMembershipHigh( tr, "MEDIA_EDIT_FOLLOW", 0, 0 ) - if GrIDsLow & intlow ~= 0 or GrIDsLow & intlowF ~= 0 then + local intSel = reaper.GetMediaTrackInfo_Value(tr, 'I_SELECTED') + + if GrIDsLow & intlow ~= 0 or GrIDsLow & intlowF ~= 0 or SelState & intSel ~= 0 then table.insert(GrTracks, tr) end - if GrIDsHigh & inthigh ~= 0 or GrIDsHigh & inthighF ~= 0 then + if GrIDsHigh & inthigh ~= 0 or GrIDsHigh & inthighF ~= 0 or SelState & intSel ~= 0 then FieldMatch(GrTracks, tr, true) end - - local sel = reaper.IsTrackSelected(tr) - if sel == true and GrSelTrs == 1 then + --[[ + if reaper.IsTrackSelected(tr) == true and GrSelTrs == 1 then + --and FieldMatch(Tracks, tr) == true then FieldMatch(GrTracks, tr, true) - end + end]] end end @@ -1458,6 +1511,19 @@ function SetItemEdges(item, startTime, endTime) else reaper.SetMediaItemTakeInfo_Value(take, 'D_STARTOFFS', offs) end + + local takeenvs = reaper.CountTakeEnvelopes(take) + for e = 0, takeenvs -1 do + local env = reaper.GetTakeEnvelope( take, e ) + for p = 0, reaper.CountEnvelopePoints( env ) -1 do + local ret, time, value, shape, tens, sel = reaper.GetEnvelopePoint( env, p ) + if ret then + time = time - (startTime-pos)*rate + reaper.SetEnvelopePoint( env, p, time, value, shape, tens, sel, true ) + end + end + reaper.Envelope_SortPoints( env ) + end end end @@ -1650,7 +1716,7 @@ function Main() if Opt.defSelSide then SelSide = Opt.defSelSide end - TSstart, TSend = reaper.GetSet_LoopTimeRange2( 0, false, false, 0, 0, 0 ) + TSstart, TSend = reaper.GetSet_LoopTimeRange2( 0, false, false, 0, 0, 0 ) --calculations for big zoom local startArrange, endArrange = reaper.GetSet_ArrangeView2( 0, false, 0, 0, 0, 0 ) local distance = (endArrange - startArrange)/4 @@ -1662,7 +1728,8 @@ function Main() end --end of calculations for big zoom if TSstart == TSend or Opt.UseTSall == false then TSexist = false - else TSexist = true end + else TSexist = true + end if Opt.UseTSdistance == true then if PixelDistance() == 'far' then TSexist = false end end @@ -1683,8 +1750,7 @@ function Main() if #SelectedItems > 0 then - inisel = SelectedItems - if Opt.UseTSselItems == false then TSexist = false end + inisel = SelectedItems elseif MouseOnItem == true then inisel = {Item_mouse} else @@ -1700,59 +1766,66 @@ function Main() if Opt.RespLock ~= 0 and reaper.GetMediaItemInfo_Value(Item_mouse, 'C_LOCK') ~= 0 then return --no undo - else + end --if item under mouse is not locked - if Opt.SnapMouseEcur ~= 0 then - local zoom = reaper.GetHZoomLevel() - local distance = Opt.SnapMouseEcur / zoom - if math.abs(Ecur_pos - Mcur_pos) <= distance then Opt.SnapMouseEcur = true end - end - - timeT = { MouseSnapped } - UndoString = 'Smart split at mouse cursor' - - if Opt['MouseT/B'] == 'left/right crossfade' then - if Half == 'top' then Opt.CrossType = 'Left' - elseif Half == 'bottom' then Opt.CrossType = 'Right' - end - elseif Opt['MouseT/B'] == 'select left/right item' then - if Half == 'top' then SelSide = 'Left' - elseif Half == 'bottom' then SelSide = 'Right' - end - end - - if reaper.IsMediaItemSelected( Item_mouse ) == false then - SelectAllMediaItems(0, false) - inisel = {Item_mouse} - elseif Opt.eCurPriority == true then - if isItemsForSplit(inisel, Ecur_pos) == true then - timeT = {Ecur_pos} - UndoString = nil - if Opt.DontMoveECurSplit == true then Opt.MoveEditCursor = false end - end + if Opt.SnapMouseEcur ~= 0 then + local zoom = reaper.GetHZoomLevel() + local distance = Opt.SnapMouseEcur / zoom + if math.abs(Ecur_pos - Mcur_pos) <= distance then Opt.SnapMouseEcur = true end + end + + timeT = { MouseSnapped } + UndoString = 'Smart split at mouse cursor' + + if Opt['MouseT/B'] == 'left/right crossfade' then + if Half == 'top' then Opt.CrossType = 'Left' + elseif Half == 'bottom' then Opt.CrossType = 'Right' end - - if Opt.SnapMouseEcur == true then - if isItemsForSplit(inisel, Ecur_pos) == true then - timeT = {Ecur_pos} - UndoString = nil - if Opt.DontMoveECurSplit == true then Opt.MoveEditCursor = false end - end + elseif Opt['MouseT/B'] == 'select left/right item' then + if Half == 'top' then SelSide = 'Left' + elseif Half == 'bottom' then SelSide = 'Right' end - - end --if item under mouse is not locked + end + + if reaper.IsMediaItemSelected( Item_mouse ) == false then + SelectedItems = {} + if Opt.AllowTSunsel == false then TSexist = false end + SelectAllMediaItems(0, false) + inisel = {Item_mouse} + elseif Opt.eCurPriority == true then + if isItemsForSplit(inisel, Ecur_pos) == true then + timeT = {Ecur_pos} + UndoString = nil + if Opt.DontMoveECurSplit == true then Opt.MoveEditCursor = false end + end + end + + if Opt.SnapMouseEcur == true then + if isItemsForSplit(inisel, Ecur_pos) == true then + timeT = {Ecur_pos} + UndoString = nil + if Opt.DontMoveECurSplit == true then Opt.MoveEditCursor = false end + end + end else --if mouse is not over item timeT = {Ecur_pos} end - if TSexist == true and #SelectedItems > 0 then --TS can't split unselected item under mouse + if TSexist == true + and (#SelectedItems > 0 or Opt.AllowTSunsel == true) then --TS can't split unselected item under mouse + if #SelectedItems > 0 and Opt.UseTSselItems == false then + goto skip + end + if isItemsForSplit(inisel, TSstart) == true - or isItemsForSplit(inisel, TSend) == true then + or isItemsForSplit(inisel, TSend) == true then timeT = {TSstart, TSend} UndoString = 'Smart split at time selection' - end + end + ::skip:: + elseif UndoString == nil then if isItemsForSplit(inisel, Ecur_pos) == true then UndoString = 'Smart split at edit cursor' @@ -1767,6 +1840,7 @@ function Main() end end + if #SelectedItems > 0 and Opt.UseTSselItems == false then TSexist = false end if UndoString ~= nil then if GlobalSplit ~= true then @@ -1850,7 +1924,7 @@ end ------------------ -------START------ -CurVers = 3.3 +CurVers = 3.4 version = tonumber( reaper.GetExtState(ExtStateName, "version") ) if version ~= CurVers then if not version or version < 3 then @@ -1872,9 +1946,9 @@ else if Window == 'transport' or Window == 'mcp' then OptionsWindow() elseif Window == 'tcp' and tcpActIDstr ~= '' then - if tcpActIDstr:gsub('%d+', '') == '' then + if tcpActIDstr:gsub('%d+', '') == '' then tcpActID = tonumber(tcpActIDstr) - elseif tcpActIDstr ~= '' then + elseif tcpActIDstr ~= '' then tcpActID = tonumber(reaper.NamedCommandLookup(tcpActIDstr)) end reaper.Main_OnCommandEx(tcpActID,0,0) diff --git a/Items Editing/az_Trim left, right or both item edges via mouse and razor.lua b/Items Editing/az_Trim left, right or both item edges via mouse and razor.lua index 835895971..2ac9c2532 100644 --- a/Items Editing/az_Trim left, right or both item edges via mouse and razor.lua +++ b/Items Editing/az_Trim left, right or both item edges via mouse and razor.lua @@ -1,9 +1,7 @@ -- @description Trim left, right or both item edges via mouse and razor -- @author AZ --- @version 1.3 --- @changelog --- - fixed bug when stretch markers were blocking the left trim --- - fixed potential bug with empty takes +-- @version 1.4 +-- @changelog - fixed bug with take envelopes -- @provides [main] az_Trim left, right or both item edges via mouse and razor/az_Open options for az_Trim left, right or both item edges via mouse and razor.lua -- @link Forum thread https://forum.cockos.com/showthread.php?t=288069 -- @donation Donate via PayPal https://www.paypal.me/AZsound @@ -394,13 +392,26 @@ function SetItemEdges(item, startTime, endTime) if strmarksnum > 0 then reaper.SetMediaItemTakeInfo_Value(take, 'D_STARTOFFS', offs) for s = 0, strmarksnum -1 do - local retval, strpos, srcpos = reaper.GetTakeStretchMarker( take, s ) + local retval, strpos, srcpos = reaper.GetTakeStretchMarker( take, s ) reaper.SetTakeStretchMarker( take, s, strpos - (startTime-pos)*rate, srcpos ) end else reaper.SetMediaItemTakeInfo_Value(take, 'D_STARTOFFS', offs) end + local takeenvs = reaper.CountTakeEnvelopes(take) + for e = 0, takeenvs -1 do + local env = reaper.GetTakeEnvelope( take, e ) + for p = 0, reaper.CountEnvelopePoints( env ) -1 do + local ret, time, value, shape, tens, sel = reaper.GetEnvelopePoint( env, p ) + if ret then + time = time - (startTime-pos)*rate + reaper.SetEnvelopePoint( env, p, time, value, shape, tens, sel, true ) + end + end + reaper.Envelope_SortPoints( env ) + end + end end end @@ -644,7 +655,7 @@ function GetRazorEdits() i=i+1 end end - else + else ---OLD WAY for backward compatibility------- @@ -870,7 +881,7 @@ end -------------------------------------------- -function trim_sel_items(side, trimTime) +function trim_sel_items(side, trimTime) --side is 'left' or 'right' local undoDesc local iCount = reaper.CountSelectedMediaItems(0) @@ -883,11 +894,17 @@ for i=0, iCount-1 do local iEnd = iPos + reaper.GetMediaItemInfo_Value(item,'D_LENGTH') local fIn = reaper.GetMediaItemInfo_Value(item,'D_FADEINLEN') local fOut = reaper.GetMediaItemInfo_Value(item,'D_FADEOUTLEN') + local fInA = reaper.GetMediaItemInfo_Value(item,'D_FADEINLEN_AUTO') + local fOutA = reaper.GetMediaItemInfo_Value(item,'D_FADEOUTLEN_AUTO') + local fInShape = reaper.GetMediaItemInfo_Value(item,'C_FADEINSHAPE') local fOutShape = reaper.GetMediaItemInfo_Value(item,'C_FADEOUTSHAPE') local fInCurv = reaper.GetMediaItemInfo_Value(item,'D_FADEINDIR') local fOutCurv = reaper.GetMediaItemInfo_Value(item,'D_FADEOUTDIR') + if fInA ~= 0 then fIn = fInA end + if fOutA~= 0 then fOut = fOutA end + if iPos < trimTime and trimTime < iEnd then if side == 'left' then @@ -895,7 +912,9 @@ for i=0, iCount-1 do undoDesc = 'left' if trimTime < iPos+fIn then - reaper.SetMediaItemInfo_Value(item,'D_FADEINLEN', fIn-(trimTime-iPos)) + local param = 'D_FADEINLEN' + if fInA ~= 0 then param = 'D_FADEINLEN_AUTO' end + reaper.SetMediaItemInfo_Value(item, param, fIn-(trimTime-iPos)) reaper.SetMediaItemInfo_Value(item,'C_FADEINSHAPE', fInShape) reaper.SetMediaItemInfo_Value(item,'D_FADEINDIR', fInCurv) else @@ -903,7 +922,6 @@ for i=0, iCount-1 do --^^--Item: Toggle enable/disable default fadein/fadeout reaper.SetMediaItemInfo_Value(item,'D_FADEINLEN', fIn) reaper.SetMediaItemInfo_Value(item,'C_FADEINSHAPE', defFshape) - --reaper.SetMediaItemInfo_Value(item,'D_FADEINDIR', defFshape) end elseif side == 'right' then @@ -911,7 +929,9 @@ for i=0, iCount-1 do undoDesc = 'right' if trimTime > iEnd-fOut then - reaper.SetMediaItemInfo_Value(item,'D_FADEOUTLEN', fOut-(iEnd-trimTime)) + local param = 'D_FADEOUTLEN' + if fOutA ~= 0 then param = 'D_FADEOUTLEN_AUTO' end + reaper.SetMediaItemInfo_Value(item, param, fOut-(iEnd-trimTime)) reaper.SetMediaItemInfo_Value(item,'C_FADEOUTSHAPE', fOutShape) reaper.SetMediaItemInfo_Value(item,'D_FADEOUTDIR', fOutCurv) else @@ -919,7 +939,6 @@ for i=0, iCount-1 do --^^--Item: Toggle enable/disable default fadein/fadeout reaper.SetMediaItemInfo_Value(item,'D_FADEOUTLEN', fOut) reaper.SetMediaItemInfo_Value(item,'C_FADEOUTSHAPE', defFshape) - --reaper.SetMediaItemInfo_Value(item,'D_FADEOUTDIR', defFshape) end end --left/right @@ -979,7 +998,7 @@ end -------------------------- -------START------ -CurVers = 1.3 +CurVers = 1.4 version = tonumber( reaper.GetExtState(ExtStateName, "version") ) if version ~= CurVers then diff --git a/Items Editing/mrtnz_Preview item from Starkovsky.lua b/Items Editing/mrtnz_Preview item from Starkovsky.lua new file mode 100644 index 000000000..c969e886c --- /dev/null +++ b/Items Editing/mrtnz_Preview item from Starkovsky.lua @@ -0,0 +1,229 @@ +-- @description Preview item from Starkovsky +-- @author mrtnz +-- @version 1.0 +-- @provides . > mrtnz_Reverse preview item from mouse cursor (perform until shortcut released) from Starkovsky.lua +-- @screenshot https://i.imgur.com/YFmQuAq.gif +-- @about # Playback reverse from Starkovsky + +local r = reaper +local reverse = true +local preview +local data = {} +local p = debug.getinfo(1, "S").source:match [[^@?(.*[\/])[^\/]-$]] +local start_time = r.time_precise() +local key_state, KEY = r.JS_VKeys_GetState(start_time - 2), nil + +r.BR_GetMouseCursorContext() + +local function get_item_under_mouse() + local _, _, details = r.BR_GetMouseCursorContext() + if details == "item" then + return r.BR_GetMouseCursorContext_Item() + end + return nil +end + +local function main() + local item = get_item_under_mouse() + if not item then + --r.ShowConsoleMsg(getLocalizedMessage("item_not_found")) + return nil + end + + local take = r.GetActiveTake(item) + if not take then + --r.ShowConsoleMsg(getLocalizedMessage("active_take_failed")) + return nil + end + + local track = r.GetMediaItem_Track(item) + if not track then + --r.ShowConsoleMsg(getLocalizedMessage("track_failed")) + return nil + end + + -- Получаем различные параметры айтема + local item_info = { + track = track, + playrate = r.GetMediaItemTakeInfo_Value(take, "D_PLAYRATE"), + volume = r.GetMediaItemInfo_Value(item, "D_VOL"), + bppitch = r.GetMediaItemInfo_Value(item, "B_PPITCH"), + pitch = r.GetMediaItemTakeInfo_Value(take, "D_PITCH"), + ipitchmode = r.GetMediaItemInfo_Value(item, "I_PITCHMODE"), + take_offset = r.GetMediaItemTakeInfo_Value(take, "D_STARTOFFS"), + length = r.GetMediaItemInfo_Value(item, "D_LENGTH"), + item_start = r.GetMediaItemInfo_Value(item, "D_POSITION"), + item = item + } + + local take_source = r.GetMediaItemTake_Source(take) + if not take_source then + --r.ShowConsoleMsg(getLocalizedMessage("take_source_failed")) + return nil + end + item_info.take_file_path = r.GetMediaSourceFileName(take_source, "") + + item_info.cursor_pos = r.BR_GetMouseCursorContext_Position() + local source_length = r.GetMediaSourceLength(take_source) / item_info.playrate + local preview_area = math.abs((item_info.cursor_pos - item_info.item_start)) + item_info.end_length = (source_length - preview_area) - item_info.take_offset / item_info.playrate + + return item_info +end + +local function createPreview(item_info, reverse) + local source = r.PCM_Source_CreateFromFile(item_info.take_file_path) + if not source then + --r.ShowConsoleMsg(getLocalizedMessage("pcm_source_failed")) + return nil + end + + if reverse then + local section = r.PCM_Source_CreateFromType('SECTION') + if not section then + --r.ShowConsoleMsg(getLocalizedMessage("pcm_section_failed")) + r.PCM_Source_Destroy(source) + return nil + end + r.CF_PCM_Source_SetSectionInfo(section, source, 0, 0, true) + r.PCM_Source_Destroy(source) + source = section + end + + local preview = r.CF_CreatePreview(source) + if not preview then + --r.ShowConsoleMsg(getLocalizedMessage("preview_failed")) + r.PCM_Source_Destroy(source) + return nil + end + + local preview_settings = { + {'D_POSITION', item_info.end_length}, + {'D_VOLUME', item_info.volume}, + {'D_PLAYRATE', item_info.playrate}, + {'D_PITCH', item_info.pitch}, + {'I_PITCHMODE', item_info.ipitchmode}, + {'B_LOOP', 0} + } + + for _, setting in ipairs(preview_settings) do + r.CF_Preview_SetValue(preview, setting[1], setting[2]) + end + + r.CF_Preview_SetOutputTrack(preview, r.EnumProjects(-1), item_info.track) + r.CF_Preview_Play(preview) + + -- Уничтожаем источник, чтобы освободить память + r.PCM_Source_Destroy(source) + + return preview +end + +local function PrintTraceback(err) + local byLine = "([^\r\n]*)\r?\n?" + local trimPath = "[\\/]([^\\/]-:%d+:.+)$" + local stack = {} + for line in string.gmatch(err, byLine) do + local str = string.match(line, trimPath) or line + stack[#stack + 1] = str + end + r.ShowConsoleMsg( + "Error: " .. stack[1] .. "\n\n" .. + "Stack traceback:\n\t" .. table.concat(stack, "\n\t", 3) .. "\n\n" .. + "Reaper: \t" .. r.GetAppVersion() .. "\n" .. + "Platform: \t" .. r.GetOS() + ) +end + +local function Release() + if data and data.is_track_solo == 0.0 then + r.SetMediaTrackInfo_Value(data.track, "I_SOLO", 0) + end + --r.JS_Mouse_SetCursor(r.JS_Mouse_LoadCursor(32512)) + if original_edit_cursor_pos then + r.SetEditCurPos(original_edit_cursor_pos, false, false) + end + r.JS_VKeys_Intercept(KEY, -1) + r.CF_Preview_StopAll() +end + +local function PDefer(func) + r.defer(function() + local status, err = xpcall(func, debug.traceback) + if not status then + PrintTraceback(err) + Release() -- Очистка ресурсов + end + end) +end + +for i = 1, 255 do + if key_state:byte(i) ~= 0 then + KEY = i + r.JS_VKeys_Intercept(KEY, 1) + break + end +end +if not KEY then return end + +local function Key_held() + key_state = r.JS_VKeys_GetState(start_time - 2) + return key_state:byte(KEY) == 1 +end + +local function CheckPreview(preview) + if not Key_held() then return end + if preview then + local _, _, position = pcall(r.CF_Preview_GetValue, preview, 'D_POSITION') + local success, length = pcall(select, 2, r.CF_Preview_GetValue(preview, 'D_LENGTH')) + + if success then + -- Ограничиваем значение edit_cursor_value, чтобы оно не могло быть меньше data.item_start + local edit_cursor_value = math.max(data.item_start, data.item_start + (length - position) - data.take_offset / data.playrate) + r.SetEditCurPos(edit_cursor_value, false, false) + --local cursor = r.JS_Mouse_LoadCursorFromFile(p..'/speaker.cur') + --r.JS_Mouse_SetCursor(cursor) + if edit_cursor_value <= data.item_start then + r.CF_Preview_StopAll() + return + end + else + --r.ShowConsoleMsg("error value\n") + end + + end + PDefer(function() CheckPreview(preview) end) +end + +local function Main() + if not Key_held() then return end + if not main_executed then + main_executed = true + original_edit_cursor_pos = r.GetCursorPosition() + + data = main(reverse) + if not data then return end + data.is_track_solo = r.GetMediaTrackInfo_Value(data.track, "I_SOLO") + + if data.is_track_solo == 0.0 then + r.SetMediaTrackInfo_Value(data.track, "I_SOLO", 1) + end + + + r.CF_Preview_StopAll() + + r.BR_GetMouseCursorContext() + r.SetEditCurPos(r.BR_GetMouseCursorContext_Position(), false, false) + + preview = createPreview(data, reverse) + if not preview then return end + + + CheckPreview(preview) + end + PDefer(Main) +end + + +PDefer(Main) +r.atexit(Release) diff --git a/MIDI Editor/talagan_OneSmallStep.lua b/MIDI Editor/talagan_OneSmallStep.lua index 7516f9c17..a83982a31 100644 --- a/MIDI Editor/talagan_OneSmallStep.lua +++ b/MIDI Editor/talagan_OneSmallStep.lua @@ -1,6 +1,6 @@ --[[ @description One Small Step : Alternative Step Input -@version 0.9.14 +@version 0.9.15 @author Ben 'Talagan' Babut @license MIT @metapackage @@ -57,8 +57,7 @@ @screenshot https://stash.reaper.fm/48269/oss_094.png @changelog - - [Feature] Added note highlighting during play - - [Feature] Reintroduced "Disarm OSS" mechanism + - [Feature] [Experimental] Markup Articulation Manager @about # Purpose @@ -89,7 +88,7 @@ --]] -VERSION = "0.9.14" +VERSION = "0.9.15" DOC_URL = "https://bentalagan.github.io/onesmallstep-doc/index.html?ver=" .. VERSION PATH = debug.getinfo(1,"S").source:match[[^@?(.*[\/])[^\/]-$]] @@ -138,6 +137,7 @@ local MK = E.MK local TGT = E.TGT local F = E.F local ED = E.ED +local ART = E.ART -- Get the debugger setting at launch local DEBUGGER_IS_ON = S.getSetting("UseDebugger") @@ -1482,6 +1482,55 @@ function SettingsPanel() reaper.ImGui_EndTabItem(ctx) end + if reaper.ImGui_BeginTabItem(ctx, 'Articulations') then + ImGui_VerticalSpacer(ctx,5) + + local track = nil; + local take = TGT.TakeForEdition() + + if take then + track = reaper.GetMediaItemTake_Track(take) + else + track = TGT.TrackForEditionIfNoItemFound() + end + + local help = "The articulation markup manager is an experimental feature that will create text events\n\z + in the \"Text Events\" CC Lane automatically to match notes that are KeySwitches.\n\z + Basically, it aims to translate KeySwitches from a note representation to a more\n\z + compact/friendly/readable horizontal representation.\n\z + \n\z + Those events will be created/synced after each note input, but also when OSS is running and\n\z + and you're mouse editing the notes in the MIDI Editor (a background task is watching those changes\n\z + and automatically converts them to the \"Text Events\" CC Lane).\n\z + \n\z + Inversely, notes that are suppressed will see their corresponding text events disappear.\n\z + \n\z + The condition for creating such a text event is that a note name is attached to the note/key\n\z + that was pressed/modified. For this to work, you need to load a Note Name map file on your track's\n\z + piano roll (note that this can be done per channel).\n\z" + + if track then + local curval = S.getTrackSetting(track, "OSSArticulationManagerEnabled") + + if reaper.ImGui_Checkbox(ctx, "Use articulation markup manager", curval) then + S.setTrackSetting(track, "OSSArticulationManagerEnabled", not curval); + end + + SL(); + reaper.ImGui_TextColored(ctx, 0xB0B0B0FF, "(?)"); + TT(help) + SL(); + reaper.ImGui_TextColored(ctx, 0x00EEFFFF, "[Per track setting]") + else + reaper.ImGui_TextColored(ctx, 0x00EEFFFF, "Please select a track to access the articulation manager") + SL(); + reaper.ImGui_TextColored(ctx, 0xB0B0B0FF, "(?)"); + TT(help) + end + + reaper.ImGui_EndTabItem(ctx) + end + if reaper.ImGui_TabItemButton(ctx, "?##go_to_help") then reaper.CF_ShellExecute(DOC_URL) end @@ -1623,7 +1672,45 @@ local function UpdateToolbarButtonState(v) reaper.RefreshToolbar2(sectionID, cmdID); end + +local lastArtUpdateProjState = nil; +local lastArtUpdateTake = nil; +local lastArtUpdateWatch = nil; + +local function WatchForArticulationsToUpdate() + if not lastArtUpdateWatch or reaper.time_precise() - lastArtUpdateWatch > 0.1 then + lastArtUpdateWatch = reaper.time_precise(); + + local take = TGT.TakeForEdition(); + + if not take then + return + end + + local track = reaper.GetMediaItemTake_Track(take) + + if not S.getTrackSetting(track, "OSSArticulationManagerEnabled") then + -- Nothing to do, but reset stuff + lastArtUpdateTake = nil + return + end + + local pscc = reaper.GetProjectStateChangeCount(); + + if take ~= lastArtUpdateTake or pscc ~= lastArtUpdateProjState then + lastArtUpdateTake = take; + lastArtUpdateProjState = pscc; + + ART.UpdateArticulationTextEventsIfNeeded(track, take) + end + + end +end + function MainLoop() + + WatchForArticulationsToUpdate(); + local engine_ret = E.atLoop(); if engine_ret == -42 then diff --git a/MIDI Editor/talagan_OneSmallStep/classes/engine_lib.lua b/MIDI Editor/talagan_OneSmallStep/classes/engine_lib.lua index 459e4877f..e3a43c6f4 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/engine_lib.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/engine_lib.lua @@ -25,6 +25,7 @@ local TGT = require "modules/target" local F = require "modules/focus" local MOD = require "modules/modifiers" local ED = require "modules/edition" +local ART = require "modules/articulations" local NAVIGATE = require "operations/navigate" local REPITCH = require "operations/repitch" @@ -404,6 +405,7 @@ return { ED = ED, SNP = SNP, N = N, + ART = ART, atStart = atStart, atExit = atExit, diff --git a/MIDI Editor/talagan_OneSmallStep/classes/modules/articulations.lua b/MIDI Editor/talagan_OneSmallStep/classes/modules/articulations.lua new file mode 100644 index 000000000..de9b792fb --- /dev/null +++ b/MIDI Editor/talagan_OneSmallStep/classes/modules/articulations.lua @@ -0,0 +1,50 @@ +-- @noindex +-- @author Ben 'Talagan' Babut +-- @license MIT +-- @description This is part of One Small Step + +local MU = require "lib/MIDIUtils" +local S = require "modules/settings" + +local TEXT_CHANNEL = 1 -- This is the one which is called "Text", I hesitate with "Program", which is 8 + +local function UpdateArticulationTextEventsIfNeeded(track, take) + + if not track or not take then + return + end + + if not S.getTrackSetting(track, "OSSArticulationManagerEnabled") then + return + end + + MU.MIDI_InitializeTake(take) + MU.MIDI_OpenWriteTransaction(take) + + local _, nc, _, tsc = MU.MIDI_CountEvts(take) + for ti = tsc, 1, -1 do + local _, _, _, _, type, msg = MU.MIDI_GetTextSysexEvt(take, ti - 1) + + -- Avoid destroying texts from other tool/people, we're not alone here + -- Beware, we use an unicode diamond char and string.sub is picky + if type == TEXT_CHANNEL and string.sub(msg,1,4) == "◈ " then + MU.MIDI_DeleteTextSysexEvt(take, ti - 1) + end + end + + for ni = 1, nc do + local _, _, _, startppqpos, _, chan, pitch, _ = MU.MIDI_GetNote(take, ni - 1) + + local note_name = reaper.GetTrackMIDINoteNameEx(0, track, pitch, chan) + + if note_name ~= nil and note_name ~= "" then + MU.MIDI_InsertTextSysexEvt(take, false, false, startppqpos, 1, "◈ " .. note_name) + end + end + + MU.MIDI_CommitWriteTransaction(take); +end + +return { + UpdateArticulationTextEventsIfNeeded= UpdateArticulationTextEventsIfNeeded +} diff --git a/MIDI Editor/talagan_OneSmallStep/classes/modules/settings.lua b/MIDI Editor/talagan_OneSmallStep/classes/modules/settings.lua index 3e2e15b2f..02c7291de 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/modules/settings.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/modules/settings.lua @@ -5,6 +5,10 @@ local D = require "modules/defines" +local TrackSettingDefs = { + OSSArticulationManagerEnabled = { type = "bool", default = false } +} + local SettingDefs = { StepBackModifierKey = { type = "int", default = D.IsMacOs and 16 or 16 }, @@ -63,7 +67,6 @@ local SettingDefs = { Disarmed = { type = "bool", default = false }, NoteHiglightingDuringPlay = { type = "bool", default = false }, - UseDebugger = { type = "bool", default = false }, VelocityLimiterEnabled = { type = "bool", default = false }, @@ -101,14 +104,8 @@ local function unsafestr(str) return str end -local function getSetting(setting) - local spec = SettingDefs[setting]; - - if spec == nil then - error("Trying to get unknown setting " .. setting); - end - - local val = unsafestr(reaper.GetExtState("OneSmallStep", setting)); +local function serializedStringToValue(str, spec) + local val = unsafestr(str); if val == nil then val = spec.default; @@ -123,8 +120,37 @@ local function getSetting(setting) -- No conversion needed end end - return val; + + return val +end + +local function valueToSerializedString(val, spec) + local str = '' + if spec.type == 'bool' then + str = (val == true) and "true" or "false"; + elseif spec.type == 'int' then + str = tostring(val); + elseif spec.type == 'double' then + str = tostring(val); + elseif spec.type == "string" then + -- No conversion needed + str = val + end + return str +end + +local function getSetting(setting) + local spec = SettingDefs[setting]; + + if spec == nil then + error("Trying to get unknown setting " .. setting); + end + + local str = reaper.GetExtState("OneSmallStep", setting) + + return serializedStringToValue(str, spec) end + local function setSetting(setting, val) local spec = SettingDefs[setting]; @@ -135,18 +161,41 @@ local function setSetting(setting, val) if val == nil then reaper.DeleteExtState("OneSmallStep", setting, true); else - if spec.type == 'bool' then - val = (val == true) and "true" or "false"; - elseif spec.type == 'int' then - val = tostring(val); - elseif spec.type == 'double' then - val = tostring(val); - elseif spec.type == "string" then - -- No conversion needed - end - reaper.SetExtState("OneSmallStep", setting, val, true); + local str = valueToSerializedString(val, spec); + reaper.SetExtState("OneSmallStep", setting, str, true); + end +end + +local function getTrackSetting(track, setting) + local spec = TrackSettingDefs[setting]; + + if track == nil then + error("Trying to get setting " .. setting .. " from nil track ") + end + + if spec == nil then + error("Trying to get unknown track setting " .. setting); + end + + local succ, str = reaper.GetSetMediaTrackInfo_String(track, "P_EXT:OneSmallStep:" .. setting, '', false); + + return serializedStringToValue(str or '', spec) +end + +local function setTrackSetting(track, setting, val) + local spec = TrackSettingDefs[setting]; + + if track == nil then + error("Trying to set setting " .. setting .. " to nil track ") + end + if spec == nil then + error("Trying to set unknown track setting " .. setting); end + + local str = valueToSerializedString(val, spec); + reaper.GetSetMediaTrackInfo_String(track, "P_EXT:OneSmallStep:" .. setting, str, true) end + local function resetSetting(setting) setSetting(setting, SettingDefs[setting].default) end @@ -242,6 +291,9 @@ return { resetSetting = resetSetting, getSettingSpec = getSettingSpec, + getTrackSetting = getTrackSetting, + setTrackSetting = setTrackSetting, + setPlaybackMeasureCount = setPlaybackMeasureCount, getPlaybackMeasureCount = getPlaybackMeasureCount, diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/insert.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/insert.lua index 5389c2ff6..cff59f387 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/insert.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/insert.lua @@ -7,6 +7,8 @@ local T = require "modules/time" local S = require "modules/settings" local D = require "modules/defines" local N = require "modules/notes" +local ART = require "modules/articulations" + local GEN = require "operations/generic" local MU = require "lib/MIDIUtils" @@ -111,6 +113,8 @@ local function Insert(km, track, take, new_notes, held_notes, triggered_by_key_e GEN.AddAndExtendNotes(c, new_notes, held_notes) GEN.ForwardOperationFinish(c, c.advanceTime, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1) end @@ -140,6 +144,8 @@ local function InsertBack(km, track, take, notes_to_shorten, triggered_by_key_ev GEN.GenericDelete(c,notes_to_shorten, false, true) GEN.BackwardOperationFinish(c, c.rewindTime) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/navigate.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/navigate.lua index 061a3a30c..421d95dc6 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/navigate.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/navigate.lua @@ -6,6 +6,7 @@ local SNP = require "modules/snap" local T = require "modules/time" local S = require "modules/settings" +local ART = require "modules/articulations" local function Navigate(track, direction) local ns = SNP.nextSnapFromCursor(track, direction) diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/repitch.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/repitch.lua index c7acf3ba1..e29ed7938 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/repitch.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/repitch.lua @@ -8,6 +8,8 @@ local T = require "modules/time" local S = require "modules/settings" local N = require "modules/notes" local D = require "modules/defines" +local ART = require "modules/articulations" + local GEN = require "operations/generic" local MU = require "lib/MIDIUtils" @@ -140,6 +142,8 @@ local function Repitch(km, track, take, notes_to_add, notes_to_extend, triggered end end + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1) end @@ -162,6 +166,8 @@ local function RepitchBack(km, track, take, notes_to_add, notes_to_extend, trigg T.KeepEditCursorOnScreen() end + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1) end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/replace.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/replace.lua index 367da873a..54bde474f 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/replace.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/replace.lua @@ -7,6 +7,8 @@ local T = require "modules/time" local S = require "modules/settings" local D = require "modules/defines" local N = require "modules/notes" +local ART = require "modules/articulations" + local GEN = require "operations/generic" local MU = require "lib/MIDIUtils" @@ -134,6 +136,8 @@ local function Replace(km, track, take, notes_to_add, notes_to_extend, triggered GEN.AddAndExtendNotes(c, notes_to_add, notes_to_extend) GEN.ForwardOperationFinish(c, c.advanceTime, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1) end @@ -162,6 +166,8 @@ local function ReplaceBack(km, track, take, notes_to_shorten, triggered_by_key_e GEN.GenericDelete(c, notes_to_shorten, false, false) GEN.BackwardOperationFinish(c, c.rewindTime) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/stretch.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/stretch.lua index a925dff32..addb626ea 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/stretch.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/stretch.lua @@ -7,6 +7,8 @@ local T = require "modules/time" local D = require "modules/defines" local N = require "modules/notes" local MK = require "modules/markers" +local ART = require "modules/articulations" + local MU = require "lib/MIDIUtils" local GEN = require "operations/generic" @@ -125,6 +127,8 @@ local function Stretch(km, track, take, notes_to_add, notes_to_extend, triggered GEN.ForwardOperationFinish(c, c.advanceTime, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end @@ -241,6 +245,8 @@ function StretchBack(km, track, take, notes_to_shorten, triggered_by_key_event) GEN.BackwardOperationFinish(c, c.rewindTime) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/stuff.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/stuff.lua index 3ab9c77dc..30eb8fba5 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/stuff.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/stuff.lua @@ -8,6 +8,8 @@ local S = require "modules/settings" local D = require "modules/defines" local N = require "modules/notes" local MK = require "modules/markers" +local ART = require "modules/articulations" + local GEN = require "operations/generic" local MU = require "lib/MIDIUtils" @@ -191,6 +193,8 @@ local function Stuff(km, track, take, notes_to_add, notes_to_extend, triggered_b -- Pass nil as jump time, we don't want to jump in stuff mode GEN.ForwardOperationFinish(c, nil, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts),-1); end @@ -356,6 +360,8 @@ local function StuffBack(km, track, take, notes_to_shorten, triggered_by_key_eve GEN.BackwardOperationFinish(c, nil) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts),-1); end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/write.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/write.lua index 642b911f9..744c01e05 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/write.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/write.lua @@ -7,6 +7,8 @@ local T = require "modules/time" local S = require "modules/settings" local D = require "modules/defines" local AT = require "modules/action_triggers" +local ART = require "modules/articulations" + local MU = require "lib/MIDIUtils" local GEN = require "operations/generic" @@ -22,9 +24,12 @@ local function Write(km, track, take, notes_to_add, notes_to_extend, triggered_b MU.MIDI_InitializeTake(take) MU.MIDI_OpenWriteTransaction(take) + GEN.AddAndExtendNotes(c, notes_to_add, notes_to_extend) GEN.ForwardOperationFinish(c, c.advanceTime, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1); end @@ -77,6 +82,8 @@ local function WriteBack(km, track, take, notes_to_shorten, triggered_by_key_eve GEN.BackwardOperationFinish(c, jumpTime) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end diff --git a/Tracks/reapertips_Track icon selector.lua b/Tracks/reapertips_Track icon selector.lua new file mode 100644 index 000000000..7f3227105 --- /dev/null +++ b/Tracks/reapertips_Track icon selector.lua @@ -0,0 +1,525 @@ +-- @description Track icon selector +-- @author Reapertips & Sexan +-- @version 1.0 +-- @screenshot +-- https://i.imgur.com/bFK2HYk.png +-- https://i.imgur.com/MabMOW1.png +-- @about +-- # Track Icon Selector +-- Created by Reapertips and Sexan +-- +-- The Track Icon Selector is a tool designed to streamline the process of applying custom icons to your tracks in REAPER. By scanning REAPER's "track_icons" folder, the script automatically populates a user-friendly interface with icons and their categories. +-- +-- ## Key Features: +-- +-- - Intuitive Interface: Quickly browse and select icons from a visually appealing interface. +-- - Instant Search: Efficiently find specific icons using the built-in search function. +-- - Categorization: Organize icons into categories for easy navigation. +-- - Customizable Icons: Resize icons to fit your preferences and customize their appearance. +-- - Sidebar Preferences: Control whether the sidebar opens automatically on script launch +-- - Dockable Window: Conveniently dock the script window to the left of your tracks for easy access. +-- - Yellow Outline Around Used Icons: Instantly identify tracks that already have icons applied. +-- +-- ## Creating and Modifying Categories: +-- +-- To create or modify categories for your icons, navigate to the following REAPER folder: +-- +-- REAPER Folder > Data > track_icons +-- Within this folder, create or modify folders to represent your desired categories. The script will automatically recognize these folders as categories in the sidebar, allowing you to filter icons based on their assigned categories. + +-- @license GPL v3 + +if not reaper.ImGui_GetBuiltinPath then + reaper.ShowMessageBox("Script needs ReaImGui.\nPlease Install it in next window", "MISSING DEPENDENCIES", 0) + reaper.ReaPack_BrowsePackages('^ReaImGui:') + return +end + +local r = reaper +package.path = r.ImGui_GetBuiltinPath() .. '/?.lua' +local os_separator = package.config:sub(1, 1) +local imgui = require "imgui" "0.9.1" +local reaper_path = r.GetResourcePath() + +local COLORS = { + ["win_bg"] = 0x282828ff, + ["sidebar_col"] = 0x2c2c2cff, + ["hover_col"] = 0x3c6191ff, + ["outline_col"] = 0xffcb40ff, + ["text_active"] = 0xf1f2f2ff, + ["text_inactive"] = 0x999B9Fff +} + +local ALWAYS_SHOW_CATEGORIES = false +local QUIT_ON_SELECT = false +local TOOLTIPS = true +local SIDE_WIDTH = 100 + +function StringToTable(str) + local f, err = load("return " .. str) + return f ~= nil and f() or nil +end + +if r.HasExtState("TRACK_ICONS4", "STORED_DATA") then + local stored = r.GetExtState("TRACK_ICONS4", "STORED_DATA") + if stored ~= nil then + local storedTable = StringToTable(stored) + if storedTable ~= nil then + COLORS["win_bg"] = storedTable.win_col + COLORS["sidebar_col"] = storedTable.sidebar_col + COLORS["hover_col"] = storedTable.hover_col + COLORS["text_active"] = storedTable.text_active + COLORS["text_inactive"] = storedTable.text_inactive + COLORS["outline_col"] = storedTable.outline_col + ALWAYS_SHOW_CATEGORIES = storedTable.ALWAYS_SHOW_CATEGORIES + QUIT_ON_SELECT = storedTable.QUIT_ON_SELECT + TOOLTIPS = storedTable.TOOLTIPS + SIDE_WIDTH = storedTable.SIDE_WIDTH + end + end +end + +local OPEN_CATEGORIES = ALWAYS_SHOW_CATEGORIES + +local MAIN_PNG_TBL = { + [0] = {} +} +local FILTERED_PNG + +local icon_size = 32 + +r.set_action_options(1) + +local ctx = imgui.CreateContext('Track icon selector') +local WND_W, WND_H = 500, 500 +local FLT_MIN, FLT_MAX = imgui.NumericLimits_Float() + +local SYSTEM_FONT_FACTORY = imgui.CreateFont('sans-serif', 12, imgui.FontFlags_Bold) +imgui.Attach(ctx, SYSTEM_FONT_FACTORY) + +local png_path_track_icons = "/Data/track_icons/" + +function SerializeTable(val, name, skipnewlines, depth) + skipnewlines = skipnewlines or false + depth = depth or 0 + local tmp = string.rep(" ", depth) + if name then + if type(name) == "number" and math.floor(name) == name then + name = "[" .. name .. "]" + elseif not string.match(name, '^[a-zA-z_][a-zA-Z0-9_]*$') then + name = string.gsub(name, "'", "\\'") + name = "['" .. name .. "']" + end + tmp = tmp .. name .. " = " + end + if type(val) == "table" then + tmp = tmp .. "{" + for k, v in pairs(val) do + tmp = tmp .. SerializeTable(v, k, skipnewlines, depth + 1) .. "," + end + tmp = tmp .. string.rep(" ", depth) .. "}" + elseif type(val) == "number" then + tmp = tmp .. tostring(val) + elseif type(val) == "string" then + tmp = tmp .. string.format("%q", val) + elseif type(val) == "boolean" then + tmp = tmp .. (val and "true" or "false") + else + tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\"" + end + return tmp +end + +function TableToString(table) + return SerializeTable(table) +end + +local function GetDirFilesRecursive(dir, tbl, filter) + for index = 0, math.huge do + local path = r.EnumerateSubdirectories(dir, index) + if not path then break end + tbl[#tbl + 1] = { dir = path, {} } + GetDirFilesRecursive(dir .. path, tbl[#tbl], filter) + end + + for index = 0, math.huge do + local file = r.EnumerateFiles(dir, index) + if not file then break end + if file:find(filter, nil, true) then + tbl[#tbl + 1] = { name = dir .. os_separator .. file, short_name = file } + table.insert(MAIN_PNG_TBL[0], { name = dir .. os_separator .. file, short_name = file}) + end + end +end + +GetDirFilesRecursive(reaper_path .. png_path_track_icons, MAIN_PNG_TBL, ".png") +for i = 0, #MAIN_PNG_TBL do + table.sort(MAIN_PNG_TBL[i], function(a, b) if a.name and b.name then return a.name:lower() < b.name:lower() end end) +end + +local function RefreshImgObj() + for i = 1, #FILTERED_PNG do + if FILTERED_PNG[i].img_obj then + FILTERED_PNG[i].img_obj = nil + end + end +end + +local old_t, old_filter +local function FilterActions(actions, filter_text) + if old_filter == filter_text then + return old_t + end + local t = {} + for i = 1, #actions do + local action = actions[i] + if action.name then + local name = action.short_name:lower() + local found = true + for word in filter_text:gmatch("%S+") do + if not name:find(word:lower(), 1, true) then + found = false + break + end + end + if found then + table.insert(t, action) + end + end + end + old_filter = filter_text + old_t = t + return t +end + +local function SteppedSliderDouble(label, val, v_min, v_max, step, format, flags) + local changed, value = imgui.SliderDouble(ctx, label, val, v_min, v_max, format, flags) + if changed then + value = math.floor((value / step) + 0.5) * step + NEED_REFRESH = true + end + return changed, value +end + +local function DrawTooltips(str) + if imgui.BeginTooltip(ctx) then + imgui.Text(ctx, str) + imgui.EndTooltip(ctx) + end +end + +local PNG_FILTER = '' +local cur_category = 0 +local sl_val = 1 +local function PngSelector(button_size) + local ww = imgui.GetWindowSize(ctx) + local padding = imgui.GetStyleVar(ctx, imgui.StyleVar_FramePadding) + + imgui.PushStyleColor(ctx, imgui.Col_Text, COLORS["text_active"]) + imgui.PushStyleColor(ctx, imgui.Col_ChildBg, COLORS["win_bg"]) + imgui.PushStyleColor(ctx, imgui.Col_Button, COLORS["win_bg"]) + imgui.PushStyleColor(ctx, imgui.Col_FrameBg, COLORS["sidebar_col"]) + + imgui.BeginGroup(ctx) + if imgui.Button(ctx, "||") then + OPEN_CATEGORIES = not OPEN_CATEGORIES + end + + if imgui.IsPopupOpen(ctx,"R_CTX") then + local minx, miny = imgui.GetItemRectMin(ctx) + local maxx, maxy = imgui.GetItemRectMax(ctx) + imgui.DrawList_AddRect(DRAW_LIST, minx, miny, maxx, maxy, COLORS["outline_col"], 0, 0, 2) + end + + if imgui.IsItemHovered(ctx) then + DrawTooltips("Toggle Categories Panel") + if imgui.IsMouseClicked(ctx, 1) then + imgui.OpenPopup(ctx, "R_CTX") + end + end + imgui.SameLine(ctx) + imgui.SetNextItemWidth(ctx, 50) + RV_SLD, sl_val = SteppedSliderDouble("Size ", sl_val, 1, 3, 0.25, "") + button_size = button_size * sl_val + + if imgui.IsItemHovered(ctx) and not imgui.IsItemActive(ctx) then + DrawTooltips("Increase/Decrease Icon Size") + end + + if not OPEN_CATEGORIES and ww > 250 or (OPEN_CATEGORIES and ww > (SIDE_WIDTH+250)) then + imgui.SameLine(ctx, 0, 0) + end + imgui.SetNextItemWidth(ctx, -FLT_MIN - 15) + RV_F, PNG_FILTER = imgui.InputTextWithHint(ctx, "##input2", "Search Icons", PNG_FILTER) + imgui.SameLine(ctx, 0, 0) + + imgui.PushStyleColor(ctx, imgui.Col_Button, COLORS["sidebar_col"]) + if imgui.Button(ctx, "X") then + PNG_FILTER = '' + NEED_REFRESH = true + end + imgui.PopStyleColor(ctx) + if imgui.IsItemHovered(ctx) then + DrawTooltips("Clear Search") + end + + FILTERED_PNG = FilterActions(MAIN_PNG_TBL[cur_category], PNG_FILTER) + if RV_F or NEED_REFRESH or RV_SLD then + RefreshImgObj() + if NEED_REFRESH then NEED_REFRESH = nil end + end + local item_spacing_x, item_spacing_y = imgui.GetStyleVar(ctx, imgui.StyleVar_ItemSpacing) + item_spacing_x = item_spacing_y + imgui.PushStyleVar(ctx, imgui.StyleVar_ItemSpacing, item_spacing_y, item_spacing_y) + local buttons_count = #FILTERED_PNG + local window_visible_x2 = ({ imgui.GetWindowPos(ctx) })[1] + + ({ imgui.GetWindowContentRegionMax(ctx) })[1] + imgui.PushStyleColor(ctx, imgui.Col_Button, COLORS["win_bg"]) + + if imgui.BeginChild(ctx, "filtered_pngs_list", 0, 0) then + for n = 0, #FILTERED_PNG - 1 do + local image = FILTERED_PNG[n + 1].name + local stripped_name = FILTERED_PNG[n + 1].short_name + local xx, yy = imgui.GetCursorPos(ctx) + + imgui.PushID(ctx, n) + imgui.Dummy(ctx, button_size, button_size) -- PLACE HOLDER + + local minx, miny = imgui.GetItemRectMin(ctx) + local maxx, maxy = imgui.GetItemRectMax(ctx) + if imgui.IsRectVisibleEx(ctx, minx, miny, maxx, maxy) then + if not imgui.ValidatePtr(FILTERED_PNG[n + 1].img_obj, 'ImGui_Image*') then + FILTERED_PNG[n + 1].img_obj = imgui.CreateImage(image) + end + + imgui.SetCursorPos(ctx, xx, yy) + + if imgui.ImageButton(ctx, "##png_select", FILTERED_PNG[n + 1].img_obj, button_size, button_size, 0, 0, 1, 1) then + for i = 1, #TRACKS do + r.GetSetMediaTrackInfo_String(TRACKS[i], "P_ICON", image, true) + end + if QUIT_ON_SELECT then + WANT_CLOSE = true + end + LAST_ICON = image + CUR_ICON = image + end + + if imgui.IsItemHovered(ctx) and TOOLTIPS then + DrawTooltips(stripped_name) + end + end + + if CUR_ICON == image then + if LAST_ICON ~= CUR_ICON then + SCROLL_TO_IMG = true + LAST_ICON = CUR_ICON + end + imgui.DrawList_AddRect(DRAW_LIST, minx, miny, maxx + (padding*2), maxy + padding*2, COLORS["outline_col"], 0, 0, 2) + if SCROLL_TO_IMG then + SCROLL_TO_IMG = nil + imgui.SetScrollHereY(ctx) + end + end + + local next_button_x2 = maxx + item_spacing_x + button_size + + if n + 1 < buttons_count and next_button_x2 < window_visible_x2 then + imgui.SameLine(ctx) + end + + imgui.PopID(ctx) + end + imgui.EndChild(ctx) + end + imgui.PopStyleVar(ctx) + imgui.PopStyleColor(ctx, 5) + imgui.EndGroup(ctx) +end + +local function Categories() + local item_spacing_x = imgui.GetStyleVar(ctx, imgui.StyleVar_ItemSpacing) + + imgui.PushStyleVar(ctx, imgui.StyleVar_ItemSpacing, item_spacing_x, 20) + imgui.PushStyleColor(ctx, imgui.Col_ChildBg, COLORS["sidebar_col"]) + if imgui.BeginChild(ctx, "PM_INSPECTOR", SIDE_WIDTH) then + for i = 0, #MAIN_PNG_TBL do + if i ~= PREV_CATEGORY then + if MAIN_PNG_TBL[i].sel then + imgui.PushStyleColor(ctx, imgui.Col_Text, COLORS["text_active"]) + else + imgui.PushStyleColor(ctx, imgui.Col_Text, COLORS["text_inactive"]) + end + else + imgui.PushStyleColor(ctx, imgui.Col_Text, COLORS["text_active"]) + end + if i == 0 then + imgui.SetCursorPosY(ctx, imgui.GetCursorPosY(ctx) + 10) + if imgui.Selectable(ctx, " All Icons", cur_category == 0, nil) then + cur_category = 0 + NEED_REFRESH = true + old_filter = nil + end + else + if MAIN_PNG_TBL[i].dir then + if imgui.Selectable(ctx, " " .. MAIN_PNG_TBL[i].dir, cur_category == i, nil) then + cur_category = i + NEED_REFRESH = true + old_filter = nil + end + end + end + if imgui.IsItemHovered(ctx) then + MAIN_PNG_TBL[i].sel = true + else + MAIN_PNG_TBL[i].sel = nil + end + if i ~= PREV_CATEGORY then + if MAIN_PNG_TBL[i].sel then + imgui.PopStyleColor(ctx) + else + imgui.PopStyleColor(ctx) + end + else + imgui.PopStyleColor(ctx) + end + end + imgui.EndChild(ctx) + end + if cur_category ~= PREV_CATEGORY then + PREV_CATEGORY = cur_category + end + imgui.PopStyleVar(ctx) + imgui.PopStyleColor(ctx) +end + +local function SaveSettings() + local data = TableToString({ + win_col = COLORS["win_bg"], + win_col_alt = COLORS["win_bg_alt"], + hover_col = COLORS["hover_col"], + sidebar_col = COLORS["sidebar_col"], + outline_col = COLORS["outline_col"], + text_active = COLORS["text_active"], + text_inactive = COLORS["text_inactive"], + ALWAYS_SHOW_CATEGORIES = ALWAYS_SHOW_CATEGORIES, + QUIT_ON_SELECT = QUIT_ON_SELECT, + TOOLTIPS = TOOLTIPS, + SIDE_WIDTH = SIDE_WIDTH, + }) + r.SetExtState("TRACK_ICONS4", "STORED_DATA", data, true) +end + +local function DrawRClickCtx() + if imgui.BeginPopup(ctx, "R_CTX") then + if imgui.BeginMenu(ctx, "Customize") then + local RV_UPDATED + RV_COL1, COLORS["win_bg"] = r.ImGui_ColorEdit4(ctx, "Primary background color", COLORS["win_bg"], + r.ImGui_ColorEditFlags_NoInputs()) + RV_COL2, COLORS["sidebar_col"] = r.ImGui_ColorEdit4(ctx, "Secondary background color", COLORS["sidebar_col"], + r.ImGui_ColorEditFlags_NoInputs()) + RV_COL3, COLORS["hover_col"] = r.ImGui_ColorEdit4(ctx, "Hover icon color", COLORS["hover_col"], + r.ImGui_ColorEditFlags_NoInputs()) + RV_COL4, COLORS["outline_col"] = r.ImGui_ColorEdit4(ctx, "Selected outline color", COLORS["outline_col"], + r.ImGui_ColorEditFlags_NoInputs()) + RV_COL5, COLORS["text_active"] = r.ImGui_ColorEdit4(ctx, "Text active color", COLORS["text_active"], + r.ImGui_ColorEditFlags_NoInputs()) + RV_COL6, COLORS["text_inactive"] = r.ImGui_ColorEdit4(ctx, "Text Inactive color", COLORS["text_inactive"], + r.ImGui_ColorEditFlags_NoInputs()) + RV_COL7, SIDE_WIDTH = imgui.SliderInt( ctx, "Side panel width", SIDE_WIDTH, 80, 200) + if RV_COL1 or RV_COL2 or RV_COL3 or RV_COL4 or RV_COL5 or RV_COL6 or RV_COL7 then + RV_UPDATED = true + end + if imgui.MenuItem(ctx, "Reset to Default", nil) then + COLORS["win_bg"] = 0x282828ff + COLORS["sidebar_col"] = 0x2c2c2cff + COLORS["hover_col"] = 0x3c6191ff + COLORS["outline_col"] = 0xffcb40ff + COLORS["text_active"] = 0xf1f2f2ff + COLORS["text_inactive"] = 0x999B9Fff + SIDE_WIDTH = 100 + SaveSettings() + end + if RV_UPDATED then + SaveSettings() + end + imgui.EndMenu(ctx) + end + if imgui.BeginMenu(ctx, "Preferences") then + if imgui.MenuItem(ctx, "Show sidebar by default", nil, ALWAYS_SHOW_CATEGORIES == true) then + ALWAYS_SHOW_CATEGORIES = not ALWAYS_SHOW_CATEGORIES + SaveSettings() + end + if imgui.MenuItem(ctx, "Quit after applying icon", nil, QUIT_ON_SELECT == true) then + QUIT_ON_SELECT = not QUIT_ON_SELECT + SaveSettings() + end + if imgui.MenuItem(ctx, "Show file name on icon hover", nil, TOOLTIPS == true) then + TOOLTIPS = not TOOLTIPS + SaveSettings() + end + imgui.EndMenu(ctx) + end + if imgui.MenuItem(ctx, "Dock to LEFT") then + SET_DOCK_ID = -2 + end + if imgui.MenuItem(ctx, "Undock") then + SET_DOCK_ID = 0 + end + imgui.EndPopup(ctx) + end +end + +local function PushTheme() + imgui.PushStyleColor(ctx, imgui.Col_ButtonHovered, COLORS["hover_col"]) + imgui.PushStyleColor(ctx, imgui.Col_HeaderHovered, COLORS["hover_col"]) + imgui.PushStyleColor(ctx, imgui.Col_ScrollbarBg, COLORS["win_bg"]) +end + +imgui.SetNextWindowSizeConstraints(ctx, WND_H, WND_W, FLT_MAX, FLT_MAX) +local function main() + if SET_DOCK_ID then + imgui.SetNextWindowDockID(ctx, SET_DOCK_ID) + SET_DOCK_ID = nil + end + + TRACKS = {} + for i = 1, r.CountSelectedTracks(nil) do + TRACKS[#TRACKS + 1] = r.GetSelectedTrack(nil, i - 1) + end + imgui.PushStyleColor(ctx, imgui.Col_WindowBg, COLORS["win_bg"]) + imgui.PushStyleColor(ctx, imgui.Col_TitleBgActive, COLORS["win_bg"]) + local visible, p_open = imgui.Begin(ctx, 'Track Icons', true) + imgui.PopStyleColor(ctx, 2) + if visible then + imgui.PushFont(ctx, SYSTEM_FONT_FACTORY) + PushTheme() + DRAW_LIST = imgui.GetWindowDrawList(ctx) + if #TRACKS ~= 0 then + if #TRACKS == 1 then + RV_I, CUR_ICON = r.GetSetMediaTrackInfo_String(TRACKS[1], "P_ICON", "", false) + else + CUR_ICON = nil + end + if OPEN_CATEGORIES then + Categories() + imgui.SameLine(ctx) + end + PngSelector(icon_size) + DrawRClickCtx() + else + LAST_ICON = nil + NEED_REFRESH = true + imgui.Text(ctx, "SELECT TRACK") + end + imgui.PopStyleColor(ctx, 3) + imgui.PopFont(ctx) + imgui.End(ctx) + end + if WANT_CLOSE then p_open = false end + if p_open then + r.defer(main) + end +end + +r.defer(main) diff --git a/Various/amagalma_gianfini_ReaNamer (track-item-region-marker renaming utility).lua b/Various/amagalma_gianfini_ReaNamer (track-item-region-marker renaming utility).lua index b0cfc8c53..c33383781 100644 --- a/Various/amagalma_gianfini_ReaNamer (track-item-region-marker renaming utility).lua +++ b/Various/amagalma_gianfini_ReaNamer (track-item-region-marker renaming utility).lua @@ -1,9 +1,8 @@ -- @description ReaNamer (track-item-region-marker renaming utility) -- @author amagalma & gianfini --- @version 1.45 +-- @version 1.47 -- @changelog --- - All shortcuts working (requires JS_ReaScriptAPI) --- - Fixed crashes when adding Regions or Markers +-- - Fixed bugs in Markers or Regions mode and time selection present -- @provides -- amagalma_ReaNamer Replace Help.lua -- amagalma_ReaNamer utf8data.lua @@ -48,7 +47,7 @@ ----------------------------------------------------------------------------------------------- -local version = "1.45" +local version = "1.47" if not reaper.APIExists( "BR_Win32_FindWindowEx" ) then reaper.MB( "SWS / S&M extension is required for this script to work", "SWS / S&M extension is not installed!", 0 ) @@ -454,8 +453,8 @@ utf8_lc_uc, utf8_uc_lc = nil, nil -- Global variables local has_changed, what, trackCount, indexed_track, scroll_line_selected, itemCount, indexed_item local undo_stack, ToBeTrackNames, OriginalTrackNames, ToBeItemNames, OriginalItemNames, redo_stack -local regionCount, ToBeRegionNames, OriginalRegionNames = 0 -local markerCount, ToBeMarkerNames, OriginalMarkerNames = 0 +local TotalRegionCount, regionCount, ToBeRegionNames, OriginalRegionNames = 0, 0 +local TotalMarkerCount, markerCount, ToBeMarkerNames, OriginalMarkerNames = 0, 0 local mod_stack_name, mod_stack_parm1, mod_stack_parm2 = {}, {}, {} -- management of modifiers' undo stack local scroll_list_x, scroll_list_y, scroll_list_w, scroll_list_h, scroll_line_selected local matchCase = "y" @@ -630,7 +629,7 @@ local function AllRegionNames() -- in Time Selection else st, en = st+.01, en-.01 -- exclude adjacent regions end - local marker_cnt = reaper.CountProjectMarkers( 0 ) + local marker_cnt, _, regions = reaper.CountProjectMarkers( 0 ) local table = {} local table2 = {} local count = 0 @@ -646,7 +645,7 @@ local function AllRegionNames() -- in Time Selection end end end - return table, table2 + return table, table2, (regions and regions or 0) end local function AllMarkerNames() -- in Time Selection @@ -654,7 +653,7 @@ local function AllMarkerNames() -- in Time Selection if st == en then st, en = -math.huge, math.huge end - local marker_cnt = reaper.CountProjectMarkers( 0 ) + local marker_cnt, markers = reaper.CountProjectMarkers( 0 ) local table = {} local table2 = {} local count = 0 @@ -668,7 +667,7 @@ local function AllMarkerNames() -- in Time Selection end end end - return table, table2 + return table, table2, (markers and markers or 0) end -- Replace button Help if "amagalma_ReaNamer Replace Help.lua" cannot be loaded @@ -1376,9 +1375,11 @@ local function init_tables() ToBeRegionNames, RegionNamesIndex = nil, nil OriginalRegionNames = nil indexed_region = nil + TotalRegionCount = 0 ToBeMarkerNames, MarkerNamesIndex = nil, nil OriginalMarkerNames = nil indexed_marker = nil + TotalMarkerCount = 0 elseif what == "items" then ToBeItemNames = AllItemNames() OriginalItemNames = AllItemNames() @@ -1392,11 +1393,13 @@ local function init_tables() ToBeRegionNames, RegionNamesIndex = nil, nil OriginalRegionNames = nil indexed_region = nil + TotalRegionCount = 0 ToBeMarkerNames, MarkerNamesIndex = nil, nil OriginalMarkerNames = nil indexed_marker = nil + TotalMarkerCount = 0 elseif what == "regions" then - ToBeRegionNames, RegionNamesIndex = AllRegionNames() + ToBeRegionNames, RegionNamesIndex, TotalRegionCount = AllRegionNames() OriginalRegionNames = AllRegionNames() indexed_region = 1 undo_stack = 0 @@ -1411,8 +1414,9 @@ local function init_tables() ToBeMarkerNames, MarkerNamesIndex = nil, nil OriginalMarkerNames = nil indexed_marker = nil + TotalMarkerCount = 0 elseif what == "markers" then - ToBeMarkerNames, MarkerNamesIndex = AllMarkerNames() + ToBeMarkerNames, MarkerNamesIndex, TotalMarkerCount = AllMarkerNames() OriginalMarkerNames = AllMarkerNames() indexed_marker = 1 undo_stack = 0 @@ -1427,6 +1431,7 @@ local function init_tables() ToBeRegionNames, RegionNamesIndex = nil, nil OriginalRegionNames = nil indexed_region = nil + TotalRegionCount = 0 end btn_DOWN_can_move = 0 end @@ -1545,18 +1550,18 @@ local function main() -- MAIN FUNCTION ----------------------------------------- else DOWN_btn:set_color_updown() end - +---HERE if what == "regions" then - local _, _, num_regions = reaper.CountProjectMarkers( 0 ) + local _, _, num_regions = reaper.CountProjectMarkers(0) local st, en = reaper.GetSet_LoopTimeRange2( 0, 0, 0, 0, 0, 0 ) - if st ~= prev_st or en ~= prev_en or num_regions ~= regionCount then + if st ~= prev_st or en ~= prev_en or num_regions ~= TotalRegionCount then init_tables() prev_st, prev_en = st, en end elseif what == "markers" then local _, num_markers = reaper.CountProjectMarkers( 0 ) local st, en = reaper.GetSet_LoopTimeRange2( 0, 0, 0, 0, 0, 0 ) - if st ~= prev_st or en ~= prev_en or num_markers ~= markerCount then + if st ~= prev_st or en ~= prev_en or num_markers ~= TotalMarkerCount then init_tables() prev_st, prev_en = st, en end diff --git a/Various/az_Auto-send all inputs to the virtual midi keyboard (VKB) if mouse is in empty track space or recording is running (Background).lua b/Various/az_Auto-send all inputs to the virtual midi keyboard (VKB) if mouse is in empty track space or recording is running (Background).lua new file mode 100644 index 000000000..b743f585b --- /dev/null +++ b/Various/az_Auto-send all inputs to the virtual midi keyboard (VKB) if mouse is in empty track space or recording is running (Background).lua @@ -0,0 +1,107 @@ +-- @description Auto-send all inputs to the virtual midi keyboard (VKB) if mouse is in empty track space or recording is running (Background) +-- @author AZ +-- @version 1.0 +-- @link Forum thread https://forum.cockos.com/showthread.php?t=288069 +-- @donation Donate via PayPal https://www.paypal.me/AZsound +-- @about +-- # Auto-send all inputs to the virtual midi keyboard (VKB) if mouse is in empty track space or recording is running (Background) +-- +-- The script is useful to release keyboard for the editing shortcuts in arrange while you use virtual midi keyboard. +-- So you can use one-button shortcuts when mouse cursor placed on items or envelopes. +-- Move mouse to the empty track space and you are ready to use computer keyboard for the virtual midi keyboard. +-- +-- Place this script into startup section and forget about extra toggling. (look for SWS startup action as example) +-- +-- Choose Terminate and save if it asks on first closing the script. + +function msg(value) + reaper.ShowConsoleMsg(tostring(value)..'\n') +end +------------------ +------------------- +function GetTopBottomItemHalf() +local itempart +local x, y = reaper.GetMousePosition() + +local item_under_mouse = reaper.GetItemFromPoint(x,y,true) + +if item_under_mouse then + + local item_h = reaper.GetMediaItemInfo_Value( item_under_mouse, "I_LASTH" ) + + local OScoeff = 1 + if reaper.GetOS():match("^Win") == nil then + OScoeff = -1 + end + + local test_point = math.floor( y + (item_h-1) *OScoeff) + local test_item, take = reaper.GetItemFromPoint( x, test_point, true ) + + if item_under_mouse == test_item then + itempart = "header" + else + local test_point = math.floor( y + item_h/2 *OScoeff) + local test_item, take = reaper.GetItemFromPoint( x, test_point, true ) + + if item_under_mouse ~= test_item then + itempart = "bottom" + else + itempart = "top" + end + end + + return item_under_mouse, itempart +else return nil end + +end +----------------- +----------------- +function main() + local kb_state = reaper.GetToggleCommandState(40377) --View: Show virtual MIDI keyboard + if kb_state == 1 then + local window, segment, details = reaper.BR_GetMouseCursorContext() + local inp_state = reaper.GetToggleCommandState(40637) --Virtual MIDI keyboard: Send all input to VKB + local playstate = reaper.GetPlayStateEx(0) + local item, part = GetTopBottomItemHalf() + + if window ~= 'arrange' + --or ((not item or part == 'header') and segment ~= 'envelope') + or (not item and segment ~= 'envelope') + or playstate == 5 then + if inp_state == 0 then reaper.Main_OnCommandEx(40637,0,0) end --Toggle ON + else + if inp_state == 1 then reaper.Main_OnCommandEx(40637,0,0) end --Toggle OFF + end + + end + reaper.defer(main) +end +----------------- +----------------- +ExtStateName = 'autoToggleVKBinputs_AZ' + +local state = reaper.GetExtState(ExtStateName, 'state') + +if state ~= '' then + local _,_,secID,cmdID = reaper.get_action_context() + local realstate = reaper.GetToggleCommandStateEx(secID, cmdID) + if realstate == tonumber(state) then + state = -tonumber(state) +1 + else state = tonumber(state) + end +else + state = 1 +end +reaper.SetExtState(ExtStateName, 'state', state, true) + +local is_new_value, filename, sec, cmd, mode, resolution, val = reaper.get_action_context() +if state > 0 then --work + reaper.SetToggleCommandState( sec, cmd, state ) + reaper.RefreshToolbar2( sec, cmd ) + main() + +else + reaper.SetToggleCommandState( sec, cmd, state ) + reaper.RefreshToolbar2( sec, cmd ) + reaper.defer(function()end) +end diff --git a/Various/az_Simple project reconform.lua b/Various/az_Simple project reconform.lua new file mode 100644 index 000000000..c620cc522 --- /dev/null +++ b/Various/az_Simple project reconform.lua @@ -0,0 +1,1374 @@ +-- @description Simple project reconform +-- @author AZ +-- @version 0.5 +-- @link Forum thread https://forum.cockos.com/showthread.php?t=288069 +-- @about +-- # Simple Project Reconform +-- The script is under development, but you can try to use it. +-- Read more: https://forum.cockos.com/showthread.php?t=288069 + +----------------------------- +function msg(value) + reaper.ShowConsoleMsg(tostring(value)..'\n') +end +----------------------------- + +-------------------------- +function rgbToHex(rgba) -- passing a table with percentage like {100, 50, 20, 90} + local hexadecimal = '0X' + + for key, value in pairs(rgba) do + local hex = '' + if value > 100 or value < 0 then return error('Color must be a percantage value\n between 0 and 100') end + value = (255/100)*value + while(value > 0)do + local index = math.floor(math.fmod(value, 16) + 1) + value = math.floor(value / 16) + hex = string.sub('0123456789ABCDEF', index, index) .. hex + end + + if(string.len(hex) == 0)then + hex = '00' + + elseif(string.len(hex) == 1)then + hex = '0' .. hex + end + + hexadecimal = hexadecimal .. hex + end + + return hexadecimal +end +----------------------- + +ExtStateName = "SimpleReconform_AZ" + +function GetExtStates() + for i, option in ipairs(OptDefaults) do + if option[3] ~= nil then + local state = reaper.GetExtState(ExtStateName, option[2]) + + if state ~= "" then + local stateType = type(option[3]) + if stateType == 'number' then state = tonumber(state) end + if stateType == 'boolean' then + if state == 'true' then state = true else state = false end + end + OptDefaults[i][3] = state + else + reaper.SetExtState(ExtStateName, option[2], tostring(option[3]), true) + end + + end + end +end + +--------------------- + +function SetExtStates() + for i, option in ipairs(OptDefaults) do + if option[3] ~= nil then + reaper.SetExtState(ExtStateName, option[2], tostring(option[3]), true) + end + end +end + +--------------------- + +function OptionsDefaults() + OptDefaults = {} + local text + + text = 'Content for reconform:' + table.insert(OptDefaults, {text, 'Separator', nil }) + + text = 'Master track envelopes' + table.insert(OptDefaults, {text, 'ReconfMaster', true }) + + text = 'Locked items' + table.insert(OptDefaults, {text, 'ReconfLockedItems', true }) + + text = 'Disabled lanes on fixed lanes tracks' + table.insert(OptDefaults, {text, 'ReconfDisabledLanes', true }) + + text = 'Project markers' + table.insert(OptDefaults, {text, 'ReconfMarkers', true }) + + text = 'Regions' + table.insert(OptDefaults, {text, 'ReconfRegions', true }) + + text = 'Tempo envelope' + table.insert(OptDefaults, {text, 'ReconfTempo', true }) + + text = 'Processing options:' + table.insert(OptDefaults, {text, 'Separator', nil }) + + text = 'Heal gaps if time is consistent' + table.insert(OptDefaults, {text, 'HealGaps', true }) + + text = 'Heal splits if time is consistent' + table.insert(OptDefaults, {text, 'HealSplits', true }) + + text = 'Fill in gaps using content from the' + table.insert(OptDefaults, {text, 'FillGaps', "don't fill", { + 'left region', + 'right region', + "don't fill" + } }) + + text = 'Ignore crossfades, prefer content from the' + table.insert(OptDefaults, {text, 'IgnoreCrossfades', "don't ignore", { + 'left region', + 'right region', + "don't ignore" + } }) + + text = 'Reduce gaps in regions (with care!)' + table.insert(OptDefaults, {text, 'ReduceGapsRegions', false }) + +end + +-------------------------------- + +function SetOptGlobals() + Opt = {} + for i = 1, #OptDefaults do + local name = OptDefaults[i][2] + Opt[name] = OptDefaults[i][3] + end +end + +------------------------ + +function OptionsWindow() + local imgui_path = reaper.GetResourcePath() .. '/Scripts/ReaTeam Extensions/API/imgui.lua' + if not reaper.file_exists(imgui_path) then + reaper.ShowMessageBox('Please, install ReaImGui from Reapack!', 'No Imgui library', 0) + return + end + dofile(imgui_path) '0.8.7.6' + OptionsDefaults() + GetExtStates() + local fontSize = 17 + local ctx, font, fontSep, fontBig + local H = fontSize + local W = fontSize + local loopcnt = 0 + local _, imgui_version_num, _ = reaper.ImGui_GetVersion() + + OldPrjStart = 0 + NewPrjStart = nil + local oldprjOffsetTime = '' + local newprjOffsetTime = '' + local SourcePrj + local prjselpreview + tracksForImportFlag = 1 + + local esc + local enter + local space + local escMouse + local enterMouse + local spaceMouse + + local gui_colors = { + White = rgbToHex({90,90,90,100}), + Green = rgbToHex({52,85,52,100}), + Red = rgbToHex({90,10,10,100}), + Blue = rgbToHex({30,60,80,100}), + TitleBg = rgbToHex({30,20,30,100}), + Background = rgbToHex({11,14,14,95}), + Text = rgbToHex({92,92,81.5,100}), + activeText = rgbToHex({50,95,80,100}), + ComboBox = { + Default = rgbToHex({20,25,30,100}), + Hovered = rgbToHex({35,40,45,80}), + Active = rgbToHex({42,42,37,100}), + }, + --[[ + Input = { + Background = rgbToHex({50,50,50,100}), + Hover = rgbToHex({10,10,90,100}), + Text = rgbToHex({90,90,80,100}), + Label = rgbToHex({90,80,90,100}), + },]] + Button = { + Default = rgbToHex({25,30,30,100}), + Hovered = rgbToHex({35,40,45,100}), + Active = rgbToHex({42,42,37,100}), + }, + MainButton = { + Default = rgbToHex({25,50,40,80}), + Hovered = rgbToHex({35,60,55,80}), + Active = rgbToHex({56,56,42,90}), + } + } + + -------------- + function SplitFilename(strFilename) + -- Returns the Path, Filename, and Extension as 3 values + return string.match(strFilename, "(.-)([^\\]-([^\\%.]+))$") + end + -------------- + function frame() + reaper.ImGui_PushFont(ctx, font) + + --About button + reaper.ImGui_SameLine(ctx, fontSize*25, nil) + if reaper.ImGui_Button(ctx, 'About - forum page', nil, nil) then + local doc = 'https://forum.cockos.com/showthread.php?t=288069' + if reaper.CF_ShellExecute then + reaper.CF_ShellExecute(doc) + else + reaper.MB(doc, 'Simple Project Reconform forum page', 0) + end + end + + reaper.ImGui_Text(ctx, 'EDL reconform may be here in the future...\n' + ..'For now it based on a reference file, on the take "Start in source" offset.' ) + + if reaper.ImGui_CollapsingHeader(ctx, 'Reconform options') then + for i, v in ipairs(OptDefaults) do + local option = v + + if type(option[3]) == 'boolean' then + local _, newval = reaper.ImGui_Checkbox(ctx, option[1], option[3]) + option[3] = newval + end + + if type(option[3]) == 'number' then + reaper.ImGui_PushItemWidth(ctx, fontSize*3 ) + local _, newval = + reaper.ImGui_InputDouble(ctx, option[1], option[3], nil, nil, option[4]) + + option[3] = newval + end + + if type(option[3]) == 'string' then + local choice + for k = 1, #option[4] do + if option[4][k] == option[3] then choice = k end + end + + reaper.ImGui_Text(ctx, option[1]) + reaper.ImGui_SameLine(ctx, nil, nil) + + reaper.ImGui_PushItemWidth(ctx, fontSize*10.3 ) + --reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.activeText) + if reaper.ImGui_BeginCombo(ctx, '##'..i, option[3], nil) then + for k,f in ipairs(option[4]) do + local is_selected = choice == k + if reaper.ImGui_Selectable(ctx, option[4][k], is_selected) then + choice = k + end + + -- Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if is_selected then + reaper.ImGui_SetItemDefaultFocus(ctx) + end + end + reaper.ImGui_EndCombo(ctx) + end + --reaper.ImGui_PopStyleColor(ctx) + + option[3] = option[4][choice] + end + + if type(option[3]) == 'nil' then + reaper.ImGui_PushFont(ctx, fontSep) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.White) + + reaper.ImGui_Text(ctx, '' ) + reaper.ImGui_SeparatorText( ctx, option[1] ) + + reaper.ImGui_PopStyleColor(ctx, 1) + reaper.ImGui_PopFont(ctx) + end + + OptDefaults[i] = option + end -- for + reaper.ImGui_SetWindowSize(ctx, 0, 0, nil ) + end -- end of collapsing header + + reaper.ImGui_Text(ctx, '' ) --vertical space + + --Reference track processing + if reaper.ImGui_Button(ctx, 'Create report markers for gaps, crossfades, splits', nil, nil ) then + reaper.Undo_BeginBlock2( 0 ) + reaper.PreventUIRefresh( 1 ) + CreateReportMarkers() + reaper.Undo_EndBlock2( 0, 'Create report markers - Simple reconform', -1 ) + reaper.UpdateArrange() + end + + reaper.ImGui_Text(ctx, '' ) --vertical space + + --Input box + button + reaper.ImGui_PushItemWidth(ctx, fontSize*7 ) + + local fieldState, retOldPrjOffsetTime = + reaper.ImGui_InputText(ctx, 'Old project position ', reaper.format_timestr_pos( OldPrjStart, '', 5 ), nil, nil) + if fieldState == true then + OldPrjStart = reaper.parse_timestr_pos( retOldPrjOffsetTime, 5 ) + end + reaper.ImGui_SameLine(ctx, nil, fontSize*0.7) + reaper.ImGui_PushID(ctx, 1) + if reaper.ImGui_Button(ctx, 'Get edit cursor', nil, nil ) then + OldPrjStart = reaper.GetCursorPosition() + end + reaper.ImGui_PopID(ctx) + + --Save button + reaper.ImGui_SameLine(ctx, nil, fontSize*1.25) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Button(), gui_colors.MainButton.Default) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_ButtonHovered(), gui_colors.MainButton.Hovered) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_ButtonActive(), gui_colors.MainButton.Active) + if enter == true then + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Button(), gui_colors.MainButton.Active) + end + enterMouse = reaper.ImGui_Button(ctx, 'Reconform', fontSize*8, nil) + if enter == true then reaper.ImGui_PopStyleColor(ctx, 1) end + reaper.ImGui_PopStyleColor(ctx, 3) + + --Input box + button + if NewPrjStart then + newprjOffsetTime = reaper.format_timestr_pos( NewPrjStart, '', 5 ) + end + local fieldState, retNewPrjOffsetTime = + reaper.ImGui_InputText(ctx, 'New project position', newprjOffsetTime, nil, nil) + if newprjOffsetTime ~= '' and fieldState == true then + NewPrjStart = reaper.parse_timestr_pos( retNewPrjOffsetTime, 5 ) + end + reaper.ImGui_SameLine(ctx, nil, nil) + reaper.ImGui_PushID(ctx, 2) + if reaper.ImGui_Button(ctx, 'Get edit cursor', nil, nil ) then + NewPrjStart = reaper.GetCursorPosition() + end + reaper.ImGui_PopID(ctx) + + --Paste new items for gaps header + reaper.ImGui_Text(ctx, '' ) --vertical space + if reaper.ImGui_CollapsingHeader(ctx, 'Paste new items for gaps from another project tab') then + --Project selector + local retprj = true + local path, projfn, extension + local Projects = {} + local cur_retprj, cur_projfn = reaper.EnumProjects( -1 ) + local idx = 0 + while retprj ~= nil do + retprj, projfn = reaper.EnumProjects( idx ) + if retprj and retprj ~= cur_retprj and projfn ~= '' then + path, projfn, extension = SplitFilename(projfn) + table.insert(Projects, {retprj, projfn}) + end + idx = idx+1 + end + + if cur_retprj == SourcePrj then SourcePrj = nil end + + if #Projects > 0 and SourcePrj == nil then + SourcePrj = Projects[#Projects][1] + prjselpreview = Projects[#Projects][2] + elseif #Projects == 0 then + prjselpreview = 'Open a named project in another tab' + SourcePrj = nil + end + + reaper.ImGui_PushItemWidth(ctx, fontSize*23 ) + local choicePrj + if reaper.ImGui_BeginCombo(ctx, 'Source project', prjselpreview, nil) then + for k,f in ipairs(Projects) do + local is_selected = choicePrj == k + if reaper.ImGui_Selectable(ctx, Projects[k][2], is_selected) then + prjselpreview = Projects[k][2] + SourcePrj = Projects[k][1] + choicePrj = k + end + + -- Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if is_selected then + reaper.ImGui_SetItemDefaultFocus(ctx) + end + end + reaper.ImGui_EndCombo(ctx) + end + + --Source tracks + local SrcTrks = { + 'All tracks', + 'Selected tracks', + "Don't import from muted tracks" + } + local choiceTrks + if reaper.ImGui_BeginCombo(ctx, 'Source tracks', SrcTrks[tracksForImportFlag], nil) then + for k,f in ipairs(SrcTrks) do + local is_selected = choiceTrks == k + if reaper.ImGui_Selectable(ctx, SrcTrks[k], is_selected) then + tracksForImportFlag = k + choiceTrks = k + end + + -- Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if is_selected then + reaper.ImGui_SetItemDefaultFocus(ctx) + end + end + reaper.ImGui_EndCombo(ctx) + end + + --Button for paste items + reaper.ImGui_Text(ctx, '' ) --space before buttons + reaper.ImGui_SameLine(ctx, nil, fontSize*24.25) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Button(), gui_colors.MainButton.Default) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_ButtonHovered(), gui_colors.MainButton.Hovered) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_ButtonActive(), gui_colors.MainButton.Active) + if reaper.ImGui_Button(ctx, 'Paste Items', fontSize*8, nil ) then + reaper.Undo_BeginBlock2( 0 ) + reaper.PreventUIRefresh( 1 ) + + PasteItemsFromPrj(cur_retprj, SourcePrj) + + reaper.Undo_EndBlock2( 0, 'Paste new items - Simple reconform', -1 ) + reaper.UpdateArrange() + end + reaper.ImGui_PopStyleColor(ctx, 3) + + reaper.ImGui_SetWindowSize(ctx, 0, 0, nil ) + end -- end of collapsing header + + reaper.ImGui_Text(ctx, '' ) --space before buttons + + --Esc button + --[[ + reaper.ImGui_SameLine(ctx, fontSize*2, fontSize) + if esc == true then + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Button(), gui_colors.Button.Active) + end + escMouse = reaper.ImGui_Button(ctx, 'Esc', nil, nil ) + if esc == true then reaper.ImGui_PopStyleColor(ctx, 1) end + ]] + + reaper.ImGui_PopFont(ctx) + end + + -------------- + function loop() + esc = reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Escape()) + --enter = reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Enter()) + space = reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Space()) + undo = ( reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_RightCtrl()) + or + reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_LeftCtrl()) ) + and + reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Z()) + + redo = ( reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_RightCtrl()) + or + reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_LeftCtrl()) ) + and + reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_Z()) + and( + reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_RightShift()) + or + reaper.ImGui_IsKeyPressed(ctx, reaper.ImGui_Key_LeftShift()) + ) + + reaper.ImGui_PushFont(ctx, font) + + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_WindowBg(), gui_colors.Background) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_TitleBgActive(), gui_colors.TitleBg) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.Text) + + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Button(), gui_colors.Button.Default) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_ButtonHovered(), gui_colors.Button.Hovered) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_ButtonActive(), gui_colors.Button.Active) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_CheckMark(), gui_colors.Green) + + --Combo box and check box background + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_FrameBg(), gui_colors.ComboBox.Default) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_FrameBgHovered(), gui_colors.ComboBox.Hovered) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_FrameBgActive(), gui_colors.ComboBox.Active) + --Combo box drop down list + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Header(), gui_colors.ComboBox.Default) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_HeaderHovered(), gui_colors.ComboBox.Hovered) + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_HeaderActive(), gui_colors.ComboBox.Active) + + local window_flags = reaper.ImGui_WindowFlags_None()--reaper.ImGui_WindowFlags_MenuBar() + reaper.ImGui_SetNextWindowSize(ctx, W, H, reaper.ImGui_Cond_Once()) -- Set the size of the windows. Use in the 4th argument reaper.ImGui_Cond_FirstUseEver() to just apply at the first user run, so ImGUI remembers user resize s2 + + reaper.ImGui_PushStyleColor(ctx, reaper.ImGui_Col_Text(), gui_colors.White) + local visible, open = reaper.ImGui_Begin(ctx, 'Simple Project Reconform', true, window_flags) + reaper.ImGui_PopStyleColor(ctx, 1) + + if visible then + frame() + if loopcnt == 0 then reaper.ImGui_SetWindowSize(ctx, 0, 0, nil ) end + reaper.ImGui_End(ctx) + end + + reaper.ImGui_PopStyleColor(ctx, 13) + reaper.ImGui_PopFont(ctx) + + esc = escMouse or reaper.ImGui_IsKeyReleased(ctx, reaper.ImGui_Key_Escape()) + enter = enterMouse --or reaper.ImGui_IsKeyReleased(ctx, reaper.ImGui_Key_Enter()) + + if undo then reaper.Main_OnCommandEx(40029,0,0) end + if redo then reaper.Main_OnCommandEx(40030,0,0) end + + if open and esc ~= true and enter ~= true then + SetExtStates() + reaper.defer(loop) + elseif enter == true then + SetExtStates() + reaper.Undo_BeginBlock2( 0 ) + reaper.PreventUIRefresh( 1 ) + + main() + + if UndoString then + reaper.Main_OnCommandEx(42406,0,0) --Razor edit: Clear all areas + reaper.Undo_EndBlock2( 0, UndoString, -1 ) + reaper.UpdateArrange() + else reaper.defer(function()end) + end + reaper.defer(loop) + --reaper.ImGui_DestroyContext(ctx) + else + reaper.ImGui_DestroyContext(ctx) + end + loopcnt = loopcnt+1 + end + ----------------- + local fontName + ctx = reaper.ImGui_CreateContext('Smart Split Options') -- Add VERSION TODO + if reaper.GetOS():match("^Win") == nil then + reaper.ImGui_SetConfigVar(ctx, reaper.ImGui_ConfigVar_ViewportsNoDecoration(), 0) + fontName = 'sans-serif' + else fontName = 'Calibri' + end + font = reaper.ImGui_CreateFont(fontName, fontSize, reaper.ImGui_FontFlags_None()) -- Create the fonts you need + fontSep = reaper.ImGui_CreateFont(fontName, fontSize-2, reaper.ImGui_FontFlags_Italic()) + fontBig = reaper.ImGui_CreateFont(fontName, fontSize+4, reaper.ImGui_FontFlags_None()) + reaper.ImGui_Attach(ctx, font) + reaper.ImGui_Attach(ctx, fontSep) + reaper.ImGui_Attach(ctx, fontBig) + + loop(ctx, font) +end +--------------------------- +------------------------- + +function CreateReportMarkers() + SetOptGlobals() + local refItems = CollectSelectedItems() + local tag = ' - SPReconform' + + if Opt.HealGaps == true or Opt.HealSplits == true then + CleanUpRefItems(refItems) + end + + if #refItems == 0 then + reaper.ShowMessageBox('Please select reference items on a track','Simple Project Reconform', 0) + return + end + + if not NewPrjStart then + reaper.ShowMessageBox('Set the point where reconformed version starts', 'Simple Project Reconform', 0) + return + else + table.insert(Gaps, {NewPrjStart, refItems[1][3]}) + end + + table.sort(Gaps, function (a,b) return (a[1] < b[1]) end ) + + local track = reaper.GetTrack(0, LastRefTrID) + local fl = reaper.SetMediaTrackInfo_Value(track, 'I_FREEMODE', 2 ) + reaper.UpdateTimeline() + local splits + local gaps + local xfades + + local it = reaper.GetTrackMediaItem(track, 0) + local lanesN = 1 / reaper.GetMediaItemInfo_Value(it, 'F_FREEMODE_H') + reaper.SetOnlyTrackSelected(track) + local splitsTake + local splcnt = 1 + local xfadecnt = 1 + + for i, item in ipairs(refItems) do + local itemPos = item[3] + local itemEnd = itemPos + item[2] - item[1] + local prev_itemEnd + if i>1 then prev_itemEnd = refItems[i-1][3] + refItems[i-1][2] - refItems[i-1][1] end + + if not splits then + reaper.Main_OnCommandEx(42647,0,0) --Track lanes: Add empty lane at bottom of track + local splitsItem = reaper.AddMediaItemToTrack(track) + splitsTake = reaper.AddTakeToMediaItem(splitsItem) + splits = lanesN + lanesN = lanesN +1 + local ret, str = reaper.GetSetMediaTrackInfo_String(track, 'P_LANENAME:'..splits, 'SPLITS', true) + reaper.SetMediaItemInfo_Value(splitsItem, 'D_POSITION', NewPrjStart) + reaper.SetMediaItemInfo_Value(splitsItem, 'D_LENGTH', refItems[#refItems][3] + refItems[#refItems][2] - refItems[#refItems][1] - NewPrjStart) + reaper.SetMediaItemInfo_Value(splitsItem, 'I_FIXEDLANE', splits) + end + + local timetxt = reaper.format_timestr_pos(item[1],'', 5) ..' - '.. reaper.format_timestr_pos(item[2],'', 5) + reaper.SetTakeMarker(splitsTake, -1, splcnt..' src '..timetxt..tag, itemPos - NewPrjStart) + splcnt = splcnt +1 + + if i>1 and itemPos < prev_itemEnd then + if not xfades then + reaper.Main_OnCommandEx(42647,0,0) --Track lanes: Add empty lane at bottom of track + xfades = lanesN + lanesN = lanesN +1 + local ret, str = reaper.GetSetMediaTrackInfo_String(track, 'P_LANENAME:'..xfades, 'xFADES', true) + end + local xfadeItem = reaper.AddMediaItemToTrack(track) + local xfadeTake = reaper.AddTakeToMediaItem(xfadeItem) + reaper.SetMediaItemInfo_Value(xfadeItem, 'D_POSITION', itemPos) + reaper.SetMediaItemInfo_Value(xfadeItem, 'D_LENGTH', prev_itemEnd - itemPos) + reaper.SetMediaItemInfo_Value(xfadeItem, 'I_FIXEDLANE', xfades) + reaper.SetTakeMarker(xfadeTake, -1, 'xfade '..xfadecnt..tag, 0) + xfadecnt = xfadecnt +1 + end + end + + local gapcnt = 1 + + for g, gap in ipairs(Gaps) do + if not gaps then + reaper.Main_OnCommandEx(42647,0,0) --Track lanes: Add empty lane at bottom of track + gaps = lanesN + lanesN = lanesN +1 + local ret, str = reaper.GetSetMediaTrackInfo_String(track, 'P_LANENAME:'..gaps, 'GAPS', true) + end + local gapStart = gap[1] + local gapEnd = gap[2] + local gapItem = reaper.AddMediaItemToTrack(track) + local gapTake = reaper.AddTakeToMediaItem(gapItem) + reaper.SetMediaItemInfo_Value(gapItem, 'D_POSITION', gapStart) + reaper.SetMediaItemInfo_Value(gapItem, 'D_LENGTH', gapEnd - gapStart) + reaper.SetMediaItemInfo_Value(gapItem, 'I_FIXEDLANE', gaps) + reaper.SetTakeMarker(gapTake, -1, 'gap '..gapcnt..tag, 0) + gapcnt = gapcnt +1 + end +end + +------------------------- + +function PasteItemsFromPrj(CurPrj, SrcPrj) + SetOptGlobals() + local refItems = CollectSelectedItems() + + if Opt.HealGaps == true or Opt.HealSplits == true then + CleanUpRefItems(refItems) + end + + if #refItems == 0 then + reaper.ShowMessageBox('Please select reference items on a track','Simple Project Reconform', 0) + return + end + + if not NewPrjStart then + reaper.ShowMessageBox('Set the point where reconformed version starts', 'Simple Project Reconform', 0) + return + else + table.insert(Gaps, {NewPrjStart, refItems[1][3]}) + end + + local pastedTrks = {} + local trcntSrc = reaper.CountTracks(SrcPrj) + + + for i = 0, trcntSrc -1 do + local tr = reaper.GetTrack(SrcPrj, i) + + if tracksForImportFlag == 2 then + if reaper.IsTrackSelected(tr) == false then tr = nil end + elseif tracksForImportFlag == 3 then + if reaper.GetMediaTrackInfo_Value(tr, 'B_MUTE') == 1 then tr = nil end + end + + if tr then + local ret, chunk = reaper.GetTrackStateChunk(tr,'',false) + reaper.InsertTrackAtIndex( LastRefTrID + #pastedTrks, false ) + local newtr = reaper.GetTrack(CurPrj, LastRefTrID + #pastedTrks) + reaper.SetTrackStateChunk( newtr, chunk, true ) + table.insert(pastedTrks, newtr) + end + end + + for t, tr in ipairs(pastedTrks) do + local itemsToDelete = {} + local icnt = reaper.CountTrackMediaItems(tr) -1 + + while icnt >= 0 do + local item = reaper.GetTrackMediaItem(tr, icnt) + local iPos = reaper.GetMediaItemInfo_Value(item, 'D_POSITION') + local iEnd = iPos + reaper.GetMediaItemInfo_Value(item, 'D_LENGTH') + + for g, gap in ipairs(Gaps) do + if (iEnd + NewPrjStart) - gap[1] > 0.005 and gap[2] - (iPos + NewPrjStart) > 0.005 then + reaper.SetMediaItemInfo_Value(item, 'D_POSITION', iPos + NewPrjStart) + break + end + end + local newiPos = reaper.GetMediaItemInfo_Value(item, 'D_POSITION') + --msg( newiPos..' '..iPos) + if newiPos == iPos then table.insert(itemsToDelete, item) end + + icnt = icnt -1 + end + + for i, item in ipairs(itemsToDelete) do reaper.DeleteTrackMediaItem(tr, item) end + + end + + reaper.Main_OnCommandEx(40297,0,CurPrj) --Track: Unselect (clear selection of) all tracks + for t, tr in ipairs(pastedTrks) do + if reaper.CountTrackMediaItems(tr) == 0 then + reaper.DeleteTrack(tr) + else reaper.SetTrackSelected(tr, true) + end + end +end + +---------------------------- +------------------------- + +function CollectSelectedItems(TableToAdd,areaStart,areaEnd) + local ItemsTable = {} + Gaps = {} + LastRefTrID = 0 + + if type(TableToAdd) == 'table' then + ItemsTable = TableToAdd + end + + if not OldPrjStart then OldPrjStart = 0 end + + local prevEnd + local selItNumb = reaper.CountSelectedMediaItems(0) + for i = 0, selItNumb - 1 do + local item = reaper.GetSelectedMediaItem(0,i) + --local itemLocked = reaper.GetMediaItemInfo_Value(item, 'C_LOCK') + local take = reaper.GetActiveTake(item) + local refPos = reaper.GetMediaItemInfo_Value(item, 'D_POSITION') + local refLength = reaper.GetMediaItemInfo_Value(item, 'D_LENGTH') + + if areaStart and areaEnd then + if refPos > areaEnd or refPos + refLength < areaStart then take = nil end + end + + if take then + local src = reaper.GetMediaItemTake_Source( take ) + local srctype = reaper.GetMediaSourceType( src ) + if srctype ~= 'EMPTY' and srctype ~= 'MIDI' then + local offset = reaper.GetMediaItemTakeInfo_Value(take, 'D_STARTOFFS') + OldPrjStart + table.insert(ItemsTable, {offset, offset + refLength, refPos}) + + local tr = reaper.GetMediaItem_Track(item) + local trID = reaper.GetMediaTrackInfo_Value( tr, 'IP_TRACKNUMBER' ) -1 + + if LastRefTrID < trID then LastRefTrID = trID end + + if prevEnd then + if prevEnd < refPos then table.insert(Gaps, {prevEnd, refPos}) end + end + + prevEnd = refPos + refLength + end + end + + end + return ItemsTable +end + +----------------------------- +function CleanUpRefItems(Items) + local i = #Items + while i>1 do + local areaStart = Items[i][1] + local areaEnd = Items[i][2] + local targetPos = Items[i][3] + + local prevAstart = Items[i-1][1] + local prevAend = Items[i-1][2] + local prevTpos = Items[i-1][3] + + if Opt.HealGaps == true then + if areaStart - (targetPos - prevTpos) == prevAstart then + Items[i-1][2] = areaEnd + table.remove(Items,i) + end + elseif Opt.HealSplits == true then + if areaStart == prevAend then + Items[i-1][2] = areaEnd + table.remove(Items,i) + end + end + + i = i - 1 + end +end + +----------------------------- +function AdjustRefItems(Items) --adjusts virtual ref items, real track in prj will be safe + local i = #Items + while i>0 do + local areaStart = Items[i][1] + --local areaEnd = Items[i][2] + local targetPos = Items[i][3] + local prevAstart + local prevAend + local prevTpos + + local prevItemEnd + + if i==1 then + if NewPrjStart then prevItemEnd = NewPrjStart + else break + end + else + prevAstart = Items[i-1][1] + prevAend = Items[i-1][2] + prevTpos = Items[i-1][3] + + prevItemEnd = prevTpos + (prevAend - prevAstart) + end + + if prevItemEnd < targetPos then + if Opt.FillGaps == 'left region' and i ~= 1 then + Items[i-1][2] = prevAend + (targetPos - prevItemEnd) + elseif Opt.FillGaps == 'right region' then + Items[i][1] = math.max(areaStart - (targetPos - prevItemEnd), 0) + Items[i][3] = targetPos - (areaStart - Items[i][1] ) + end + end + + if prevItemEnd > targetPos and i ~= 1 then + if Opt.IgnoreCrossfades == 'left region' then + Items[i][1] = areaStart + (prevItemEnd - targetPos) + Items[i][3] = prevItemEnd + elseif Opt.IgnoreCrossfades == 'right region' then + Items[i-1][2] = areaStart + end + end + + i = i - 1 + end +end + +----------------------------- + +function SetMasterREarea(areaStart, areaEnd) + local timeString = tostring(areaStart) ..' '.. tostring(areaEnd) ..' ' + + local track = reaper.GetMasterTrack(0) + local envcnt = reaper.CountTrackEnvelopes(track) + --msg(envcnt) + local string = '' + local focused + for e = 0, envcnt -1 do + local env = reaper.GetTrackEnvelope(track, e) + local _, envName = reaper.GetEnvelopeName( env ) + if envName ~= 'Tempo map' then + if focused ~= true then + reaper.SetCursorContext( 2, env ) + focused = true + end + local ret, GUID = reaper.GetSetEnvelopeInfo_String( env, 'GUID', '', false ) + string = string .. ' '.. timeString .. ' "'..GUID..'"' + end + end + local ret, string = reaper.GetSetMediaTrackInfo_String( track, 'P_RAZOREDITS', string, true ) + --msg(string) + +end +----------------------------- + +function SetREarea(areaStart, areaEnd) + local trcnt = reaper.CountTracks(0) + local timeString = tostring(areaStart) ..' '.. tostring(areaEnd) ..' ' + + for i = LastRefTrID +1, trcnt -1 do + local track = reaper.GetTrack(0,i) + local envcnt = reaper.CountTrackEnvelopes(track) + --msg(envcnt) + local string = timeString..'""' + for e = 0, envcnt -1 do + local env = reaper.GetTrackEnvelope(track, e) + local ret, GUID = reaper.GetSetEnvelopeInfo_String( env, 'GUID', '', false ) + string = string .. ' '.. timeString .. ' "'..GUID..'"' + end + local ret, string = reaper.GetSetMediaTrackInfo_String( track, 'P_RAZOREDITS', string, true ) + --msg(ret) + end + +end + +----------------------------- +function CopyTempo(areaStart, areaEnd, pasteTarget) + --first need to clean target place if there are some TimeSign markers + local timeMarkToDel = pasteTarget + areaEnd - areaStart + while timeMarkToDel > pasteTarget do + local markToDel = reaper.FindTempoTimeSigMarker( 0, timeMarkToDel - 0.002 ) + _, timeMarkToDel, _, _, _, _, _, _ = reaper.GetTempoTimeSigMarker(0, markToDel ) + if timeMarkToDel > pasteTarget then reaper.DeleteTempoTimeSigMarker( 0, markToDel ) end + end + + -- Now we can copy from source + local start_num, start_denom, startTempo = reaper.TimeMap_GetTimeSigAtTime( 0, areaStart ) + local end_num, end_denom, endTempo = reaper.TimeMap_GetTimeSigAtTime( 0, areaEnd ) + local pointTime = areaEnd + local PointsTable = {} + local exact = false + + table.insert(PointsTable, { + Time = areaEnd-areaStart, Measure = -1, Beat = -1, BPM = endTempo, + Tsign_num = end_num, Tsign_denom = end_denom, islinear = false }) + + while pointTime >= areaStart do + local p = {} + local point = reaper.FindTempoTimeSigMarker( 0, pointTime ) + local ret + ret, p.Time, p.Measure, p.Beat, p.BPM, p.Tsign_num, p.Tsign_denom, p.islinear = reaper.GetTempoTimeSigMarker(0, point ) + pointTime = p.Time - 0.002 --2 ms to avoid infinity cycle + --msg(p.Tsign_num ..' '.. p.Tsign_denom ..' '.. tostring(p.islinear)) + p.Measure = -1 + p.Beat = -1 + p.Time = p.Time - areaStart --relative position + + if p.Time == 0 then exact = true end + if p.Time >= 0 then table.insert(PointsTable, p) end + end + + + if exact == false then + local p = {} + local point = reaper.FindTempoTimeSigMarker( 0, areaStart ) + local ret + ret, _, _, _, _, _, _, p.islinear = reaper.GetTempoTimeSigMarker(0, point ) + p.Time = 0 --relative position + p.Measure = -1 + p.Beat = -1 + p.BPM = startTempo + p.Tsign_num = start_num + p.Tsign_denom = start_denom + table.insert(PointsTable, p) + end + + --for i, v in pairs(PointsTable) do msg(v.Time) end + + local pNumb = #PointsTable + + while pNumb > 0 do + local p = PointsTable[pNumb] + + reaper.SetTempoTimeSigMarker + (0, -1, p.Time + pasteTarget, p.Measure, p.Beat, p.BPM, p.Tsign_num, p.Tsign_denom, p.islinear ) + --msg(p.Time + pasteTarget) + pNumb = pNumb-1 + end +end +----------------------------- + +function CopyMarkers(areaStart, areaEnd, pasteTarget) + local destAreaEnd = pasteTarget + areaEnd - areaStart + local ret, mcnt, rcnt = reaper.CountProjectMarkers(0) + local i = ret -1 + while i >= 0 do + local retval, isrgn, pos, rgnend, name, markrgnindexnumber, color = reaper.EnumProjectMarkers3(0, i) + + if Opt.ReconfMarkers == true and isrgn == false then + if pos > areaStart and pos < areaEnd then + local newplace = pasteTarget + pos - areaStart + reaper.AddProjectMarker2( 0, isrgn, newplace, rgnend, name, -1, color ) + elseif pos > pasteTarget and pos < destAreaEnd then + reaper.DeleteProjectMarkerByIndex(0, i) + end + end + + i = i-1 + + if Opt.ReconfRegions == true and isrgn == true then + if pos < areaEnd and rgnend > areaStart then + local newplace = math.max(pasteTarget + pos - areaStart, pasteTarget) + local newrgnend = math.min((pasteTarget + pos - areaStart) + (rgnend - pos), destAreaEnd) + if not Regions[tostring(markrgnindexnumber)] then + Regions[tostring(markrgnindexnumber)] = {} + end + table.insert(Regions[tostring(markrgnindexnumber)], {newplace, newrgnend, name, color}) + end + --Here needs to clean place if it's not empty. + if pos <= destAreaEnd and rgnend > pasteTarget then + if rgnend > destAreaEnd and pos >= pasteTarget then + reaper.SetProjectMarker2(0, markrgnindexnumber, true, destAreaEnd, rgnend, name) + end + if rgnend <= destAreaEnd and pos < pasteTarget then + reaper.SetProjectMarker2(0, markrgnindexnumber, true, pos, pasteTarget, name) + end + if rgnend <= destAreaEnd and pos >= pasteTarget then + reaper.DeleteProjectMarker( 0, markrgnindexnumber, true ) + end + if rgnend > destAreaEnd and pos < pasteTarget then + reaper.SetProjectMarker2(0, markrgnindexnumber, true, pos, pasteTarget, name) + reaper.AddProjectMarker2( 0, true, destAreaEnd, rgnend, name, -1, color ) + end + end + end + + end +end +----------------------------- + +function CreateRegions(refItemsT) + --Clean up table + for i, v in pairs(Regions) do + local r = #v + while r > 1 do + local rgnPos = v[r][1] + local rgnEnd = v[r][2] + local prevEnd = v[r-1][2] + if Opt.ReduceGapsRegions == true then + Regions[i][r-1][2] = rgnEnd + table.remove(Regions[i], r) + else + if rgnPos <= prevEnd then + Regions[i][r-1][2] = rgnEnd + table.remove(Regions[i], r) + end + end + r = r-1 + end + end + + --Create regions + for i, v in pairs(Regions) do + for k, r in ipairs(v) do + local idx = reaper.AddProjectMarker2( 0, true, r[1], r[2], r[3], -1, r[4] ) + Regions[i][k][5] = idx + end + end + + --Heal splits between simillary named regions + local areaStart = refItemsT[1][3] + local areaEnd = refItemsT[#refItemsT][3] + refItemsT[#refItemsT][2] - refItemsT[#refItemsT][1] + local ret, mcnt, rcnt = reaper.CountProjectMarkers(0) + + local m = 0 + while m < ret do + local retval, isrgn, pos, rgnend, name, markrgnindexnumber, color = reaper.EnumProjectMarkers3(0, m) + if math.abs(pos - areaEnd) < 0.0002 or math.abs(rgnend - areaStart) < 0.0002 + and isrgn == true then + for i, v in pairs(Regions) do + local r = #v + while r > 0 do + local rgnPos = v[r][1] + local rgnEnd = v[r][2] + local rgnName = v[r][3] + local rgnColor = v[r][4] + + if name == rgnName and color == rgnColor then + if math.abs(pos - areaEnd) < 0.0002 and math.abs(rgnEnd - areaEnd) < 0.0002 then + --msg('R '..m ..' '.. retval ..' '.. v[r][5]) + reaper.SetProjectMarker2(0, markrgnindexnumber, true, rgnPos, rgnend, name) + if reaper.DeleteProjectMarker( 0, v[r][5], true ) then + + m = m-1 + end + elseif math.abs(rgnend - areaStart) < 0.0002 and math.abs(rgnPos - areaStart) < 0.0002 then + --msg('L '..m ..' '.. retval ..' '.. v[r][5]) + reaper.SetProjectMarker2(0, markrgnindexnumber, true, pos, rgnEnd, name) + if reaper.DeleteProjectMarker( 0, v[r][5], true ) then + table.remove(Regions[i],r) + Regions[tostring(markrgnindexnumber)] = {} + table.insert(Regions[tostring(markrgnindexnumber)], + {pos, rgnEnd, name, color, markrgnindexnumber}) + m = m-1 + end + end + end --if name... + + r = r-1 + end --while r > 0 + end --for in Regions + end --if edges match + + m = m+1 + end --cycle through all markers + +end +----------------------------- + +function SaveAndUnlockItems() + local parmname = 'P_EXT:'..'SimpleReconf_AZ'..'lock' + local icnt = reaper.CountMediaItems(0) + for i = 0, icnt -1 do + local item = reaper.GetMediaItem(0,i) + local itemLocked = reaper.GetMediaItemInfo_Value(item, 'C_LOCK') + if itemLocked == 1 then + local ret, str = reaper.GetSetMediaItemInfo_String( item, parmname, tostring(itemLocked), true ) + reaper.SetMediaItemInfo_Value(item, 'C_LOCK', 0) + end + end +end + +----------------------------- + +function RestoreLockedItems() + local parmname = 'P_EXT:'..'SimpleReconf_AZ'..'lock' + local icnt = reaper.CountMediaItems(0) + for i = 0, icnt -1 do + local item = reaper.GetMediaItem(0,i) + local ret, str = reaper.GetSetMediaItemInfo_String( item, parmname, tostring(itemLocked), false ) + + if tonumber(str) == 1 then + ret, str = reaper.GetSetMediaItemInfo_String( item, parmname, '', true ) + reaper.SetMediaItemInfo_Value(item, 'C_LOCK', 1) + end + end +end + +----------------------------- + +function ToggleDisableLanes(disable) --boolean + if disable == true then disable = 2 else disable = 1 end + if not DisLanesTrTable then + DisLanesTrTable = {} + local trcnt = reaper.CountTracks(0) + for i = LastRefTrID +1, trcnt -1 do + local track = reaper.GetTrack(0,i) + local lanesState = reaper.GetMediaTrackInfo_Value(track, 'C_LANESCOLLAPSED') + --msg(lanesState) + if lanesState >= 2 then + table.insert(DisLanesTrTable, track) + reaper.SetMediaTrackInfo_Value(track, 'C_LANESCOLLAPSED', disable) + + end + end + elseif disable == 2 then + for i, track in ipairs(DisLanesTrTable) do + reaper.SetMediaTrackInfo_Value(track, 'C_LANESCOLLAPSED', disable) + end + end +end +----------------------------- + +function RemoveExtraPoints() + local trCount = reaper.CountTracks(0) + for i = -1, trCount -1 do + local tr + if i == -1 then + tr = reaper.GetMasterTrack(0) + else + tr = reaper.GetTrack(0, i) + end + local envCount = reaper.CountTrackEnvelopes(tr) + + for e = 0, envCount -1 do + local env = reaper.GetTrackEnvelope(tr, e) + local _, name = reaper.GetEnvelopeName(env) + if name ~= "Tempo map" then + + for k, g in ipairs(Gaps) do + local leftTime = g[1] + local rightTime = g[2] + --msg(leftTime..' '..rightTime) + local idx = reaper.GetEnvelopePointByTimeEx( env, -1, rightTime ) + local ret, time, value, shape, tension, selected + time = rightTime + while time > leftTime do + ret, time, value, shape, tension, selected = reaper.GetEnvelopePointEx( env, -1, idx ) + if time > leftTime + 0.0001 and time < rightTime - 0.0001 then -- 0.1 ms to avoid some inaccurace + --msg(time) + reaper.DeleteEnvelopePointEx( env, -1, idx, true ) + end + idx = idx-1 + end -- points cycle + reaper.Envelope_SortPointsEx( env, -1 ) + end -- end Gap cycle + + end + end -- end Env cycle + + end --end track cycle +end + +----------------------------- + +function SeparateEnv() + EnvsInMediaLane = {} + + local trCount = reaper.CountTracks(0) + for i = -1, trCount -1 do + local tr + if i == -1 then + tr = reaper.GetMasterTrack(0) + else + tr = reaper.GetTrack(0, i) + end + local envCount = reaper.CountTrackEnvelopes(tr) + + for e = 0, envCount -1 do + local env = reaper.GetTrackEnvelope(tr, e) + local ret, str = reaper.GetEnvelopeStateChunk( env, '', false ) + local newchunk = '' + + if ret then + + for s in str:gmatch('[^\n]+') do + if s:find('VIS') == 1 then + if s:find('0') then + table.insert(EnvsInMediaLane, {env, s}) + s = s:gsub('0','1') + end + end + newchunk = newchunk..s..'\n' + end -- chunk lines cycle + reaper.SetEnvelopeStateChunk( env, newchunk, true ) + end + + end -- env cycle + + end -- track cycle +end +----------------------------- + +function RestoreEnvVis() + for i, v in pairs(EnvsInMediaLane) do + local env = v[1] + local vis = v[2] + local ret, str = reaper.GetEnvelopeStateChunk( env, '', false ) + local newchunk = '' + + if ret then + for s in str:gmatch('[^\n]+') do + if s:find('VIS') == 1 then + s = vis + end + newchunk = newchunk..s..'\n' + end + reaper.SetEnvelopeStateChunk( env, newchunk, true ) + end + + end +end +------------------------------ + +function GetPrefs(key) -- key need to be a string as in Reaper ini file + local retval, buf = reaper.get_config_var_string( key ) + if retval == true then return tonumber(buf) end +end + +----------------------------- + +function main() + SetOptGlobals() + local editCurPos = reaper.GetCursorPosition() + Regions = {} + local refItems = CollectSelectedItems() + if Opt.HealGaps == true or Opt.HealSplits == true then + CleanUpRefItems(refItems) + end + + if Opt.IgnoreCrossfades ~= "don't ignore" or Opt.FillGaps ~= "don't fill" then + AdjustRefItems(refItems) + end + + if #refItems == 0 then + reaper.ShowMessageBox('Please select reference items on a track','Simple Project Reconform', 0) + return + end + + if Opt.ReconfLockedItems == true then SaveAndUnlockItems() end + if Opt.ReconfDisabledLanes == true then ToggleDisableLanes(false) end + reaper.Main_OnCommandEx(reaper.NamedCommandLookup('_BR_FOCUS_ARRANGE_WND'),0,0) --SWS/BR: Focus arrange + reaper.Main_OnCommandEx(reaper.NamedCommandLookup('_SWSTL_SHOWALL'),0,0) --SWS: Show all tracks + reaper.Main_OnCommandEx(41149,0,0) --Envelope: Show all envelopes for all tracks + SeparateEnv() + --local Zero = refItems[1][3] + + local TimeSigCount = 0 + if Opt.ReconfTempo == true then + TimeSigCount = reaper.CountTempoTimeSigMarkers( 0 ) + end + + local splitautoxfade = GetPrefs('splitautoxfade') + local trimBehRazorFade = splitautoxfade & ~(1<<15) + trimBehRazorFade = trimBehRazorFade | (1<<6) + reaper.SNM_SetIntConfigVar( 'splitautoxfade', trimBehRazorFade ) + + local trimcont = reaper.GetToggleCommandState(41117) --Options: Trim content behind media items when editing + local trimcontrazor = reaper.GetToggleCommandState(42421) + --Options: Always trim content behind razor edits (otherwise, follow media item editing preferences) + + if trimcont == 1 then reaper.Main_OnCommandEx(41117, 0, 0) end + if trimcontrazor == 1 then reaper.Main_OnCommandEx(42421, 0, 0) end + + local wholeAreaStart = refItems[1][3] + local wholeAreaEnd = refItems[#refItems][3] + refItems[#refItems][2] - refItems[#refItems][1] + SetREarea(wholeAreaStart, wholeAreaEnd) + reaper.Main_OnCommandEx(40697,0,0) --Remove items/tracks/envelope points (depending on focus) + reaper.Main_OnCommandEx(42406,0,0) --Razor edit: Clear all areas + + + for i, item in ipairs(refItems) do --Start reconform cycle + + local areaStart = item[1] + local areaEnd = item[2] + local refPos = item[3] + + UndoString = 'Simple Reconform' + + if TimeSigCount > 0 then + CopyTempo(areaStart, areaEnd, refPos) + end + + + if Opt.ReconfMaster == true then + reaper.Main_OnCommandEx(42406,0,0) --Razor edit: Clear all areas + SetMasterREarea(areaStart, areaEnd) + reaper.Main_OnCommandEx(40057,0,0) + --^^Edit: Copy items/tracks/envelope points (depending on focus) ignoring time selection + reaper.SetEditCurPos2(0, refPos, false, false) + reaper.Main_OnCommandEx(42398,0,0) --Item: Paste items/tracks + end + + reaper.SetOnlyTrackSelected(reaper.GetTrack(0,LastRefTrID +1)) + reaper.Main_OnCommandEx(42406,0,0) --Razor edit: Clear all areas + SetREarea(areaStart, areaEnd) + reaper.Main_OnCommandEx(40057,0,0) + --^^Edit: Copy items/tracks/envelope points (depending on focus) ignoring time selection + reaper.SetEditCurPos2(0, refPos, false, false) + reaper.Main_OnCommandEx(42398,0,0) --Item: Paste items/tracks + + if Opt.ReconfMarkers == true or Opt.ReconfRegions == true then + CopyMarkers(areaStart, areaEnd, refPos) + end + + end --End reconform cycle + + RemoveExtraPoints() + RestoreEnvVis() + if Opt.ReconfLockedItems == true then RestoreLockedItems() end + if Opt.ReconfDisabledLanes == true then ToggleDisableLanes(true) end + + if Opt.ReconfRegions == true then + CreateRegions(refItems) + end + + reaper.SNM_SetIntConfigVar( 'splitautoxfade', splitautoxfade ) + if trimcont == 1 then reaper.Main_OnCommandEx(41117, 0, 0) end + if trimcontrazor == 1 then reaper.Main_OnCommandEx(42421, 0, 0) end + reaper.SetEditCurPos2(0, editCurPos, false, false) + reaper.SelectAllMediaItems(0, false) +end + + +----------------------------- +-------START------------------- + +OptionsWindow() + diff --git a/Various/starshine_Starmidi - microtonal scales using lanes as piano roll.lua b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll.lua new file mode 100644 index 000000000..2a2073231 --- /dev/null +++ b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll.lua @@ -0,0 +1,56 @@ +-- @description Starmidi - microtonal scales using lanes as piano roll +-- @author Starshine +-- @version 1.0-beta-1 +-- @metapackage +-- @provides +-- [main] starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Flatten.eel +-- [main] starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Sharpen.eel +-- [main] starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Small step down.eel +-- [main] starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Small step up.eel +-- [main] starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Parser.eel +-- [main] starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - UI.eel +-- [effect] starshine_Starmidi - microtonal scales using lanes as piano roll/starmidi +-- @about +-- # Starmidi - microtonal scales using lanes as piano roll +-- +-- This program seeks to solve several problems for microtonal music producers while streamlining the workflow: +-- +-- 1. Microtonal scales may contain far more than 12 notes per octave, and this quickly makes working in the conventional chromatic piano roll cumbersome or even limiting to the instrument's range. +-- +-- *This script package and plugin solve this by using an adjustable number of "fixed item lanes" that act as the "piano white key notes" for the given microtonal scale, with other pitches accessible by means of accidentals* +-- +-- 2. Different synthesizers and samplers have different ways of loading microtonal scales, sometimes requiring different file formats, inconsistent behavior, timbre warping, and more. +-- +-- *These scripts solve this issue by finding the closest MIDI note and applying a pitchbend amount of no more than 50 cents. It's tested working with Vital, Surge XT, and Pianoteq and should work with most synths and samplers. +-- +-- To achieve microtonal polyphony, the jsfx plugin (which handles generating the MIDI events from data in gmem) dynamically assigns new notes to free channels. Up to 16 simultaneous and independently tuned notes can be played this way.* +-- +-- ### Setup/Usage +-- +-- 1. ReaImGui is required +-- +-- 2. Preferences > Track Send Defaults > Fixed Lane Defaults > **Uncheck "Automatically delete empty lanes at bottom of track"** +-- +-- I strongly recommend selecting "Small lanes" here as well. +-- +-- 3. Launch the script "Starshine_starmidi_UI.eel". +-- +-- 4. Drag the # of Lanes slider to add 15+ lanes to a track. +-- +-- 5. It is beyond the scope of this package documentation to explain microtonal scale theory. If you are interested in learning more, please consider joining the Xenharmonic Alliance discord at https://discord.gg/uxvw5Vzj +-- +-- Setting the following combinations for [# of Scale Steps], [Equal Divisions], and [n\EDX as Generator] should yield interesting results. 7\16edo means to select 16 [Equal Divisions] and 7 for [n\EDX as Generator] +-- +-- * 7 steps, 7\16edo +-- * 8 steps, 10\27edo +-- * 9 steps, 4\19edo +-- * 7 steps, 13\31edo, mode 5 (this is like a regular major scale but more in tune. roughly "quarter-comma meantone") +-- +-- 6. Insert/draw empty MIDI items in lanes. These function as notes. Optionally, you may set a mouse modifier to draw MIDI items so that it functions like the standard piano roll for inserting notes. +-- +-- 7. Add a synth to the FX chain; do not enable MPE. Leave pitch bend range at 2 semitones. +-- +-- 8. You must click Play from this interface; this runs the parsing script prior to playback. (The script will also auto-add and configure the jsfx.) +-- +-- *Optionally, you may create a custom action that runs "starshine_starmidi_parser.eel" and then the Transport: Play/Stop action* + diff --git a/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starmidi b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starmidi new file mode 100644 index 000000000..2cdb272cc --- /dev/null +++ b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starmidi @@ -0,0 +1,156 @@ +desc:starmidi +options:gmem=fakemidi +//@gmem=fakemidi +// reads "midi events" from gmem by partner reascript +// partner reascript reads items in lanes into gmem +// TODO: handle incoming events on the same bus? for purpose of reassigning channel + +slider1:1<1,32,1>track num +// TODO: handle dynamic scale change -- bigger jump, let's get it working the easy way first. + +@init + +// MIDI command constants +NOTE_OFF_MSG = 0x80; +NOTE_ON_MSG = 0x90; +CHANNEL_MSG = 0xB0; +CHANNEL_STFU = 0x7B; +PITCHBEND = 0xE0; + +// gmem data offsets +MEM_PER_TRACK = 196608; +_TR_GMEM_START = 65536; +EVT_WIDTH = 8; +TRACK_COUNT = 32; // about as many tracks as we can support at once +EVT_LENGTH = 16384; +_track_evt_counter = 8192; +PLAYBACK_OKAY = 8000000; + +// local memory offsets +_MIDI_EVENTS = 65536; // must match _source_array in reascript +_CHANNELS = 0; + +// column reference constants for 2D array _MIDI_EVENTS +EventTime = 0; +EventType = 1; +MidiNote = 2; +Velocity = 3; +PitchbendAmount = 4; +ChannelPointer = 5; + +last_play_pos=0; +last_play_state=0; + +function _track_evts_gmem_ptr(track_num)(track_num*MEM_PER_TRACK+_TR_GMEM_START); + +// the 2D array structure used, array[row][col], depends on doing this +function copy_gmem_to_local()( + _gmem_ptr = _track_evts_gmem_ptr(slider1-1)-1; + _local_ptr = -1; + Loop(EVT_WIDTH*EVT_LENGTH, + _MIDI_EVENTS[_local_ptr+=1] = gmem[_gmem_ptr+=1]; + ); +); + + +// unreserve playing channels and turn off every note +function clear_channels()( + ch=-1; + Loop(16, + _CHANNELS[ch+=1]=0; + midisend(0,CHANNEL_MSG|ch, CHANNEL_STFU); + ); +); + +// scan through sorted events to catch up to current play position +function scan_to_start()( + _play_ptr=-1; t=0; + while( (t-1) )( + t = _MIDI_EVENTS[_play_ptr+=1][EventTime]; + ); +); + +copy_gmem_to_local(); +clear_channels(); +scan_to_start(); + +@slider +slider_show(slider1,0); + +@block + +@sample + +function ANO_if_stopped_or_looping()( + !play_state && last_play_state ? (clear_channels(); scan_to_start(); _play_ptr-=1); + (play_position < last_play_pos) && play_state ? (clear_channels(); scan_to_start(); _play_ptr-=1); + last_play_state = play_state; + last_play_pos = play_position; +); + +function scan_to_start_on_play()( + play_state && !last_play_state2 ? (clear_channels(); scan_to_start()); + last_play_state2 = play_state; +); + +// dynamically obtain an available channel and mark as in-use +function get_free_channel()( + ch=-1; return_ch=-1; + while((return_ch<0) && (ch < 16)) + ( _CHANNELS[ch+=1] == 0 + ? ( return_ch = ch; + _CHANNELS[ch] = 1; + ); + ); +return_ch); + + +// sends midi pitchbend and note-on. also writes channel for note-off event to use later +function note_on(_ptr)( + chan = get_free_channel(); + _chan_ptr = _MIDI_EVENTS[_ptr][ChannelPointer]; + _MIDI_EVENTS[_chan_ptr] = chan; + midisend(0, PITCHBEND|chan, _MIDI_EVENTS[_ptr][PitchbendAmount]); + midisend(0, NOTE_ON_MSG|chan, _MIDI_EVENTS[_ptr][MidiNote], _MIDI_EVENTS[_ptr][Velocity]); +); + + +// sends note-off and frees channel +function note_off(_ptr)( + _chan_ptr = _MIDI_EVENTS[_ptr][ChannelPointer]; + chan = _MIDI_EVENTS[_chan_ptr]; + _CHANNELS[chan] = 0; + midisend(0, NOTE_OFF_MSG|chan, _MIDI_EVENTS[_ptr][MidiNote], 0); +); + + +function play_scan()( + while( (t-1) )( + event = _MIDI_EVENTS[_play_ptr][EventType]; + event == NOTE_ON_MSG ? note_on(_play_ptr); + event == NOTE_OFF_MSG ? note_off(_play_ptr); + t = _MIDI_EVENTS[_play_ptr+=1][EventTime]; + ); +); + +function playback_okay()(gmem[PLAYBACK_OKAY+slider1-1]); + +ANO_if_stopped_or_looping(); + +scan_to_start_on_play(); + +play_state && playback_okay() ? play_scan(); + + + + + + + + + + + + + + diff --git a/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Flatten.eel b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Flatten.eel new file mode 100644 index 000000000..5002f2d97 --- /dev/null +++ b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Flatten.eel @@ -0,0 +1,46 @@ +// @noindex + +// helper script for starmidi tools +// modifies item title by adding or removing # or b symbols + +function apply_accidental(take)( + GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 0); + + //todo: check if item is midi + + (len=strlen(#text))==0 ? #text ="b" : //case 1: empty. just add a b + match("*b*",#text) ? #text+="b" : //case 2: has one or more b. add another + match("*#*",#text) ? str_setlen(#text,len-1); //case 3: has one or more #. remove one. + + GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 1); + + // count the number of b or # to apply colors + pos=-1; r=1; g=1; b=1; + + char = str_getchar(#text,0); + (char == 'b') ? (r-=0.125; g-=0.2;); + (char == '#') ? (b-=0.2; g-=0.125;); + + Loop(strlen(#text), + char = str_getchar(#text,pos+=1); + char == 'b' ? (r-=0.125; g-=0.2;); + char == '#' ? (b-=0.2; g-=0.125;); + ); + + r*=255; b*=255; g*=255; + SetMediaItemTakeInfo_Value(take, "I_CUSTOMCOLOR", ColorToNative(r,g,b)|0x1000000); +); + +function loop_selected()( + item_idx = -1; + Loop(CountSelectedMediaItems(0), + item = GetSelectedMediaItem(0,item_idx+=1); + take = GetMediaItemTake(item,0); + apply_accidental(take); + ); +); + +GetMousePosition(x,y); +GetItemFromPoint(x,y,0,take); +!take ? loop_selected() : apply_accidental(take); + diff --git a/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Parser.eel b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Parser.eel new file mode 100644 index 000000000..766f12d52 --- /dev/null +++ b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Parser.eel @@ -0,0 +1,320 @@ +// @noindex + +//upgrade gmem to 8,388,608 slots by naming it. next line is read by compiler and must stay near the top +//@gmem=fakemidi + +// author: Starshine777 +// purpose: read midi items (and text thereupon) in lanes as notes. like piano roll but better +// put data into global memory to be interpreted by accompanying jsfx +// +// this version ONLY works with the accompanying ReaImGui scale selector script, which is somewhat primitive yet. +// (A separate version of this script for JI processing exists. they have been separated to reduce development complexity) + +///////////////////////////////////////////////////////////////////////////////// +// initialization and memory [mis]management +///////////////////////////////////////////////////////////////////////////////// + +// treat any track with at least this many lanes as "fakemidi" +MIN_LANES_TO_PROCESS = 5; +function yes_parse_this_track(track)(GetMediaTrackInfo_Value(track, "I_NUMFIXEDLANES") >= MIN_LANES_TO_PROCESS); + +// constants +NOTE_OFF = 0x80; +NOTE_ON = 0x90; +PB_FACTOR_DOWN = 0x2000; +PB_FACTOR_UP = 0x1FFF; +#empty_string=""; + +// local memory regions +_tracks_root_shifts = 0; +_tracks_relabs_mode = 128; +_source_array = 65536; +_L = 212984; +_R = 360432; + +// data structure defs +EVT_WIDTH = 8; +TRACK_COUNT = 32; +MAX_TRACK_LANES = 255; +EVT_LENGTH = 16384; +MEM_PER_TRACK = 196608; +EVT_LINK_OFFSET = 131072; + +// global memory regions +_TRACKS_R2_SCALES = 512; +_TR_GMEM_START = 65536; +PLAYBACK_OKAY = 8000000; + +// some counters that make sense as global +item_count_MIDI = 0; + +// for getting the track-specific locations in memory +function _track_evts_gmem_ptr(track_idx)(track_idx*MEM_PER_TRACK+_TR_GMEM_START); +function _track_scale_gmem_ptr(track_idx)(track_idx*MAX_TRACK_LANES+_TRACKS_R2_SCALES); + +// clean the slates before use +function gmem_init()(i=65535; Loop(8323072, gmem[i+=1]=-1)); +function base_cents_init()(i=-1; Loop(16, _tracks_root_shifts[i+=1]=0;);); +function array_2d_init(start)( + i=-1; _data_ptr=start+EVT_LENGTH-EVT_WIDTH; + Loop(EVT_LENGTH, + memset(start[i+=1]=(_data_ptr+=EVT_WIDTH),-1,EVT_WIDTH); + ); +); + +// note: can't use memcpy because it doesn't work on gmem +function copy_evts_to_gmem(track_idx)( + _gmem_ptr = _track_evts_gmem_ptr(track_idx)-1; + _local_ptr = -1; + Loop(EVT_WIDTH*EVT_LENGTH, + gmem[_gmem_ptr+=1] = _source_array[_local_ptr+=1]; + ); +); + +// this is intended to load the jsfx and set the param that tells it where to look in gmem +function load_track_jsfx()( + track_idx = -1; + Loop(CountTracks(0), + track = GetTrack(0, track_idx+=1); + yes_parse_this_track(track) + ? ( pos = TrackFX_AddByName(track, "starmidi", 0, 0); + pos<0 ? TrackFX_AddByName(track, "starmidi", 0, -1000); + TrackFX_SetParam(track, 0, 0, track_idx+1); // +1 for 1-based track number + ); + ); +); + +///////////////////////////////////////////////////////////////////////////////// +// load scale data from project EXT. set by partner script +///////////////////////////////////////////////////////////////////////////////// + +function load_track_chroma(track)( + GetSetMediaTrackInfo_String(track, "P_EXT:scale_settings", #scale_settings, 0); + match("%i|%f|%f|%i|%f|%f|%f|%i",#scale_settings,d_steps,period,generator,mode,chroma,small,edo,edo_steps); +); + +function load_track_scale(track,track_idx)( + lane_idx=-1; + GetSetMediaTrackInfo_String(track, "GUID", #GUID, 0); + #section="track_lane_tuning_"; + #section+=#GUID; + _track_scale_gmem_ptr = _track_scale_gmem_ptr(track_idx); + Loop(GetMediaTrackInfo_Value(track, "I_NUMFIXEDLANES"), + sprintf(#key,"P_EXT:lane_pitch%i",lane_idx+=1); + GetSetMediaTrackInfo_String(track, #key, #value, 0) + ? ( match("%f",#value,lane_cents); + _track_scale_gmem_ptr[lane_idx]=lane_cents; + ); + ); + load_track_chroma(track); +); + +///////////////////////////////////////////////////////////////////////////////// +// merge sort functions +///////////////////////////////////////////////////////////////////////////////// + +// helper function for mergeSort. merges second dimension +function merge(array, low, mid, high, width, col)( + n1=mid-low+1; n2=high-mid; + // Copy data to temporary arrays L[] and R[] + i=-1; loop(n1, memcpy(_L[i+=1], array[low+i], width);); + j=-1; loop(n2, memcpy(_R[j+=1], array[mid+j+1], width);); + // Merge the temporary arrays back into arr[low..high] + i=0; j=0; k=low; + while(i0 ? PB_FACTOR_UP : PB_FACTOR_DOWN)/2; + pb_amount = round(remain_st * pb_factor + 0x2000); + // flip endianness to store and send as a single value, i.e. midisend(0,0xE_,LSB+(MSB*256)) + pb_msg23_value = pb_amount & 0x7F | ((pb_amount >> 7 & 0x7F) << 8); +); + +function get_note_base_cents(track_idx,color,lane)( + base_cents =_track_scale_gmem_ptr[lane]; + base_cents+= get_accidental_cents(#note_text); +base_cents); + + +///////////////////////////////////////////////////////////////////////////////// +// track data collection functions +///////////////////////////////////////////////////////////////////////////////// + +function write_note_data(track_idx,take,pos,len,color,lane,velocity)( + + // wipe default-named MIDI obejcts for clarity + match("*MIDI*",#note_text) ? GetSetMediaItemTakeInfo_String(take, "P_NAME", #empty_string, 1); + + // tune it + base_cents = get_note_base_cents(track_idx,color,lane); + semitones = calc_midi_note_num_unrounded(base_cents,track_idx); + midi_note_num = round(semitones); + pb_value = st_to_pitchbend(semitones); + + _gmem_ptr = _track_evts_gmem_ptr(track_idx); + + // note-on + _source_array[evt_idx][0] = pos; + _source_array[evt_idx][1] = NOTE_ON; + _source_array[evt_idx][2] = midi_note_num; + _source_array[evt_idx][3] = velocity; + _source_array[evt_idx][4] = pb_value; + _source_array[evt_idx][5] = _gmem_ptr + evt_idx + EVT_LINK_OFFSET; + _source_array[evt_idx][6] = base_cents; + _source_array[evt_idx][7] = track_idx; + + evt_idx+=1; + + // note-off + _source_array[evt_idx][0] = pos+len-0.005; // the MIDI event bus is slow, and not doing this causes problems + _source_array[evt_idx][1] = NOTE_OFF; + _source_array[evt_idx][2] = midi_note_num; + _source_array[evt_idx][3] = 0; + // [evt_idx][4] = 0; (no pitch bend for note-off) + _source_array[evt_idx][5] = _gmem_ptr + evt_idx-1 + EVT_LINK_OFFSET; + _source_array[evt_idx][7] = track_idx; + + evt_idx+=1; +); + +function do_track_command(track_idx)( + match("! %{#command}s",#note_text); + // !strcmp(#command,"ABSOLUTE") ? _tracks_relabs_mode[track_idx] = 0; + // !strcmp(#command,"RELATIVE") ? _tracks_relabs_mode[track_idx] = 1; + match("%{#r1}s=%{#r2}s",#command) ? ( + r1 = text_to_cents(#r1); + r2 = text_to_cents(#r2); + _tracks_root_shifts[track_idx] += r2-r1; + ); +); + +function parse_item(track_idx,item)( + take = GetMediaItemTake(item, 0); + source = GetMediaItemTake_Source(take); + GetMediaSourceType(source, #item_type); + GetTakeName(#note_text, take); + GetSetMediaItemTakeInfo_String(take, "P_NAME", #note_text, 0); + color = GetMediaItemInfo_Value(item, "I_CUSTOMCOLOR"); + lane = GetMediaItemInfo_Value(item, "I_FIXEDLANE"); + muted = GetMediaItemInfo_Value(item, "B_MUTE"); + + ! strcmp(#item_type,"MIDI") && !muted // item *is* MIDI and not muted + ? ( pos = GetMediaItemInfo_Value(item, "D_POSITION"); + len = GetMediaItemInfo_Value(item, "D_LENGTH"); + vel = GetMediaItemInfo_Value(item, "D_VOL") * 0x7F; // knob is a multiplier w/ default=1.0 + str_getchar(#note_text,0) == '!' + ? do_track_command(track_idx) + : ( write_note_data(track_idx,take,pos,len,color,lane,vel); + item_count_MIDI+=2; + gmem[PLAYBACK_OKAY+track_idx] = 1; //signal jsfx that processing has occurred and there is at least one note + ); + ); +); + +function parse_tracks()( + track_idx = -1; + Loop(CountTracks(0), + track = GetTrack(0, track_idx+=1); + evt_idx = 0; + item_count_MIDI = 0; + yes_parse_this_track(track) + ? ( load_track_scale(track,track_idx); // load rank-2 notes if present + item_idx = -1; + num_items = CountTrackMediaItems(track); + array_2d_init(_source_array); + Loop(num_items, + item = GetTrackMediaItem(track, item_idx+=1); + parse_item(track_idx,item); + ); + mergeSort(_source_array, item_count_MIDI, EVT_WIDTH, 1); // sort on event type + mergeSort(_source_array, item_count_MIDI, EVT_WIDTH, 0); // sort on event time + copy_evts_to_gmem(track_idx); + ); + ); +); + + +///////////////////////////////////////////////////////////////////////////////// +// do the things +///////////////////////////////////////////////////////////////////////////////// + +function reload()( + Undo_BeginBlock(); + gmem_init(); + base_cents_init(); + load_track_jsfx(); + parse_tracks(); + Undo_EndBlock("starmidi processing",0); +); + +reload(); diff --git a/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Sharpen.eel b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Sharpen.eel new file mode 100644 index 000000000..4b52e8693 --- /dev/null +++ b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Sharpen.eel @@ -0,0 +1,45 @@ +// @noindex + +// helper script for starmidi tools +// modifies item title by adding or removing # or b symbols + +function apply_accidental(take)( + GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 0); + //todo: check if item is midi + + (len=strlen(#text))==0 ? #text ="#" : //case 1: empty. just add a # + match("*#*",#text) ? #text+="#" : //case 2: has one or more #. add another + match("*b*",#text) ? str_setlen(#text,len-1); //case 3: has one or more b. remove one. + + GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 1); + + // count the number of b or # to apply colors + pos=-1; r=1; g=1; b=1; + + char = str_getchar(#text,0); + (char == 'b') ? (r-=0.125; g-=0.2;); + (char == '#') ? (b-=0.2; g-=0.125;); + + Loop(strlen(#text), + char = str_getchar(#text,pos+=1); + char == 'b' ? (r-=0.125; g-=0.2;); + char == '#' ? (b-=0.2; g-=0.125;); + ); + + r*=255; b*=255; g*=255; + SetMediaItemTakeInfo_Value(take, "I_CUSTOMCOLOR", ColorToNative(r,g,b)|0x1000000); +); + +function loop_selected()( + item_idx = -1; + Loop(CountSelectedMediaItems(0), + item = GetSelectedMediaItem(0,item_idx+=1); + take = GetMediaItemTake(item,0); + apply_accidental(take); + ); +); + +GetMousePosition(x,y); +GetItemFromPoint(x,y,0,take); +!take ? loop_selected() : apply_accidental(take); + diff --git a/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Small step down.eel b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Small step down.eel new file mode 100644 index 000000000..09e46612a --- /dev/null +++ b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Small step down.eel @@ -0,0 +1,45 @@ +// @noindex + +// helper script for starmidi tools +// modifies item title by adding or removing # or b symbols + +function apply_accidental(take)( + GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 0); + //todo: check if item is midi + + (len=strlen(#text))==0 ? #text ="v" : //case 1: empty. just add a b + match("*v*",#text) ? #text+="v" : //case 2: has one or more b. add another + match("*^*",#text) ? str_setlen(#text,len-1); //case 3: has one or more #. remove one. + + GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 1); + + // count the number of b or # to apply colors + pos=-1; r=1; g=1; b=1; + + char = str_getchar(#text,0); + (char == 'v') ? (g-=0.125; r-=0.2;); + (char == '^') ? (g-=0.2; b-=0.125;); + + Loop(strlen(#text), + char = str_getchar(#text,pos+=1); + char == 'v' ? (g-=0.125; r-=0.2;); + char == '^' ? (g-=0.2; b-=0.125;); + ); + + r*=255; b*=255; g*=255; + SetMediaItemTakeInfo_Value(take, "I_CUSTOMCOLOR", ColorToNative(r,g,b)|0x1000000); +); + +function loop_selected()( + item_idx = -1; + Loop(CountSelectedMediaItems(0), + item = GetSelectedMediaItem(0,item_idx+=1); + take = GetMediaItemTake(item,0); + apply_accidental(take); + ); +); + +GetMousePosition(x,y); +GetItemFromPoint(x,y,0,take); +!take ? loop_selected() : apply_accidental(take); + diff --git a/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Small step up.eel b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Small step up.eel new file mode 100644 index 000000000..5b643c109 --- /dev/null +++ b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - Small step up.eel @@ -0,0 +1,45 @@ +// @noindex + +// helper script for starmidi tools +// modifies item title by adding or removing # or b symbols + +function apply_accidental(take)( + GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 0); + //todo: check if item is midi + + (len=strlen(#text))==0 ? #text ="^" : //case 1: empty. just add a b + match("*^*",#text) ? #text+="^" : //case 2: has one or more b. add another + match("*v*",#text) ? str_setlen(#text,len-1); //case 3: has one or more #. remove one. + + GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 1); + + // count the number of b or # to apply colors + pos=-1; r=1; g=1; b=1; + + char = str_getchar(#text,0); + (char == 'v') ? (g-=0.125; r-=0.2;); + (char == '^') ? (g-=0.2; b-=0.125;); + + Loop(strlen(#text), + char = str_getchar(#text,pos+=1); + char == 'v' ? (g-=0.125; r-=0.2;); + char == '^' ? (g-=0.2; b-=0.125;); + ); + + r*=255; b*=255; g*=255; + SetMediaItemTakeInfo_Value(take, "I_CUSTOMCOLOR", ColorToNative(r,g,b)|0x1000000); +); + +function loop_selected()( + item_idx = -1; + Loop(CountSelectedMediaItems(0), + item = GetSelectedMediaItem(0,item_idx+=1); + take = GetMediaItemTake(item,0); + apply_accidental(take); + ); +); + +GetMousePosition(x,y); +GetItemFromPoint(x,y,0,take); +!take ? loop_selected() : apply_accidental(take); + diff --git a/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - UI.eel b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - UI.eel new file mode 100644 index 000000000..35d7088e5 --- /dev/null +++ b/Various/starshine_Starmidi - microtonal scales using lanes as piano roll/starshine_Starmidi - UI.eel @@ -0,0 +1,265 @@ +// @noindex + +//@gmem=fakemidi + +// Author: Starshine777 +// This ImGui interface allows the user to create a rank-2 scale and add track lanes +// It operates on the selected track +// The data saved to the project file is loaded and used by starmicro_midi.EEL +// +// Note on coding style: many variables are being passed implicitly; use caution when +// modifying. This may be cleaned up in a future revision. + +///////////////////////////////////////////////////////////////////////////////////////////// +// init +///////////////////////////////////////////////////////////////////////////////////////////// +FONT_SIZE = 16; +SCALE_MAX_LEN = 46; +MAX_TRACK_LANES = 255; +_tracks_r2_scales = 512; +color_stack_count = 0; +color = 0; +#notes = ""; +_scale_mem = 0; +ctx = ImGui_CreateContext("Scale Selector"); +ImGui_NumericLimits_Float(FLT_MIN, FLT_MAX); +font = ImGui_CreateFont("sans_serif", FONT_SIZE); +ImGui_Attach(ctx, font); + +function init_defaults()( + edo = 22; + edo_steps = 3; + d_steps = 7; + period = 1200; + mode = 0; + init_done = 1; +); + +///////////////////////////////////////////////////////////////////////////////////////////// +// functions for scale construction and drawing the GUI +///////////////////////////////////////////////////////////////////////////////////////////// +function hsv(h,s,v,a)( + ImGui_ColorConvertHSVtoRGB(h,s,v,r,g,b); + ImGui_ColorConvertDouble4ToU32(r,g,b,a|1); +); + +function _track_scale_gmem_ptr(track)(track*MAX_TRACK_LANES+_tracks_r2_scales); + +function save_lane_data(track,track_idx,mos_size,start_char,init_char,start_equave,period)( + track_lane_name_ptr=_track_scale_gmem_ptr(track_idx); + lane_count=GetMediaTrackInfo_Value(track, "I_NUMFIXEDLANES"); + lane_idx=lane_count; + scale_degree=init_char-start_char; + equave=start_equave-1; + Loop(lane_count, + scale_degree%=mos_size; + scale_degree==0 + ? ( equave+=1; + sprintf(#lane_name,"%s%i%s","==== [",equave-1,"]"); + ) + : sprintf(#lane_name,"%s%i%s","[",equave-1,"]"); + scale_degree+=1; + + // update the lanes starting from the bottom + sprintf(#P_LANENAME,"%s%i","P_LANENAME:",lane_idx-=1); + sprintf(#cents_and_lane,"%.1f %s",_scale_mem[scale_degree-1],#lane_name); + GetSetMediaTrackInfo_String(track, #P_LANENAME, #cents_and_lane, 1); + + // update the pitch data for each lane + sprintf(#key,"P_EXT:lane_pitch%i",lane_idx); + sprintf(#value,"%f",_scale_mem[scale_degree-1]+(equave*period)); + GetSetMediaTrackInfo_String(track, #key, #value, 1); + + //make sure all lanes play + sprintf(#LANEPLAYS,"%s%i","C_LANEPLAYS:",lane_idx); + SetMediaTrackInfo_Value(track, #LANEPLAYS, 2); + ); +); + +function init_scale_mem(scale)( + memset(scale,99999,SCALE_MAX_LEN); +); + +function get_chroma(_mos_size,period)( + i=0; small=99999; large=0; + Loop(_mos_size, + (i==_mos_size-1) + ? step=period-_scale_mem[i] + : step=_scale_mem[i+1]-_scale_mem[i] + ; + steplarge ? large=step; + i+=1; + ); +large-small); + +function construct_scale(_mos_size,_period,_generator,mode)( + init_scale_mem(_scale_mem); + num_gens=-mode; + Loop(_mos_size, + scale_note=num_gens*_generator; + while(scale_note > _period)(scale_note-=_period); + while(scale_note < 0 )(scale_note+=_period); + num_gens+=1; + insert_pos=-1; + while(scale_note>_scale_mem[insert_pos+=1]); + mem_insert_shuffle(_scale_mem+insert_pos, SCALE_MAX_LEN-insert_pos, scale_note); + ); + chroma=get_chroma(_mos_size,period); +); + +function print_scale_test(num_notes)( + #scale = ""; + i = -1; + Loop(num_notes, + #scale+=sprintf(#,"%.2f\n",_scale_mem[i+=1]); + ); +); + +function save_track_scale(track,track_idx,d_steps,period,generator,mode,small,edo,edo_steps)( + sprintf(#scale_settings,"%i|%f|%f|%i|%f|%f|%f|%i",d_steps,period,generator,mode,chroma,small,edo,edo_steps); //TODO must add EDO settings + GetSetMediaTrackInfo_String(track, "P_EXT:scale_settings", #scale_settings, 1); +); + +function load_track_scale(track,track_idx)( + GetSetMediaTrackInfo_String(track, "P_EXT:scale_settings", #scale_settings, 0); + ! match("%i|%f|%f|%i|%f|%f|%f|%i",#scale_settings,d_steps,period,generator,mode,chroma,small,edo,edo_steps) + ? init_defaults(); +); + +function item_shift_on_lane_count_change(track,amount)( + item_idx = -1; + Loop(CountTrackMediaItems(track), + item = GetTrackMediaItem(track, item_idx+=1); + current_lane = GetMediaItemInfo_Value(item,"I_FIXEDLANE"); + new_lane = current_lane + amount; + SetMediaItemInfo_Value(item,"I_FIXEDLANE",new_lane); + ); +); + +function is_edo_slider_changed()( + (last_edo != edo) || (last_edo_steps != edo_steps) || (last_period != period) +); + +function edo_slider_check_update()( + last_edo = edo; + last_edo_steps = edo_steps; + last_period = period; +); + +function update_generator()( + generator = 1200/edo*edo_steps; +); + +function change_slider_colors(ctx,h,s,v)( + ImGui_PushStyleColor(ctx,ImGui_Col_FrameBg(),hsv(h,s,v,1)); + ImGui_PushStyleColor(ctx,ImGui_Col_SliderGrab(),hsv(h-0.05,s+0.3,v+0.4,1)); + color_stack_count+=2; +); + +function pop_colors(ctx)( + ImGui_PopStyleColor(ctx, color_stack_count); + color_stack_count=0; +); + +function frame_init()( + ImGui_PushFont(ctx, font); + ImGui_TextWrapped(ctx, "Welcome to my scale dojo! Ctrl+Click to type exact values."); + ImGui_Spacing(ctx); + + track = GetSelectedTrack(0, 0); + track_idx=GetMediaTrackInfo_Value(track, "IP_TRACKNUMBER")-1; + + track ? ( + GetTrackGUID(#guid, track); + ImGui_PushID(ctx, #guid); + ) : ( + ImGui_BeginDisabled(ctx); + ); +); + +function create_sliders()( + // # of lanes + lane_count=GetMediaTrackInfo_Value(track, "I_NUMFIXEDLANES"); + last_lane_count = lane_count; + ImGui_Spacing(ctx); ImGui_SliderInt(ctx, "# of Track Lanes", lane_count, 5, 255,"%i",ImGui_SliderFlags_Logarithmic()); + last_lane_count != lane_count ? ( + SetMediaTrackInfo_Value(track, "I_NUMFIXEDLANES", lane_count); + item_shift_on_lane_count_change(track,lane_count-last_lane_count); + ); + + ImGui_Spacing(ctx); ImGui_Spacing(ctx); + + // # of scale steps + change_slider_colors(ctx,0.133,0.6,0.6); + ImGui_Spacing(ctx); ImGui_SliderInt(ctx, "# of Scale Steps", d_steps, 1, SCALE_MAX_LEN); + + // period + change_slider_colors(ctx,0.8,0.6,0.35); + ImGui_Spacing(ctx); ImGui_SliderDouble(ctx, "Period (cents)", period, 0, 1901.955, "%.2f"); + + // ED + change_slider_colors(ctx,0.95,0.6,0.6); + ImGui_Spacing(ctx); ImGui_SliderInt(ctx, "Equal Divisions (of Period)", edo, 5, 311,"%i",ImGui_SliderFlags_Logarithmic()); + ImGui_Spacing(ctx); ImGui_SliderInt(ctx, "n\\EDX as Generator", edo_steps, 1, edo); + + // generator + is_edo_slider_changed() ? update_generator(); + change_slider_colors(ctx,0.45,0.6,0.4); + ImGui_Spacing(ctx); ImGui_SliderDouble(ctx, "Generator (cents)", generator, 0, period/2, "%.2f"); + ImGui_Spacing(ctx); ImGui_SliderInt(ctx, "Mode", mode, 0, d_steps-1); +); + +function action_buttons()( + ImGui_Spacing(ctx); + ImGui_Button(ctx, "Play/Stop") + ? ( Main_OnCommand(NamedCommandLookup("_RS4f0401222eb33809e4e04b9693bcd14ca7d99b0f"),0); + Main_OnCommand(40044,0); + ); + ImGui_SameLine(ctx); ImGui_Button(ctx, " b ") ? Main_OnCommand(NamedCommandLookup("_RS71b7cfc777a450acc4930c2719c38a437a3f33d9"),0); + ImGui_SameLine(ctx); ImGui_Button(ctx, " # ") ? Main_OnCommand(NamedCommandLookup("_RS366fe3883adb723dde13b90b44be99e4a9cc5207"),0); + ImGui_SameLine(ctx); ImGui_Button(ctx, " v ") ? Main_OnCommand(NamedCommandLookup("_RS2dbc76ff1464140d969d92fc52228d6399df2cf0"),0); + ImGui_SameLine(ctx); ImGui_Button(ctx, " ^ ") ? Main_OnCommand(NamedCommandLookup("_RSb11a8b54723ca6d7df691a1525ce2656dcbf1b7e"),0); +); + + +function create_and_store_scale()( + construct_scale(d_steps,period,generator,mode); + save_track_scale(track,track_idx,d_steps,period,generator,mode,small,edo,edo_steps); + print_scale_test(d_steps); + save_lane_data(track,track_idx,d_steps,'A','A',4,period); + edo_slider_check_update(); + change_slider_colors(ctx,0.55,0,0.2); + ImGui_Spacing(ctx); ImGui_InputTextMultiline(ctx, "Scale Preview", #scale, 0, 240); +); + +function frame_cleanup()( + pop_colors(ctx); + track ? ImGui_PopID(ctx) : ImGui_EndDisabled(ctx); + ImGui_PopFont(ctx); + ImGui_End(ctx); +); + +///////////////////////////////////////////////////////////////////////////////////////////// +// main loop +///////////////////////////////////////////////////////////////////////////////////////////// +function frame()( + frame_init(); + load_track_scale(track,track_idx); + create_sliders(); + create_and_store_scale(); + action_buttons(); + UpdateItemLanes(0); + UpdateArrange(); + frame_cleanup(); +); + +function loop() ( + open = 1; + ImGui_SetNextWindowSize(ctx, 960, 540, ImGui_Cond_FirstUseEver()); + ImGui_Begin(ctx, "Scale Selector", open) ? frame(); + open ? defer("loop()"); +); + +defer("loop()"); + diff --git a/index.xml b/index.xml index 4c3996e1d..fa29fc525 100644 --- a/index.xml +++ b/index.xml @@ -1,5 +1,5 @@ - + @@ -3699,6 +3699,10 @@ Fix a potential infinite loop]]> https://github.com/ReaTeam/ReaScripts/raw/278a6624b820f53acf55f9541f14dbb663c57f56/FX/80icio_Floating%20FX%20bypass%20toggle.lua + + + https://github.com/ReaTeam/ReaScripts/raw/0abc962f62ba35d99591a4f131213f141f396a2d/FX/80icio_Floating%20FX%20bypass%20toggle.lua + @@ -4894,6 +4898,11 @@ Better error-checking in general]]> Implement FX Browser parser]]> https://github.com/ReaTeam/ReaScripts/raw/d016832a9d0e549a1077f386d4338889ec249e3b/FX/sexan_Lil%20FX%20Slot%20Homie.lua + + + https://github.com/ReaTeam/ReaScripts/raw/4747fa0091bf61a50a1e47b35584826fe71087b3/FX/sexan_Lil%20FX%20Slot%20Homie.lua + @@ -5094,6 +5103,11 @@ BugFix - Changed layout for Visualizer zoom bounds reference on tracks on Main window]]> https://github.com/ReaTeam/ReaScripts/raw/84d40f0ddb894f38e537ce02002f6fda09686f19/Items%20Editing/80icio_Perfect%20Timing!%20-%20Audio%20Quantizer.lua + + + https://github.com/ReaTeam/ReaScripts/raw/f5b35d9a55fa132cac6469fa3f7ad76f9f0d308e/Items%20Editing/80icio_Perfect%20Timing!%20-%20Audio%20Quantizer.lua + @@ -5715,6 +5729,18 @@ website https://github.com/ReaTeam/ReaScripts/raw/f2600f6b425c44022c8529a16a75b8b1a968fc56/Items%20Editing/amagalma_Smart%20Crossfade.lua + + + https://github.com/ReaTeam/ReaScripts/raw/ae5efc8dd2cc95757b026dc2eabb2355b9d42a58/Items%20Editing/amagalma_Smart%20Crossfade.lua + + + + https://github.com/ReaTeam/ReaScripts/raw/2b699f19926837c71ac56dd53a043e60da03e19a/Items%20Editing/amagalma_Smart%20Crossfade.lua + + + + https://github.com/ReaTeam/ReaScripts/raw/181c4d64496e364dd3466755ac7746560250b0a9/Items%20Editing/amagalma_Smart%20Crossfade.lua + @@ -5960,7 +5986,7 @@ website https://github.com/ReaTeam/ReaScripts/raw/4b68451766e7a06b601a4b646b4c077cdf7e8736/Items%20Editing/AtmanActive_Apply%20track-take%20FX%20to%20selected%20items%20and%20propagate%20new%20files%20to%20other%20items%20with%20same%20source.eel - + Author's page + Forum thread + Donate via PayPal https://github.com/ReaTeam/ReaScripts/raw/d2612ce21752277c396fb7689081b5728438d69c/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection).lua @@ -6012,6 +6039,52 @@ website - Ability to mirror item fade in envelopes using razor and mouse placement]]> https://github.com/ReaTeam/ReaScripts/raw/551091d503ab641b15addaac16b5f7af3335ee69/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection).lua + + + https://github.com/ReaTeam/ReaScripts/raw/cb23d703ddfbf840e277e7b2e06b7012a0d3b157/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection).lua + https://github.com/ReaTeam/ReaScripts/raw/cb23d703ddfbf840e277e7b2e06b7012a0d3b157/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Options%20window%20for%20az_Fade%20tool.lua + https://github.com/ReaTeam/ReaScripts/raw/cb23d703ddfbf840e277e7b2e06b7012a0d3b157/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Open%20options%20for%20az_Fade%20tool.lua + + + + https://github.com/ReaTeam/ReaScripts/raw/92d8e7d9212e890483069f25a968fb1a96dd8fb2/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection).lua + https://github.com/ReaTeam/ReaScripts/raw/92d8e7d9212e890483069f25a968fb1a96dd8fb2/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Options%20window%20for%20az_Fade%20tool.lua + https://github.com/ReaTeam/ReaScripts/raw/92d8e7d9212e890483069f25a968fb1a96dd8fb2/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Open%20options%20for%20az_Fade%20tool.lua + + + + https://github.com/ReaTeam/ReaScripts/raw/6e1a78a98a4ff36667207b97db96f1bac81894d2/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection).lua + https://github.com/ReaTeam/ReaScripts/raw/6e1a78a98a4ff36667207b97db96f1bac81894d2/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Options%20window%20for%20az_Fade%20tool.lua + https://github.com/ReaTeam/ReaScripts/raw/6e1a78a98a4ff36667207b97db96f1bac81894d2/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Open%20options%20for%20az_Fade%20tool.lua + + + + https://github.com/ReaTeam/ReaScripts/raw/6a1226b235c52dd4d2c6a62c780352782749409d/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection).lua + https://github.com/ReaTeam/ReaScripts/raw/6a1226b235c52dd4d2c6a62c780352782749409d/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Options%20window%20for%20az_Fade%20tool.lua + https://github.com/ReaTeam/ReaScripts/raw/6a1226b235c52dd4d2c6a62c780352782749409d/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Open%20options%20for%20az_Fade%20tool.lua + + + + https://github.com/ReaTeam/ReaScripts/raw/d7d2c113af92e141785b807a4e63979aad58eb26/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection).lua + https://github.com/ReaTeam/ReaScripts/raw/d7d2c113af92e141785b807a4e63979aad58eb26/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Options%20window%20for%20az_Fade%20tool.lua + https://github.com/ReaTeam/ReaScripts/raw/d7d2c113af92e141785b807a4e63979aad58eb26/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Open%20options%20for%20az_Fade%20tool.lua + + + + https://github.com/ReaTeam/ReaScripts/raw/7b50eda7111a8953febc462b15bb6cfc96489bd7/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection).lua + https://github.com/ReaTeam/ReaScripts/raw/7b50eda7111a8953febc462b15bb6cfc96489bd7/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Options%20window%20for%20az_Fade%20tool.lua + https://github.com/ReaTeam/ReaScripts/raw/7b50eda7111a8953febc462b15bb6cfc96489bd7/Items%20Editing/az_Fade%20tool%20(work%20on%20context%20of%20mouse,%20razor%20or%20time%20selection)/az_Open%20options%20for%20az_Fade%20tool.lua + @@ -6039,7 +6112,7 @@ website {\pard \ql \f0 \sa180 \li0 \fi0 Forum thread: https://forum.cockos.com/showthread.php?t=259751\par} {\pard \ql \f0 \sa180 \li0 \fi0 Split items respect grouping, depend on context of mouse cursor, split at razor edit area or time selection if exist, split at mouse or edit cursor otherwise.\par} {\pard \ql \f0 \sa180 \li0 \fi0 There are a lot of options. To open options window place mouse on the transport panel or mixer panel and press assigned shortcut.\par} -{\pard \ql \f0 \sa180 \li0 \fi0 By design it should be assigned to keyboard shortcut, not to a mouse modifier.\par} +{\pard \ql \f0 \sa180 \li0 \fi0 By design it should be assigned to a keyboard shortcut, not to a mouse modifier.\par} } ]]> Forum thread @@ -6113,6 +6186,15 @@ Added experimental fixed lanes support]]> https://github.com/ReaTeam/ReaScripts/raw/4f168fae0f24b40b98e00de45e364d42cb486474/Items%20Editing/az_Smart%20split%20items%20by%20mouse%20cursor.lua https://github.com/ReaTeam/ReaScripts/raw/4f168fae0f24b40b98e00de45e364d42cb486474/Items%20Editing/az_Smart%20split%20items%20by%20mouse%20cursor/az_Open%20options%20for%20az_Smart%20split%20items%20by%20mouse%20cursor.lua + + + https://github.com/ReaTeam/ReaScripts/raw/9468b44c02bf00adc974825b237c89061bea0c22/Items%20Editing/az_Smart%20split%20items%20by%20mouse%20cursor.lua + https://github.com/ReaTeam/ReaScripts/raw/9468b44c02bf00adc974825b237c89061bea0c22/Items%20Editing/az_Smart%20split%20items%20by%20mouse%20cursor/az_Open%20options%20for%20az_Smart%20split%20items%20by%20mouse%20cursor.lua + @@ -6147,6 +6229,11 @@ Added experimental fixed lanes support]]> https://github.com/ReaTeam/ReaScripts/raw/3fc1ab0ae6dd9c7852b04c56ae013136adeca1e3/Items%20Editing/az_Trim%20left,%20right%20or%20both%20item%20edges%20via%20mouse%20and%20razor.lua https://github.com/ReaTeam/ReaScripts/raw/3fc1ab0ae6dd9c7852b04c56ae013136adeca1e3/Items%20Editing/az_Trim%20left,%20right%20or%20both%20item%20edges%20via%20mouse%20and%20razor/az_Open%20options%20for%20az_Trim%20left,%20right%20or%20both%20item%20edges%20via%20mouse%20and%20razor.lua + + + https://github.com/ReaTeam/ReaScripts/raw/3c182bdb84ee7d1797e55029637548035c08f633/Items%20Editing/az_Trim%20left,%20right%20or%20both%20item%20edges%20via%20mouse%20and%20razor.lua + https://github.com/ReaTeam/ReaScripts/raw/3c182bdb84ee7d1797e55029637548035c08f633/Items%20Editing/az_Trim%20left,%20right%20or%20both%20item%20edges%20via%20mouse%20and%20razor/az_Open%20options%20for%20az_Trim%20left,%20right%20or%20both%20item%20edges%20via%20mouse%20and%20razor.lua + @@ -7936,6 +8023,21 @@ Fix: Expand error message when the library is missing]]> https://github.com/ReaTeam/ReaScripts/raw/c2dbb59ba8a7a4bfaf0499a944b9bf1e3bdc40e3/Items%20Editing/Mordi_spk77_Sort%20items%20by%20length.lua + + + + https://i.imgur.com/YFmQuAq.gif + + + https://github.com/ReaTeam/ReaScripts/raw/dd31a331bc54ce325ffb3715261db5f63143111d/Items%20Editing/mrtnz_Preview%20item%20from%20Starkovsky.lua + + https://github.com/ReaTeam/ReaScripts/raw/c8ee093ec1661c1a832316d1cb173e881cecf94f/MIDI%20Editor/talagan_OneSmallStep/toolbar_icons/toolbar_one_small_step.png https://github.com/ReaTeam/ReaScripts/raw/c8ee093ec1661c1a832316d1cb173e881cecf94f/MIDI%20Editor/talagan_OneSmallStep/toolbar_icons/toolbar_one_small_step_cleanup.png + + + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20input%20mode.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20input%20mode.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20input%20mode.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20edit%20mode.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20edit%20mode.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20edit%20mode.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20edit%20mode.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20edit%20mode.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len%20param%20source.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len%20param%20source.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len%20param%20source.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len%20modifier.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len%20modifier.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len%20modifier.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len%20modifier.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len%20modifier.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Change%20note%20len.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Edit%20Action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Increase%20note%20len.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Decrease%20note%20len.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Cleanup%20helper%20JSFXs.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Set%20or%20remove%20operation%20marker.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Set%20or%20remove%20playback%20marker.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Playback.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Toggle%20armed.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/actions/talagan_OneSmallStep%20Toggle%20Debugger.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/engine_lib.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/helper_lib.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/input_managers/KeyActivityManager.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/input_managers/KeyPressActivityManager.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/input_managers/KeyReleaseActivityManager.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/lib/MIDIUtils.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/action_triggers.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/articulations.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/debugger.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/defines.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/edition.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/focus.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/markers.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/modifiers.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/notes.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/settings.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/snap.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/target.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/modules/time.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/operations/generic.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/operations/insert.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/operations/navigate.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/operations/repitch.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/operations/replace.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/operations/stretch.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/operations/stuff.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/classes/operations/write.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/edit_mode_insert.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/edit_mode_navigate.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/edit_mode_repitch.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/edit_mode_replace.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/edit_mode_write.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_1.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_1_16.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_1_2.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_1_4.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_1_8.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_1_n.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_2.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_2_3.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_3_2.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/frac_4.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_compress.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_insert_back.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_insert_forward.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_navigate_back.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_navigate_forward.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_repitch_back.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_repitch_forward.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_replace_back.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_replace_forward.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_stretch.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_stuff.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_unstuff.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_write_back.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/indicator_write_forward.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/input_mode_action.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/input_mode_keyboard_press.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/input_mode_keyboard_release.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/input_mode_pedal.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/marker.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_1.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_1_16.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_1_2.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_1_32.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_1_4.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_1_64.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_1_8.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_dotted.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_highlighting.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_len_mode_inote.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_len_mode_oss.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_len_mode_pgrid.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_modified.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_triplet.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/note_tuplet.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/playback.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/settings.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/snap.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/snap_btn_ibounds.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/snap_btn_igrid.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/snap_btn_note.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/images/snap_btn_pgrid.lua + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/One%20Small%20Step%20Helper.jsfx + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/toolbar_icons/toolbar_one_small_step.png + https://github.com/ReaTeam/ReaScripts/raw/acb633a369981d906a147763b222f1cc32b0519e/MIDI%20Editor/talagan_OneSmallStep/toolbar_icons/toolbar_one_small_step_cleanup.png + @@ -18762,6 +18997,35 @@ is off-screen. Reaper issue, should be fixed soon.]]> https://github.com/ReaTeam/ReaScripts/raw/9a9e73d39709e6dd0fcff89840e03ffb54734edd/Tracks/kaddaok_Import%20UVR%20separations%20into%20comping%20lane%20structure.lua + + + Data > track_icons Within this folder, create or modify folders to represent your desired categories. The script will automatically recognize these folders as categories in the sidebar, allowing you to filter icons based on their assigned categories.\par} +} +]]> + https://i.imgur.com/bFK2HYk.png + https://i.imgur.com/MabMOW1.png + + + https://github.com/ReaTeam/ReaScripts/raw/e6d097c8bf2131d1fc0e0b5d6e3af96f312f3bbb/Tracks/reapertips_Track%20icon%20selector.lua + + https://github.com/ReaTeam/ReaScripts/raw/ba2734d57564b37be35e1659fbe535c278da85bc/Various/amagalma_ReaNamer%20Replace%20Help.lua https://github.com/ReaTeam/ReaScripts/raw/ba2734d57564b37be35e1659fbe535c278da85bc/Various/amagalma_ReaNamer%20utf8data.lua + + + https://github.com/ReaTeam/ReaScripts/raw/446dc379abe2987cfea4912bf9bd5c85e71c42f3/Various/amagalma_gianfini_ReaNamer%20(track-item-region-marker%20renaming%20utility).lua + https://github.com/ReaTeam/ReaScripts/raw/446dc379abe2987cfea4912bf9bd5c85e71c42f3/Various/amagalma_ReaNamer%20Replace%20Help.lua + https://github.com/ReaTeam/ReaScripts/raw/446dc379abe2987cfea4912bf9bd5c85e71c42f3/Various/amagalma_ReaNamer%20utf8data.lua + @@ -22108,6 +22378,41 @@ v1.0 https://github.com/ReaTeam/ReaScripts/raw/6506bc2ef7528a9090651d91e20ff44607c7e6c7/Various/ausbaxter_Render%20item%20columns.lua + + + + Forum thread + Donate via PayPal + + + https://github.com/ReaTeam/ReaScripts/raw/b3c4cb6b4a3500487095f55dbc96ebb735f9e90e/Various/az_Auto-send%20all%20inputs%20to%20the%20virtual%20midi%20keyboard%20(VKB)%20if%20mouse%20is%20in%20empty%20track%20space%20or%20recording%20is%20running%20(Background).lua + + + + + + Forum thread + + + https://github.com/ReaTeam/ReaScripts/raw/a274a7044d05135934ca1ca1415ee1097c9f9880/Various/az_Simple%20project%20reconform.lua + + https://github.com/ReaTeam/ReaScripts/raw/1e3fa40657aaea838cc724c15f99562add85e37f/Various/spk77_Track%20Tags.lua + + + Track Send Defaults > Fixed Lane Defaults > {\b Uncheck "Automatically delete empty lanes at bottom of track"}\sa180\par} +{\pard \ql \f0 \sa180 \li0 \fi0 I strongly recommend selecting "Small lanes" here as well.\par} +{\pard \ql \f0 \sa180 \li360 \fi-360 3.\tx360\tab Launch the script "Starshine_starmidi_UI.eel".\par} +{\pard \ql \f0 \sa180 \li360 \fi-360 4.\tx360\tab Drag the # of Lanes slider to add 15+ lanes to a track.\par} +{\pard \ql \f0 \sa180 \li360 \fi-360 5.\tx360\tab It is beyond the scope of this package documentation to explain microtonal scale theory. If you are interested in learning more, please consider joining the Xenharmonic Alliance discord at https://discord.gg/uxvw5Vzj\sa180\par} +{\pard \ql \f0 \sa180 \li0 \fi0 Setting the following combinations for [# of Scale Steps], [Equal Divisions], and [n\\EDX as Generator] should yield interesting results. 7\\16edo means to select 16 [Equal Divisions] and 7 for [n\\EDX as Generator]\par} +{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab 7 steps, 7\\16edo\par} +{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab 8 steps, 10\\27edo\par} +{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab 9 steps, 4\\19edo\par} +{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab 7 steps, 13\\31edo, mode 5 (this is like a regular major scale but more in tune. roughly "quarter-comma meantone")\sa180\par} +{\pard \ql \f0 \sa180 \li360 \fi-360 6.\tx360\tab Insert/draw empty MIDI items in lanes. These function as notes. Optionally, you may set a mouse modifier to draw MIDI items so that it functions like the standard piano roll for inserting notes.\par} +{\pard \ql \f0 \sa180 \li360 \fi-360 7.\tx360\tab Add a synth to the FX chain; do not enable MPE. Leave pitch bend range at 2 semitones.\par} +{\pard \ql \f0 \sa180 \li360 \fi-360 8.\tx360\tab You must click Play from this interface; this runs the parsing script prior to playback. (The script will also auto-add and configure the jsfx.)\sa180\par} +{\pard \ql \f0 \sa180 \li0 \fi0 {\i Optionally, you may create a custom action that runs "starshine_starmidi_parser.eel" and then the Transport: Play/Stop action}\par} +} +]]> + + + https://github.com/ReaTeam/ReaScripts/raw/3908802c4082d34fa7280d651827e406123fa75c/Various/starshine_Starmidi%20-%20microtonal%20scales%20using%20lanes%20as%20piano%20roll/starshine_Starmidi%20-%20Flatten.eel + https://github.com/ReaTeam/ReaScripts/raw/3908802c4082d34fa7280d651827e406123fa75c/Various/starshine_Starmidi%20-%20microtonal%20scales%20using%20lanes%20as%20piano%20roll/starshine_Starmidi%20-%20Sharpen.eel + https://github.com/ReaTeam/ReaScripts/raw/3908802c4082d34fa7280d651827e406123fa75c/Various/starshine_Starmidi%20-%20microtonal%20scales%20using%20lanes%20as%20piano%20roll/starshine_Starmidi%20-%20Small%20step%20down.eel + https://github.com/ReaTeam/ReaScripts/raw/3908802c4082d34fa7280d651827e406123fa75c/Various/starshine_Starmidi%20-%20microtonal%20scales%20using%20lanes%20as%20piano%20roll/starshine_Starmidi%20-%20Small%20step%20up.eel + https://github.com/ReaTeam/ReaScripts/raw/3908802c4082d34fa7280d651827e406123fa75c/Various/starshine_Starmidi%20-%20microtonal%20scales%20using%20lanes%20as%20piano%20roll/starshine_Starmidi%20-%20Parser.eel + https://github.com/ReaTeam/ReaScripts/raw/3908802c4082d34fa7280d651827e406123fa75c/Various/starshine_Starmidi%20-%20microtonal%20scales%20using%20lanes%20as%20piano%20roll/starshine_Starmidi%20-%20UI.eel + https://github.com/ReaTeam/ReaScripts/raw/3908802c4082d34fa7280d651827e406123fa75c/Various/starshine_Starmidi%20-%20microtonal%20scales%20using%20lanes%20as%20piano%20roll/starmidi + +