better color choice for control master controls box in editor
[ardour.git] / scripts / tomsloop.lua
1 ardour { ["type"] = "EditorAction", name = "Tom's Loop",
2         license     = "MIT",
3         author      = "Ardour Team",
4         description = [[Bounce the loop-range of all non muted audio tracks, paste N times at playhead]]
5 }
6
7 function action_params ()
8         return { ["times"]   = { title = "Number of copies to add", default = "1"}, }
9 end
10
11 function factory (params) return function ()
12         -- get options
13         local p = params or {}
14         local n_paste  = tonumber (p["times"] or 1)
15         assert (n_paste > 0)
16
17         local proc     = ARDOUR.LuaAPI.nil_proc () -- bounce w/o processing
18         local itt      = ARDOUR.InterThreadInfo () -- bounce progress info (unused)
19
20         local loop     = Session:locations ():auto_loop_location ()
21         local playhead = Session:transport_frame ()
22
23         -- make sure we have a loop, and the playhead (edit point) is after it
24         if not loop then
25                 print ("A Loop range must be set.")
26                 goto errorout
27         end
28         assert (loop:start () < loop:_end ())
29         if loop:_end () >= playhead then
30                 print ("The Playhead (paste point) needs to be after the loop.")
31                 goto errorout
32         end
33
34         -- prepare undo operation
35         Session:begin_reversible_command ("Tom's Loop")
36         local add_undo = false -- keep track if something has changed
37
38         -- prefer solo'ed tracks
39         local soloed_track_found = false
40         for route in Session:get_tracks ():iter () do
41                 if route:soloed () then
42                         soloed_track_found = true
43                         break
44                 end
45         end
46
47         -- count regions that are bounced
48         local n_regions_created = 0
49
50         -- loop over all tracks in the session
51         for route in Session:get_tracks ():iter () do
52                 if soloed_track_found then
53                         -- skip not soloed tracks
54                         if not route:soloed () then
55                                 goto continue
56                         end
57                 end
58
59                 -- skip muted tracks (also applies to soloed + muted)
60                 if route:muted () then
61                         goto continue
62                 end
63
64                 -- at this point the track is either soloed (if at least one track is soloed)
65                 -- or not muted (if no track is soloed)
66
67                 -- test if bouncing is possible
68                 local track = route:to_track ()
69                 if not track:bounceable (proc, false) then
70                         goto continue
71                 end
72
73                 -- only audio tracks
74                 local playlist = track:playlist ()
75                 if playlist:data_type ():to_string () ~= "audio" then
76                         goto continue
77                 end
78
79                 -- check if there is at least one unmuted region in the loop-range
80                 local reg_unmuted_count = 0
81                 for reg in playlist:regions_touched (loop:start (), loop:_end ()):iter () do
82                         if not reg:muted() then
83                                 reg_unmuted_count = reg_unmuted_count + 1
84                         end
85                 end
86
87                 if reg_unmuted_count < 1 then
88                         goto continue
89                 end
90
91                 -- clear existing changes, prepare "diff" of state for undo
92                 playlist:to_stateful ():clear_changes ()
93
94                 -- do the actual work
95                 local region = track:bounce_range (loop:start (), loop:_end (), itt, proc, false)
96                 playlist:add_region (region, playhead, n_paste, false)
97
98                 n_regions_created = n_regions_created + 1
99
100                 -- create a diff of the performed work, add it to the session's undo stack
101                 -- and check if it is not empty
102                 if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then
103                         add_undo = true
104                 end
105
106                 ::continue::
107         end
108
109         --advance playhead so it's just after the newly added regions
110         if n_regions_created > 0 then
111                 Session:request_locate((playhead + loop:length() * n_paste),false)
112         end
113
114         -- all done, commit the combined Undo Operation
115         if add_undo then
116                 -- the 'nil' Command here mean to use the collected diffs added above
117                 Session:commit_reversible_command (nil)
118         else
119                 Session:abort_reversible_command ()
120         end
121
122         print ("bounced " .. n_regions_created .. " regions from loop range (" .. loop:length() ..  " frames) to playhead @ frame # " .. playhead)
123
124         ::errorout::
125 end end