Random automation, continued: LUA scripting

Hi,

I am coming again with the random automation subject (Random automation).

I have tried the “Transforming MIDI method”, which works perfectly! But I wish to go further and would like to try LUA scripting.

So, I found several resources about it (like The Ardour Manual - Lua Scripting and The Ardour Manual - Lua Bindings Class Reference), but I currently have no idea how to, through LUA:

Get the beginning time of a note.
Change this time.
Get the duration of a note.
Change this duration.

Does someone please know how to proceed?

Thanks!

Aka

1 Like

Never underestimate the power of example code :slight_smile:

ardour/share/scripts at master · Ardour/ardour · GitHub

I expect the following script may come in handy:

3 Likes

Thank you,

I think more than half of what I need is in your example.

I tried running your script, but I probably made a mistake. When I launch it, I don’t see any effect on my midi track. Also, I can only run it once; I have to close my Ardour session and reopen it to run your lua script again.

I launched Ardour from the command line, and I don’t receive any messages.

I wonder what I can do.

Looks like you did not select at MIDI regions (the track is selected).

Hello,

I tried with:

  • One midi region selected
  • Two midi regions selected
  • One midi track selected
  • Two midi tracks selected
  • One note selected
  • Two notes selected

And I still have the same problem…

Only the first two (MIDI region selection) work.

I suggest to add some debug message:

Open the script in Ardour Menu > Window > Scripting, then add some print messages.
Select a MIDI region (with grab tool) and then Run the script in that window

Perhaps some strategic messages like:

diff --git a/share/scripts/brutalize_midi.lua b/share/scripts/brutalize_midi.lua
index 7a8e734431..0a461fbcc0 100644
--- a/share/scripts/brutalize_midi.lua
+++ b/share/scripts/brutalize_midi.lua
@@ -41,9 +41,11 @@ function factory () return function ()
        -- iterate over all selected regions
        local sel = Editor:get_selection ()
        for r in sel.regions:regionlist ():iter () do
+               print ("Selected:", r:name())
                local mr = r:to_midiregion ()
                -- skip non MIDI regions
                if mr:isnil () then goto continue end
+               print ("Found MIDI Region:", r:name())
 
                -- get MIDI Model of the region
                local mm = mr:midi_source(0):model ()
@@ -55,6 +57,7 @@ function factory () return function ()
                        -- note is-a https://manual.ardour.org/lua-scripting/class_reference/#Evoral:NotePtr
                        -- get current position ..
                        local old_pos = note:time ()
+                       print ("Found MIDI Note at:", old_pos:get_beats ())
 
                        -- ..generate random offset..
                        local tickdiff = math.floor (rv['rand']() * max_distance);
1 Like

Hello,

I copied and pasted the script, with some additional print messages, in the script window:

---- this header is (only) required to save the script
-- ardour { ["type"] = "Snippet", name = "" }
-- function factory () return function () -- -- end end
function factory () return function ()

	-- Ask user about max randomness to introduce
	local dialog_options = {
		{ type = "label", align="left", title = "Brutalize MIDI" },
		{
			type = "dropdown", key = "divisor", title="Max randomness to introduce:", values =
			{
				["8th note"]             = 2,
				["16th note"]            = 4,
				["16th triplett (1/24)"] = 6,
				["32nd"]                 = 8,
				["32nd triplett (1/48)"] = 12,
				["64th"]                 = 16
			},
			default = "16th note"
		},
		{
			type = "dropdown", key = "rand", title="Move Notes..", values =
			{
				["only forward in time"]  = function () return math.random() end,          --  0 .. +1
				["only backward in time"] = function () return math.random() - 2; end,     -- -1 ..  0
				["either way"]            = function () return 2 * math.random() - 1; end  -- -1 .. +1
			},
			default = "either way"
		}
	}
	local rv = LuaDialog.Dialog ("Select Automation State", dialog_options):run()
	if not rv then return end

	-- calclate max distance in 'ticks'
	local ticks_per_beat = Temporal.Beats (1, 0):to_ticks ();
	local max_distance   = ticks_per_beat / rv['divisor']

	-- iterate over all selected regions
	local sel = Editor:get_selection ()
	for r in sel.regions:regionlist ():iter () do
	    print ("Selected:", r:name())
		local mr = r:to_midiregion ()
		-- skip non MIDI regions
		if mr:isnil () then goto continue end
		print ("Found MIDI Region:", r:name())

		-- get MIDI Model of the region
		local mm = mr:midi_source(0):model ()
		-- Prepare Undo command
		local midi_command = mm:new_note_diff_command ("MIDI Note Brutalize")

		-- Iterate over all notes of the MIDI region
		for note in ARDOUR.LuaAPI.note_list (mm):iter () do
			-- note is-a https://manual.ardour.org/lua-scripting/class_reference/#Evoral:NotePtr
			-- get current position ..
			local old_pos = note:time ()
			print ("Found MIDI Note at:", old_pos:get_beats ())

			-- ..generate random offset..
			local tickdiff = math.floor (rv['rand']() * max_distance);
			print (old_pos:get_beats (), old_pos:get_ticks (), tickdiff)

			-- .. and calculate new position.
			local new_pos = Temporal.Beats (old_pos:get_beats (), old_pos:get_ticks () + tickdiff)

			-- now modify the note (but don't allow to move a note before the session start [1|1|0])
			if old_pos ~= new_pos and new_pos > Temporal.Beats (0, 0) then
				local new_note = ARDOUR.LuaAPI.new_noteptr (note:channel (), new_pos, note:length (), note:note (), note:velocity ())
				midi_command:remove (note)
				midi_command:add (new_note)
			end
		end
		-- apply changes, and save undo
		mm:apply_command (Session, midi_command)
		::continue::
	end
end end

I received this:

LuaException : [string "---- this header is (only) required to save t..."]:35: attempt to call a nil value (method 'to_ticks')

So

 Temporal.Beats (1, 0):to_ticks ()

causes the issue.

Which version of Ardour as you running? to_ticks was added in Ardour 7.5
I suppose you could hardcode

local ticks_per_beat = 1920

which is Ardour’s default value.