Lua copy midi region whlie playing for "live looping"

Hi, I’m trying to make lua script that copies midi region several times after it, immediately after being recorded - yet another try to use Ardour for live looping. But I can’t get it to work cause the region is “written” to the playlist of a track only when I hit stop button. Is there a way to access that midi region without stopping playback or tell Ardour to write region to playlist?

This is my code so far:

ardour { ["type"] = "Snippet", name = "dup_midi_region" }

function factory() 
	return function()
		local route = Session:route_by_name("synth")
		local track = route:to_track()
		local playhead = Session:transport_frame()
		local loop = Session:locations():auto_loop_location()
		local proc = ARDOUR.LuaAPI.nil_proc() -- bounce w/o processing
		local itt = ARDOUR.InterThreadInfo() -- bounce progress info (unused)
		
		if loop then
			print("Loop set.")
		
			-- prepare undo operation
			Session:begin_reversible_command("dup_midi_region")
			local add_undo = false -- keep track if something has changed

			local loop_start = loop:start()
			local loop_end = loop:_end()
			local playlist = track:playlist()
			
			for r in playlist:region_list():iter() do
				print("region name: "..tostring(r:name()))
			end
		
			local loop_region = playlist:regions_touched(loop_start, loop_end)
			local region = track:bounce_range(loop_start, loop_end, itt, proc, false)
			
			print("region captured: "..tostring(region:captured()))

			if region then
				print("Region found. Copying.")
				playlist:add_region(region, playhead, 1, false, 0, 0, false)

			end
		
			-- 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
		
			-- all done, commit the combined Undo Operation
			if add_undo then
				-- the 'nil' Command here mean to use the collected diffs added above
				Session:commit_reversible_command (nil)
			else
				Session:abort_reversible_command ()
			end
		else
			print("Loop not set.")
		end
	end 
end

Btw I tried to do it by stopping and starting playback again in lua script using Editor:access_action(), but can’t figure out what action is for playback start (Roll didn’t work and PlaySelection and PlayPreroll aren’t suitable).

Thank you.

Alas, no.
The data is written to a temporary buffer. It can only be written to disk at a later time (disk i/o is not realtime-safe). Alignment also happens at rec-stop, and if you “stop and forget capture” it’s thrown away.
Besides the playlist cannot be modified click-free by while playing.

see http://manual.ardour.org/appendix/menu-actions-list/

Editor:access_action ("Transport", "Stop")
Editor:access_action ("Transport", "Roll") 
ARDOUR.LuaAPI.usleep (100000) -- wait for 100ms 

or you could directly call into backend:

Session:request_transport_speed (0.0, true)
Session:request_transport_speed (1.0, true)

Events will be queued for a later time, handled in realtime context.

I’ll have to think about this, but I don’t you can reasonably automate this from the GUI.

Best I can think of right now is subscribing to the TransportLooped or TransportStateChange signal, so the Lua script (type = “EditorHook”) is automatically invoked.

Thank you for the hints Robin. I got it working somehow - when I run it from Scripting window and playhead is at punch out position (see code below), it works - it copies 5 times region between punch in and out right after punch out position.

But when I save it as [“type”] = “session”, load it through Script Manager under Session Scripts, it doesn’t work - it seems as if it’s not even run, because I don’t see any regions created under Regions window (on right side of editor). Is this correct way to run it? I think I can’t use TransportLooped and TransportStateChange as you suggested, because I need it being run while rolling.

Current code:

ardour { ["type"] = "session", name = "dup_midi_region" }

function factory() 
        return function()
                local playhead = Session:transport_frame()
                print("playhead: "..tostring(playhead))
                local punch = Session:locations():auto_punch_location()

                if punch then
                        print("Punch set.")

                        local punch_in = punch:start()
                        local punch_out = punch:_end()
                        print("punch_in: "..tostring(punch_in))
                        print("punch_out: "..tostring(punch_out))

                        if playhead == punch_out then           
                                print("We are at punch out.")

                                Editor:access_action("Transport", "Stop")

                                local route = Session:route_by_name("synth")
                                local track = route:to_track()
                                local proc = ARDOUR.LuaAPI.nil_proc() -- bounce w/o processing
                                local itt = ARDOUR.InterThreadInfo() -- bounce progress info (unused)
                
                                -- prepare undo operation
                                Session:begin_reversible_command("dup_midi_region")
                                local add_undo = false -- keep track if something has changed
        
                                local playlist = track:playlist()
                        
                                local region = track:bounce_range(punch_in, punch_out, itt, proc, false)
                        
                                if region then
                                        print("Region found. Copying.")
                                        playlist:add_region(region, playhead, 5, false, 0, 0, false)
                                end
                
                                -- 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
                
                                -- all done, commit the combined Undo Operation
                                if add_undo then
                                        -- the 'nil' Command here mean to use the collected diffs added above
                                        Session:commit_reversible_command(nil)
                                else
                                        Session:abort_reversible_command()
                                end

                                Editor:access_action("Transport", "Roll") 
                                ARDOUR.LuaAPI.usleep(100000) -- wait for 100ms 
                        end
                else
                        print("Punch not set.")
                end 
        end
end