summaryrefslogtreecommitdiff
path: root/scripts/tomsloop.lua
blob: 2a7880111e949c781c263b4571ac598c51c805fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
ardour { ["type"] = "Snippet", name = "Tom's Loop",
	license     = "MIT",
	author      = "Robin Gareus",
	email       = "robin@gareus.org",
	site        = "http://gareus.org",
	description = [[Bounce the loop-range of all non muted audio tracks, paste N times at playhead]]
}

-- unused ;  cfg parameter for ["type"] = "EditorAction"
function action_params ()
	return { ["times"]   = { title = "Number of copies to add", default = "1"}, }
end

function factory () return function ()
	-- get options
	local p = params or {}
	local npaste   = p["times"] or 1
	assert (npaste > 0)

	local proc     = ARDOUR.LuaAPI.nil_proc () -- bounce w/o processing
	local itt      = ARDOUR.InterThreadInfo () -- bounce progress info (unused)

	local loop     = Session:locations ():auto_loop_location ()
	local playhead = Session:transport_frame ()

	-- make sure we have a loop, and the playhead (edit point) is after it
	-- TODO: only print an error and return
	-- TODO: use the edit-point (not playhead) ? maybe.
	if not loop then
		print ("A Loop range must be set.")
		goto errorout
	end
	assert (loop:start () < loop:_end ())
	if loop:_end () >= playhead then
		print ("The Playhead (paste point) needs to be after the loop.")
		goto errorout
	end

	-- prepare undo operation
	Session:begin_reversible_command ("Tom's Loop")
	local add_undo = false -- keep track if something has changed

	for route in Session:get_tracks ():iter () do
		-- skip muted tracks
		if route:muted () then
			goto continue
		end
		-- test if bouncing is possible
		local track = route:to_track ()
		if not track:bounceable (proc, false) then
			goto continue
		end
		-- only audio tracks
		local playlist = track:playlist ()
		if playlist:data_type ():to_string () ~= "audio" then
			goto continue
		end

		-- check if there are any regions in the loop-range of this track
		local range = Evoral.Range (loop:start (), loop:_end ())
		if playlist:regions_with_start_within (range):empty ()
			and playlist:regions_with_end_within (range):empty () then
			goto continue
		end

		-- clear existing changes, prepare "diff" of state
		playlist:to_stateful ():clear_changes ()

		-- do the actual work
		local region = track:bounce_range (loop:start (), loop:_end (), itt, proc, false)
		playlist:add_region (region, playhead, npaste, false)

		-- create a diff of the performed work, add it to the session's undo stack
		-- and check if it is not empty
		if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then
			add_undo = true
		end

		::continue::
	end
	-- all done, commit the combined Undo Operation
	if add_undo then
		-- the 'nil' Commend here mean to use the collected diffs added above
		Session:commit_reversible_command (nil)
	else
		Session:abort_reversible_command ()
	end
	::errorout::
end end