1 ardour { ["type"] = "EditorAction", name = "Tom's Loop",
3 author = "Robin Gareus",
4 email = "robin@gareus.org",
5 site = "http://gareus.org",
6 description = [[Bounce the loop-range of all non muted audio tracks, paste N times at playhead]]
9 function action_params ()
10 return { ["times"] = { title = "Number of copies to add", default = "1"}, }
13 function factory (params) return function ()
15 local p = params or {}
16 local n_paste = tonumber (p["times"] or 1)
19 local proc = ARDOUR.LuaAPI.nil_proc () -- bounce w/o processing
20 local itt = ARDOUR.InterThreadInfo () -- bounce progress info (unused)
22 local loop = Session:locations ():auto_loop_location ()
23 local playhead = Session:transport_frame ()
25 -- make sure we have a loop, and the playhead (edit point) is after it
27 print ("A Loop range must be set.")
30 assert (loop:start () < loop:_end ())
31 if loop:_end () >= playhead then
32 print ("The Playhead (paste point) needs to be after the loop.")
36 -- prepare undo operation
37 Session:begin_reversible_command ("Tom's Loop")
38 local add_undo = false -- keep track if something has changed
40 -- prefer solo'ed tracks
41 local soloed_track_found = false
42 for route in Session:get_tracks ():iter () do
43 if route:soloed () then
44 soloed_track_found = true
49 -- count regions that are bounced
50 local n_regions_created = 0
52 -- loop over all tracks in the session
53 for route in Session:get_tracks ():iter () do
54 if soloed_track_found then
55 -- skip not soloed tracks
56 if not route:soloed () then
61 -- skip muted tracks (also applies to soloed + muted)
62 if route:muted () then
66 -- at this point the track is either soloed (if at least one track is soloed)
67 -- or not muted (if no track is soloed)
69 -- test if bouncing is possible
70 local track = route:to_track ()
71 if not track:bounceable (proc, false) then
76 local playlist = track:playlist ()
77 if playlist:data_type ():to_string () ~= "audio" then
81 -- check if there is at least one unmuted region in the loop-range
82 local reg_unmuted_count = 0
83 for reg in playlist:regions_touched (loop:start (), loop:_end ()):iter () do
84 if not reg:muted() then
85 reg_unmuted_count = reg_unmuted_count + 1
89 if reg_unmuted_count < 1 then
93 -- clear existing changes, prepare "diff" of state for undo
94 playlist:to_stateful ():clear_changes ()
97 local region = track:bounce_range (loop:start (), loop:_end (), itt, proc, false)
98 playlist:add_region (region, playhead, n_paste, false)
100 n_regions_created = n_regions_created + 1
102 -- create a diff of the performed work, add it to the session's undo stack
103 -- and check if it is not empty
104 if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then
111 --advance playhead so it's just after the newly added regions
112 if n_regions_created > 0 then
113 Session:request_locate((playhead + loop:length() * n_paste),false)
116 -- all done, commit the combined Undo Operation
118 -- the 'nil' Command here mean to use the collected diffs added above
119 Session:commit_reversible_command (nil)
121 Session:abort_reversible_command ()
124 print ("bounced " .. n_regions_created .. " regions from loop range (" .. loop:length() .. " frames) to playhead @ frame # " .. playhead)