]> Shamusworld >> Repos - ardour-manual/blob - include/lua-scripting.html
Edit pages to reflect jackd not being essential
[ardour-manual] / include / lua-scripting.html
1
2 <p>
3 Starting with version 4.7.213, Ardour supports Lua scripts.
4 </p>
5
6
7 <p class="warning">
8 This Documentation is Work in Progress and far from complete. Also the documented API may be subject to change.
9 </p>
10
11 <h2 id="Preface">Preface</h2>
12 <p>
13 There are cases that Ardour cannot reasonably cater to with core functionality alone, either because they're session specific
14 or user specific edge cases.
15 </p><p>
16 Examples for these include voice-activate (record-arm specific tracks and roll transport depending on signal levels),
17 rename all regions after a specific timecode, launch an external application when a certain track is soloed, generate
18 automation curves or simply provide a quick shortcut for a custom batch operation.
19 </p><p>
20 Cases like this call for means to extend the DAW without actually changing the DAW itself. This is where scripting comes in.
21 </p><p>
22 "Scripting" refers to tasks that could alternatively be executed step-by-step by a human operator.
23 </p><p>
24 Lua is a tiny and simple language which is easy to learn, yet allows for comprehensive solutions.
25 Lua is also a glue language it allows to tie existing component in Ardour together in unprecedented ways,
26 and most importantly Lua is one of the few scripting-languages which can be safely used in a real-time environment.
27 </p><p>
28 A good introduction to Lua is the book <a href="http://www.lua.org/pil/">Programming in Lua</a>. The first edition is
29 available online, but if you have the means buy a copy of the book, it not only helps to support the Lua project, but provides
30 for a much nicer reading and learning experience.
31 </p>
32
33 <h2 id="Overview">Overview</h2>
34 <p>
35 The core of Ardour is a real-time audio engine that runs and processes audio. One interfaces with an engine by sending it
36 commands. Scripting can be used to interact with or modify the active Ardour session, just like a user uses the Editor/Mixer
37 GUI to modify the state or parameters of the session.
38 </p><p>
39 Doing this programmatically requires some knowledge about the objects used internally.
40 Most Ardour C++ objects and their methods are directly exposed to Lua and one can call functions or modify variables:
41 </p>
42
43 <div style="width:80%; margin:.5em auto;">
44         <div style="width:45%; float:left;">
45                 C++<br/>
46                 <code class="cxx">
47                         session-&gt;set_transport_speed (1.0);
48                 </code>
49         </div>
50         <div style="width:45%; float:right;">
51                 Lua<br/>
52                 <code class="lua">
53                         Session:set_transport_speed (1.0)
54                 </code>
55         </div>
56 </div>
57 <div style="clear:both;"></div>
58
59 <p>
60 You may notice that there is only a small syntactic difference in this case.
61 While C++ requires recompiling the application for every change, Lua script can be loaded, written or modified while the
62 application is running. Lua also abstracts away many of the C++ complexities such as object lifetime, type conversion and
63 null-pointer checks.
64 </p><p>
65 Close ties with the underlying C++ components is where the power of scripting comes from.
66 A script can orchestrate interaction of lower-level components which take the bulk of the CPU time of the final program.
67 </p>
68 </p><p>
69 At the time of writing Ardour integrates Lua 5.3.2: <a href="http://www.lua.org/manual/5.3/manual.html">Lua 5.3 reference
70 manual</a>.
71 </p>
72
73 <h2 id="Integration">Integration</h2>
74 <p>
75 Like Control surfaces and the GUI, Lua Scripts are confined to certain aspects of the program. Ardour provides the framework
76 and runs Lua (not the other way around).
77 </p>
78 <p>
79 In Ardour's case Lua is available:
80 </p>
81
82 <table class="dl">
83         <tr><th>Editor Action Scripts</th><td>User initiated actions (menu, shortcuts) for batch processing</td></tr>
84         <tr><th>Editor Hooks/Callbacks</th><td>Event triggered actions for the Editor/Mixer GUI</td></tr>
85         <tr><th>Session Scripts</th><td>Scripts called at the start of every audio cycle (session, real-time)</td></tr>
86         <tr><th>DSP Scripts</th><td>Audio/Midi processor - plugins with access to the Ardour session (per track/bus, real-time)</td></tr>
87         <tr><th>Script Console</th><td>Action Script commandline</td></tr>
88 </table>
89
90 <p>
91 There are is also a special mode:
92 </p>
93 <table class="dl">
94         <tr><th>Commandline Tool</th><td>Replaces the complete Editor GUI, direct access to libardour (no GUI) from the
95         commandline.<br/>
96         <em>Be aware that the vast majority of complex functionality is provided by the Editor UI.</em></td></tr>
97 </table>
98
99 <h2 id="Managing Scripts">Managing Scripts</h2>
100
101 <p>
102 Ardour searches for Lua scripts in the <code>scripts</code> folder in <code>$ARDOUR_DATA_PATH</code>,
103 Apart from scripts included directly with Ardour, this includes</p>
104 <table>
105         <tr><th>GNU/Linux</th><td><code>$HOME/.config/ardour5/scripts</code></td></tr>
106         <tr><th>Mac OS X</th><td><code>$HOME/Library/Preferences/Ardour5/scripts</code></td></tr>
107         <tr><th>Windows</th><td><code>%localappdata%\ardour5\scripts</code></td></tr>
108 </table>
109
110 <p>Files must end with <code>.lua</code> file extension.</p>
111
112 <p>Scripts are managed via the GUI</p>
113 <table class="dl">
114         <tr><th>Editor Action Scripts</th><td>Menu &rarr; Edit &rarr; Scripted Actions &rarr; Manage</td></tr>
115         <tr><th>Editor Hooks/Callbacks</th><td>Menu &rarr; Edit &rarr; Scripted Actions &rarr; Manage</td></tr>
116         <tr><th>Session Scripts</th><td>Menu &rarr; Session &rarr; Scripting &rarr; Add/Remove Script</td></tr>
117         <tr><th>DSP Scripts</th><td>Mixer-strip &rarr; context menu (right click) &rarr; New Lua Proc</td></tr>
118         <tr><th>Script Console</th><td>Menu &rarr; Window &rarr; Scripting</td></tr>
119 </table>
120
121 <h2 id="Script Layout">Script Layout</h2>
122 <ul>
123         <li>Every script must include an <code>ardour</code> descriptor table. Required fields are "Name" and "Type".</li>
124         <li>A script must provide a <em>Factory method</em>: A function with optional instantiation parameters which returns the
125         actual script.</li>
126         <li>[optional]: list of parameters for the "factory".</li>
127         <li>in case of DSP scripts, an optional list of automatable parameters and possible audio/midi port configurations, and a
128         <code>dsp_run</code> function, more on that later.</li>
129 </ul>
130
131
132 <p>A minimal example script looks like:</p>
133 <div>
134 <pre><code class="lua">
135         ardour {
136           ["type"]    = "EditorAction",
137           name        = "Rewind",
138         }
139
140         function factory (unused_params)
141           return function ()
142            Session:goto_start()  -- rewind the transport
143           end
144         end
145 </code></pre>
146 </div>
147
148 <p>
149 The common part for all scripts is the "Descriptor". It's a Lua function which returns a table (key/values) with the following
150 keys (the keys are case-sensitive):
151 </p>
152 <table class="dl">
153         <tr><th>type [required]</th><td>one of "<code>DSP</code>", "<code>Session</code>", "<code>EditorHook</code>",
154         "<code>EditorAction</code>" (the type is not case-sensitive)</td></tr>
155         <tr><th>name [required]</th><td>Name/Title of the script</td></tr>
156         <tr><th>author</th><td>Your Name</td></tr>
157         <tr><th>license</th><td>The license of the script (e.g. "GPL" or "MIT")</td></tr>
158         <tr><th>description</th><td>A longer text explaining to the user what the script does</td></tr>
159 </table>
160
161 <p class="note">
162 Scripts that come with Ardour (currently mostly examples) can be found in the
163 <a href="https://github.com/Ardour/ardour/tree/master/scripts">Source Tree</a>.
164 </p>
165
166 <h3 id="Action Scripts">Action Scripts</h3>
167 <p>Action scripts are the simplest form. An anonymous Lua function is called whenever the action is triggered. A simple action
168 script is shown above.</p>
169 <p>There are 10 action script slots available, each of which is a standard GUI action available from the menu and hence can be
170 bound to a keyboard shortcut.</p>
171
172 <h3 id="Session Scripts">Session Scripts</h3>
173 <p>Session scripts similar to Actions Scripts, except the anonymous function is called periodically every process cycle.
174 The function receives a single parameter - the number of audio samples which are processed in the given cycle</p>
175 <div>
176 <pre><code class="lua">
177 ardour {
178   ["type"]    = "session",
179   name        = "Example Session Script",
180   description = [[
181   An Example Ardour Session Script.
182   This example stops the transport after rolling for a specific time.]]
183 }
184
185
186 -- instantiation options, these are passed to the "factory" method below
187 function sess_params ()
188   return
189   {
190     ["print"]  = { title = "Debug Print (yes/no)", default = "no", optional = true },
191     ["time"] = { title = "Timeout (sec)", default = "90", optional = false },
192   }
193 end
194
195 function factory (params)
196   return function (n_samples)
197     local p = params["print"] or "no"
198     local timeout = params["time"] or 90
199     a = a or 0
200     if p ~= "no" then print (a, n_samples, Session:frame_rate (), Session:transport_rolling ()) end -- debug output (not rt safe)
201     if (not Session:transport_rolling()) then
202       a = 0
203       return
204     end
205     a = a + n_samples
206     if (a &gt; timeout * Session:frame_rate()) then
207       Session:request_transport_speed(0.0, true)
208     end
209   end
210 end
211 </code></pre>
212 </div>
213
214 <h3 id="Action Hooks">Action Hooks</h3>
215 <p>Action hook scripts must define an additional function which returns a <em>Set</em> of Signal that which trigger the
216 callback (documenting available slots and their parameters remains to be done).</p>
217 <div>
218 <pre><code class="lua">
219 ardour {
220   ["type"]    = "EditorHook",
221   name        = "Hook Example",
222   description = "Rewind On Solo Change, Write a file when regions are moved.",
223 }
224
225 function signals ()
226   s = LuaSignal.Set()
227   s:add (
228     {
229       [LuaSignal.SoloActive] = true,
230       [LuaSignal.RegionPropertyChanged] = true
231     }
232   )
233   return s
234 end
235
236 function factory (params)
237   return function (signal, ref, ...)
238     -- print (signal, ref, ...)
239
240     if (signal == LuaSignal.SoloActive) then
241       Session:goto_start()
242     end
243
244     if (signal == LuaSignal.RegionPropertyChanged) then
245       obj,pch = ...
246       file = io.open ("/tmp/test" ,"a")
247       io.output (file
248       io.write (string.format ("Region: '%s' pos-changed: %s, length-changed: %s\n",
249         obj:name (),
250         tostring (pch:containsFramePos (ARDOUR.Properties.Start)),
251         tostring (pch:containsFramePos (ARDOUR.Properties.Length))
252         ))
253       io.close (file)
254     end
255   end
256 end
257 </code></pre>
258 </div>
259
260 <h3 id="DSP Scripts">DSP Scripts</h3>
261 <p>See the scripts folder for examples for now.</p>
262 <p>Some notes for further doc:</p>
263 <ul>
264         <li>required function: <code>dsp_ioconfig ()</code>: return a list of possible audio I/O configurations - follows Audio
265         Unit conventions.</li>
266         <li>optional function: <code>dsp_dsp_midi_input ()</code>: return true if the plugin can receive midi input</li>
267         <li>optional function: <code>dsp_params ()</code>: return a table of possible parameters (automatable)</li>
268         <li>optional function: <code>dsp_init (samplerate)</code>: called when instantiation the plugin with given samplerate.</li>
269         <li>optional function: <code>dsp_configure (in, out)</code>: called after instantiation with configured plugin i/o.</li>
270         <li>required function: <code>dsp_run (ins, outs, n_samples)</code> OR <code>dsp_runmap (bufs, in_map, out_map, n_samples,
271         offset)</code>: DSP process callback. The former is a convenient abstraction that passes mapped buffers (as table). The
272         latter is a direct pass-through matching Ardour's internal <code>::connect_and_run()</code> API, which requires the caller
273         to map and offset raw buffers.</li>
274         <li>plugin parameters are handled via the global variable <code>CtrlPorts</code>.</li>
275         <li>midi data is passed via the global variable <code>mididata</code> which is valid during <code>dsp_run</code> only.
276         (dsp_runmap requires the script to pass raw data from the buffers according to in_map)</li>
277         <li>The script has access to the current session via the global variable Session, but access to the session methods are
278         limited to realtime safe functions</li>
279 </ul>
280
281 <h2 id="Accessing Ardour Objects">Accessing Ardour Objects</h2>
282 <p>
283 The top most object in Ardour is the <code>ARDOUR::Session</code>.
284 Fundamentally, a Session is just a collection of other things:
285 Routes (tracks, busses), Sources (Audio/Midi), Regions, Playlists, Locations, Tempo map, Undo/Redo history, Ports, Transport
286 state and controls, etc.
287 </p><p>
288 Every Lua interpreter can access it via the global variable <code>Session</code>.
289 </p><p>
290 GUI context interpreters also have an additional object in the global environment: The Ardour <code>Editor</code>. The Editor
291 provides access to high level functionality which is otherwise triggered via GUI interaction such as undo/redo, open/close
292 windows, select objects, drag/move regions. It also holds the current UI state: snap-mode, zoom-range, etc.
293 The Editor also provides complex operations such as "import audio" which under the hood, creates a new Track, adds a new
294 Source Objects (for every channel) with optional resampling, creates both playlist and regions and loads the region onto the
295 Track all the while displaying a progress information to the user.
296 </p>
297
298 <p class="note">
299 Documenting the bound C++ methods and class hierarchy is somewhere on the ToDo list.
300 Meanwhile <a href="https://github.com/Ardour/ardour/blob/master/libs/ardour/luabindings.cc">luabindings.cc</a> is the best we
301 can offer.
302 </p>
303
304 <h2 id="Concepts">Concepts</h2>
305 <ul>
306         <li>There are no bound constructors: Lua asks Ardour to create objects (e.g. add a new track), then receives a reference
307         to the object to modify it.</li>
308         <li>Scripts, once loaded, are saved with the Session (no reference to external files). This provides for portable
309         Sessions.</li>
310         <li>Lua Scripts are never executed directly. They provide a "factory" method which can have optional instantiation
311         parameters, which returns a Lua closure.</li>
312         <li>No external Lua modules/libraries can be used, scripts need to be self contained (portable across different systems
313         (libs written in Lua can be used, and important c-libs/functions can be included with Ardour if needed).</li>
314 </ul>
315 <p>
316 Ardour is a highly multithreaded application and interaction between the different threads, particularly real-time threads,
317 needs to to be done with care. This part has been abstracted away by providing separate Lua interpreters in different contexts
318 and restricting available interaction:
319 </p>
320 <ul>
321         <li>Editor Actions run in a single instance interpreter in the GUI thread.</li>
322         <li>Editor Hooks connect to libardour signals. Every Callback uses a dedicated Lua interpreter which is in the GUI thread
323         context.</li>
324         <li>All Session scripts run in a single instance in the main real-time thread (audio callback)</li>
325         <li>DSP scripts have a separate instance per script and run in one of the DSP threads.</li>
326 </ul>
327 <p>
328 The available interfaces differ between contexts. For example, it is not possible to create new tracks or import audio from
329 real-time context; while it is not possible to modify audio buffers from the GUI thread.
330 </p>
331
332 <h2 id="Current State">Current State</h2>
333 Fully functional, yet still in a prototyping stage:
334
335 <ul>
336         <li>The GUI to add/configure scripts is rather minimalistic.</li>
337         <li>The interfaces may change (particularly DSP, and Session script <code>run()</code>.</li>
338         <li>Further planned work includes:
339                 <ul>
340                         <li>Built-in Script editor (customize/modify Scripts in-place)</li>
341                         <li>convenience methods (wrap more complex Ardour actions into a library). e.g set plugin parameters, write
342                         automation lists from a Lua table</li>
343                         <li>Add some useful scripts and more examples</li>
344                         <li>Documentation (Ardour API), also usable for tab-expansion, syntax highlighting</li>
345                         <li>bindings for GUI Widgets (plugin UIs, message boxes, etc)</li>
346                 </ul>
347                 <li>
348 </ul>
349
350
351 <h2 id="Examples">Examples</h2>
352 <p>Apart from the <a href="https://github.com/Ardour/ardour/tree/master/scripts">scripts included with the source-code</a>
353 here are a few examples without further comments...
354
355 <h3 id="Editor Console Examples">Editor Console Examples</h3>
356 <div>
357 <pre><code class="lua">
358 print (Session:route_by_remote_id(1):name())
359
360 a = Session:route_by_remote_id(1);
361 print (a:name());
362
363 print(Session:get_tracks():size())
364
365 for i, v in ipairs(Session:unknown_processors():table()) do print(v) end
366 for i, v in ipairs(Session:get_tracks():table()) do print(v:name()) end
367
368 for t in Session:get_tracks():iter() do print(t:name()) end
369 for r in Session:get_routes():iter() do print(r:name()) end
370
371
372 Session:tempo_map():add_tempo(ARDOUR.Tempo(100,4), Timecode.BBT_TIME(4,1,0))
373
374
375 Editor:set_zoom_focus(Editing.ZoomFocusRight)
376 print(Editing.ZoomFocusRight);
377 Editor:set_zoom_focus(1)
378
379
380 files = C.StringVector();
381 files:push_back("/home/rgareus/data/coding/ltc-tools/smpte.wav")
382 pos = -1
383 Editor:do_import(files, Editing.ImportDistinctFiles, Editing.ImportAsTrack, ARDOUR.SrcQuality.SrcBest, pos, ARDOUR.PluginInfo())
384
385 #or in one line:
386 Editor:do_import(C.StringVector():add({"/path/to/file.wav"}), Editing.ImportDistinctFiles, Editing.ImportAsTrack, ARDOUR.SrcQuality.SrcBest, -1, ARDOUR.PluginInfo())
387
388 # called when a new session is loaded:
389 function new_session (name) print("NEW SESSION:", name) end
390
391
392 # read/set/describe a plugin parameter
393 route = Session:route_by_remote_id(1)
394 processor = route:nth_plugin(0)
395 plugininsert = processor:to_insert()
396
397 plugin = plugininsert:plugin(0)
398 print (plugin:label())
399 print (plugin:parameter_count())
400
401 x = ARDOUR.ParameterDescriptor ()
402 _, t = plugin:get_parameter_descriptor(2, x) -- port #2
403 paramdesc = t[2]
404 print (paramdesc.lower)
405
406 ctrl = Evoral.Parameter(ARDOUR.AutomationType.PluginAutomation, 0, 2)
407 ac = plugininsert:automation_control(ctrl, false)
408 print (ac:get_value ())
409 ac:set_value(1.0, PBD.GroupControlDisposition.NoGroup)
410
411 # the same using a convenience wrapper:
412 route = Session:route_by_remote_id(1)
413 proc = t:nth_plugin (i)
414 ARDOUR.LuaAPI.set_processor_param (proc, 2, 1.0)
415
416 </code></pre>
417 </div>
418
419 <h3 id="Commandline Session">Commandline Session</h3>
420 <p>The standalone tool <code>luasession</code> allows one to access an Ardour session directly from the commandline.
421 Interaction is limited by the fact that most actions in Ardour are provided by the Editor GUI.
422 </p><p>
423 <code>luasession</code> provides only two special functions <code>load_session</code> and <code>close_session</code> and
424 exposes the <code>AudioEngine</code> instance as global variable.
425 </p>
426
427 <div>
428 <pre><code class="lua">
429 for i,_ in AudioEngine:available_backends():iter() do print (i.name) end
430
431 backend = AudioEngine:set_backend("ALSA", "", "")
432 print (AudioEngine:current_backend_name())
433
434 for i,_ in backend:enumerate_devices():iter() do print (i.name) end
435
436 backend:set_input_device_name("HDA Intel PCH")
437 backend:set_output_device_name("HDA Intel PCH")
438
439 print (backend:buffer_size())
440 print (AudioEngine:get_last_backend_error())
441
442 s = load_session ("/home/rgareus/Documents/ArdourSessions/lua2/", "lua2")
443 s:request_transport_speed (1.0)
444 print (s:transport_rolling())
445 s:goto_start()
446 close_session()
447
448 </code></pre>
449 </div>
450