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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
ardour {
["type"] = "EditorAction",
name = "Swing It (Rubberband)",
license = "MIT",
author = "Ardour Team",
description = [[
Create a 'swing feel' in selected regions.
The beat position of selected audio regions is analyzed,
then the audio is time-stretched, moving 8th notes back in
time while keeping 1/4-note beats in place to produce
a rhythmic swing style.
(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.
-- https://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable
-- This allows to read from the master-source of the given audio-region.
-- Any prior time-stretch or pitch-shift are ignored when reading, however
-- processing retains the previous settings
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
-- construct a progress-dialog with cancle button
local pdialog = LuaDialog.ProgressWindow ("Rubberband", true)
-- progress dialog callbacks
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 state (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
-- configure rubberband stretch tool
rb:set_strech_and_pitch (1, 1) -- no overall stretching, no pitch-shift
rb:set_mapping (beat_map) -- apply beat-map from/to
-- now stretch the region
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
end end
function icon (params) return function (ctx, width, height, fg)
local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(width * .7) .. "px")
txt:set_text ("\u{266b}\u{266a}") -- 8th note symbols
local tw, th = txt:get_pixel_size ()
ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
ctx:move_to (.5 * (width - tw), .5 * (height - th))
txt:show_in_cairo_context (ctx)
end end
|