Add some example Lua scripts
authorRobin Gareus <robin@gareus.org>
Tue, 14 Jan 2020 15:52:42 +0000 (16:52 +0100)
committerRobin Gareus <robin@gareus.org>
Tue, 14 Jan 2020 16:04:04 +0000 (17:04 +0100)
scripts/_rubberband_swing.lua [new file with mode: 0644]
scripts/_tempo_map_dump.lua [new file with mode: 0644]

diff --git a/scripts/_rubberband_swing.lua b/scripts/_rubberband_swing.lua
new file mode 100644 (file)
index 0000000..da151c6
--- /dev/null
@@ -0,0 +1,160 @@
+ardour {
+       ["type"] = "EditorAction",
+       name     = "Swing It (Rubberband)",
+       license  = "MIT",
+       author   = "Ardour Team",
+description = [[
+Create a 'swing feel' in selected regions.
+
+Analyze beat-position from the selected audio regions,
+then time-stretch the audio and move 8th notes back in
+time while keeping 1/4 note beats in place.
+
+(This script also servers as example for both VAMP
+analysis as well as Rubberband region stretching.)
+
+Kudos to Chris Cannam.
+]]
+}
+
+function factory () return function ()
+
+       -- helper function --
+       -- there is currently no direct way to find the track
+       -- corresponding to a [selected] region
+       function find_track_for_region (region_id)
+               for route in Session:get_tracks ():iter () do
+                       local track = route:to_track ()
+                       local pl = track:playlist ()
+                       if not pl:region_by_id (region_id):isnil () then
+                               return track
+                       end
+               end
+               assert (0) -- can't happen, region must be in a playlist
+       end
+
+       -- get Editor selection
+       -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Editor
+       -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection
+       local sel = Editor:get_selection ()
+
+       -- Instantiate the QM BarBeat Tracker
+       -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp
+       -- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-barbeattracker
+       local vamp = ARDOUR.LuaAPI.Vamp ("libardourvampplugins:qm-barbeattracker", Session:nominal_sample_rate ())
+
+       -- prepare undo operation
+       Session:begin_reversible_command ("Rubberband Regions")
+       local add_undo = false -- keep track if something has changed
+
+       -- for each selected region
+       -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection
+       for r in sel.regions:regionlist ():iter () do
+               -- "r" is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region
+
+               -- test if it's an audio region
+               local ar = r:to_audioregion ()
+               if ar:isnil () then
+                       goto next
+               end
+
+               -- create Rubberband stretcher
+               local rb = ARDOUR.LuaAPI.Rubberband (ar, false)
+
+               -- the rubberband-filter also implements the readable API to read from
+               -- the master-source of the given audio-region (ignoring any prior time-stretch
+               -- or pitch-shiting).
+               -- https://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable
+               local max_pos = rb:readable ():readable_length ()
+
+               -- prepare table to hold analysis results
+               -- the beat-map is a table holding audio-sample positions:
+               -- [from] = to
+               local beat_map = {}
+               local prev_beat = 0
+
+               local pdialog = LuaDialog.ProgressWindow ("Rubberband", true)
+
+               -- callback to handle Vamp-Plugin analysis results
+               function vamp_callback (_, pos)
+                       return pdialog:progress (pos / max_pos, "Analyzing")
+               end
+               function rb_progress (_, pos)
+                       return pdialog:progress (pos / max_pos, "Stretching")
+               end
+
+               -- run VAMP plugin, analyze the first channel of the audio-region
+               vamp:analyze (rb:readable (), 0, vamp_callback)
+               -- getRemainingFeatures returns a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet
+               -- get the first output. here: Beats, estimated beat locations & beat-number
+               -- "fl" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureList
+               local fl = vamp:plugin ():getRemainingFeatures ():at (0)
+               local beatcount = 0
+               -- iterate over returned features
+               for f in fl:iter () do
+                       -- "f" is-a  http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature
+                       local fn = Vamp.RealTime.realTime2Frame (f.timestamp, Session:nominal_sample_rate ())
+                       beat_map[fn] = fn -- keep beats (1/4 notes) unchanged
+                       if prev_beat > 0 then
+                               -- move the half beats (1/8th) back
+                               local diff = (fn - prev_beat) / 2
+                               beat_map[fn - diff] = fn - diff + diff / 3 -- moderate swing 2:1 (triplet)
+                               --beat_map[fn - diff] = fn - diff + diff / 2 -- hard swing 3:1 (dotted 8th)
+                               beatcount = beatcount + 1
+                       end
+                       prev_beat = fn
+               end
+               -- reset the plugin (prepare for next iteration)
+               vamp:reset ()
+
+               if pdialog:canceled () then goto out end
+
+               -- skip regions shorter than a bar
+               if beatcount < 8 then
+                       pdialog:done ()
+                       goto next
+               end
+
+               -- now stretch the region
+               rb:set_strech_and_pitch (1, 1)
+               rb:set_mapping (beat_map)
+
+               local nar = rb:process (rb_progress)
+
+               if pdialog:canceled () then goto out end
+
+               -- hide modal progress dialog and destroy it
+               pdialog:done ()
+               pdialog = nil
+
+               -- replace region
+               if not nar:isnil () then
+                       print ("new audio region: ", nar:name (), nar:length ())
+                       local track = find_track_for_region (r:to_stateful ():id ())
+                       local playlist = track:playlist ()
+                       playlist:to_stateful ():clear_changes () -- prepare undo
+                       playlist:remove_region (r)
+                       playlist:add_region (nar, r:position (), 1, false, 0, 0, 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
+               end
+
+               ::next::
+       end
+
+       ::out::
+
+       -- 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
+
+       vamp = nil
+       collectgarbage ()
+end end
diff --git a/scripts/_tempo_map_dump.lua b/scripts/_tempo_map_dump.lua
new file mode 100644 (file)
index 0000000..7e85cd5
--- /dev/null
@@ -0,0 +1,14 @@
+ardour { ["type"] = "Snippet", name = "Tempo Map Dump" }
+
+function factory () return function ()
+
+       local tm = Session:tempo_map ()
+       local ts = tm:tempo_section_at_sample (0)
+
+       while true do
+               print ("TS @", ts:sample(), " | ", ts:to_tempo():note_types_per_minute (), "..", ts:to_tempo():end_note_types_per_minute (), "bpm")
+               ts = tm:next_tempo_section (ts)
+               if not ts then break end
+       end
+
+end end