Track deploy, social, and Reaper scripts
Scripts already referenced from CLAUDE.md but never committed: deploy_searxng.sh (which stands up the SearXNG instance Devon's web_search depends on), post_milestone.py, generate_milestone_images.py, make_x_launch_assets.py, schedule_x_launch.py, download_music.py, and the Reaper bleep-selection script. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
-- Bleep Selection — censor a time range on the selected track(s)
|
||||
--
|
||||
-- Replaces the audio inside the time selection with a 1kHz tone rather than
|
||||
-- muting it. Muted regions read as silence to strip_silence_dialog.lua
|
||||
-- (SILENCE_DB -30, thresholds 5-6s), so a muted bleep over a slowly-read phone
|
||||
-- number can get stripped and shift everything after it out of sync. A tone at
|
||||
-- TONE_GAIN_DB is nowhere near -30, so the silence pass leaves it alone.
|
||||
--
|
||||
-- Usage: make a time selection over the digits, select the track, run.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- SETTINGS
|
||||
---------------------------------------------------------------------------
|
||||
local TONE_GAIN_DB = 0.0 -- adjust bleep level; file is generated at -15 dBFS
|
||||
local FADE_MS = 4.0 -- fade in/out on the tone, prevents clicks
|
||||
local TONE_FILE = "bleep_1khz.wav" -- 60s 1kHz sine @48k, sits next to this script
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local EPS = 1e-9
|
||||
|
||||
local function script_dir()
|
||||
local src = debug.getinfo(1, "S").source
|
||||
return src:match("@?(.*[/\\])") or ""
|
||||
end
|
||||
|
||||
local function items_on(track)
|
||||
local t = {}
|
||||
for i = 0, reaper.CountTrackMediaItems(track) - 1 do
|
||||
t[#t + 1] = reaper.GetTrackMediaItem(track, i)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function item_bounds(item)
|
||||
local pos = reaper.GetMediaItemInfo_Value(item, "D_POSITION")
|
||||
return pos, pos + reaper.GetMediaItemInfo_Value(item, "D_LENGTH")
|
||||
end
|
||||
|
||||
-- Split every item crossing either edge so the range is cleanly separable
|
||||
local function split_at_edges(track, sel_start, sel_end)
|
||||
for _, item in ipairs(items_on(track)) do
|
||||
local pos, fin = item_bounds(item)
|
||||
if fin > sel_start + EPS and pos < sel_end - EPS then
|
||||
local right = item
|
||||
if pos < sel_start - EPS then
|
||||
right = reaper.SplitMediaItem(item, sel_start)
|
||||
end
|
||||
if right then
|
||||
local rpos, rfin = item_bounds(right)
|
||||
if rfin > sel_end + EPS and rpos < sel_end - EPS then
|
||||
reaper.SplitMediaItem(right, sel_end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function delete_inside(track, sel_start, sel_end)
|
||||
local removed = 0
|
||||
for _, item in ipairs(items_on(track)) do
|
||||
local pos, fin = item_bounds(item)
|
||||
if pos >= sel_start - EPS and fin <= sel_end + EPS then
|
||||
reaper.DeleteTrackMediaItem(track, item)
|
||||
removed = removed + 1
|
||||
end
|
||||
end
|
||||
return removed
|
||||
end
|
||||
|
||||
-- Build the item directly rather than via InsertMedia(): InsertMedia behaves
|
||||
-- like the user-facing "insert media file" action — it obeys ripple editing
|
||||
-- (shifting other tracks), can spawn a new track, and moves the edit cursor.
|
||||
-- AddMediaItemToTrack touches nothing but this track.
|
||||
local function insert_tone(track, sel_start, sel_len, tone_source)
|
||||
local item = reaper.AddMediaItemToTrack(track)
|
||||
if not item then return nil end
|
||||
local take = reaper.AddTakeToMediaItem(item)
|
||||
if not take then return nil end
|
||||
reaper.SetMediaItemTake_Source(take, tone_source)
|
||||
|
||||
reaper.SetMediaItemInfo_Value(item, "D_POSITION", sel_start)
|
||||
reaper.SetMediaItemInfo_Value(item, "D_LENGTH", sel_len)
|
||||
reaper.SetMediaItemInfo_Value(item, "B_LOOPSRC", 0)
|
||||
reaper.SetMediaItemInfo_Value(item, "D_VOL", 10 ^ (TONE_GAIN_DB / 20))
|
||||
|
||||
local fade = math.min(FADE_MS / 1000, sel_len / 2)
|
||||
reaper.SetMediaItemInfo_Value(item, "D_FADEINLEN", fade)
|
||||
reaper.SetMediaItemInfo_Value(item, "D_FADEOUTLEN", fade)
|
||||
return item
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local function main()
|
||||
local sel_start, sel_end = reaper.GetSet_LoopTimeRange(false, false, 0, 0, false)
|
||||
local sel_len = sel_end - sel_start
|
||||
if sel_len <= 0 then
|
||||
reaper.ShowMessageBox("Make a time selection over the audio to bleep.", "Bleep Selection", 0)
|
||||
return
|
||||
end
|
||||
|
||||
local n_tracks = reaper.CountSelectedTracks(0)
|
||||
if n_tracks == 0 then
|
||||
reaper.ShowMessageBox("Select the track to bleep.", "Bleep Selection", 0)
|
||||
return
|
||||
end
|
||||
|
||||
local tone_path = script_dir() .. TONE_FILE
|
||||
local f = io.open(tone_path, "rb")
|
||||
if not f then
|
||||
reaper.ShowMessageBox("Tone file not found:\n" .. tone_path, "Bleep Selection", 0)
|
||||
return
|
||||
end
|
||||
f:close()
|
||||
|
||||
local tone_source = reaper.PCM_Source_CreateFromFile(tone_path)
|
||||
if not tone_source then
|
||||
reaper.ShowMessageBox("Could not load tone file:\n" .. tone_path, "Bleep Selection", 0)
|
||||
return
|
||||
end
|
||||
|
||||
local targets = {}
|
||||
for i = 0, n_tracks - 1 do
|
||||
targets[#targets + 1] = reaper.GetSelectedTrack(0, i)
|
||||
end
|
||||
|
||||
reaper.Undo_BeginBlock()
|
||||
reaper.PreventUIRefresh(1)
|
||||
|
||||
-- Ripple editing would shift unrelated items (and other tracks) when items
|
||||
-- are removed. Force it off for the duration, restore the user's mode after.
|
||||
local ripple_per_track = reaper.GetToggleCommandStateEx(0, 40310) == 1
|
||||
local ripple_all = reaper.GetToggleCommandStateEx(0, 40311) == 1
|
||||
if ripple_per_track or ripple_all then
|
||||
reaper.Main_OnCommand(40309, 0) -- ripple editing off
|
||||
end
|
||||
|
||||
local bleeped = 0
|
||||
for _, track in ipairs(targets) do
|
||||
split_at_edges(track, sel_start, sel_end)
|
||||
delete_inside(track, sel_start, sel_end)
|
||||
if insert_tone(track, sel_start, sel_len, tone_source) then
|
||||
bleeped = bleeped + 1
|
||||
end
|
||||
end
|
||||
|
||||
if ripple_all then
|
||||
reaper.Main_OnCommand(40311, 0)
|
||||
elseif ripple_per_track then
|
||||
reaper.Main_OnCommand(40310, 0)
|
||||
end
|
||||
|
||||
reaper.PreventUIRefresh(-1)
|
||||
reaper.UpdateArrange()
|
||||
reaper.Undo_EndBlock(string.format("Bleep %.2fs on %d track(s)", sel_len, bleeped), -1)
|
||||
end
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user