Also move Lua scripts to share subfolder

This commit is contained in:
Robin Gareus 2020-02-23 20:48:02 +01:00
parent bf649cd68a
commit 180843f9bd
No known key found for this signature in database
GPG key ID: A090BCE02CF57F04
129 changed files with 2 additions and 2 deletions

View file

@ -1,396 +0,0 @@
ardour {
["type"] = "dsp",
name = "a-High/Low Pass Filter",
category = "Filter",
license = "GPLv2",
author = "Ardour Team",
description = [[High and Low Pass Filter with de-zipped controls, written in Ardour-Lua]]
}
function dsp_ioconfig ()
return
{
-- allow any number of I/O as long as port-count matches
{ audio_in = -1, audio_out = -1},
}
end
function dsp_params ()
return
{
{ ["type"] = "input", name = "High Pass Steepness", min = 0, max = 4, default = 1, enum = true, scalepoints =
{
["Off"] = 0,
["12dB/oct"] = 1,
["24dB/oct"] = 2,
["36dB/oct"] = 3,
["48dB/oct"] = 4,
}
},
{ ["type"] = "input", name = "High Pass Cut off frequency", min = 5, max = 20000, default = 100, unit="Hz", logarithmic = true },
{ ["type"] = "input", name = "High Pass Resonance", min = 0.1, max = 6, default = .707, logarithmic = true },
{ ["type"] = "input", name = "Low Pass Steepness", min = 0, max = 4, default = 1, enum = true, scalepoints =
{
["Off"] = 0,
["12dB/oct"] = 1,
["24dB/oct"] = 2,
["36dB/oct"] = 3,
["48dB/oct"] = 4,
}
},
{ ["type"] = "input", name = "Low Pass Cut off frequency", min = 20, max = 20000, default = 18000, unit="Hz", logarithmic = true },
{ ["type"] = "input", name = "Low Pass Resonance", min = 0.1, max = 6, default = .707, logarithmic = true },
}
end
-- these globals are *not* shared between DSP and UI
local hp = {} -- the biquad high-pass filter instances (DSP)
local lp = {} -- the biquad high-pass filter instances (DSP)
local filt = nil -- the biquad filter instance (GUI, response)
local cur = {0, 0, 0, 0, 0, 0} -- current parameters
local lpf = 0.03 -- parameter low-pass filter time-constant
local chn = 0 -- channel/filter count
local lpf_chunk = 0 -- chunk size for audio processing when interpolating parameters
local max_freq = 20000
local mem = nil -- memory x-fade buffer
function dsp_init (rate)
-- allocate some mix-buffer
mem = ARDOUR.DSP.DspShm (8192)
-- max allowed cut-off frequency
max_freq = .499 * rate
-- create a table of objects to share with the GUI
local tbl = {}
tbl['samplerate'] = rate
tbl['max_freq'] = max_freq
self:table ():set (tbl)
-- Parameter smoothing: we want to filter out parameter changes that are
-- faster than 15Hz, and interpolate between parameter values.
-- For performance reasons, we want to ensure that two consecutive values
-- of the interpolated "steepness" are less that 1 apart. By choosing the
-- interpolation chunk size to be 64 in most cases, but 32 if the rate is
-- strictly less than 22kHz (there's only 8kHz in standard rates), we can
-- ensure that steepness interpolation will never change the parameter by
-- more than ~0.86.
lpf_chunk = 64
if rate < 22000 then lpf_chunk = 32 end
-- We apply a discrete version of the standard RC low-pass, with a cutoff
-- frequency of 15Hz. For more information about the underlying math, see
-- https://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization
-- (here Δt is lpf_chunk / rate)
local R = 2 * math.pi * lpf_chunk * 15 -- Hz
lpf = R / (R + rate)
end
function dsp_configure (ins, outs)
assert (ins:n_audio () == outs:n_audio ())
local tbl = self:table ():get () -- get shared memory table
chn = ins:n_audio ()
cur = {0, 0, 0, 0, 0, 0}
hp = {}
lp = {}
collectgarbage ()
for c = 1, chn do
hp[c] = {}
lp[c] = {}
-- initialize filters
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:Biquad
-- A different Biquad is needed for each pass and channel because they
-- remember the last two samples seen during the last call of Biquad:run().
-- For continuity these have to come from the previous audio chunk of the
-- same channel and pass and would be clobbered if the same Biquad was
-- called several times by cycle.
for k = 1,4 do
hp[c][k] = ARDOUR.DSP.Biquad (tbl['samplerate'])
lp[c][k] = ARDOUR.DSP.Biquad (tbl['samplerate'])
end
end
end
function santize_params (ctrl)
-- don't allow manual cross-fades. enforce enums
ctrl[1] = math.floor(ctrl[1] + .5)
ctrl[4] = math.floor(ctrl[4] + .5)
-- high pass, clamp range
ctrl[2] = math.min (max_freq, math.max (5, ctrl[2]))
ctrl[3] = math.min (6, math.max (0.1, ctrl[3]))
-- low pass, clamp range
ctrl[5] = math.min (max_freq, math.max (20, ctrl[5]))
ctrl[6] = math.min (6, math.max (0.1, ctrl[6]))
return ctrl
end
-- helper functions for parameter interpolation
function param_changed (ctrl)
for p = 1,6 do
if ctrl[p] ~= cur[p] then
return true
end
end
return false
end
function low_pass_filter_param (old, new, limit)
if math.abs (old - new) < limit then
return new
else
return old + lpf * (new - old)
end
end
-- apply parameters, re-compute filter coefficients if needed
function apply_params (ctrl)
if not param_changed (ctrl) then
return
end
-- low-pass filter ctrl parameter values, smooth transition
cur[1] = low_pass_filter_param (cur[1], ctrl[1], 0.05) -- HP order x-fade
cur[2] = low_pass_filter_param (cur[2], ctrl[2], 1.0) -- HP freq/Hz
cur[3] = low_pass_filter_param (cur[3], ctrl[3], 0.01) -- HP quality
cur[4] = low_pass_filter_param (cur[4], ctrl[4], 0.05) -- LP order x-fade
cur[5] = low_pass_filter_param (cur[5], ctrl[5], 1.0) -- LP freq/Hz
cur[6] = low_pass_filter_param (cur[6], ctrl[6], 0.01) -- LP quality
for c = 1, chn do
for k = 1,4 do
hp[c][k]:compute (ARDOUR.DSP.BiquadType.HighPass, cur[2], cur[3], 0)
lp[c][k]:compute (ARDOUR.DSP.BiquadType.LowPass, cur[5], cur[6], 0)
end
end
end
-- the actual DSP callback
function dsp_run (ins, outs, n_samples)
assert (n_samples <= 8192)
assert (#ins == chn)
local ctrl = santize_params (CtrlPorts:array ())
local changed = false
local siz = n_samples
local off = 0
-- if a parameter was changed, process at most lpf_chunk samples
-- at a time and interpolate parameters until the current settings
-- match the target values
if param_changed (ctrl) then
changed = true
siz = lpf_chunk
end
while n_samples > 0 do
if changed then apply_params (ctrl) end
if siz > n_samples then siz = n_samples end
local ho = math.floor(cur[1])
local lo = math.floor(cur[4])
-- process all channels
for c = 1, #ins do
-- High Pass
local xfade = cur[1] - ho
-- prepare scratch memory
ARDOUR.DSP.copy_vector (mem:to_float (off), ins[c]:offset (off), siz)
-- run at least |ho| biquads...
for k = 1,ho do
hp[c][k]:run (mem:to_float (off), siz)
end
ARDOUR.DSP.copy_vector (outs[c]:offset (off), mem:to_float (off), siz)
-- mix the output of |ho| biquads (with weight |1-xfade|)
-- with the output of |ho+1| biquads (with weight |xfade|)
if xfade > 0 then
ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, 1 - xfade)
hp[c][ho+1]:run (mem:to_float (off), siz)
ARDOUR.DSP.mix_buffers_with_gain (outs[c]:offset (off), mem:to_float (off), siz, xfade)
-- also run the next biquad because it needs to have the correct state
-- in case it start affecting the next chunck of output. Higher order
-- ones are guaranteed not to be needed for the next run because the
-- interpolated order won't increase more than 0.86 in one step thanks
-- to the choice of the value of |lpf|.
if ho + 2 <= 4 then hp[c][ho+2]:run (mem:to_float (off), siz) end
elseif ho + 1 <= 4 then
-- run the next biquad in case it is used next chunk
hp[c][ho+1]:run (mem:to_float (off), siz)
end
-- Low Pass
xfade = cur[4] - lo
-- prepare scratch memory (from high pass output)
ARDOUR.DSP.copy_vector (mem:to_float (off), outs[c]:offset (off), siz)
-- run at least |lo| biquads...
for k = 1,lo do
lp[c][k]:run (mem:to_float (off), siz)
end
ARDOUR.DSP.copy_vector (outs[c]:offset (off), mem:to_float (off), siz)
-- mix the output of |lo| biquads (with weight |1-xfade|)
-- with the output of |lo+1| biquads (with weight |xfade|)
if xfade > 0 then
ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, 1 - xfade)
lp[c][lo+1]:run (mem:to_float (off), siz)
ARDOUR.DSP.mix_buffers_with_gain (outs[c]:offset (off), mem:to_float (off), siz, xfade)
-- also run the next biquad in case it start affecting the next
-- chunck of output.
if lo + 2 <= 4 then lp[c][lo+2]:run (mem:to_float (off), siz) end
elseif lo + 1 <= 4 then
-- run the next biquad in case it is used next chunk
lp[c][lo+1]:run (mem:to_float (off), siz)
end
end
n_samples = n_samples - siz
off = off + siz
end
if changed then
-- notify display
self:queue_draw ()
end
end
-------------------------------------------------------------------------------
--- inline display
function round (n)
return math.floor (n + .5)
end
function freq_at_x (x, w)
-- frequency in Hz at given x-axis pixel
return 20 * 1000 ^ (x / w)
end
function x_at_freq (f, w)
-- x-axis pixel for given frequency, power-scale
return w * math.log (f / 20.0) / math.log (1000.0)
end
function db_to_y (db, h)
-- y-axis gain mapping
if db < -60 then db = -60 end
if db > 12 then db = 12 end
return -.5 + round (0.2 * h) - h * db / 60
end
function grid_db (ctx, w, h, db)
-- draw horizontal grid line
-- note that a cairo pixel at Y spans [Y - 0.5 to Y + 0.5]
local y = -.5 + round (db_to_y (db, h))
ctx:move_to (0, y)
ctx:line_to (w, y)
ctx:stroke ()
end
function grid_freq (ctx, w, h, f)
-- draw vertical grid line
local x = -.5 + round (x_at_freq (f, w))
ctx:move_to (x, 0)
ctx:line_to (x, h)
ctx:stroke ()
end
function response (ho, lo, f)
-- calculate transfer function response for given
-- hi/po pass order at given frequency [Hz]
local db = ho * filt['hp']:dB_at_freq (f)
return db + lo * filt['lp']:dB_at_freq (f)
end
function render_inline (ctx, w, max_h)
if not filt then
local tbl = self:table ():get () -- get shared memory table
-- instantiate filter (to calculate the transfer function's response)
filt = {}
filt['hp'] = ARDOUR.DSP.Biquad (tbl['samplerate'])
filt['lp'] = ARDOUR.DSP.Biquad (tbl['samplerate'])
max_freq = tbl['max_freq']
end
local ctrl = santize_params (CtrlPorts:array ())
-- set filter coefficients if they have changed
if param_changed (ctrl) then
for k = 1,6 do cur[k] = ctrl[k] end
filt['hp']:compute (ARDOUR.DSP.BiquadType.HighPass, cur[2], cur[3], 0)
filt['lp']:compute (ARDOUR.DSP.BiquadType.LowPass, cur[5], cur[6], 0)
end
-- calc height of inline display
local h = 1 | math.ceil (w * 9 / 16) -- use 16:9 aspect, odd number of y pixels
if (h > max_h) then h = max_h end -- but at most max-height
-- ctx is a http://cairographics.org/ context
-- http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context
-- clear background
ctx:rectangle (0, 0, w, h)
ctx:set_source_rgba (.2, .2, .2, 1.0)
ctx:fill ()
ctx:rectangle (0, 0, w, h)
ctx:clip ()
-- set line width: 1px
ctx:set_line_width (1.0)
-- draw grid
local dash3 = C.DoubleVector ()
local dash2 = C.DoubleVector ()
dash2:add ({1, 2})
dash3:add ({1, 3})
ctx:set_dash (dash2, 2) -- dotted line: 1 pixel 2 space
ctx:set_source_rgba (.5, .5, .5, .8)
grid_db (ctx, w, h, 0)
ctx:set_dash (dash3, 2) -- dashed line: 1 pixel 3 space
ctx:set_source_rgba (.5, .5, .5, .5)
grid_db (ctx, w, h, -12)
grid_db (ctx, w, h, -24)
grid_db (ctx, w, h, -36)
grid_freq (ctx, w, h, 100)
grid_freq (ctx, w, h, 1000)
grid_freq (ctx, w, h, 10000)
ctx:unset_dash ()
-- draw transfer function line
local ho = math.floor(cur[1])
local lo = math.floor(cur[4])
ctx:set_source_rgba (.8, .8, .8, 1.0)
ctx:move_to (-.5, db_to_y (response(ho, lo, freq_at_x (0, w)), h))
for x = 1,w do
local db = response(ho, lo, freq_at_x (x, w))
ctx:line_to (-.5 + x, db_to_y (db, h))
end
-- stoke a line, keep the path
ctx:stroke_preserve ()
-- fill area to zero under the curve
ctx:line_to (w, -.5 + round (db_to_y (0, h)))
ctx:line_to (0, -.5 + round (db_to_y (0, h)))
ctx:close_path ()
ctx:set_source_rgba (.5, .5, .5, .5)
ctx:fill ()
return {w, h}
end

View file

@ -1,29 +0,0 @@
Ardour Lua Scripts
==================
https://manual.ardour.org/lua-scripting/
For upstream script addition, please file a pull-request at
https://github.com/Ardour/ardour/tree/master/scripts
Script Naming conventions:
^_
A script filename with a leading underscore indicates an example script.
These scripts are only available from ardour's git repository and not
installed nor included with binary bundles.
^__
Scripts with a filename starting with two underscores are excluded from
unit-tests. This is currently the case for convolver, fluidsynth and
plugin-modulation.
They depend on external files (soundfont, impulse-response) or a specific
session-setup (plugin-modulation needs an automatable plugin).
^s_
A filename beginning with "s_" indicates a code snippet.
These scripts can only be used in the interactive interpreter
(Window > Scripting). They may be useful by themselves or handy for copy/edit
to create EditorActions.
The filename prefix is only for convenience, "type" = "Snippet" is used when
scripts are listed at runtime.

View file

@ -1,59 +0,0 @@
ardour { ["type"] = "dsp", name = "Lua Convolver", license = "MIT", author = "Ardour Lua Task Force", description = [[Another simple DSP example]] }
function dsp_ioconfig () return
{
{ audio_in = 1, audio_out = 1},
{ audio_in = 1, audio_out = 2},
{ audio_in = 2, audio_out = 2},
}
end
local conv, mode, ir_file
ir_file = "/tmp/reverbs/St Nicolaes Church.wav"
ir_file = "/tmp/reverbs/Large Wide Echo Hall.wav"
function dsp_configure (ins, outs)
if outs:n_audio() == 1 then
assert (ins:n_audio() == 1)
mode = ARDOUR.DSP.IRChannelConfig.Mono
elseif ins:n_audio() == 1 then
assert (outs:n_audio() == 2)
mode = ARDOUR.DSP.IRChannelConfig.MonoToStereo
else
assert (ins:n_audio() == 2)
assert (outs:n_audio() == 2)
mode = ARDOUR.DSP.IRChannelConfig.Stereo
end
local irs = ARDOUR.DSP.IRSettings()
irs.gain = 0.5
conv = ARDOUR.DSP.Convolver (Session, ir_file, mode, irs)
collectgarbage ()
end
function dsp_latency ()
if conv then
return conv:latency()
else
return 0
end
end
-- the DSP callback function to process audio audio
-- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
function dsp_run (ins, outs, n_samples)
assert (#ins <= #outs)
for c = 1, #ins do
if ins[c] ~= outs[c] then -- if processing is not in-place..
ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) -- ..copy data from input to output.
end
end
if #outs == 1 then
conv:run_mono (outs[1], n_samples)
else
conv:run_stereo (outs[1], outs[2], n_samples)
end
end

View file

@ -1,51 +0,0 @@
ardour {
["type"] = "dsp",
name = "Lua Fluid Synth",
category = "Instrument",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[An Example Synth for Prototyping.]]
}
function dsp_ioconfig ()
return
{
{ midi_in = 1, audio_in = 0, audio_out = 2},
}
end
fluidsynth = nil
function dsp_init (rate)
fluidsynth = ARDOUR.FluidSynth (rate, 32)
assert (fluidsynth:load_sf2 ("/usr/share/sounds/sf2/FluidR3_GM.sf2"))
end
function dsp_run (ins, outs, n_samples)
local tme = 1
assert (#outs == 2)
-- parse midi messages
assert (type(midiin) == "table") -- global table of midi events (for now)
for _, e in pairs (midiin) do
local t = e["time"] -- t = [ 1 .. n_samples ]
-- synth sound until event
if t > tme then
local off = tme - 1
local len = t - tme
fluidsynth:synth (outs[1]:offset (off), outs[2]:offset (off), len)
end
tme = t + 1
fluidsynth:midi_event (e["bytes"], e["size"]) -- parse midi event
end
-- synth rest of cycle
if tme <= n_samples then
local off = tme - 1
local len = 1 + n_samples - tme
fluidsynth:synth (outs[1]:offset (off), outs[2]:offset (off), len)
end
end

View file

@ -1,46 +0,0 @@
--- session-script example to modulate plugin parameter(s) globally
--
-- Ardour > Menu > Session > Scripting > Add Lua Script
-- "Add" , select "Modulate Plugin Parameter", click "Add" + OK.
--
-----------------------------------------------------------------------------
-- This script currently assumes you have a track named "Audio"
-- which as a plugin at the top, where the first parameter has a range > 200
-- e.g. "No Delay Line"
--
-- edit below..
-- plugin descriptor
ardour {
["type"] = "session",
name = "Modulate Plugin Parameter",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[An example session to modulate a plugin parameter.]]
}
function factory () -- generate a new script instance
local count = 0 -- script-instance "global" variable
-- the "run" function called at the beginning of every process cycle
return function (n_samples)
count = (count + 1) % 200; -- count process cycles
local tri = math.abs (100 - count) -- triangle wave 0..100
-- get the track named "Audio"
-- see also http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session
-- and http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route
local route = Session:route_by_name ("Audio")
assert (not route:isnil ()) -- make sure it exists
-- the 1st plugin (from top) on that track, ardour starts counting at zero
-- see also http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Processor
local plugin = route:nth_plugin (0)
assert (not plugin:isnil ()) -- make sure it exists
-- modulate the plugin's first parameter (0) from 200 .. 300
ARDOUR.LuaAPI.set_processor_param (plugin, 0, 200.0 + tri)
end
end

View file

@ -1,47 +0,0 @@
ardour { ["type"] = "Snippet", name = "Readable Test" }
function factory () return function ()
local file = "/tmp/reverbs/Large Wide Echo Hall.wav"
local rl = ARDOUR.Readable.load (Session, file)
local cmem = ARDOUR.DSP.DspShm (8192)
local d = cmem:to_float (0):array()
for i = 1, 8192 do d[i] = 0 end
d[1] = .5
local ar = ARDOUR.AudioRom.new_rom (cmem:to_float (0), 8192)
rl:push_back (ar)
local c = 1
for rd in rl:iter () do
local n_samples = rd:readable_length ()
assert (rd:n_channels () == 1)
local peak = 0
local pos = 0
repeat
-- read at most 8K samples starting at 'pos'
local s = rd:read (cmem:to_float (0), pos, 8192, 0)
pos = pos + s
-- access the raw audio data
-- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
local d = cmem:to_float (0):array()
-- iterate over the audio sample data
for i = 0, s do
if math.abs (d[i]) > peak then
peak = math.abs (d[i])
end
end
until s < 8192
assert (pos == n_samples)
if (peak > 0) then
print ("File:", file, "channel", c, "peak:", 20 * math.log (peak) / math.log(10), "dBFS")
else
print ("File:", file, "channel", c, " is silent")
end
c = c + 1;
end
end end

View file

@ -1,47 +0,0 @@
ardour {
["type"] = "dsp",
name = "Simple Amp",
category = "Example",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[
An Example DSP Plugin for processing audio, to
be used with Ardour's Lua scripting facility.]]
}
-- return possible i/o configurations
function dsp_ioconfig ()
-- -1, -1 = any number of channels as long as input and output count matches
return { [1] = { audio_in = -1, audio_out = -1}, }
end
-- optional function, called when configuring the plugin
function dsp_configure (ins, outs)
-- store configuration in global variable
audio_ins = ins:n_audio();
local audio_outs = outs:n_audio()
assert (audio_ins == audio_outs)
end
-- this variant asks for a complete *copy* of the
-- audio data in a lua-table.
-- after processing the data is copied back.
--
-- this also exemplifies the direct "connect and run" process function,
-- where the channel-mapping needs to be done in lua.
function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
for c = 1,audio_ins do
-- Note: lua starts counting at 1, ardour's ChanMapping::get() at 0
local ib = in_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped input buffer for given channel
local ob = out_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped output buffer for given channel
assert (ib ~= ARDOUR.ChanMapping.Invalid);
assert (ob ~= ARDOUR.ChanMapping.Invalid);
local a = bufs:get_audio (ib):data (offset):get_table(n_samples) -- copy audio-data from input buffer
for s = 1,n_samples do
a[s] = a[s] * 2; -- amplify data in lua table
end
bufs:get_audio(ob):data(offset):set_table(a, n_samples) -- copy back
end
end

View file

@ -1,51 +0,0 @@
ardour {
["type"] = "dsp",
name = "Simple Amp II",
category = "Example",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[
An Example DSP Plugin for processing audio, to
be used with Ardour's Lua scripting facility.]]
}
-- see amp1.lua
function dsp_ioconfig ()
return { [1] = { audio_in = -1, audio_out = -1}, }
end
function dsp_configure (ins, outs)
audio_ins = ins:n_audio();
local audio_outs = outs:n_audio()
assert (audio_ins == audio_outs)
end
-- this variant modifies the audio data in-place
-- in Ardour's buffer.
--
-- It relies on the fact that by default Ardour requires
-- plugins to process data in-place (zero copy).
--
-- Every assignment directly calls a c-function behind
-- the scenes to get/set the value.
-- It's a bit more efficient than "Amp I" on most systems.
function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
for c = 1,audio_ins do
-- ensure that processing does happen in-place
local ib = in_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped input buffer for given cannel
local ob = out_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped output buffer for given cannel
assert (ib ~= ARDOUR.ChanMapping.Invalid);
assert (ob ~= ARDOUR.ChanMapping.Invalid);
local bi = bufs:get_audio(ib)
local bo = bufs:get_audio(ob)
assert (bi == bo)
local a = bufs:get_audio(ib):data(offset):array() -- get a reference (pointer to array)
for s = 1,n_samples do
a[s] = a[s] * 2; -- modify data in-place (shared with ardour)
end
end
end

View file

@ -1,44 +0,0 @@
ardour {
["type"] = "dsp",
name = "Simple Amp III",
category = "Example",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[
An Example DSP Plugin for processing audio, to
be used with Ardour's Lua scripting facility.]]
}
function dsp_ioconfig ()
return
{
{ audio_in = -1, audio_out = -1},
}
end
function dsp_params ()
return
{
{ ["type"] = "input", name = "Gain", min = -20, max = 20, default = 6, unit="dB", scalepoints = { ["0"] = 0, ["twice as loud"] = 6 , ["half as loud"] = -6 } },
}
end
-- use ardour's vectorized functions
--
-- This is as efficient as Ardour doing it itself in C++
-- Lua function overhead is negligible
--
-- this also exemplifies the /simpler/ way of delegating the
-- channel-mapping to ardour.
function dsp_run (ins, outs, n_samples)
local ctrl = CtrlPorts:array() -- get control port array (read/write)
local gain = ARDOUR.DSP.dB_to_coefficient (ctrl[1])
assert (#ins == #outs) -- ensure that we can run in-place (channel count matches)
for c = 1,#ins do
assert (ins[c] == outs[c]) -- check in-place
ARDOUR.DSP.apply_gain_to_buffer (ins[c], n_samples, gain); -- process in-place
end
end

View file

@ -1,280 +0,0 @@
ardour {
["type"] = "dsp",
name = "Biquad Filter",
category = "Filter",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[A Versatile Filter Plugin]]
}
function dsp_ioconfig ()
return
{
-- allow any number of I/O as long as port-count matches
{ audio_in = -1, audio_out = -1},
}
end
function dsp_params ()
return
{
{ ["type"] = "input", name = "Enable", min = 0, max = 1, default = 1, bypass = true, toggled = true },
{ ["type"] = "input", name = "Type", min = 0, max = 4, default = 0, enum = true, scalepoints =
{
["Peaking"] = 0,
["Low Shelf"] = 1,
["High Shelf"] = 2,
["Low Pass"] = 3,
["High Pass"] = 4,
}
},
{ ["type"] = "input", name = "Gain", min = -20, max = 20, default = 0, unit="dB" },
{ ["type"] = "input", name = "Freq", min = 20, max = 20000, default = 1000, unit="Hz", logarithmic = true },
{ ["type"] = "input", name = "Q", min = 0.1, max = 8, default = .707, logarithmic = true },
}
end
-- translate type parameter to DSP enum
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR.DSP.Biquad.Type
function map_type (t)
if t == 1 then
return ARDOUR.DSP.BiquadType.LowShelf
elseif t == 2 then
return ARDOUR.DSP.BiquadType.HighShelf
elseif t == 3 then
return ARDOUR.DSP.BiquadType.LowPass
elseif t == 4 then
return ARDOUR.DSP.BiquadType.HighPass
else
return ARDOUR.DSP.BiquadType.Peaking
end
end
function ctrl_data ()
local ctrl = CtrlPorts:array ()
if ctrl[1] <= 0 then -- when disabled
ctrl[3] = 0; -- force gain to 0dB
end
return ctrl
end
-- these globals are *not* shared between DSP and UI
local filters = {} -- the biquad filter instances (DSP)
local filt -- the biquad filter instance (GUI, response)
local cur = {0, 0, 0, 0, 0} -- current parameters
local lpf = 0.03 -- parameter low-pass filter time-constant
local chn = 0 -- channel/filter count
function dsp_init (rate)
self:shmem ():allocate (1) -- shared mem to tell the GUI the samplerate
local cfg = self:shmem ():to_int (0):array ()
cfg[1] = rate
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:Biquad
filt = ARDOUR.DSP.Biquad (rate) -- initialize filter
lpf = 13000 / rate -- interpolation time constant
end
function dsp_configure (ins, outs)
assert (ins:n_audio () == outs:n_audio ())
local cfg = self:shmem ():to_int (0):array ()
local rate = cfg[1]
chn = ins:n_audio ()
for c = 1, chn do
filters[c] = ARDOUR.DSP.Biquad (rate) -- initialize filters
end
cur = {0, 0, 0, 0, 0}
end
-- helper functions for parameter interpolation
function param_changed (ctrl)
if ctrl[2] == cur[2] and ctrl[3] == cur[3] and ctrl[4] == cur[4] and ctrl[5] == cur[5] then
return false
end
return true
end
function low_pass_filter_param (old, new, limit)
if math.abs (old - new) < limit then
return new
else
return old + lpf * (new - old)
end
end
-- apply parameters, re-compute filter coefficients if needed
function apply_params (ctrl)
if not param_changed (ctrl) then
return
end
if cur[2] ~= ctrl[2] then
-- reset filter state when type changes
filt:reset ()
for k = 2,5 do cur[k] = ctrl[k] end
else
-- low-pass filter ctrl parameter values, smooth transition
cur[3] = low_pass_filter_param (cur[3], ctrl[3], 0.1) -- gain/dB
cur[4] = low_pass_filter_param (cur[4], ctrl[4], 1.0) -- freq/Hz
cur[5] = low_pass_filter_param (cur[5], ctrl[5], 0.01) -- quality
end
for c = 1, chn do
filters[c]:compute (map_type (cur[2]), cur[4], cur[5], cur[3])
end
end
-- the actual DSP callback
function dsp_run (ins, outs, n_samples)
local changed = false
local siz = n_samples
local off = 0
local ctrl = ctrl_data ()
-- if a parameter was changed, process at most 64 samples at a time
-- and interpolate parameters until the current settings match
-- the target values
if param_changed (ctrl) then
changed = true
siz = 64
end
while n_samples > 0 do
if changed then apply_params (ctrl) end
if siz > n_samples then siz = n_samples end
-- process all channels
for c = 1,#ins do
-- check if output and input buffers for this channel are identical
-- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
if ins[c] == outs[c] then
filters[c]:run (ins[c]:offset (off), siz) -- in-place processing
else
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP
ARDOUR.DSP.copy_vector (outs[c]:offset (off), ins[c]:offset (off), siz)
filters[c]:run (outs[c]:offset (off), siz)
end
end
n_samples = n_samples - siz
off = off + siz
end
if changed then
-- notify display
self:queue_draw ()
end
end
-------------------------------------------------------------------------------
--- inline display
function round (n)
return math.floor (n + .5)
end
function freq_at_x (x, w)
-- x-axis pixel for given freq, power-scale
return 20 * 1000 ^ (x / w)
end
function x_at_freq (f, w)
-- frequency at given x-axis pixel
return w * math.log (f / 20.0) / math.log (1000.0)
end
function db_to_y (db, h)
-- y-axis gain mapping
if db < -20 then db = -20 end
if db > 20 then db = 20 end
return -.5 + 0.5 * h * (1 - db / 20)
end
function grid_db (ctx, w, h, db)
-- draw horizontal grid line
local y = -.5 + round (db_to_y (db, h))
ctx:move_to (0, y)
ctx:line_to (w, y)
ctx:stroke ()
end
function grid_freq (ctx, w, h, f)
-- draw vertical grid line
local x = -.5 + round (x_at_freq (f, w))
ctx:move_to (x, 0)
ctx:line_to (x, h)
ctx:stroke ()
end
function render_inline (ctx, w, max_h)
if not filt then
-- the GUI is separate from the DSP, but the GUI needs to know
-- the sample-rate that the DSP is using.
local shmem = self:shmem () -- get shared memory region
local cfg = shmem:to_int (0):array () -- "cast" into lua-table
-- instantiate filter (to calculate the transfer function's response)
filt = ARDOUR.DSP.Biquad (cfg[1])
end
-- set filter coefficients if they have changed
if param_changed (CtrlPorts:array ()) then
local ctrl = ctrl_data ()
for k = 2,5 do cur[k] = ctrl[k] end
filt:compute (map_type (cur[2]), cur[4], cur[5], cur[3])
end
-- calc height of inline display
local h = math.ceil (w * 10 / 16) -- use 16:10 aspect
h = 2 * round (h / 2) -- with an even number of vertical pixels
if (h > max_h) then h = max_h end -- but at most max-height
-- ctx is a http://cairographics.org/ context
-- http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context
-- clear background
ctx:rectangle (0, 0, w, h)
ctx:set_source_rgba (.2, .2, .2, 1.0)
ctx:fill ()
-- set line width: 1px
-- Note: a cairo pixel at [1,1] spans [0.5->1.5 , 0.5->1.5]
-- hence the offset -0.5 in various move_to(), line_to() calls
ctx:set_line_width (1.0)
-- draw grid
local dash3 = C.DoubleVector ()
dash3:add ({1, 3})
ctx:set_dash (dash3, 2) -- dotted line
ctx:set_source_rgba (.5, .5, .5, .5)
grid_db (ctx, w, h, 0)
grid_db (ctx, w, h, 6)
grid_db (ctx, w, h, 12)
grid_db (ctx, w, h, 18)
grid_db (ctx, w, h, -6)
grid_db (ctx, w, h, -12)
grid_db (ctx, w, h, -18)
grid_freq (ctx, w, h, 100)
grid_freq (ctx, w, h, 1000)
grid_freq (ctx, w, h, 10000)
ctx:unset_dash ()
-- draw transfer function line
ctx:set_source_rgba (.8, .8, .8, 1.0)
ctx:move_to (-.5, db_to_y (filt:dB_at_freq (freq_at_x (0, w)), h))
for x = 1,w do
local db = filt:dB_at_freq (freq_at_x (x, w))
ctx:line_to (-.5 + x, db_to_y (db, h))
end
ctx:stroke_preserve ()
-- fill area to zero under the curve
ctx:line_to (w, -.5 + h * .5)
ctx:line_to (0, -.5 + h * .5)
ctx:close_path ()
ctx:set_source_rgba (.5, .5, .5, .5)
ctx:fill ()
return {w, h}
end

View file

@ -1,27 +0,0 @@
ardour { ["type"] = "Snippet", name = "Calculate DSP stats",
license = "MIT",
author = "Ardour Team",
}
function factory () return function ()
for t in Session:get_routes ():iter () do
local i = 0
while true do
local rv, stats
local proc = t:nth_processor (i)
if proc:isnil () then break end
if proc:to_plugininsert():isnil() then goto continue end
rv, stats = proc:to_plugininsert():get_stats (0, 0, 0, 0)
if not rv then goto continue end
print (string.format (" * %-28s | min: %.2f max: %.2f avg: %.3f std-dev: %.3f [ms]",
string.sub (proc:name() .. ' (' .. t:name() .. ')', 0, 28),
stats[1] / 1000.0, stats[2] / 1000.0, stats[3] / 1000.0, stats[4] / 1000.0))
::continue::
i = i + 1
end
end
end end

View file

@ -1,37 +0,0 @@
ardour {
["type"] = "EditorHook",
name = "Timed Event Example",
author = "Ardour Lua Task Force",
description = "Perform actions at specific wallclock time, example record",
}
function signals ()
return LuaSignal.Set():add ({[LuaSignal.LuaTimerDS] = true})
end
function factory ()
local _last_time = 0
return function (signal, ref, ...)
-- calculate seconds since midnight
function hhmmss (hh, mm, ss) return hh * 3600 + mm * 60 + ss end
-- current seconds since midnight UTC
-- (unix-time is UTC, no leap seconds, a day always has 86400 sec)
local now = os.time () % 86400
-- event at 09:30:00 UTC (here: rec-arm + roll)
if (now >= hhmmss (09, 30, 00) and _last_time < hhmmss (09, 30, 00)) then
Session:maybe_enable_record (false)
Session:request_transport_speed (1.0, true, ARDOUR.TransportRequestSource.TRS_UI)
end
-- event at 09:32:00 UTC (here: rec-stop)
if (now >= hhmmss (09, 32, 00) and _last_time < hhmmss (09, 32, 00)) then
Session:disable_record (false, false)
Session:request_transport_speed (0.0, true, ARDOUR.TransportRequestSource.TRS_UI)
end
_last_time = now
end
end

View file

@ -1,95 +0,0 @@
ardour { ["type"] = "Snippet", name = "Dialog Test" }
function factory () return function ()
local md = LuaDialog.Message ("title", "hello", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close)
print (md:run())
md = nil
collectgarbage ()
-----------------------------------------------------------------
function basic_serialize (o)
if type(o) == "number" then
return tostring(o)
else
return string.format("%q", o)
end
end
function serialize (name, value)
local rv = name .. ' = '
collectgarbage()
if type(value) == "number" or type(value) == "string" or type(value) == "nil" or type (value) == "boolean" then
return rv .. basic_serialize(value) .. ' '
elseif type(value) == "table" then
rv = rv .. '{} '
for k,v in pairs(value) do
local fieldname = string.format("%s[%s]", name, basic_serialize(k))
rv = rv .. serialize(fieldname, v) .. ' '
end
return rv
elseif type(value) == "function" then
--return rv .. string.format("%q", string.dump(value, true))
return rv .. "(function)"
else
error('cannot serialize a ' .. type(value))
end
end
-----------------------------------------------------------------
function func () print "Hello" end
local dialog_options = {
{ type = "checkbox", key = "onoff", default = true, title = "OnOff" },
{ type = "entry", key = "text", default = "changeme", title = "Text Entry" },
{
type = "radio", key = "select", title = "RadioBtn", values =
{
["Option 1"] = 1, ["Option 2"] = "2", ["Option A"] = 'A'
},
default = "Option 1"
},
{
type = "dropdown", key = "dropdown", title = "Menu", values =
{
["Option 1"] = 1, ["Option 2"] = "2", ["Callback"] = func,
["Option 4"] =
{
["Option 4a"] = "test", ["Option 4b"] = 4.2
}
},
default = "Option 2"
},
{ type = "fader", key = "gain", title = "Level", default = -10 }, -- unit = 'dB"
{
type = "slider", key = "freq", title = "Frequency", min = 20, max = 20000, scalepoints =
{
[20] = "20", [200] = "nice", [2000] = "2k", [10000] = "too much"
},
default = 500
},
{ type = "heading", title = "Heading" },
{ type = "number", key = "number", title = "Whatever", min = 0, max = 10, step = 1, digits = 2 },
{ type = "file", key = "file", title = "Select a File", path = ARDOUR.LuaAPI.build_filename (Session:path (), Session:name () .. ".ardour") },
{ type = "folder", key = "folder", title = "Select a Folder", path = Session:path() }
}
local od = LuaDialog.Dialog ("title", dialog_options)
local rv = od:run()
if (rv) then
print (serialize ("dialog", rv))
end
od = nil
collectgarbage ()
end end

View file

@ -1,78 +0,0 @@
ardour { ["type"] = "dsp", name = "DSP Plugin Communication" }
function dsp_ioconfig () return { { audio_in = -1, audio_out = -1} } end
function dsp_init (rate)
self:shmem ():allocate (1)
end
function dsp_configure (ins, outs)
end
function dsp_params ()
return
{
{ ["type"] = "output", name = "self", min = 0, max = 8},
{ ["type"] = "output", name = "gain", min = 0, max = 2},
}
end
function dsp_run (ins, outs, n_samples)
local ctrl = CtrlPorts:array ()
local route = self:route ()
local shmem = self:shmem ()
-- count plugins
local i = 0;
local l = 0;
local s = -1; -- 'self' this plugin instance
-- iterate overall plugins on this track,
-- find all LuaProc instances of this plugin (unique_id),
repeat
local proc = route:nth_plugin (i)
if not proc:isnil ()
and not proc:to_insert():plugin (0):to_luaproc():isnil ()
and proc:to_insert():plugin (0):unique_id () == self:unique_id () then
if (self:id ():to_s() == proc:to_insert():plugin (0):id ():to_s()) then
s = l; -- *this* plugin instance
end
if l == 0 then
-- use shared-memory are of the first plugin instance for all.
--
-- (the first plugin writes there, all later plugins only read,
-- plugins on a track are executed in order, in the same thread)
shmem = proc:to_insert():plugin (0):to_luaproc():shmem ()
end
l = l + 1 -- count total instances of this plugin-type
end
i = i + 1
until proc:isnil ()
assert (s >= 0)
ctrl[1] = s;
-- calculate digital peak of all channels
local peak = 0
for c = 1,#ins do
if not ins[c]:sameinstance (outs[c]) then
ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples)
end
peak = ARDOUR.DSP.compute_peak(outs[c], n_samples, peak)
end
-- actual inter-plugin communication
local a = shmem:to_float (0):array ()
if s == 0 then
-- the first plugin saves the peak
a[0] = peak
ctrl[2] = -1
else
-- all later plugins display the difference to the first.
if (a[0] == 0) then
ctrl[2] = 1
else
ctrl[2] = peak / a[0]
end
end
end

View file

@ -1,77 +0,0 @@
ardour { ["type"] = "Snippet", name = "Dump Latency",
license = "MIT",
author = "Ardour Team",
}
function factory () return function ()
local all_procs = true
local show_ports = true
print (" -- Session --")
print ("Worst Output Latency: ", Session:worst_output_latency ())
print ("Worst Input Latency: ", Session:worst_input_latency ())
print ("Worst Track Latency: ", Session:worst_route_latency ())
print ("Worst Latency Preroll: ", Session:worst_latency_preroll ())
print (" -- Routes --")
for t in Session:get_routes ():iter () do
print (string.format ("%-30s signal-latency: %4d align: %4d play: %4d || in: %4d out: %4d",
t:name(),
t:signal_latency (), t:playback_latency (false), t:playback_latency (true),
t:input():latency(), t:output():latency()))
local i = 0
while true do
local proc = t:nth_processor (i)
if proc:isnil () then break end
if all_procs and not proc:to_send():isnil () then
print (string.format (" * %-27s L: %4d in: %4d out: %4d capt: %4d play %4d DLY-SRC: %4d DLY-DST: %4d",
string.sub (proc:name(), 0, 27) , proc:signal_latency(),
proc:input_latency(), proc:output_latency(),
proc:capture_offset(), proc:playback_offset(),
proc:to_send():get_delay_in(), proc:to_send():get_delay_out()
))
elseif all_procs or not proc:to_diskioprocessor():isnil () then
print (string.format (" * %-27s L: %4d in: %4d out: %4d capt: %4d play %4d",
string.sub (proc:name(), 0, 27) , proc:signal_latency(),
proc:input_latency(), proc:output_latency(),
proc:capture_offset(), proc:playback_offset()
))
end
i = i + 1
end
end
if show_ports then
print (" -- Ports -- (latencies: port, priv, pub)")
local a = Session:engine()
_, t = a:get_ports (ARDOUR.DataType("audio"), ARDOUR.PortList())
-- table 't' holds argument references. t[2] is the PortList
for p in t[2]:iter() do
local lp = p:get_connected_latency_range (ARDOUR.LatencyRange(), true)
local lc = p:get_connected_latency_range (ARDOUR.LatencyRange(), false)
local ppl = p:private_latency_range (true)
local pcl = p:private_latency_range (false)
local bpl = p:public_latency_range (true)
local bcl = p:public_latency_range (false)
print (string.format ("%-30s play: (%4d, %4d) (%4d, %4d) (%4d, %4d) capt: (%4d, %4d) (%4d, %4d) (%4d, %4d)",
p:name(),
lp[1].min, lp[1].max, ppl.min, ppl.max, bpl.min, bpl.max,
lc[1].min, lc[1].max, pcl.min, pcl.max, bcl.min, bcl.max))
end
_, t = a:get_ports (ARDOUR.DataType("midi"), ARDOUR.PortList())
-- table 't' holds argument references. t[2] is the PortList
for p in t[2]:iter() do
local lp = p:get_connected_latency_range (ARDOUR.LatencyRange(), true)
local lc = p:get_connected_latency_range (ARDOUR.LatencyRange(), false)
local ppl = p:private_latency_range (true)
local pcl = p:private_latency_range (false)
local bpl = p:public_latency_range (true)
local bcl = p:public_latency_range (false)
print (string.format ("%-30s play: (%4d, %4d) (%4d, %4d) (%4d, %4d) capt: (%4d, %4d) (%4d, %4d) (%4d, %4d)",
p:name(),
lp[1].min, lp[1].max, ppl.min, ppl.max, bpl.min, bpl.max,
lc[1].min, lc[1].max, pcl.min, pcl.max, bcl.min, bcl.max))
end
end
collectgarbage ()
end end

View file

@ -1,20 +0,0 @@
ardour { ["type"] = "Snippet", name = "Dump MIDI Region" }
function factory () return function ()
local sel = Editor:get_selection ()
for r in sel.regions:regionlist ():iter () do
local mr = r:to_midiregion ()
if mr:isnil () then goto next end
print (r:name (), "Pos:", r:position (), "Start:", r:start ())
local bfc = ARDOUR.BeatsSamplesConverter (Session:tempo_map (), r:position ())
local nl = ARDOUR.LuaAPI.note_list (mr:model ())
for n in nl:iter () do
print (" Note @", bfc:to (n:time ()),
ARDOUR.ParameterDescriptor.midi_note_name (n:note ()),
"Vel:", n:velocity ())
end
print ("----")
::next::
end
end end

View file

@ -1,31 +0,0 @@
ardour { ["type"] = "Snippet", name = "Dump Playlists" }
function factory () return function ()
print ("Number of playlists:", Session:playlists():n_playlists())
print ()
print ("Used playlists:")
for p in Session:playlists():get_used():iter() do
print ("-", p:name(), p:n_regions())
end
print ()
print ("Unused playlists:")
for p in Session:playlists():get_unused():iter() do
print ("-", p:name(), p:n_regions())
end
print ()
print ("Playlists by Track:")
for r in Session:get_tracks():iter() do
print ("*", r:name())
for p in Session:playlists():playlists_for_track (r:to_track()):iter() do
if (p == r:to_track():playlist()) then
print (" >-", p:name(), p:n_regions())
else
print (" -", p:name(), p:n_regions())
end
end
end
end end

View file

@ -1,44 +0,0 @@
ardour {
["type"] = "EditorHook",
name = "Save Extra Data (instruments)",
author = "Ardour Lua Task Force",
description = "Export custom data when the session is saved",
}
-- subscribe to signals
-- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal
function signals ()
s = LuaSignal.Set()
s:add ({[LuaSignal.StateSaved] = true})
return s
end
-- create callback functions
function factory () return function (signal, ...)
assert (signal == LuaSignal.StateSaved)
local all_instruments = {}
-- iterate over all routes
for r in Session:get_routes():iter() do
local proc = r:the_instrument() -- get instrument processor (if any)
if proc:isnil() then goto nextroute end -- skip tracks/busses without instrument
local pi = proc:to_insert() -- check if it's a plugin-insert
if pi:isnil() then goto nextroute end
local pp = pi:plugin (0) -- get first instance
all_instruments[r:name()] = string.format ("%s (%s)", proc:name(), pp:unique_id())
::nextroute::
end
if next (all_instruments) ~= nil then -- check if table is not empty
-- write to a file in the session-folder
file = io.open (ARDOUR.LuaAPI.build_filename (Session:path(), Session:name () .. ".instruments.txt"), "w")
for nme, nfo in pairs (all_instruments) do
file:write (nme .. ": " .. nfo)
end
file:close()
end
end end

View file

@ -1,10 +0,0 @@
ardour { ["type"] = "Snippet", name = "Export Track XML" }
function factory () return function ()
local rlp = ARDOUR.RouteListPtr ()
local sel = Editor:get_selection ()
for r in sel.tracks:routelist ():iter () do
rlp:push_back (r)
end
print (Session:export_track_state (rlp, "/tmp/rexport"))
end end

View file

@ -1,69 +0,0 @@
ardour { ["type"] = "EditorAction", name = "Fan Out Instrument",
license = "MIT",
author = "Ardour Team",
description = [[Create Busses for every Instrument Output on selected Tracks]]
}
function factory () return function ()
local outputs = 2
local mst = Session:master_out();
if not mst:isnil() then
outputs = mst:n_inputs():n_audio()
end
mst = nil -- drop reference
local sel = Editor:get_selection ()
for r in sel.tracks:routelist ():iter () do
local proc = r:the_instrument ():to_insert()
if proc:isnil () then
print ("Track", r:name(), "does not have an instrument plugin")
goto next
end
local plugin = proc:plugin(0);
if (r:n_outputs ():n_audio() ~= proc:output_streams():n_audio()) then
print ("Instrument ", proc:name(), "outputs", proc:output_streams():n_audio(), "do not match track outputs", r:n_outputs ():n_audio())
goto next
end
-- collect port-group information, count target bus width
local targets = {}
for i = 1, proc:output_streams():n_audio() do
local pd = plugin:describe_io_port (ARDOUR.DataType("Audio"), false, i - 1)
local nn = proc:name() .. " " .. pd.group_name; -- TODO use track-name prefix?
targets[nn] = targets[nn] or 0
targets[nn] = targets[nn] + 1
end
if #targets < 2 then
print ("Instrument ", proc:name(), "has only 1 output bus. Nothing to fan out.")
goto next
end
-- create busses ; TODO retain order
for t,c in pairs (targets) do
local rt = Session:route_by_name (t)
if rt:isnil () then
Session:new_audio_route (c, outputs, nil, 1, t, ARDOUR.PresentationInfo.Flag.AudioBus, ARDOUR.PresentationInfo.max_order)
end
end
r:output():disconnect_all (nil)
r:panner_shell():set_bypassed (true)
-- connect the busses
for i = 1, proc:output_streams():n_audio() do
local pd = plugin:describe_io_port (ARDOUR.DataType("Audio"), false, i - 1)
local nn = proc:name() .. " " .. pd.group_name;
local rt = Session:route_by_name (nn)
assert (rt)
local op = r:output():audio (i - 1)
local ip = rt:input():audio (pd.group_channel)
op:connect (ip:name())
end
::next::
end
end end

View file

@ -1,74 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Find non zero audio sample",
author = "Ardour Team",
description = [[Find the position of first non-zero audio sample in selected regions.]]
}
function factory () return function ()
-- get Editor GUI Selection
-- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection
local sel = Editor:get_selection ()
-- allocate a buffer (float* in C)
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:DspShm
local cmem = ARDOUR.DSP.DspShm (8192)
local msg = ""
-- iterate over selected regions
-- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection
for r in sel.regions:regionlist ():iter () do
-- test if it's an audio region
if r:to_audioregion ():isnil () then
goto next
end
-- to read the Region data, we use the Readable interface of the Region
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable
local rd = r:to_readable ()
local n_samples = rd:readable_length ()
local n_channels = rd:n_channels ()
local nonzeropos = -1
-- iterate over all channels in Audio Region
for c = 0, n_channels -1 do
local pos = 0
repeat
-- read at most 8K samples of channel 'c' starting at 'pos'
local s = rd:read (cmem:to_float (0), pos, 8192, c)
-- access the raw audio data
-- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
local d = cmem:to_float (0):array()
-- iterate over the audio sample data
for i = 0, s do
if math.abs (d[i]) > 0 then
if (nonzeropos < 0 or pos + i < nonzeropos) then
nonzeropos = pos + i
end
break
end
end
pos = pos + s
if (nonzeropos >= 0 and pos > nonzeropos) then
break
end
until s < 8192
end
if (nonzeropos >= 0) then
msg = msg .. string.format("%s: %d\n", r:name (), nonzeropos + r:position())
else
msg = msg .. "Region: '%s' is silent\n"
end
::next::
end
if (msg ~= "") then
local md = LuaDialog.Message ("First Non Zero Sample", msg, LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close)
md:run()
end
end end

View file

@ -1,36 +0,0 @@
ardour { ["type"] = "dsp", name = "Lua FIR Convolver", license = "MIT", author = "Ardour Lua Task Force", description = [[Another simple DSP example]] }
function dsp_ioconfig () return
{
{ audio_in = 1, audio_out = 1},
}
end
local conv
function dsp_configure (ins, outs)
conv = ARDOUR.DSP.Convolution (Session, ins:n_audio (), outs:n_audio ())
local cmem = ARDOUR.DSP.DspShm (4)
cmem:clear ()
local d = cmem:to_float (0):array()
d[1] = .5
d[2] = .5
local ar = ARDOUR.AudioRom.new_rom (cmem:to_float (0), 4)
conv:add_impdata (0, 0, ar, 1.0, 0, 0, 0, 0)
cmem:to_float (0):set_table({1, -1, 0, 0}, 4)
ar = ARDOUR.AudioRom.new_rom (cmem:to_float (0), 3)
conv:add_impdata (0, 0, ar, 1.0, 0, 0, 0, 0)
conv:restart ()
collectgarbage ()
end
function dsp_latency ()
return conv:latency()
end
function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
conv:run (bufs, in_map, out_map, n_samples, offset)
end

View file

@ -1,41 +0,0 @@
ardour {
["type"] = "EditorHook",
name = "Callback Example",
author = "Ardour Lua Task Force",
description = "Rewind On Solo Change, Write a file when regions are moved",
}
function signals ()
s = LuaSignal.Set()
--s:add ({[LuaSignal.SoloActive] = true, [LuaSignal.RegionPropertyChanged] = true})
s:add (
{
[LuaSignal.SoloActive] = true,
[LuaSignal.RegionPropertyChanged] = true
}
)
--for k,v in pairs (s:table()) do print (k, v) end
return s
end
function factory (params)
return function (signal, ref, ...)
print (signal, ref, ...)
if (signal == LuaSignal.SoloActive) then
Session:goto_start()
end
if (signal == LuaSignal.RegionPropertyChanged) then
obj,pch = ...
file = io.open ("/tmp/test" ,"a")
io.output (file)
io.write (string.format ("Region: '%s' pos-changed: %s, length-changed: %s\n",
obj:name (),
tostring (pch:containsSamplePos (ARDOUR.Properties.Start)),
tostring (pch:containsSamplePos (ARDOUR.Properties.Length))
))
io.close (file)
end
end
end

View file

@ -1,62 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Insert Gaps",
license = "MIT",
author = "Ardour Team",
description = [[Insert gaps between all regions on selected tracks]]
}
function action_params ()
return
{
["gap"] = { title = "Gap size (in sec)", default = "2" },
}
end
function factory () return function ()
-- get configuration
local p = params or {}
local gap = p["gap"] or 2
if gap <= 0 then gap = 2 end
local sel = Editor:get_selection () -- get current selection
local add_undo = false -- keep track of changes
Session:begin_reversible_command ("Insert Gaps")
-- iterate over all selected tracks
for route in sel.tracks:routelist ():iter () do
local track = route:to_track ()
if track:isnil () then goto continue end
-- get track's playlist
local playlist = track:playlist ()
local offset = 0
-- iterate over all regions in the playlist
for region in playlist:region_list():iter() do
-- preare for undo operation
region:to_stateful ():clear_changes ()
-- move region
region:set_position (region:position() + offset, 0)
offset = offset + Session:nominal_sample_rate () * gap
-- 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 (region:to_statefuldestructible ()):empty () then
add_undo = true
end
end
::continue::
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
end end

View file

@ -1,74 +0,0 @@
ardour {
["type"] = "dsp",
name = "MIDI LFO",
category = "Example", -- Utility
license = "MIT",
author = "Ardour Lua Task Force",
description = [[MIDI CC LFO Example -- Triangle full scale CC Parameter automation]]
}
function dsp_ioconfig ()
return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, }
end
function dsp_params ()
return
{
{ ["type"] = "input", name = "BPM", min = 40, max = 200, default = 60, unit="BPM"},
{ ["type"] = "input", name = "CC", min = 0, max = 127, default = 1, integer = true },
}
end
local samplerate
local time = 0
local step = 0
function dsp_init (rate)
samplerate = rate
local bpm = 120
spb = rate * 60 / bpm
end
function dsp_run (_, _, n_samples)
assert (type(midiin) == "table")
assert (type(midiout) == "table")
local ctrl = CtrlPorts:array ()
local bpm = ctrl[1]
local cc = ctrl[2]
local spb = samplerate * 60 / bpm -- samples per beat
local sps = spb / 254 -- samples per step (0..127..1 = 254 steps)
assert (sps > 1)
local i = 1
local m = 1
for ts = 1, n_samples do
time = time + 1
-- forward incoming midi
if i <= #midiin then
while midiin[i]["time"] == ts do
midiout[m] = midiin[i]
i = i + 1
m = m + 1
if i > #midiin then break end
end
end
-- inject LFO events
if time >= spb then
local val
if step > 127 then val = 254 - step else val = step end
midiout[m] = {}
midiout[m]["time"] = ts
midiout[m]["data"] = { 0xb0, cc, val }
m = m + 1
time = time - sps
if step == 253 then step = 0 else step = step + 1 end
end
end
end

View file

@ -1,37 +0,0 @@
ardour {
["type"] = "session",
name = "MIDI Record Enable",
category = "Example", -- "Utility"
license = "MIT",
author = "Ardour Lua Task Force",
description = [[An example script to start recording on note-on.]]
}
function factory ()
return function (n_samples)
if Session:actively_recording() then return end -- when recording already, do nothing
-- iterate over all MIDI ports
_, t = Session:engine ():get_ports (ARDOUR.DataType.midi (), ARDOUR.PortList ())
for p in t[2]:iter () do
-- skip output ports
if not p:receives_input () then goto next end
local midiport = p:to_midiport ()
-- and skip async event ports
if midiport:isnil () then goto next end
local mb = midiport:get_midi_buffer (n_samples) -- get the midi-data buffers
local events = mb:table () -- copy event list into lua table
for _,e in pairs (events) do -- iterate over all events in the midi-buffer
if (e:buffer():array()[1] & 0xf0) == 0x90 then -- note on
Session:maybe_enable_record (true) -- global record-enable from rt-context
-- maybe-enable may fail if there are no tracks or step-entry is active
-- roll transport if record-enable suceeded:
if ARDOUR.Session.RecordState.Enabled == Session:record_status() then
Session:request_transport_speed (1.0, true, ARDOUR.TransportRequestSource.TRS_UI) -- ...and go.
end
return
end
end
::next::
end
end
end

View file

@ -1,34 +0,0 @@
ardour {
["type"] = "session",
name = "Rewrite Midi",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[An example session script preprocesses midi buffers.]]
}
function factory ()
-- this function is called in every process cycle, before processing
return function (n_samples)
_, t = Session:engine ():get_ports (ARDOUR.DataType.midi (), ARDOUR.PortList ())
for p in t[2]:iter () do
if not p:receives_input () then goto next end
if not p:name () == "MIDI/midi_in 1" then goto next end
midiport = p:to_midiport ()
assert (not midiport:isnil ())
mb = midiport:get_midi_buffer (n_samples);
events = mb:table() -- copy event list into lua table
mb:silence (n_samples, 0); -- clear existing buffer
for _,e in pairs (events) do
-- e is-a http://manual.ardour.org/lua-scripting/class_reference/#Evoral:MidiEvent
e:set_channel (2)
mb:push_event (e)
end
::next::
end
end
end

View file

@ -1,39 +0,0 @@
ardour {
["type"] = "dsp",
name = "Midi Filter",
category = "Example", -- "Utility"
license = "MIT",
author = "Ardour Lua Task Force",
description = [[An Example Midi Filter for prototyping.]]
}
function dsp_ioconfig ()
return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, }
end
function dsp_run (_, _, n_samples)
assert (type(midiin) == "table")
assert (type(midiout) == "table")
local cnt = 1;
function tx_midi (time, data)
midiout[cnt] = {}
midiout[cnt]["time"] = time;
midiout[cnt]["data"] = data;
cnt = cnt + 1;
end
-- for each incoming midi event
for _,b in pairs (midiin) do
local t = b["time"] -- t = [ 1 .. n_samples ]
local d = b["data"] -- get midi-event
local event_type
if #d == 0 then event_type = -1 else event_type = d[1] >> 4 end
if (#d == 3 and event_type == 9) then -- note on
tx_midi (t, d)
elseif (#d == 3 and event_type == 8) then -- note off
tx_midi (t, d)
end
end
end

View file

@ -1,48 +0,0 @@
ardour {
["type"] = "dsp",
name = "MIDI Generator",
category = "Example", -- "Utility"
license = "MIT",
author = "Ardour Lua Task Force",
description = [[An Example Midi Generator for prototyping.]]
}
function dsp_ioconfig ()
return { { midi_out = 1, audio_in = 0, audio_out = 0}, }
end
local tme = 0 -- sample-counter
local seq = 1 -- sequence-step
local spb = 0 -- samples per beat
local midi_sequence = {
{ 0x90, 64, 127 },
{ 0x80, 64, 0 },
}
function dsp_init (rate)
local bpm = 120
spb = rate * 60 / bpm
if spb < 2 then spb = 2 end
end
function dsp_run (_, _, n_samples)
assert (type(midiout) == "table")
assert (spb > 1)
local m = 1
for time = 1,n_samples do -- not very efficient
-- TODO, timestamp the sequence in beats, calc/skip to next event
tme = tme + 1
if tme >= spb then
midiout[m] = {}
midiout[m]["time"] = time
midiout[m]["data"] = midi_sequence[seq]
tme = 0
m = m + 1
if seq == #midi_sequence then seq = 1 else seq = seq + 1 end
end
end
end

View file

@ -1,34 +0,0 @@
ardour {
["type"] = "dsp",
name = "MIDI Generator II",
category = "Example",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[An Example Midi Generator for prototyping.]]
}
function dsp_ioconfig ()
return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, }
end
function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
local ob = out_map:get (ARDOUR.DataType ("midi"), 0)
if ob ~= ARDOUR.ChanMapping.Invalid then
local mb = bufs:get_midi (ob)
-- see _midigenerator.lua for
-- how to use a timed sequence
local ba = C.ByteVector () -- construct a byte vector
ba:add ({0x90, 64, 127}) -- add some data to the vector
-- send a message at cycle-start
mb:push_back (offset, ba:size (), ba:to_array());
ba:clear ()
ba:add ({0x80, 64, 127})
mb:push_back (n_samples - 1 - offset, ba:size (), ba:to_array());
end
-- passthrough audio, apply pin/channel mapping
ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio"))
end

View file

@ -1,52 +0,0 @@
ardour {
["type"] = "EditorHook",
name = "OSC Callback Example",
author = "Ardour Lua Task Force",
description = "Send OSC messages",
}
function action_params ()
return
{
["uri"] = { title = "OSC URI ", default = "osc.udp://localhost:7890"},
}
end
function signals ()
s = LuaSignal.Set()
s:add (
{
[LuaSignal.SoloActive] = true,
[LuaSignal.RegionPropertyChanged] = true,
[LuaSignal.Exported] = true,
[LuaSignal.TransportStateChange] = true
}
)
return s
end
function factory (params)
return function (signal, ref, ...)
local uri = params["uri"] or "osc.udp://localhost:7890"
local tx = ARDOUR.LuaOSC.Address (uri)
-- debug print (stdout)
-- print (signal, ref, ...)
if (signal == LuaSignal.Exported) then
tx:send ("/session/exported", "ss", ...)
elseif (signal == LuaSignal.SoloActive) then
tx:send ("/session/solo_changed", "")
elseif (signal == LuaSignal.TransportStateChange) then
tx:send ("/session/transport", "if",
Session:transport_sample(), Session:transport_speed())
elseif (signal == LuaSignal.RegionPropertyChanged) then
obj,pch = ...
tx:send ("/region_property_changed", "sTTiii",
obj:name (),
(pch:containsSamplePos (ARDOUR.Properties.Start)),
(pch:containsSamplePos (ARDOUR.Properties.Length)),
obj:position (), obj:start (), obj:length ())
end
end
end

View file

@ -1,24 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Plot Process Graph",
author = "Ardour Team",
description = [[Export process graph to a graphviz file, and launch xdot]]
}
function factory () return function ()
if Session:plot_process_graph ("/tmp/ardour_graph.gv") then
os.forkexec ("/bin/sh", "-c", "xdot /tmp/ardour_graph.gv")
end
end end
function icon (params) return function (ctx, width, height, fg)
ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
local txt = Cairo.PangoLayout (ctx, "Sans ".. math.ceil(height / 3) .. "px")
txt:set_alignment (Cairo.Alignment.Center);
txt:set_width (width);
txt:set_ellipsize (Cairo.EllipsizeMode.Middle);
txt:set_text ("plot\ngrph")
local tw, th = txt:get_pixel_size ()
ctx:move_to (0, .5 * (height - th))
txt:show_in_cairo_context (ctx)
end end

View file

@ -1,260 +0,0 @@
ardour {
["type"] = "dsp",
name = "a-Pong",
category = "Toy",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[A console classic for your console]]
}
-- return possible i/o configurations
function dsp_ioconfig ()
-- -1, -1 = any number of channels as long as input and output count matches
return { [1] = { audio_in = -1, audio_out = -1}, }
end
-- control port(s)
function dsp_params ()
return
{
{ ["type"] = "input", name = "Bar", min = 0, max = 1, default = 0.5 },
{ ["type"] = "input", name = "Reset", min = 0, max = 1, default = 0, toggled = true },
{ ["type"] = "input", name = "Difficulty", min = 1, max = 10, default = 3},
}
end
-- Game State (for this instance)
-- NOTE: these variables are for the DSP part (not shared with the GUI instance)
local sample_rate -- sample-rate
local fps -- audio samples per game-step
local game_time -- counts up to fps
local game_score
local ball_x, ball_y -- ball position [0..1]
local dx, dy -- current ball speed
local lost_sound -- audio-sample counter for game-over [0..3*fps]
local ping_sound -- audio-sample counter for ping-sound [0..fps]
local ping_phase -- ping note phase-difference per sample
local ping_pitch
function dsp_init (rate)
-- allocate a "shared memory" area to transfer state to the GUI
self:shmem ():allocate (3)
self:shmem ():clear ()
-- initialize some variables
sample_rate = rate
fps = rate / 25
ping_pitch = 752 / rate
ball_x = 0.5
ball_y = 0
dx = 0.00367
dy = 0.01063
game_score = 0
game_time = fps -- start the ball immediately (notify GUI)
ping_sound = fps -- set to end of synth cycle
lost_sound = 3 * fps
end
function queue_beep ()
-- queue 'ping' sound (unless one is already playing to prevent clicks)
if (ping_sound >= fps) then
-- major scale, 2 octaves
local scale = { 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24 }
local midi_note = 60 + scale[1 + math.floor (math.random () * 14)]
ping_pitch = (440 / 32) * 2^((midi_note - 10.0) / 12.0) / sample_rate
ping_sound = 0
ping_phase = 0
end
end
-- callback: process "n_samples" of audio
-- ins, outs are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
-- pointers to the audio buffers
function dsp_run (ins, outs, n_samples)
local ctrl = CtrlPorts:array () -- get control port array (read/write)
local changed = false -- flag to notify GUI on every game-step
game_time = game_time + n_samples
-- reset (allow to write automation from a given start-point)
-- ctrl[2] corresponds to the "Reset" input control
if ctrl[2] > 0 then
game_time = 0
ball_x = 0.5
ball_y = 0
dx = 0.00367
dy = 0.01063
game_score = 0
end
-- simple game engine
while game_time > fps and ctrl[2] <= 0 do
changed = true
game_time = game_time - fps
-- move the ball
ball_x = ball_x + dx * ctrl[3]
ball_y = ball_y + dy * ctrl[3]
-- reflect left/right
if ball_x >= 1 or ball_x <= 0 then
dx = -dx
queue_beep ()
end
-- single player (reflect top) -- TODO "stereo" version, 2 ctrls :)
if ball_y <= 0 then
dy = - dy y = 0
queue_beep ()
end
-- keep the ball in the field at all times
if ball_x >= 1 then ball_x = 1 end
if ball_x <= 0 then ball_x = 0 end
-- bottom edge
if ball_y > 1 then
local bar = ctrl[1] -- get bar position
if math.abs (bar - ball_x) < 0.1 then
-- reflect the ball
dy = - dy
ball_y = 1.0
dx = dx - 0.04 * (bar - ball_x)
-- make sure it's moving (not stuck on borders)
if math.abs (dx) < 0.0001 then dx = 0.0001 end
game_score = game_score + 1
queue_beep ()
else
-- game over, reset game
lost_sound = 0 -- re-start noise
ball_y = 0
game_score = 0
dx = 0.00367
end
end
end
-- forward audio if processing is not in-place
for c = 1,#outs do
-- check if output and input buffers for this channel are identical
-- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
if ins[c] ~= outs[c] then
-- fast (accelerated) copy
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP
ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples)
end
end
-- simple synth -- TODO Optimize
if ping_sound < fps then
-- cache audio data buffers for direct access, later
local abufs = {}
for c = 1,#outs do
abufs[c] = outs[c]:array()
end
-- simple sine synth with a sine-envelope
for s = 1, n_samples do
ping_sound = ping_sound + 1
ping_phase = ping_phase + ping_pitch
local snd = 0.7 * math.sin(6.283185307 * ping_phase) * math.sin (3.141592 * ping_sound / fps)
-- add synthesized sound to all channels
for c = 1,#outs do
abufs[c][s] = abufs[c][s] + snd
end
-- break out of the loop when the sound finished
if ping_sound >= fps then goto ping_end end
end
::ping_end::
end
if lost_sound < 3 * fps then
local abufs = {}
for c = 1,#outs do
abufs[c] = outs[c]:array()
end
for s = 1, n_samples do
lost_sound = lost_sound + 1
-- -12dBFS white noise
local snd = 0.5 * (math.random () - 0.5)
for c = 1,#outs do
abufs[c][s] = abufs[c][s] + snd
end
if lost_sound >= 3 * fps then goto noise_end end
end
::noise_end::
end
if changed then
-- notify the GUI
local shmem = self:shmem () -- get the shared memory region
local state = shmem:to_float (0):array () -- "cast" into lua-table
-- update data..
state[1] = ball_x
state[2] = ball_y
state[3] = game_score
-- ..and wake up the UI
self:queue_draw ()
end
end
-------------------------------------------------------------------------------
--- inline display
local txt = nil -- cache font description (in GUI context)
function render_inline (ctx, w, max_h)
local ctrl = CtrlPorts:array () -- control port array
local shmem = self:shmem () -- shared memory region (game state from DSP)
local state = shmem:to_float (0):array () -- cast to lua-table
if (w > max_h) then
h = max_h
else
h = w
end
-- prepare text rendering
if not txt then
-- allocate PangoLayout and set font
--http://manual.ardour.org/lua-scripting/class_reference/#Cairo:PangoLayout
txt = Cairo.PangoLayout (ctx, "Mono 10px")
end
-- ctx is-a http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context
-- 2D vector graphics http://cairographics.org/
-- clear background
ctx:rectangle (0, 0, w, h)
ctx:set_source_rgba (.2, .2, .2, 1.0)
ctx:fill ()
-- print the current score
if (state[3] > 0) then
txt:set_text (string.format ("%.0f", state[3]));
local tw, th = txt:get_pixel_size ()
ctx:set_source_rgba (1, 1, 1, 1.0)
ctx:move_to (w - tw - 3, 3)
txt:show_in_cairo_context (ctx)
end
-- prepare line and dot rendering
ctx:set_line_cap (Cairo.LineCap.Round)
ctx:set_line_width (3.0)
ctx:set_source_rgba (.8, .8, .8, 1.0)
-- display bar
local bar_width = w * .1
local bar_space = w - bar_width
ctx:move_to (bar_space * ctrl[1], h - 3)
ctx:rel_line_to (bar_width, 0)
ctx:stroke ()
-- display ball
ctx:move_to (1 + state[1] * (w - 3), state[2] * (h - 5))
ctx:close_path ()
ctx:stroke ()
return {w, h}
end

View file

@ -1,110 +0,0 @@
ardour {
["type"] = "dsp",
name = "Midi Passthru",
category = "Example",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[An Example Audio/MIDI Passthrough Plugin using Buffer Pointers]]
}
-- return possible audio i/o configurations
function dsp_ioconfig ()
-- -1, -1 = any number of channels as long as input and output count matches
-- require 1 MIDI in, 1 MIDI out.
return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, }
end
-- "dsp_runmap" uses Ardour's internal processor API, eqivalent to
-- 'connect_and_run()". There is no overhead (mapping, translating buffers).
-- The lua implementation is responsible to map all the buffers directly.
function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:ChanMapping
local ib = in_map:get (ARDOUR.DataType ("midi"), 0) -- get index of the 1st mapped midi input buffer
if ib ~= ARDOUR.ChanMapping.Invalid then
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:MidiBuffer
local mb = bufs:get_midi (ib) -- get the mapped buffer
local events = mb:table () -- copy event list into a lua table
-- iterate over all MIDI events
for _, e in pairs (events) do
-- e is-a http://manual.ardour.org/lua-scripting/class_reference/#Evoral:MidiEvent
-- do something with the event e.g.
print (e:channel (), e:time (), e:size (), e:buffer ():array ()[1], e:buffer ():get_table (e:size ())[1])
end
end
----
-- The following code is needed with "dsp_runmap" to work for arbitrary pin connections
-- this passes though all audio/midi data unprocessed.
ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio"))
ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("midi"))
-- equivalent lua code.
-- NOTE: the lua implementation below is intended for io-config [-1,-1].
-- It only works for actually mapped channels due to in_map:count() out_map:count()
-- being identical to the i/o pin count in this case.
--
-- Plugins that have multiple possible configurations will need to implement
-- dsp_configure() and remember the actual channel count.
--
-- ARDOUR.DSP.process_map() does iterate over the mapping itself and works generally.
-- Still the lua code below does lend itself as elaborate example.
--
--[[
local audio_ins = in_map:count (): n_audio () -- number of mapped audio input buffers
local audio_outs = out_map:count (): n_audio () -- number of mapped audio output buffers
assert (audio_outs, audio_ins) -- ioconfig [-1, -1]: must match
-- copy audio data if any
for c = 1, audio_ins do
local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped input buffer
local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped output buffer
if ib ~= ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob then
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:AudioBuffer
ARDOUR.DSP.copy_vector (bufs:get_audio (ob):data (offset), bufs:get_audio (ib):data (offset), n_samples)
end
end
-- Clear unconnected output buffers.
-- In case we're processing in-place some buffers may be identical,
-- so this must be done *after* copying relvant data from that port.
for c = 1, audio_outs do
local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1)
local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1)
if ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid then
bufs:get_audio (ob):silence (n_samples, offset)
end
end
-- copy midi data
local midi_ins = in_map:count (): n_midi () -- number of midi input buffers
local midi_outs = out_map:count (): n_midi () -- number of midi input buffers
-- with midi_in=1, midi_out=1 in dsp_ioconfig
-- the following will always be true
assert (midi_ins == 1)
assert (midi_outs == 1)
for c = 1, midi_ins do
local ib = in_map:get (ARDOUR.DataType ("midi"), c - 1)
local ob = out_map:get (ARDOUR.DataType ("midi"), c - 1)
if ib ~= ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob then
bufs:get_midi (ob):copy (bufs:get_midi (ib))
end
end
-- silence unused midi outputs
for c = 1, midi_outs do
local ib = in_map:get (ARDOUR.DataType ("midi"), c - 1)
local ob = out_map:get (ARDOUR.DataType ("midi"), c - 1)
if ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid then
bufs:get_midi (ob):silence (n_samples, offset)
end
end
--]]
end

View file

@ -1,16 +0,0 @@
ardour { ["type"] = "Snippet", name = "Region Transient List" }
function factory () return function ()
local sel = Editor:get_selection ()
for r in sel.regions:regionlist ():iter () do
local region_pos = r:position()
local region_off = r:start()
print (r:name(), r:position(), r:start())
local trans = r:transients()
for t in trans:iter() do
-- print absolute timeline position of transients
print (t + region_pos - region_off)
end
print ("----")
end
end end

View file

@ -1,37 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "File Name Test",
author = "Ardour Lua Taskforce",
description = [[Example Plugin to show to to select a file and remember the most recently used file.]]
}
function factory ()
local file_name_testscript_last_filename -- this acts as "global" variable, use a unique name
return function ()
print (file_name_testscript_last_filename) -- debug
--set filename to most recently used, fall back to use a default
local fn = file_name_testscript_last_filename or ARDOUR.LuaAPI.build_filename (Session:path (), Session:name () .. ".ardour")
-- prepare a dialog
local dialog_options = {
{ type = "file", key = "file", title = "Select a File", path = fn }
}
-- show dialog
local od = LuaDialog.Dialog ("title", dialog_options)
local rv = od:run()
if rv then
-- remember most recently selected file
file_name_testscript_last_filename = rv['file']
LuaDialog.Message ("title", "set path to " .. file_name_testscript_last_filename, LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
else
-- unset most recently used filename on dialog "cancel"
file_name_testscript_last_filename = nil
end
od = nil
collectgarbage ()
end
end

View file

@ -1,12 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Rewind",
author = "Ardour Lua Task Force",
description = [[An Example Ardour Editor Action Script.]]
}
function factory (params)
return function ()
Session:goto_start()
end
end

View file

@ -1,81 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Rob's 16 MIDI Trick Pony",
description = [[clearly broken approach to go about things]]
}
function route_setup ()
return {
['Insert_at'] = ARDOUR.PresentationInfo.max_order,
['name'] = 'Sweet16',
['group'] = false, -- return value will be a RouteGroup* or nil
}
end
function factory (p) return function ()
local name = "Sweet16"
local insert_at = ARDOUR.PresentationInfo.max_order
local group = nil
-- check for "MIDI Channel Map" LV2 from x42 midifilters.lv2
if ARDOUR.LuaAPI.new_plugin_info ("http://gareus.org/oss/lv2/midifilter#channelmap", ARDOUR.PluginType.LV2):isnil () then
LuaDialog.Message ("16 MIDI Tracks", "Error: Plugin 'MIDI Simple Channel Map' was not found.", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run ()
return
end
if type (p) == 'table' and p['how_many'] ~= nil then
-- used from the AddRouteDialog (or w/action_params)
name = p["name"] or 'Sweet16'
insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order;
group = p["group"] or nil
else
-- used standalone, prompt for name and insert position
local dialog_options = {
{ type = "entry", key = "name", default = 'Sweet16', title = "Name Prefix" },
{ type = "entry", key = "group", default = '', title = "Group (empty for none)" },
{ type = "dropdown", key = "insertpos", title = "Position", default = "Last", values =
{
["First"] = ArdourUI.InsertAt.First,
["Before Selection"] = ArdourUI.InsertAt.BeforeSelection,
["After Selection"] = ArdourUI.InsertAt.AfterSelection,
["Last"] = ArdourUI.InsertAt.Last
}
}
}
local od = LuaDialog.Dialog ("16 MIDI Tracks", dialog_options)
local rv = od:run()
if (not rv) then return end
name = rv['name'] or 'Sweet16'
if rv['insertpos'] then
insert_at = ArdourUI.translate_order (rv['insertpos'])
end
if rv['group'] and rv['group'] ~= '' then
group = Session:new_route_group (rv['group'])
end
end
collectgarbage ()
-- all systems go
local tl = Session:new_midi_track (
ARDOUR.ChanCount(ARDOUR.DataType ("midi"), 1),
ARDOUR.ChanCount(ARDOUR.DataType ("midi"), 1),
true, -- strict i/o
ARDOUR.PluginInfo(), nil, -- no instrument, no instrument preset
group,
16, -- how many
name, insert_at, ARDOUR.TrackMode.Normal)
local i = 1
for track in tl:iter() do
local p = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/midifilter#channelmap", ARDOUR.PluginType.LV2, "")
assert (not p:isnil ())
track:add_processor_by_index(p, 0, nil, true)
for j = 1, 16 do
ARDOUR.LuaAPI.set_processor_param (p, j, i)
end
i = i + 1
end
collectgarbage () -- drop references to tracks.
end end

View file

@ -1,119 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Generic Audio Track",
description = [[Add Audio tracks, by default as many as there are physical inputs]]
}
-- If a route_setup function is present in an Editor Action Script
-- the script is also listed in the "add track/bus" dialog as meta-template
--
-- The function is expected to return a Lua table. The table may be empty.
function route_setup ()
local e = Session:engine()
local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector())
return
{
-- keys control which AddRouteDialog controls are made sensitive.
-- The following keys accept a default value to pre-seed the dialog.
['how_many'] = t[4]:size(),
['name'] = 'Audio',
['channels'] = 2,
['track_mode'] = ARDOUR.TrackMode.Normal,
['strict_io'] = true,
-- these keys just need to be set (to something other than nil)
-- in order to set the control sensitives
['insert_at'] = ARDOUR.PresentationInfo.max_order,
['group'] = false, -- return value will be a RouteGroup*
['instrument'] = nil, -- return value will be a PluginInfoPtr
}
end
-- The Script can be used as EditorAction in which case it *could*
-- optionally provide instantiation parmaters..
--[[
function action_params ()
return
{
['how_many'] = { title = "How Many tracks to add", default = "1" },
["name"] = { title = "Track Name Prefix", default = "Audio" },
}
end
--]]
function factory (p)
-- when used from the AddRouteDialog (or w/action_params)
if type (p) == 'table' and p['how_many'] ~= nil then
return function ()
-- When called from the AddRouteDialog, 'p' will be a table with
-- keys as described in route_setup() above.
local name = p["name"] or 'Audio'
local how_many = p["how_many"] or 1
local channels = p["channels"] or 1
local insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order;
local group = p["group"] or nil
local mode = p["track_mode"] or ARDOUR.TrackMode.Normal
local strict_io = p["strict_io"] or false
local chan_out = 0
if ARDOUR.config():get_output_auto_connect() == ARDOUR.AutoConnectOption.AutoConnectMaster then
if not Session:master_out():isnil() then
chan_out = Session:master_out():n_inputs ():n_audio ()
end
end
if chan_out == 0 then
chan_out = channels;
end
local tl = Session:new_audio_track (channels, chan_out, group, how_many, name, insert_at, mode)
if strict_io then
for t in tl:iter() do
t:set_strict_io (true)
end
end
end
end
-- when used as standalone (no action parameters): interactive
return function ()
local e = Session:engine()
local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector())
local tracks = t[4]:size();
local dialog_options = {
{ type = "number", key = "tracks", title = "Create Tracks", min = 1, max = 128, step = 1, digits = 0, default = tracks },
{ type = "entry", key = "name", default = 'Audio', title = "Name Prefix" },
{ type = "checkbox", key = "stereo", default = false, title = "Stereo" },
{ type = "checkbox", key = "recarm", default = false, title = "Record Arm Tracks" },
}
local dlg = LuaDialog.Dialog ("Create Audio Tracks", dialog_options)
local rv = dlg:run()
if (not rv or rv['tracks'] == 0) then
return
end
local chan_in = stereo and 2 or 1
local chan_out = 0
if ARDOUR.config():get_output_auto_connect() == ARDOUR.AutoConnectOption.AutoConnectMaster then
if not Session:master_out():isnil() then
chan_out = Session:master_out():n_inputs ():n_audio ()
end
end
if chan_out == 0 then
chan_out = chan_in;
end
-- create tracks
local tl = Session:new_audio_track (chan_in, chan_out, nil, rv['tracks'], "", ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal)
-- and optionally record-arm them
if rv['recarm'] then
for track in tl:iter() do
track:rec_enable_control ():set_value (1, PBD.GroupControlDisposition.NoGroup)
end
end
end
end

View file

@ -1,78 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Generic MIDI Track",
description = [[Example]]
}
-- If a route_setup function is present in an Editor Action Script
-- the script is also listed in the "add track/bus" dialog as meta-template
--
-- The function is expected to return a Lua table. The table may be empty.
function route_setup ()
return
{
-- keys control which AddRouteDialog controls are made sensitive.
-- The following keys accept a default value to pre-seed the dialog.
['how_many'] = 1,
['name'] = 'MIDI',
['channels'] = nil,
['track_mode'] = nil,
['strict_io'] = true,
-- these keys just need to be set (to something other than nil)
-- in order to set the control sensitives
['insert_at'] = ARDOUR.PresentationInfo.max_order,
['group'] = false, -- return value will be a RouteGroup*
['instrument'] = true, -- return value will be a PluginInfoPtr
}
end
-- The Script can be used as EditorAction in which case it can
-- optionally provide instantiation parmaters
function action_params ()
return
{
['how_many'] = { title = "How Many tracks to add", default = "1" },
["name"] = { title = "Track Name Prefix", default = "MIDI" },
["instrument"] = { title = "Add Instrument", default = "true" },
}
end
function factory (params) return function ()
-- When called from the AddRouteDialog, 'params' will be a table with
-- keys as described in route_setup() above.
local p = params or route_setup ()
local name = p["name"] or 'Audio'
local how_many = p["how_many"] or 1
local insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order;
local group = p["group"] or nil
local strict_io = p["strict_io"] or false
local instrument = p["instrument"] or nil
-- used in 'action-script mode'
if instrument == "true" then
instrument = ARDOUR.LuaAPI.new_plugin_info ("http://gareus.org/oss/lv2/gmsynth", ARDOUR.PluginType.LV2) -- general midi synth
if instrument:isnil () then
instrument = ARDOUR.LuaAPI.new_plugin_info ("https://community.ardour.org/node/7596", ARDOUR.PluginType.LV2) -- reasonable synth
end
if instrument:isnil () then
LuaDialog.Message ("MIDI track add", "Cannot find instrument plugin",
LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run ()
return
end
end
-- add no instrument
if type (instrument) ~= "userdata" then
instrument = ARDOUR.PluginInfo ()
end
Session:new_midi_track(
ARDOUR.ChanCount(ARDOUR.DataType ("midi"), 1),
ARDOUR.ChanCount(ARDOUR.DataType ("audio"), 2),
strict_io,
instrument, nil,
group, how_many, name, insert_at, ARDOUR.TrackMode.Normal)
end end

View file

@ -1,175 +0,0 @@
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
-- clean up, unload vamp plugin
vamp = nil
collectgarbage ()
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

View file

@ -1,32 +0,0 @@
ardour {
["type"] = "EditorHook",
name = "Load Session Hook Example",
author = "Ardour Lua Task Force",
description = "Display some dialogs during session load and execute actions",
}
-- subscribe to signals
-- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal
function signals ()
s = LuaSignal.Set()
s:add ({[LuaSignal.SetSession] = true})
return s
end
-- create callback functions
function factory () return function (signal, ...)
assert (signal == LuaSignal.SetSession)
local md = LuaDialog.Message ("Set Session", "Loading Session:" .. Session:name(), LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close)
md:run()
local dialog_options = {
{ type = "checkbox", key = "tempo", default = true, title = "Show Tempo Ruler" },
{ type = "checkbox", key = "meter", default = true, title = "Show Meter Ruler" },
}
local dlg = LuaDialog.Dialog ("Tweak Rulers", dialog_options)
local rv = dlg:run()
if (rv) then
Editor:set_toggleaction ("Rulers", "toggle-tempo-ruler", rv['tempo'])
Editor:set_toggleaction ("Rulers", "toggle-meter-ruler", rv['meter'])
end
end end

View file

@ -1,34 +0,0 @@
ardour {
["type"] = "session",
name = "Good Night",
author = "Ardour Lua Task Force",
description = [[
Example Ardour Session Script.
Session scripts are called at the beginning of every process-callback (before doing any audio processing).
This example stops the transport after rolling for a configurable time which can be set when instantiating the script.]]
}
function sess_params ()
return
{
["print"] = { title = "Debug Print (yes/no)", default = "no", optional = true },
["time"] = { title = "Timeout (sec)", default = "90", optional = false },
}
end
function factory (params)
return function (n_samples)
local p = params["print"] or "no"
local timeout = params["time"] or 90
a = a or 0
if p ~= "no" then print (a, n_samples, Session:sample_rate (), Session:transport_rolling ()) end -- debug output (not rt safe)
if (not Session:transport_rolling()) then
a = 0
return
end
a = a + n_samples
if (a > timeout * Session:sample_rate()) then
Session:request_transport_speed(0.0, true, ARDOUR.TransportRequestSource.TRS_Engine)
end
end
end

View file

@ -1,31 +0,0 @@
ardour { ["type"] = "dsp", name = "Sound Smasher", category = "Dynamics", license = "MIT", author = "Ardour Lua Task Force", description = [[Another simple DSP example]] }
function dsp_ioconfig () return
-- -1, -1 = any number of channels as long as input and output count matches
{ { audio_in = -1, audio_out = -1}, }
end
-- the DSP callback function to process audio audio
-- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
function dsp_run (ins, outs, n_samples)
for c = 1, #outs do -- for each output channel (count from 1 to number of output channels)
if ins[c] ~= outs[c] then -- if processing is not in-place..
ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) -- ..copy data from input to output.
end
-- direct audio data access, in-place processing of output buffer
local buf = outs[c]:array() -- get channel's 'c' data as lua array reference
-- process all audio samples
for s = 1, n_samples do
buf[s] = math.atan (1.5707 * buf[s]) -- some non-linear gain.
-- NOTE: doing the maths per sample in lua is not super-efficient
-- (vs C/C++ vectorized functions -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP)
-- but it is very convenient, especially for prototypes and quick solutions.
end
end
end

View file

@ -1,36 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Track Sort",
author = "Ardour Lua Taskforce",
description = [[Sort tracks alphabetically by name]]
}
function factory () return function ()
-- sort compare function
-- a,b here are http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route
-- return true if route "a" should be ordered before route "b"
function tsort (a, b)
return a:name() < b:name()
end
-- create a sortable list of tracks
local tracklist = {}
for t in Session:get_tracks():iter() do
table.insert(tracklist, t)
end
-- sort the list using the compare function
table.sort(tracklist, tsort)
-- traverse the sorted list and assign "presentation-order" to each track
local pos = 1;
for _, t in ipairs(tracklist) do
t:set_presentation_order(pos)
pos = pos + 1
end
-- drop all track references
tracklist = nil
collectgarbage ()
end end

View file

@ -1,37 +0,0 @@
ardour {
["type"] = "dsp",
name = "Spike Synth",
category = "Instrument",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[A debug and test-instrumentation synth. This plugin is useful with Ardour's "Dummy" backend "Engine-Pulse" mode to verify capture alignment. This plugin generate the exact same audio-signal from MIDI data that the backend also generates: Note-on: +1, Note-off: -1.]]
}
function dsp_ioconfig ()
return { { midi_in = 1, audio_in = 0, audio_out = 1} }
end
function dsp_run (ins, outs, n_samples)
local a = {}
for s = 1, n_samples do a[s] = 0 end
for c = 1,#outs do
ARDOUR.DSP.memset (outs[c], 0, n_samples)
end
assert (type(midiin) == "table")
for _,b in pairs (midiin) do
local t = b["time"] -- t = [ 1 .. n_samples ]
local d = b["data"] -- get midi-event
if (#d == 3 and (d[1] & 240) == 144) then -- note on
for c = 1,#outs do
outs[c]:array()[t] = 1.0
end
end
if (#d == 3 and (d[1] & 240) == 128) then -- note off
for c = 1,#outs do
outs[c]:array()[t] = -1.0
end
end
end
end

View file

@ -1,58 +0,0 @@
-- [disable CPU freq scaling for benchmark]
-- create a session
-- add 16 mono tracks
-- record 2-3 mins on each track starting at 00:00:00:00
-- rewind the playhead to 00:00:00:00
-- run this script in Menu > Window. Scripting 10 times
ardour { ["type"] = "EditorAction", name = "Split Benchmark" }
function factory (params) return function ()
function split_at (pos)
local add_undo = false -- keep track if something has changed
Session:begin_reversible_command ("Auto Region Split")
for route in Session:get_tracks():iter() do
local playlist = route:to_track():playlist ()
playlist:to_stateful ():clear_changes ()
for region in playlist:regions_at (pos):iter () do
playlist:split_region (region, ARDOUR.MusicSample (pos, 0))
end
if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then
add_undo = true
end
end
if add_undo then
Session:commit_reversible_command (nil)
else
Session:abort_reversible_command ()
end
end
function count_regions ()
local total = 0
for route in Session:get_tracks():iter() do
total = total + route:to_track():playlist():region_list():size()
end
return total
end
for x = 1, 3 do
local playhead = Session:transport_sample ()
local step = Session:samples_per_timecode_frame()
local n_steps = 20
local t_start = ARDOUR.LuaAPI.monotonic_time ()
for i = 1, n_steps do
split_at (playhead + step * i)
end
local t_end = ARDOUR.LuaAPI.monotonic_time ()
Session:request_locate((playhead + step * n_steps), ARDOUR.LocateTransportDisposition.MustStop, ARDOUR.TransportRequestSource.TRS_UI)
print (count_regions (), (t_end - t_start) / 1000 / n_steps)
collectgarbage ();
ARDOUR.LuaAPI.usleep(500000)
end
end end

View file

@ -1,57 +0,0 @@
ardour { ["type"] = "EditorAction", name = "Stereo to Mono",
license = "MIT",
author = "Ardour Team",
description = [[Convert a Stereo Track into two Mono Tracks]]
}
function factory (params) return function ()
-- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection
-- the Ardour Selection can include multiple items
-- (regions, tracks, ranges, markers, automation, midi-notes etc)
local sel = Editor:get_selection ()
-- for each track..
for t in sel.tracks:routelist ():iter () do
local track = t:to_track ()
if track:isnil() then goto next end
-- only audio tracks
local playlist = track:playlist ()
if playlist:data_type ():to_string () ~= "audio" then goto next end
-- skip tracks without any regions
if playlist:region_list ():size() == 0 then goto next end
-- we can't access diskstream n_channels()
local channels = track:n_inputs(): n_audio()
-- stereo only
if channels ~= 2 then goto next end
-- create 2 new tracks (using the name of the original track)(
local newtracks = Session:new_audio_track (2, 2, nil, 2, t:name(), ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal)
assert (newtracks:size() == 2)
for r in playlist:region_list ():iter () do
local region = r:to_audioregion ()
local rl = ARDOUR.RegionVector ()
local _, rv = region:separate_by_channel (rl)
assert (rv[1]:size () == 2)
-- 1:1 mapping of regions to new tacks
local plc = 1
for nr in rv[1]:iter () do
local pl = newtracks:table()[plc]:playlist()
pl:add_region (nr, r:position(), 1, false, 0, 0, false)
plc = plc + 1
end
end
-- TODO remove the old track
-- drop references for good.
collectgarbage ()
::next::
end
end end

View file

@ -1,20 +0,0 @@
ardour { ["type"] = "EditorAction", name = "System Exec" }
function factory () return function ()
-- ** EXAMPLES TO RUN EXTERNAL APPLICATIONS ** --
-- run a command in a shell and wait for it to complete.
--
-- Details: basically just system(3), except on Unix like systems with
-- memory-locking, this call is special-cased to use vfork and close
-- file-descriptors. On other systems it defaults to Lua's os-library
-- built-in os.execute system() call.
os.execute ("date > /tmp/testdate")
-- os.forkexec() works as fire-and-forget. execv(3) style
--
-- Details: It calls vfork() and exec() under the hood, passing each
-- argument separately to exec (and needs a full-path to the binary).
os.forkexec ("/usr/bin/xterm")
os.forkexec ("/bin/sh", "-c", "import --frame \"/tmp/scr_$(date).png\"")
end end

View file

@ -1,14 +0,0 @@
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

View file

@ -1,10 +0,0 @@
ardour { ["type"] = "Snippet", name = "Toggle Monitor Section" }
function factory () return function ()
if Session:monitor_out():isnil() then
ARDOUR.config():set_use_monitor_bus (true)
else
ARDOUR.config():set_use_monitor_bus (false)
collectgarbage ()
end
end end

View file

@ -1,128 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Send Raw MIDI from File",
license = "MIT",
author = "Ardour Team",
description = [[Read raw binary midi (.syx) from file and send it to a control port]]
}
function factory () return function ()
function portlist ()
local rv = {}
local a = Session:engine()
local _, t = a:get_ports (ARDOUR.DataType("midi"), ARDOUR.PortList())
for p in t[2]:iter() do
local amp = p:to_asyncmidiport ()
if amp:isnil() or not amp:sends_output() then goto continue end
rv[amp:name()] = amp
print (amp:name(), amp:sends_output())
::continue::
end
return rv
end
local dialog_options = {
{ type = "file", key = "file", title = "Select .syx MIDI file" },
{ type = "dropdown", key = "port", title = "Target Port", values = portlist () }
}
local rv = LuaDialog.Dialog ("Select Taget", dialog_options):run ()
dialog_options = nil -- drop references (ports, shared ptr)
collectgarbage () -- and release the references immediately
if not rv then return end -- user cancelled
local f = io.open (rv["file"], "rb")
if not f then
LuaDialog.Message ("Raw MIDI Tx", "File Not Found", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run ()
goto out
end
do -- scope for 'local'
local size = f:seek("end") -- determine file size
f:seek("set", 0)
if size > 1048576 then
local ok = LuaDialog.Message ("Raw MIDI Tx",
string.format ("File is larger than 1MB.\nFile-size = %.1f kB\n\nContinue?", size / 1024),
LuaDialog.MessageType.Question, LuaDialog.ButtonType.Yes_No):run ()
if ok ~= LuaDialog.Response.Yes then
f:close ()
goto out
end
end
end
do -- scope for 'local'
local midi_byte_count = 0
local total_read = 0
local message_count = 0
local long_message = false
local async_midi_port = rv["port"] -- reference to port
local parser = ARDOUR.RawMidiParser () -- construct a MIDI parser
while true do
-- read file in 64byte chunks
local bytes = f:read (64)
if not bytes then break end
total_read = total_read + #bytes
-- parse MIDI data byte-by-byte
for i = 1, #bytes do
if parser:process_byte (bytes:byte (i)) then
if parser:buffer_size () > 255 then
long_message = true
print ("WARNING -- single large message > 255, bytes: ", parser:buffer_size ())
end
-- parsed complete normalized MIDI message, send it
async_midi_port:write (parser:midi_buffer (), parser:buffer_size (), 0)
-- Physical MIDI is sent at 31.25kBaud.
-- Every message is sent as 10bit message on the wire,
-- so every MIDI byte needs 320usec.
ARDOUR.LuaAPI.usleep (400 * parser:buffer_size ())
-- count msgs and valid bytes sent
midi_byte_count = midi_byte_count + parser:buffer_size ()
message_count = message_count + 1
if 0 == message_count % 50 then
-- print() wakes up the GUI, prevent stalling the event loop
print ("Sent", message_count, "messages, bytes so far: ", midi_byte_count)
end
end
end
end
f:close ()
print ("Sent", message_count, "messages, total bytes: ", midi_byte_count, "/", total_read)
if long_message then
LuaDialog.Message ("Raw MIDI Tx", "Dataset contained messages longer than 127 bytes. Which may or may not have been transmitted successfully.", LuaDialog.MessageType.Warning, LuaDialog.ButtonType.Close):run ()
end
end
::out::
rv = nil
collectgarbage ()
end end
function icon (params) return function (ctx, width, height, fg)
ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .45) .. "px")
txt:set_text ("S")
ctx:move_to (1, 1)
txt:show_in_cairo_context (ctx)
txt:set_text ("Y")
local tw, th = txt:get_pixel_size ()
ctx:move_to (.5 * (width - tw), .5 * (height - th))
txt:show_in_cairo_context (ctx)
txt:set_text ("X")
tw, th = txt:get_pixel_size ()
ctx:move_to ((width - tw - 1), (height - th -1))
txt:show_in_cairo_context (ctx)
end end

View file

@ -1,63 +0,0 @@
ardour { ["type"] = "Snippet", name = "Vamp Plugin Example" }
function factory () return function ()
-- get a list of all available plugins
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp
-- returns a http://manual.ardour.org/lua-scripting/class_reference/#C:StringVector
local plugins = ARDOUR.LuaAPI.Vamp.list_plugins ();
for id in plugins:iter () do
print ("--", id)
end
local sel = Editor:get_selection ()
-- load the Vamp Plugin with Id "libardourvampplugins:dBTP"
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp
local vamp = ARDOUR.LuaAPI.Vamp("libardourvampplugins:dBTP", Session:nominal_sample_rate())
print (vamp:plugin():getName())
-- for each selected region
for r in sel.regions:regionlist ():iter () do
print ("Region:", r:name ())
-- run the plugin, analyze the first channel of the audio-region
vamp:analyze (r:to_readable (), 0, nil)
-- get analysis results
local f = vamp:plugin ():getRemainingFeatures ()
-- f is-a Vamp::Plugin::FeatureSet aka std::map<int, Vamp::Plugin::FeatureList>
-- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet
for id, featlist in f:iter () do
print (id, featlist)
end
-- get the first FeatureList
local featurelist = f:table()[0]
-- Vamp::Plugin::FeatureList is a typedef for std::vector<Feature>
for feat in featurelist:iter () do
print ("-", feat.label)
end
-- get the first feature..
-- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature
local feature = featurelist:at(0)
-- ..and the values of the feature, which is-a std::vector<float>
local values = feature.values
-- iterate over the std::vector<float>
for val in values:iter () do
print ("*", val)
end
-- access the first element of Vamp::Plugin::Feature's "values" vector
-- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatVector
local value = values:at(0)
-- in case of libardourvampplugins:dBTP that's the true-peak (signal value)
local dbtp = 20 * math.log (value) / math.log(10) -- convert it to dB
print (string.format ("Region '%s': %.2f dBTP", r:name (), dbtp))
-- reset the plugin for the next iteration
vamp:reset ()
end
end end

View file

@ -1,68 +0,0 @@
ardour { ["type"] = "Snippet", name = "Vamp Audio Transcription Example" }
function factory () return function ()
-- 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 ()
local sr = Session:nominal_sample_rate ()
-- Instantiate a Vamp Plugin
-- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp
local vamp = ARDOUR.LuaAPI.Vamp ("libardourvampplugins:qm-transcription", sr)
-- prepare progress dialog
local progress_total = 0;
local progress_part = 0
local pdialog = LuaDialog.ProgressWindow ("Audio to MIDI", true)
function cb (_, pos)
return pdialog:progress ((pos + progress_part) / progress_total, "Analyzing")
end
-- calculate max progress
for r in sel.regions:regionlist ():iter () do
progress_total = progress_total + r:to_readable ():readable_length ()
end
-- 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
vamp:analyze (r:to_readable (), 0, cb)
if pdialog:canceled () then
goto out
end
progress_part = progress_part + r:to_readable ():readable_length ()
pdialog:progress (progress_part / progress_total, "Post Processing")
print ("-- Post Processing: ", r:name ())
-- post-processing takes longer than actually parsing the data :(
local f = vamp:plugin ():getRemainingFeatures ()
local fl = f:table ()[0]
print (" Time (sample) | Len | Midi-Note");
if fl then for f in fl:iter () do
assert (f.hasTimestamp and f.hasDuration);
local ft = Vamp.RealTime.realTime2Frame (f.timestamp, sr)
local fd = Vamp.RealTime.realTime2Frame (f.duration, sr)
local fn = f.values:at (0) -- midi note number
print (string.format (" %14d %7d %d", ft, fd, fn))
end end
-- reset the plugin (prepare for next iteration)
vamp:reset ()
end
::out::
-- hide modal progress dialog and destroy it
pdialog:done ();
pdialog = nil
vamp = nil;
collectgarbage ()
end end

View file

@ -1,83 +0,0 @@
ardour { ["type"] = "Snippet", name = "Vamp Onset Detection Example" }
function factory () return function ()
-- 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 a Vamp Plugin
-- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp
--
-- here: the "Queen Mary Note Onset Detector" Vamp plugin (which comes with Ardour)
-- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-onsetdetector
local vamp = ARDOUR.LuaAPI.Vamp("libardourvampplugins:qm-onsetdetector", Session:nominal_sample_rate())
-- prepare table to hold results
local onsets = {}
-- 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
-- prepare lua table to hold results for the given region (by name)
onsets[r:name ()] = {}
-- callback to handle Vamp-Plugin analysis results
function callback (feats)
-- "feats" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet
-- get the first output. here: Detected note onset times
local fl = feats:table()[0]
-- "fl" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureList
-- which may be empty or not nil
if fl then
-- iterate over returned features
for f in fl:iter () do
-- "f" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature
if f.hasTimestamp then
local fn = Vamp.RealTime.realTime2Frame (f.timestamp, 48000)
--print ("-", f.timestamp:toString(), fn)
table.insert (onsets[r:name ()], fn)
end
end
end
return false -- continue, !cancel
end
-- Configure Vamp plugin
--
-- One could query the Parameter and Program List:
-- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin
-- but since the Plugin is known, we can directly refer to the plugin doc:
-- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-onsetdetector
vamp:plugin ():setParameter ("dftype", 3);
vamp:plugin ():setParameter ("sensitivity", 50);
vamp:plugin ():setParameter ("whiten", 0);
-- in this case the above (3, 50, 0) is equivalent to
--vamp:plugin ():selectProgram ("General purpose");
-- run the plugin, analyze the first channel of the audio-region
--
-- This uses a "high-level" convenience wrapper provided by Ardour
-- which reads raw audio-data from the region and and calls
-- f = vamp:plugin ():process (); callback (f)
vamp:analyze (r:to_readable (), 0, callback)
-- get remaining features (end of analyis)
callback (vamp:plugin ():getRemainingFeatures ())
-- reset the plugin (prepare for next iteration)
vamp:reset ()
end
-- print results
for n,o in pairs(onsets) do
print ("Onset analysis for region:", n)
for _,t in ipairs(o) do
print ("-", t)
end
end
end end

View file

@ -1,85 +0,0 @@
ardour { ["type"] = "Snippet", name = "Vamp TempoMap Test" }
function factory () return function ()
-- 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 table to hold results
local beats = {}
local bars = {}
-- 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
-- prepare lua table to hold results for the given region (by name)
beats[r:name ()] = {}
bars[r:name ()] = {}
-- callback to handle Vamp-Plugin analysis results
function callback (feats)
-- "feats" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet
-- get the first output. here: Beats, estimated beat locations & beat-number
local fl = feats:table()[0]
-- "fl" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureList
-- which may be empty or not nil
if fl then
-- iterate over returned features
for f in fl:iter () do
-- "f" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature
if f.hasTimestamp then
local fn = Vamp.RealTime.realTime2Frame (f.timestamp, 48000)
table.insert (beats[r:name ()], {pos = fn, beat = f.label})
end
end
end
-- get the 2nd output. here: estimated bar locations
local fl = feats:table()[1]
if fl then
for f in fl:iter () do
if f.hasTimestamp then
local fn = Vamp.RealTime.realTime2Frame (f.timestamp, 48000)
table.insert (bars[r:name ()], fn)
end
end
end
return false -- continue, !cancel
end
vamp:plugin ():setParameter ("Beats Per Bar", 4); -- TODO ask
-- run the plugin, analyze the first channel of the audio-region
vamp:analyze (r:to_readable (), 0, callback)
-- get remaining features (end of analyis)
callback (vamp:plugin ():getRemainingFeatures ())
-- reset the plugin (prepare for next iteration)
vamp:reset ()
end
-- print results (for now)
-- TODO: calculate and set tempo-map
for n,o in pairs(bars) do
print ("Bar analysis for region:", n)
for _,t in ipairs(o) do
print ("-", t)
end
end
for n,o in pairs(beats) do
print ("Beat analysis for region:", n)
for _,t in ipairs(o) do
print ("-", t['pos'], t['beat'])
end
end
end end

View file

@ -1,32 +0,0 @@
ardour {
["type"] = "EditorHook",
name = "Varispeed Test - 100ms Callback",
author = "Ardour Lua Task Force",
description = "An example script that invokes a callback a every 0.1sec and modifies the transport speed",
}
function signals ()
s = LuaSignal.Set()
s:add (
{
[LuaSignal.LuaTimerDS] = true,
}
)
return s
end
function factory (params)
-- upindex variables
local cnt = 0
local speed = 0
local delta = 0.01
return function (signal, ref, ...)
cnt = (cnt + 1) % 5 -- divide clock: every half a second
if cnt == 0 then
if speed < -0.25 then delta = delta * -1 end
if speed > 0.25 then delta = delta * -1 end
speed = speed + delta
Session:request_transport_speed (speed, true, ARDOUR.TransportRequestSource.TRS_UI)
end
end
end

View file

@ -1,73 +0,0 @@
ardour { ["type"] = "Snippet", name = "VCA Slave Examples",
license = "MIT",
author = "Ardour Team",
}
function factory () return function ()
-- find possible masters & slave, allow selection in dropdown menu
local targets = {}
local sources = {}
local have_masters = false
local have_slaves = false
for v in Session:vca_manager ():vcas() :iter () do -- for each VCA
sources [v:name ()] = v
have_masters = true
end
for s in Session:get_stripables ():iter () do -- for every track/bus/vca
if s:is_monitor () or s:is_auditioner () then goto nextroute end -- skip special routes
targets [s:name ()] = s
have_slaves = true;
::nextroute::
end
-- bail out if there are no parameters
if not have_slaves then
LuaDialog.Message ("VCA Slave Example", "No Slavables found", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run ()
sources = nil
collectgarbage ()
return
end
if not have_masters then
LuaDialog.Message ("VCA Slave Example", "No VCA masters found", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run ()
targets = nil
collectgarbage ()
return
end
-- create a dialog, ask user which master to assign to which slave
local dialog_options = {
{ type = "dropdown", key = "master", title = "Control Master", values = sources },
{ type = "dropdown", key = "slave", title = "Control Slave", values = targets }
}
local rv = LuaDialog.Dialog ("Select VCA assignment", dialog_options):run ()
targets = nil -- drop references (the table holds shared-pointer references to all strips)
collectgarbage () -- and release the references immediately
if not rv then return end -- user canceled the operation
-- parse user response
local mst = rv["master"]
local slv = rv["slave"]
assert (not slv:to_slavable ():isnil ())
-- test if mst is already controlled by slv (directly or indirectly)
-- if so, don't allow the connection
if (not slv:to_slavable ():assigned_to (Session:vca_manager(), mst)) then
-- if slv controlled by master, disconnect it
if (slv:slaved_to (mst)) then
slv:to_slavable ():unassign (mst)
else
slv:to_slavable ():assign (mst)
end
else
LuaDialog.Message ("VCA Slave Example", "Recursive VCA assignment ignored", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run ()
end
-- drop references
mst = nil slv = nil
collectgarbage ()
end end

View file

@ -1,66 +0,0 @@
ardour {
["type"] = "dsp",
name = "a-Slow-Mute",
category = "Amplifier",
license = "MIT",
author = "Ardour Team",
description = [[Mute button with slow fade in/out]]
}
function dsp_ioconfig ()
-- -1, -1 = any number of channels as long as input and output count matches
return { { audio_in = -1, audio_out = -1} }
end
function dsp_params ()
return { { ["type"] = "input", name = "Mute", min = 0, max = 1, default = 0, toggled = true } }
end
local cur_gain = 1
local lpf = 0.002 -- parameter low-pass filter time-constant
function dsp_init (rate)
lpf = 100 / rate -- interpolation time constant
end
function low_pass_filter_param (old, new, limit)
if math.abs (old - new) < limit then
return new
else
return old + lpf * (new - old)
end
end
-- the DSP callback function
-- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
function dsp_run (ins, outs, n_samples)
local ctrl = CtrlPorts:array() -- get control port array
local target_gain = ctrl[1] > 0 and 0.0 or 1.0; -- when muted, target_gain = 0.0; otherwise use 1.0
local siz = n_samples -- samples remaining to process
local off = 0 -- already processed samples
local changed = false
-- if the target gain changes, process at most 32 samples at a time,
-- and interpolate gain until the current settings match the target values
if cur_gain ~= target_gain then
changed = true
siz = 32
end
while n_samples > 0 do
if siz > n_samples then siz = n_samples end
if changed then
cur_gain = low_pass_filter_param (cur_gain, target_gain, 0.001)
end
for c = 1,#ins do -- process all channels
if ins[c] ~= outs[c] then
ARDOUR.DSP.copy_vector (outs[c]:offset (off), ins[c]:offset (off), siz)
end
ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, cur_gain);
end
n_samples = n_samples - siz
off = off + siz
end
end

View file

@ -1,37 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Shortcut",
license = "MIT",
author = "me",
description = [[Trigger a keyboard shortcut. You will be prompted for the shortcut's action in the next step.]]
}
function action_params ()
local actionlist = {
{
type = "dropdown", key = "action", title = "Action", values = ArdourUI:actionlist(),
default = "Save"
}
}
local rv = LuaDialog.Dialog ("Select Action", actionlist):run ()
if not rv then -- user cancelled
return { ["x-script-abort"] = { title = "", preseeded = true} }
end
local action = rv["action"]
local name = "Shortcut - " .. action
return {
["action"] = { title = "Action to trigger", default = action, preseeded = true},
["x-script-name"] = { title = "Unique Script name", default = name, preseeded = true},
}
end
function factory (params) return function ()
local p = params or { }
local as = assert (p["action"])
local sp = assert (as:find('/'))
local group = assert (as:sub(0, sp - 1))
local item = assert (as:sub(1 + sp))
Editor:access_action (group, item)
end end

View file

@ -1,54 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Add Filters",
license = "MIT",
author = "PSmith",
description = [[Add 'HPF/LPF' Lua Processor to all Tracks]]
}
function action_params ()
return
{
["unique"] = { title = "Only add HPF/LPF if not already present (yes/no)", default = "yes"},
["position"] = { title = "Insert Position from top (0,..)", default = "0"},
}
end
function factory (params)
return function ()
-- get configuration
local p = params or {}
local uniq = p["unique"] or "yes"
local pos = p["position"] or 0
-- loop over all tracks
for t in Session:get_tracks():iter() do
local insert = true;
-- check if filters are present
if uniq ~= "no" then
local proc;
local i = 0;
repeat
-- get Nth Ardour::Processor
proc = t:nth_plugin (i)
-- check if it's a filter
if (not proc:isnil() and proc:display_name () == "a-High/Low Pass Filter") then
insert = false;
end
i = i + 1
until proc:isnil() or insert == false
end
-- create a new processor and insert it
if insert then
local a = ARDOUR.LuaAPI.new_luaproc(Session, "a-High/Low Pass Filter");
if (not a:isnil()) then
t:add_processor_by_index(a, pos, nil, true)
a = nil -- explicitly drop shared-ptr reference
end
end
end
end
end

View file

@ -1,75 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Add Scopes",
license = "MIT",
author = "Ardour Team",
description = [[Add 'Inline Scope' Lua Processor to all Tracks]]
}
function action_params ()
return
{
["unique"] = { title = "Only add Scope if non is present already (yes/no)", default = "yes"},
["position"] = { title = "Insert Position from top (0,..)", default = "0"},
}
end
function factory (params)
return function ()
-- get configuration
local p = params or {}
local uniq = p["unique"] or "yes"
local pos = p["position"] or 0
-- loop over all tracks
for t in Session:get_tracks():iter() do
local insert = true;
-- check if a scope is already present
if uniq ~= "no" then
local proc;
local i = 0;
repeat
-- get Nth Ardour::Processor
proc = t:nth_plugin (i)
-- check if it's a scope
if (not proc:isnil() and proc:display_name () == "a-Inline Scope") then
insert = false;
end
i = i + 1
until proc:isnil() or insert == false
end
-- create a new processor and insert it
if insert then
local a = ARDOUR.LuaAPI.new_luaproc(Session, "a-Inline Scope");
if (not a:isnil()) then
t:add_processor_by_index(a, pos, nil, true)
ARDOUR.LuaAPI.set_processor_param (a, 0, 5) -- timescale 5sec
-- ARDOUR.LuaAPI.set_processor_param (a, 1, 1) -- logscale on
-- ARDOUR.LuaAPI.set_processor_param (a, 2, 3) -- "Max" height
a = nil -- explicitly drop shared-ptr reference
end
end
end
end
end
function icon (params) return function (ctx, width, height)
local wh = math.min (width, height) * .5
local x0 = math.ceil (wh * .4)
local x1 = math.floor (wh * 1.6)
ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh))
ctx:rectangle (wh * .4, wh * .4, wh * 1.2, wh * 1.2)
ctx:set_source_rgba (.7, .7, .7, 1)
ctx:fill ()
ctx:set_line_width (1)
ctx:set_source_rgba (.0, .0, .0, 1)
ctx:move_to (x0, wh)
for x = x0, x1 do
ctx:line_to (x, wh - math.sin (2 * math.pi * (x-x0) / (x1-x0)) * wh * .5)
end
ctx:stroke ()
end end

View file

@ -1,119 +0,0 @@
ardour {
["type"] = "dsp",
name = "a-Amplifier",
category = "Amplifier",
license = "MIT",
author = "Ardour Team",
description = [[Versatile +/- 20dB multichannel amplifier]]
}
function dsp_ioconfig ()
return
{
-- -1, -1 = any number of channels as long as input and output count matches
{ audio_in = -1, audio_out = -1},
}
end
function dsp_params ()
return
{
{ ["type"] = "input", name = "Gain", min = -20, max = 20, default = 0, unit="dB"},
}
end
local lpf = 0.02 -- parameter low-pass filter time-constant
local cur_gain = 0 -- current smoothed gain (dB)
-- called once when plugin is instantiated
function dsp_init (rate)
lpf = 780 / rate -- interpolation time constant
end
function low_pass_filter_param (old, new, limit)
if math.abs (old - new) < limit then
return new
else
return old + lpf * (new - old)
end
end
-- the DSP callback function
-- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
function dsp_run (ins, outs, n_samples)
local ctrl = CtrlPorts:array() -- get control port array (read/write)
local siz = n_samples -- samples remaining to process
local off = 0 -- already processed samples
local changed = false
-- if the gain parameter was changed, process at most 32 samples at a time
-- and interpolate gain until the current settings match the target values
if cur_gain ~= ctrl[1] then
changed = true
siz = 32
end
while n_samples > 0 do
if siz > n_samples then siz = n_samples end -- process at most "remaining samples"
if changed then
-- smooth gain changes above 0.02 dB difference
cur_gain = low_pass_filter_param (cur_gain, ctrl[1], 0.02)
end
local gain = ARDOUR.DSP.dB_to_coefficient (cur_gain) -- 10 ^ (0.05 * cur_gain)
for c = 1,#ins do -- process all channels
-- check if output and input buffers for this channel are identical
-- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
if ins[c] ~= outs[c] then
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP
ARDOUR.DSP.copy_vector (outs[c]:offset (off), ins[c]:offset (off), siz)
end
ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, gain); -- apply-gain, process in-place
end
n_samples = n_samples - siz
off = off + siz
end
--[[
if changed then
self:queue_draw () -- notify display
end
--]]
end
-------------------------------------------------------------------------------
--- inline display + text example
--[[
local txt = nil -- cache pango context globally
function render_inline (ctx, w, max_h)
local ctrl = CtrlPorts:array () -- get control ports
if not txt then
-- allocate PangoLayout and set font
--http://manual.ardour.org/lua-scripting/class_reference/#Cairo:PangoLayout
txt = Cairo.PangoLayout (ctx, "Mono 8px")
end
txt:set_text (string.format ("%+.2f dB", ctrl[1]));
tw, th = txt:get_pixel_size ()
local h = th + 4 -- use text-height with 4px padding
if (h > max_h) then h = max_h end -- but at most max-height
-- clear background
ctx:rectangle (0, 0, w, h)
ctx:set_source_rgba (.2, .2, .2, 1.0)
ctx:fill ()
-- center text
ctx:set_source_rgba (.8, .8, .8, 1.0)
ctx:move_to (.5 * (w - tw), .5 * (h - th))
txt:show_in_cairo_context (ctx)
return {w, h}
end
--]]

View file

@ -1,101 +0,0 @@
ardour { ["type"] = "EditorAction", name = "Bounce+Replace Regions",
license = "MIT",
author = "Ardour Team",
description = [[Bounce selected regions with processing and replace region]]
}
function factory (params) return 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 selection
-- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection
local sel = Editor:get_selection ()
-- prepare undo operation
Session:begin_reversible_command ("Bounce+Replace Regions")
local add_undo = false -- keep track if something has changed
-- Iterate over Regions part of the selection
-- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection
for r in sel.regions:regionlist ():iter () do
-- each of the items 'r' is a
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region
local track = find_track_for_region (r:to_stateful():id())
local playlist = track:playlist ()
-- clear existing changes, prepare "diff" of state for undo
playlist:to_stateful ():clear_changes ()
-- bounce the region with processing
local region = track:bounce_range (r:position (), r:position() + r:length (), ARDOUR.InterThreadInfo (), track:main_outs (), false);
-- remove old region..
playlist:remove_region (r);
-- ..and add the newly bounced one
playlist:add_region (region, 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
-- 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 wh = math.min (width, height) * .5
local ar = wh * .2
ctx:set_line_width (1)
function stroke_outline (c)
ctx:set_source_rgba (0, 0, 0, 1)
ctx:stroke_preserve ()
ctx:set_source_rgba (c, c, c, 1)
ctx:fill ()
end
ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh))
ctx:rectangle (wh - wh * .6, wh - .7 * wh, wh * 1.2, .5 * wh)
stroke_outline (.7)
ctx:rectangle (wh - wh * .6, wh + .1 * wh, wh * 1.2, .5 * wh)
stroke_outline (.9)
-- arrow
ctx:set_source_rgba (0, 1, 0, 1)
ctx:set_line_width (ar * .7)
ctx:move_to (wh, wh - .5 * wh)
ctx:rel_line_to (0, wh)
ctx:stroke ()
ctx:move_to (wh, wh + .5 * wh)
ctx:rel_line_to (-ar, -ar)
ctx:rel_line_to (2 * ar, 0)
ctx:close_path ()
ctx:stroke ()
end end

View file

@ -1,22 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Bypass Plugins",
license = "MIT",
author = "Ardour Team",
description = [[Bypass Plugins on selected tracks]]
}
function factory () return function ()
local sel = Editor:get_selection ()
for r in sel.tracks:routelist ():iter () do
local i = 0;
while 1 do -- iterate over all plugins/processors
local proc = r:nth_plugin (i)
if proc:isnil () then
break
end
proc:to_insert():deactivate()
i = i + 1
end
end
end end

View file

@ -1,73 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Create Drum Tracks",
author = "PSmith",
description = [[Creates 8 new tracks with representative names and colors.]]
}
function factory () return function ()
local names = {
"Kick",
"Snare",
"Hat",
"Fl Tom",
"OH L",
"OH R",
"Room 1",
"Room 2"
}
local color = 0xff8800ff --orange
local i = 1
while names[i] do
local tl = Session:new_audio_track (1, 2, nil, 1, names[i],
ARDOUR.PresentationInfo.max_order,
ARDOUR.TrackMode.Normal)
for track in tl:iter () do
track:presentation_info_ptr ():set_color (color)
end
i = i + 1
end --foreach track
end end -- function factory
function icon (params) return function (ctx, width, height)
local x = width * .5
local y = height * .5
local r = math.min (x, y) * .7
ctx:save ()
ctx:translate (x, y)
ctx:scale (1, .5)
ctx:translate (-x, -y)
ctx:arc (x, y, r, 0, 2 * math.pi)
ctx:set_source_rgba (.9, .9, 1, 1)
ctx:fill ()
ctx:arc (x, y, r, 0, math.pi)
ctx:arc_negative (x, y * 1.6, r, math.pi, 0)
ctx:set_source_rgba (.7, .7, .7, 1)
ctx:fill ()
ctx:restore ()
ctx:set_source_rgba (.6, .4, .2, 1)
ctx:translate (x, y)
ctx:scale (.7, 1)
ctx:translate (-x, -y)
ctx:set_line_cap (Cairo.LineCap.Round)
function drumstick (xp, lr)
ctx:set_line_width (r * .3)
ctx:move_to (x * xp, y)
ctx:close_path ()
ctx:stroke ()
ctx:set_line_width (r * .2)
ctx:move_to (x * xp, y)
ctx:rel_line_to (lr * x, y)
ctx:stroke ()
end
drumstick (1.2, 1.2)
drumstick (0.7, -.5)
end end

View file

@ -1,40 +0,0 @@
ardour { ["type"] = "EditorAction", name = "Delete xrun markers", author = "Ardour Team", description = [[Delete all xrun markers in the current session]] }
function factory () return function ()
for l in Session:locations():list():iter() do
if l:is_mark() and string.find (l:name(), "^xrun%d*$") then
Session:locations():remove (l);
end
end
end end
function icon (params) return function (ctx, width, height, fg)
local mh = height - 3.5;
local m3 = width / 3;
local m6 = width / 6;
ctx:set_line_width (.5)
ctx:set_source_rgba (.8, .2, .2, 1.0)
ctx:move_to (width / 2 - m6, 2)
ctx:rel_line_to (m3, 0)
ctx:rel_line_to (0, mh * 0.4)
ctx:rel_line_to (-m6, mh * 0.6)
ctx:rel_line_to (-m6, -mh * 0.6)
ctx:close_path ()
ctx:fill_preserve ()
ctx:set_source_rgba (.0, .0, .0, 1.0)
ctx:stroke ()
ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
ctx:set_line_width (1)
ctx:move_to (width * .2, height * .2)
ctx:line_to (width * .8, height * .8)
ctx:stroke ()
ctx:move_to (width * .8, height * .2)
ctx:line_to (width * .2, height * .8)
ctx:stroke ()
end end

View file

@ -1,78 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Export markers as mp4chaps",
author = "Johannes Mueller",
description = [[
Exports MP4chaps of all markers except xruns. The markers are stored in the
export directory of the session in mp4 chapter marks format. The filename
is mp4chaps.txt
Note that there's always a chapter mark "Intro" at 00:00:00.000 as some
players can't live without it. If there are no exportable markers, the file
is not created.
This is a bit more convenient than the export option, as one does not
have to wait for the export.
]],
license = "GPLv2"
}
function factory (unused_params) return function ()
local fr = Session:sample_rate()
local chaps = {}
for l in Session:locations():list():iter() do
local name = l:name()
if not l:is_mark() or string.find(name, "^xrun%d*$") then
goto next end
local t = l:start() - Session:current_start_sample()
local h = math.floor(t / (3600*fr))
local r = t - (h*3600*fr)
local m = math.floor(r / (60*fr))
r = r - m*60*fr
local s = math.floor(r / fr)
r = r - s*fr
local ms = math.floor(r*1000/fr)
table.insert(chaps, string.format("%02d:%02d:%02d.%03d %s\n", h, m, s, ms, name))
::next::
end
if next(chaps) == nil then
goto out end
table.insert(chaps, "00:00:00.000 Intro\n")
table.sort(chaps)
file = io.open(ARDOUR.LuaAPI.build_filename (Session:path(), "export", "mp4chaps.txt"), "w")
for i, line in ipairs(chaps) do
file:write(line)
end
file:close()
::out::
end end
function icon (params) return function (ctx, width, height, fg)
local mh = height - 3.5;
local m3 = width / 3;
local m6 = width / 6;
ctx:set_line_width (.5)
ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
ctx:move_to (width / 2 - m6, 2)
ctx:rel_line_to (m3, 0)
ctx:rel_line_to (0, mh * 0.4)
ctx:rel_line_to (-m6, mh * 0.6)
ctx:rel_line_to (-m6, -mh * 0.6)
ctx:close_path ()
ctx:stroke ()
local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .5) .. "px")
txt:set_text ("MP4")
local tw, th = txt:get_pixel_size ()
ctx:move_to (.5 * (width - tw), .5 * (height - th))
txt:show_in_cairo_context (ctx)
end end

View file

@ -1,72 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Faders to Trims",
license = "MIT",
author = "PSmith",
description = [[Add 'Trim' plugins to all tracks. Move the fader value into the trim.]]
}
function action_params ()
return
{
}
end
function factory (params)
return function ()
-- loop over all tracks
for t in Session:get_tracks():iter() do
fader_value = t:gain_control():get_value()
if fader_value == 1 then
goto skip
end
if t:gain_control():automation_state() ~= ARDOUR.AutoState.Off then
goto skip
end
-- TODO: skip MIDI tracks without or with a post-fader synth
-- (fader is MIDI-velocity)
v = math.log(fader_value, 10)
trim_gain = 20*v
fader_pos = 0
local proc;
local i = 0;
repeat
-- find the fader proc
proc = t:nth_processor (i)
if (not proc:isnil() and proc:display_name () == "Fader") then
fader_pos = i
end
i = i + 1
until proc:isnil()
-- apply the trim
trim = t:nth_processor (fader_pos+1)
if (not trim:isnil() and trim:display_name () == "a-Amplifier") then
--existing trim found; sum the trim and the fader gain, and set the trim to that value
cur_gain = ARDOUR.LuaAPI.get_processor_param (trim, 0)
ARDOUR.LuaAPI.set_processor_param (trim, 0, trim_gain+cur_gain)
else
--create a new Trim processor, and set its value to match the fader
local a = ARDOUR.LuaAPI.new_luaproc(Session, "a-Amplifier");
if (not a:isnil()) then
t:add_processor_by_index(a, fader_pos-1, nil, true)
ARDOUR.LuaAPI.set_processor_param (a, 0, trim_gain)
a = nil -- explicitly drop shared-ptr reference
end
end
--zero the fader gain
t:gain_control():set_value(1, PBD.GroupControlDisposition.NoGroup)
::skip::
end --foreach track
end --function
end --factory

View file

@ -1,67 +0,0 @@
ardour { ["type"] = "EditorAction", name = "Jump to Marker",
license = "MIT",
author = "Ardour Team",
description = [[Jump to the first marker matching a given pattern]]
}
function factory () return function ()
local keep = false
::restart::
local dlg = LuaDialog.Dialog ("Search and Jump to Marker",
{
{ type = "entry", key = "marker", default = '', title = "Marker Prefix" },
{ type = "checkbox", key = "keep", default = keep, title = "Keep Dialog Open" },
})
local rv = dlg:run()
if not rv then return end
keep = rv['keep']
if (rv['marker'] == "") then
if keep then goto restart end
return
end
for l in Session:locations():list():iter() do
if l:is_mark() and string.find (l:name(), "^" .. rv['marker'] .. ".*$") then
Session:request_locate (l:start (), ARDOUR.LocateTransportDisposition.RollIfAppropriate, ARDOUR.TransportRequestSource.TRS_UI)
if keep then goto restart end
return
end
end
LuaDialog.Message ("Jump to Marker", "No marker matching the given pattern was found.", LuaDialog.MessageType.Warning, LuaDialog.ButtonType.Close):run ()
if keep then goto restart end
end end
function icon (params) return function (ctx, width, height, fg)
local mh = height - 3.5;
local m3 = width / 3;
local m6 = width / 6;
ctx:set_line_width (.5)
-- compare to gtk2_ardour/marker.cc "Marker"
ctx:set_source_rgba (.6, .6, .6, 1.0)
ctx:move_to (width / 2 - m6, 2)
ctx:rel_line_to (m3, 0)
ctx:rel_line_to (0, mh * 0.4)
ctx:rel_line_to (-m6, mh * 0.6)
ctx:rel_line_to (-m6, -mh * 0.6)
ctx:close_path ()
ctx:fill_preserve ()
ctx:set_source_rgba (.0, .0, .0, 1.0)
ctx:stroke ()
ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .5) .. "px")
txt:set_text ("txt")
local tw, th = txt:get_pixel_size ()
ctx:move_to (.5 * (width - tw), .5 * (height - th))
txt:show_in_cairo_context (ctx)
end end

View file

@ -1,189 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Add LFO automation to region",
version = "0.1.1",
license = "MIT",
author = "Daniel Appelt",
description = [[Add LFO-like plugin automation to selected region]]
}
function factory (unused_params)
return function ()
-- Retrieve the first selected region
-- TODO: the following statement should do just that, no!?
-- local region = Editor:get_selection().regions:regionlist():front()
local region = nil
for r in Editor:get_selection().regions:regionlist():iter() do
if region == nil then region = r end
end
-- Bail out if no region was selected
if region == nil then
LuaDialog.Message("Add LFO automation to region", "Please select a region first!",
LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
return
end
-- Identify the track the region belongs to. There really is no better way?!
local track = nil
for route in Session:get_tracks():iter() do
for r in route:to_track():playlist():region_list():iter() do
if r == region then track = route:to_track() end
end
end
-- Get a list of all available plugin parameters on the track. This looks ugly. For the original code
-- see https://github.com/Ardour/ardour/blob/master/scripts/midi_cc_to_automation.lua
local targets = {}
local i = 0
while true do -- iterate over all plugins on the route
if track:nth_plugin(i):isnil() then break end
local proc = track:nth_plugin(i) -- ARDOUR.LuaAPI.plugin_automation() expects a proc not a plugin
local plug = proc:to_insert():plugin(0)
local plug_label = i .. ": " .. plug:name() -- Handle ambiguity if there are multiple plugin instances
local n = 0 -- Count control-ports separately. ARDOUR.LuaAPI.plugin_automation() only returns those.
for j = 0, plug:parameter_count() - 1 do -- Iterate over all parameters
if plug:parameter_is_control(j) then
local label = plug:parameter_label(j)
if plug:parameter_is_input(j) and label ~= "hidden" and label:sub(1,1) ~= "#" then
-- We need two return values: the plugin-instance and the parameter-id. We use a function to
-- return both values in order to avoid another sub-menu level in the dropdown.
local nn = n -- local scope for return value function
targets[plug_label] = targets[plug_label] or {}
targets[plug_label][label] = function() return {["p"] = proc, ["n"] = nn} end
end
n = n + 1
end
end
i = i + 1
end
-- Bail out if there are no plugin parameters
if next(targets) == nil then
LuaDialog.Message("Add LFO automation to region", "No plugin parameters found.",
LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
region, track, targets = nil, nil, nil
collectgarbage()
return
end
-- Display dialog to select (plugin and) plugin parameter, and LFO cycle type + min / max
local dialog_options = {
{ type = "heading", title = "Add LFO automation to region", align = "left"},
{ type = "dropdown", key = "param", title = "Plugin parameter", values = targets },
{ type = "dropdown", key = "wave", title = "Waveform", values = {
["Ramp up"] = 1, ["Ramp down"] = 2, ["Triangle"] = 3, ["Sine"] = 4,
["Exp up"] = 5, ["Exp down"] = 6, ["Log up"] = 7, ["Log down"] = 8 } },
{ type = "number", key = "cycles", title = "No. of cycles", min = 1, max = 16, step = 1, digits = 0 },
{ type = "slider", key = "min", title = "Minimum in %", min = 0, max = 100, digits = 1 },
{ type = "slider", key = "max", title = "Maximum in %", min = 0, max = 100, digits = 1, default = 100 }
}
local rv = LuaDialog.Dialog("Select target", dialog_options):run()
-- Return if the user cancelled
if not rv then
region, track, targets = nil, nil, nil
collectgarbage()
return
end
-- Parse user response
assert(type(rv["param"]) == "function")
local pp = rv["param"]() -- evaluate function, retrieve table {["p"] = proc, ["n"] = nn}
local al, _, pd = ARDOUR.LuaAPI.plugin_automation(pp["p"], pp["n"])
local wave = rv["wave"]
local cycles = rv["cycles"]
-- Compute minimum and maximum requested parameter values
local lower = pd.lower + rv["min"] / 100 * (pd.upper - pd.lower)
local upper = pd.lower + rv["max"] / 100 * (pd.upper - pd.lower)
track, targets, rv, pd = nil, nil, nil, nil
assert(not al:isnil())
-- Define lookup tables for our waves. Empty ones will be calculated in a separate step.
-- TODO: at this point we already know which one is needed, still we compute all.
local lut = {
{ 0, 1 }, -- ramp up
{ 1, 0 }, -- ramp down
{ 0, 1, 0 }, -- triangle
{}, -- sine
{}, -- exp up
{}, -- exp down
{}, -- log up
{} -- log down
}
-- Calculate missing look up tables
local log_min = math.exp(-2 * math.pi)
for i = 0, 20 do
-- sine
lut[4][i+1] = 0.5 * math.sin(i * math.pi / 10) + 0.5
-- exp up
lut[5][i+1] = math.exp(-2 * math.pi + i * math.pi / 10)
-- log up
lut[7][i+1] = -math.log(1 + (i / log_min - i) / 20) / math.log(log_min)
end
-- "down" variants just need the values in reverse order
for i = 21, 1, -1 do
-- exp down
lut[6][22-i] = lut[5][i]
-- log down
lut[8][22-i] = lut[7][i]
end
-- Initialize undo
Session:begin_reversible_command("Add LFO automation to region")
local before = al:get_state() -- save previous state (for undo)
al:clear_list() -- clear target automation-list
local values = lut[wave]
local last = nil
for i = 0, cycles - 1 do
-- cycle length = region:length() / cycles
local cycle_start = region:position() - region:start() + i * region:length() / cycles
local offset = region:length() / cycles / (#values - 1)
for k, v in pairs(values) do
local pos = cycle_start + (k - 1) * offset
if k == 1 and v ~= last then
-- Move event one sample further. A larger offset might be needed to avoid unwanted effects.
pos = pos + 1
end
if k > 1 or v ~= last then
-- Create automation point re-scaled to parameter target range. Do not create a new point
-- at cycle start if the last cycle ended on the same value.
al:add(pos, lower + v * (upper - lower), false, true)
end
last = v
end
end
-- remove dense events
al:thin (20) -- threashold of area below curve
-- TODO: display the modified automation lane in the time line in order to make the change visible!
-- Save undo
-- TODO: in Ardour 5.12 this does not lead to an actual entry in the undo list?!
Session:add_command(al:memento_command(before, al:get_state()))
Session:commit_reversible_command(nil)
region, al, lut = nil, nil, nil
collectgarbage()
end
end
function icon (params) return function (ctx, width, height, fg)
local yc = height * .5
local x0 = math.ceil (width * .1)
local x1 = math.floor (width * .9)
ctx:set_line_width (1)
ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
ctx:move_to (x0, yc * 1.5)
for x = x0, x1 do
ctx:line_to (x, yc + math.cos (3 * math.pi * (x-x0) / (x1-x0)) * yc * .5)
end
ctx:stroke ()
end end

View file

@ -1,50 +0,0 @@
ardour { ["type"] = "EditorAction", name = "List Plugins",
license = "MIT",
author = "Ardour Team",
description = [[List and count plugins used in this session]]
}
function factory () return function ()
local rv = "Plugins used in this session:\n<span face=\"mono\">CNT | TYPE | NAME</span>"
local all_plugs = {}
for r in Session:get_routes ():iter () do
if r:is_monitor () or r:is_auditioner () then goto nextroute end -- skip special routes
local i = 0
while true do
local proc = r:nth_plugin (i)
if proc:isnil () then break end
local pi = proc:to_insert () -- we know it's a plugin-insert (we asked for nth_plugin)
local pp = pi:plugin (0)
local id = pi:type() .. "-" .. pp:unique_id()
local cnt = 0
if all_plugs[id] then cnt = all_plugs[id]['cnt'] end
all_plugs[id] = { name = proc:name(), ["type"] = pi:type(), id = pp:unique_id(), cnt = (cnt + 1) }
i = i + 1
end
::nextroute::
end
function plugintypestr (t)
if (t == ARDOUR.PluginType.LADSPA) then return "LADSPA" end
if (t == ARDOUR.PluginType.LV2) then return "LV2" end
if (t == ARDOUR.PluginType.AudioUnit) then return "AU" end
if (t == ARDOUR.PluginType.Windows_VST) then return "VST" end
if (t == ARDOUR.PluginType.LXVST) then return "VST" end
if (t == ARDOUR.PluginType.MacVST) then return "VST" end
if (t == ARDOUR.PluginType.Lua) then return "Lua" end
return "??"
end
for k,v in pairs (all_plugs) do
print (string.format ("%2d * %-6s %-30s (%s)", v['cnt'], plugintypestr(v['type']), v['name'], v['id']))
rv = rv .. "\n" .. string.format ("%2d * %-6s %s", v['cnt'], plugintypestr(v['type']), v['name'])
end
LuaDialog.Message ("All Plugins", "<span face=\"mono\">" .. rv .. "</span>", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
all_plugs = nil
rv = ""
collectgarbage ();
end end

View file

@ -1,82 +0,0 @@
ardour {
["type"] = "dsp",
name = "LTC Reader",
category = "Utility",
author = "Ardour Team",
license = "MIT",
description = [[Linear Timecode Decoder with mixer strip inline display]]
}
function dsp_ioconfig ()
return { { audio_in = 1, audio_out = 1}, }
end
function dsp_init (rate)
timeout = rate
samplerate = rate
ltc_reader = ARDOUR.DSP.LTCReader (rate / 25, ARDOUR.DSP.LTC_TV_STANDARD.LTC_TV_FILM_24)
self:shmem():allocate(5)
end
function dsp_run (ins, outs, n_samples)
if ins[1] ~= outs[1] then
ARDOUR.DSP.copy_vector (outs[1]:offset (0), ins[1]:offset (0), n_samples)
end
ltc_reader:write (ins[1]:offset (0), n_samples, 0)
timeout = timeout + n_samples
local to_ui = self:shmem():to_int(0):array()
local rv
repeat
local tc
rv, tc = ltc_reader:read (0, 0, 0, 0)
if rv >= 0 then
timeout = 0
self:shmem():atomic_set_int (0, 1)
self:shmem():atomic_set_int (1, tc[1])
self:shmem():atomic_set_int (2, tc[2])
self:shmem():atomic_set_int (3, tc[3])
self:shmem():atomic_set_int (4, tc[4])
self:queue_draw ()
end
until rv < 0
if timeout > samplerate then
if 0 ~= self:shmem():atomic_get_int (0) then
self:shmem():atomic_set_int (0, 0)
self:queue_draw ()
end
end
end
-------------------------------------------------------------------------------
-- inline UI
--
local txt = nil -- a pango context
local vpadding = 2
function render_inline (ctx, displaywidth, max_h)
if not txt then
txt = Cairo.PangoLayout (ctx, "Mono 10px")
end
local d = self:shmem():to_int(0):array()
if d[1] == 0 then
txt:set_text("--:--:--:--")
else
txt:set_text(string.format("%02d:%02d:%02d:%02d", d[2], d[3], d[4], d[5]))
end
-- compute the size of the display
local txtwidth, lineheight = txt:get_pixel_size()
local displayheight = math.min(vpadding + (lineheight + vpadding), max_h)
-- clear background
ctx:rectangle (0, 0, displaywidth, displayheight)
ctx:set_source_rgba (.2, .2, .2, 1.0)
ctx:fill ()
ctx:set_source_rgba (.8, .8, .8, 1.0)
ctx:move_to ((displaywidth - txtwidth) / 2, 1)
txt:show_in_cairo_context (ctx)
return {displaywidth, displayheight}
end

View file

@ -1,42 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Meter Tap",
author = "Ardour Lua Taskforce",
description = [[Change Metering Point for tracks in your session.]]
}
function factory () return function ()
local dialog_options = {
{ type = "label", colspan = 5, title = "" },
{ type = "radio", col = 1, colspan = 7, key = "select", title = "", values ={ ["Set All: Input"] = ARDOUR.MeterPoint.MeterInput, ["Set All: Pre Fader"] = ARDOUR.MeterPoint.MeterPreFader, ["Set All: Post Fader"] = ARDOUR.MeterPoint.MeterPostFader, ["Set All: Output"] = ARDOUR.MeterPoint.MeterOutput, ["Set All: Custom"] = ARDOUR.MeterPoint.MeterCustom}, default = "Set All: Input"},
{ type = "label", colspan = 5, title = "" },
{ type = "checkbox", col=1, colspan = 1, key = "select-tracks", default = true, title = "Selected tracks only"},
{ type = "checkbox", col=2, colspan = 1, key = "rec-tracks", default = true, title = "Record Enabled tracks only"},
{ type = "label", colspan = 5, title = "" },
}
local rv = LuaDialog.Dialog("Change all Meter Taps:", dialog_options):run()
if not rv then return end -- user cancelled
local rl;
if rv['select-tracks'] then
rl = Editor:get_selection ()
else
rl = Session:get_routes()
end
local meter_point = rv['select']
for route in rl:iter() do
if not(route:to_track():isnil()) then
if rv['rec-tracks'] then
if route:rec_enable_control():get_value() == 1.0 then
route:to_track():set_meter_point(meter_point, false)
end
else
route:to_track():set_meter_point(meter_point, false)
end
end
end
end end

View file

@ -1,130 +0,0 @@
ardour { ["type"] = "EditorAction", name = "MIDI CC to Plugin Automation",
license = "MIT",
author = "Ardour Team",
description = [[Parse a given MIDI control changes (CC) from all selected MIDI regions and convert them into plugin parameter automation]]
}
function factory () return function ()
-- find possible target parameters, collect them in a nested table
-- [track-name] -> [plugin-name] -> [parameters]
-- to allow selection in a dropdown menu
local targets = {}
local have_entries = false
for r in Session:get_routes ():iter () do -- for every track/bus
if r:is_monitor () or r:is_auditioner () then goto nextroute end -- skip special routes
local i = 0
while true do -- iterate over all plugins on the route
local proc = r:nth_plugin (i)
if proc:isnil () then break end
local plug = proc:to_insert ():plugin (0) -- we know it's a plugin-insert (we asked for nth_plugin)
local n = 0 -- count control-ports
for j = 0, plug:parameter_count () - 1 do -- iterate over all plugin parameters
if plug:parameter_is_control (j) then
local label = plug:parameter_label (j)
if plug:parameter_is_input (j) and label ~= "hidden" and label:sub (1,1) ~= "#" then
local nn = n --local scope for return value function
-- create table parents only if needed (if there's at least one parameter)
if not targets [r:name ()] then targets [r:name ()] = {} end
-- TODO handle ambiguity if there are 2 plugins with the same name on the same track
if not targets [r:name ()][proc:display_name ()] then targets [r:name ()][proc:display_name ()] = {} end
-- we need 2 return values: the plugin-instance and the parameter-id, so we use a table (associative array)
-- however, we cannot directly use a table: the dropdown menu would expand it as another sub-menu.
-- so we produce a function that will return the table.
targets [r:name ()][proc:display_name ()][label] = function () return {["p"] = proc, ["n"] = nn} end
have_entries = true
end
n = n + 1
end
end
i = i + 1
end
::nextroute::
end
-- bail out if there are no parameters
if not have_entries then
LuaDialog.Message ("CC to Plugin Automation", "No Plugins found", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run ()
targets = nil
collectgarbage ()
return
end
-- create a dialog, ask user which MIDI-CC to map and to what parameter
local dialog_options = {
{ type = "heading", title = "MIDI CC Source", align = "left" },
{ type = "number", key = "channel", title = "Channel", min = 1, max = 16, step = 1, digits = 0 },
{ type = "number", key = "ccparam", title = "CC Parameter", min = 0, max = 127, step = 1, digits = 0 },
{ type = "heading", title = "Target Track and Plugin", align = "left"},
{ type = "dropdown", key = "param", title = "Target Parameter", values = targets }
}
local rv = LuaDialog.Dialog ("Select Taget", dialog_options):run ()
targets = nil -- drop references (the table holds shared-pointer references to all plugins)
collectgarbage () -- and release the references immediately
if not rv then return end -- user cancelled
-- parse user response
assert (type (rv["param"]) == "function")
local midi_channel = rv["channel"] - 1 -- MIDI channel 0..15
local cc_param = rv["ccparam"]
local pp = rv["param"]() -- evaluate function, retrieve table {["p"] = proc, ["n"] = nn}
local al, _, pd = ARDOUR.LuaAPI.plugin_automation (pp["p"], pp["n"])
rv = nil -- drop references
assert (not al:isnil ())
assert (midi_channel >= 0 and midi_channel < 16)
assert (cc_param >= 0 and cc_param < 128)
-- all systems go
local add_undo = false
Session:begin_reversible_command ("CC to Automation")
local before = al:get_state () -- save previous state (for undo)
al:clear_list () -- clear target automation-list
-- for all selected MIDI regions
local sel = Editor:get_selection ()
for r in sel.regions:regionlist ():iter () do
local mr = r:to_midiregion ()
if mr:isnil () then goto next end
-- get list of MIDI-CC events for the given channel and parameter
local ec = mr:control (Evoral.Parameter (ARDOUR.AutomationType.MidiCCAutomation, midi_channel, cc_param), false)
if ec:isnil () then goto next end
if ec:list ():events ():size () == 0 then goto next end
-- MIDI events are timestamped in "bar-beat" units, we need to convert those
-- using the tempo-map, relative to the region-start
local bfc = ARDOUR.DoubleBeatsSamplesConverter (Session:tempo_map (), r:start ())
-- iterate over CC-events
for av in ec:list ():events ():iter () do
-- re-scale event to target range
local val = pd.lower + (pd.upper - pd.lower) * av.value / 127
-- and add it to the target-parameter automation-list
al:add (r:position () - r:start () + bfc:to (av.when), val, false, true)
add_undo = true
end
::next::
end
-- save undo
if add_undo then
local after = al:get_state ()
Session:add_command (al:memento_command (before, after))
Session:commit_reversible_command (nil)
else
Session:abort_reversible_command ()
LuaDialog.Message ("CC to Plugin Automation", "No data was converted. Was a MIDI-region with CC-automation selected? ", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run ()
end
collectgarbage ()
end end
function icon (params) return function (ctx, width, height, fg)
local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil (height / 3) .. "px")
txt:set_text ("CC\nPA")
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

View file

@ -1,97 +0,0 @@
ardour {
["type"] = "dsp",
name = "MIDI Note Mapper",
category = "Utility",
license = "MIT",
author = "Alby Musaelian",
description = [[Map arbitrary MIDI notes to others. Affects Note On/Off and polyphonic key pressure. Note that if a single note is mapped multiple times, the last mapping wins -- MIDI events are never duplicated.]]
}
-- The number of remapping pairs to allow. Increasing this (at least in theory)
-- decreases performace, so it's set fairly low as a default. The user can
-- increase this if they have a need to.
N_REMAPINGS = 10
OFF_NOTE = -1
function dsp_ioconfig ()
return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, }
end
function dsp_params ()
local map_scalepoints = {}
map_scalepoints["None"] = OFF_NOTE
for note=0,127 do
local name = ARDOUR.ParameterDescriptor.midi_note_name(note)
map_scalepoints[string.format("%03d (%s)", note, name)] = note
end
local map_params = {}
i = 1
for mapnum = 1,N_REMAPINGS do
-- From and to
for _,name in pairs({"| #" .. mapnum .. " Map note", "|__ to"}) do
map_params[i] = {
["type"] = "input",
name = name,
min = -1,
max = 127,
default = OFF_NOTE,
integer = true,
enum = true,
scalepoints = map_scalepoints
}
i = i + 1
end
end
return map_params
end
function dsp_run (_, _, n_samples)
assert (type(midiin) == "table")
assert (type(midiout) == "table")
local cnt = 1;
function tx_midi (time, data)
midiout[cnt] = {}
midiout[cnt]["time"] = time;
midiout[cnt]["data"] = data;
cnt = cnt + 1;
end
-- We build the translation table every buffer because, as far as I can tell,
-- there's no way to only rebuild it when the parameters have changed.
-- As a result, it has to be updated every buffer for the parameters to have
-- any effect.
-- Restore translation table
local translation_table = {}
local ctrl = CtrlPorts:array()
for i=1,N_REMAPINGS*2,2 do
if not (ctrl[i] == OFF_NOTE) then
translation_table[ctrl[i]] = ctrl[i + 1]
end
end
-- for each incoming midi event
for _,b in pairs (midiin) do
local t = b["time"] -- t = [ 1 .. n_samples ]
local d = b["data"] -- get midi-event
local event_type
if #d == 0 then event_type = -1 else event_type = d[1] >> 4 end
if (#d == 3) and (event_type == 9 or event_type == 8 or event_type == 10) then -- note on, note off, poly. afterpressure
-- Do the mapping - 2 is note byte for these types
d[2] = translation_table[d[2]] or d[2]
if not (d[2] == OFF_NOTE) then
tx_midi (t, d)
end
else
tx_midi (t, d)
end
end
end

View file

@ -1,213 +0,0 @@
ardour {
["type"] = "dsp",
name = "a-MIDI Monitor",
category = "Visualization",
license = "GPLv2",
author = "Ardour Team",
description = [[Display recent MIDI events inline in the mixer strip]]
}
local maxevents = 20
local ringsize = maxevents * 3
local evlen = 3
local hpadding, vpadding = 4, 2
function dsp_ioconfig ()
return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, }
end
function dsp_params ()
return
{
{ ["type"] = "input",
name = "Font size",
doc = "Text size used by the monitor to display midi events",
min = 4, max = 12, default = 7, integer = true },
{ ["type"] = "input",
name = "Line count",
doc = "How many events will be shown at most",
min = 1, max = maxevents, default = 6, integer = true },
{ ["type"] = "input",
name = "Hexadecimal",
doc = "If enabled, values will be printed in hexadecimal notation",
min = 0, max = 1, default = 0, toggled = true },
{ ["type"] = "input",
name = "System messages",
doc = "If enabled, the monitor will show System Control and Real-Time messages",
min = 0, max = 1, default = 0, toggled = true },
{ ["type"] = "input",
name = "Numeric Notes",
doc = "If enabled, note-events displayed numerically",
min = 0, max = 1, default = 0, toggled = true },
}
end
function dsp_init (rate)
-- create a shmem space to hold latest midi events
-- a int representing the index of the last event, and
-- a C-table as storage for events.
self:shmem():allocate(1 + ringsize*evlen)
self:shmem():atomic_set_int(0, 1)
local buffer = self:shmem():to_int(1):array()
for i = 1, ringsize*evlen do
buffer[i] = -1 -- sentinel for empty slot
end
end
function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
local pos = self:shmem():atomic_get_int(0)
local buffer = self:shmem():to_int(1):array()
-- passthrough all data
ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio"))
ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("midi"))
-- then fill the event buffer
local ib = in_map:get (ARDOUR.DataType ("midi"), 0) -- index of 1st midi input
if ib ~= ARDOUR.ChanMapping.Invalid then
local events = bufs:get_midi (ib):table () -- copy event list into a lua table
-- iterate over all MIDI events
for _, e in pairs (events) do
local ev = e:buffer():array()
pos = pos % ringsize + 1
-- copy the data
for j = 1, math.min(e:size(),evlen) do
buffer[(pos-1)*evlen + j] = ev[j]
end
-- zero unused slots
for j = e:size()+1, evlen do
buffer[(pos-1)*evlen + j] = 0
end
end
end
self:shmem():atomic_set_int(0, pos)
self:queue_draw ()
end
local txt = nil -- a pango context
local cursize = 0
local hex = nil
local format_note = nil
local show_scm = nil
function format_note_name(b)
return string.format ("%5s", ARDOUR.ParameterDescriptor.midi_note_name (b))
end
function format_note_num(b)
return string.format (hex, b)
end
function show_midi(ctx, x, y, buffer, event)
local base = (event - 1) * evlen
if buffer[base+1] == -1 then return false end
local evtype = buffer[base + 1] >> 4
local channel = (buffer[base + 1] & 15) + 1 -- for System Common Messages this has no use
if evtype == 8 then
txt:set_text(string.format("%02u \u{2669}Off%s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3]))
elseif evtype == 9 then
txt:set_text(string.format("%02u \u{2669}On %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3]))
elseif evtype == 10 then
txt:set_text(string.format("%02u \u{2669}KP %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3]))
elseif evtype == 11 then
txt:set_text(string.format("%02u CC " .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
elseif evtype == 12 then
txt:set_text(string.format("%02u PRG " .. hex, channel, buffer[base+2]))
elseif evtype == 13 then
txt:set_text(string.format("%02u KP " .. hex, channel, buffer[base+2]))
elseif evtype == 14 then
txt:set_text(string.format("%02u PBnd" .. hex, channel, buffer[base+2] | buffer[base+3] << 7))
elseif show_scm > 0 then -- System Common Message
local message = buffer[base + 1] & 15
if message == 0 then
txt:set_text("-- SysEx")
elseif message == 1 then
txt:set_text(string.format("-- Time Code" .. hex, buffer[base+2]))
elseif message == 2 then
txt:set_text(string.format("-- Song Pos" .. hex, buffer[base+2] | buffer[base+3] << 7))
elseif message == 3 then
txt:set_text(string.format("-- Select Song" .. hex, buffer[base+2]))
elseif message == 6 then
txt:set_text("-- Tune Rq")
elseif message == 8 then
txt:set_text("-- Timing")
elseif message == 10 then
txt:set_text("-- Start")
elseif message == 11 then
txt:set_text("-- Continue")
elseif message == 12 then
txt:set_text("-- Stop")
elseif message == 14 then
txt:set_text("-- Active")
elseif message == 15 then
txt:set_text("-- Reset")
else
return false
end
else
return false
end
ctx:move_to (x, y)
txt:show_in_cairo_context (ctx)
return true
end
function render_inline (ctx, displaywidth, max_h)
local ctrl = CtrlPorts:array ()
local pos = self:shmem():atomic_get_int(0)
local buffer = self:shmem():to_int(1):array()
local count = ctrl[2]
if not txt or cursize ~= ctrl[1] then
cursize = math.floor(ctrl[1])
txt = Cairo.PangoLayout (ctx, "Mono " .. cursize)
end
if ctrl[3] > 0 then hex = " %02X" else hex = " %3u" end
show_scm = ctrl[4]
if ctrl[5] > 0 then
format_note = format_note_num
else
format_note = format_note_name
end
-- compute the size of the display
txt:set_text("0")
local _, lineheight = txt:get_pixel_size()
local displayheight = math.min(vpadding + (lineheight + vpadding) * count, max_h)
-- compute starting position (pango anchors text at north-west corner)
local x, y = hpadding, displayheight - lineheight - vpadding
-- clear background
ctx:rectangle (0, 0, displaywidth, displayheight)
ctx:set_source_rgba (.2, .2, .2, 1.0)
ctx:fill ()
-- color of latest event
ctx:set_source_rgba (1.0, 1.0, 1.0, 1.0)
-- print events
for i = pos, 1, -1 do
if y < 0 then break end
if show_midi(ctx, x, y, buffer, i) then
y = y - lineheight - vpadding
ctx:set_source_rgba (.8, .8, .8, 1.0)
end
end
for i = ringsize, pos+1, -1 do
if y < 0 then break end
if show_midi(ctx, x, y, buffer, i) then
y = y - lineheight - vpadding
ctx:set_source_rgba (.8, .8, .8, 1.0)
end
end
return {displaywidth, displayheight}
end

View file

@ -1,46 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Mixer Screenshot",
author = "Ardour Team",
description = [[Save a screenshot of the complete mixer-window, regardless of scrollbars or visible screen area]]
}
function factory () return function ()
local rv = LuaDialog.Dialog ("Save Mixer Screenshot",
{
{ type = "createfile", key = "file", title = "", path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "mixer.png") },
}):run()
if (rv) then
if (ARDOUR.LuaAPI.file_test (rv['file'], ARDOUR.LuaAPI.FileTest.Exists)) then
local ok = LuaDialog.Message ("File Exists", "File '".. rv['file'] .. "' exists.\nReplace?", LuaDialog.MessageType.Question, LuaDialog.ButtonType.Yes_No):run ()
if ok ~= LuaDialog.Response.Yes then
return
end
end
ArdourUI.mixer_screenshot (rv['file'])
end
collectgarbage ()
end end
function icon (params) return function (ctx, width, height, fg)
local wh = math.min (width, height) * .5
ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh))
ctx:rectangle (wh * .6, wh * .6, wh * .8, wh * .8)
ctx:set_source_rgba (.1, .1, .1, 1)
ctx:fill ()
ctx:set_line_width (1)
ctx:set_source_rgba (.9, .9, .9, 1)
ctx:move_to (wh * 0.3, wh * 0.6)
ctx:line_to (wh * 1.5, wh * 0.6)
ctx:line_to (wh * 1.5, wh * 1.7)
ctx:stroke ()
ctx:move_to (wh * 0.6, wh * 0.3)
ctx:line_to (wh * 0.6, wh * 1.4)
ctx:line_to (wh * 1.8, wh * 1.4)
ctx:stroke ()
end end

View file

@ -1,470 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Recall Mixer Settings",
author = "Mixbus Team",
description = [[
Recalls mixer settings outined by files
created by Store Mixer Settings.
Allows for some room to change Source
and Destination.
]]
}
function factory ()
local acoraida_monicas_last_used_recall_file
return function ()
local user_cfg = ARDOUR.user_config_directory(-1)
local local_path = ARDOUR.LuaAPI.build_filename(Session:path(), 'mixer_settings')
local global_path = ARDOUR.LuaAPI.build_filename(user_cfg, 'mixer_settings')
local invalidate = {}
function exists(file)
local ok, err, code = os.rename(file, file)
if not ok then
if code == 13 then -- Permission denied, but it exists
return true
end
end return ok, err
end
function whoami()
if not pcall(function() local first_check = Session:get_mixbus(0) end) then
return "Ardour"
else
local second_check = Session:get_mixbus(11)
if second_check:isnil() then
return "Mixbus"
else
return "32C"
end
end
end
function isdir(path)
return exists(path.."/")
end
function get_processor_by_name(track, name)
local i = 0
local proc = track:nth_processor(i)
repeat
if(proc:display_name() == name) then
return proc
else
i = i + 1
end
proc = track:nth_processor(i)
until proc:isnil()
end
function new_plugin(name, type)
local plugin = ARDOUR.LuaAPI.new_plugin(Session, name, type, "")
if not(plugin:isnil()) then return plugin end
end
function group_by_id(id)
local id = tonumber(id)
for g in Session:route_groups():iter() do
local group_id = tonumber(g:to_stateful():id():to_s())
if group_id == id then return g end
end
end
function group_by_name(name)
for g in Session:route_groups():iter() do
if g:name() == name then return g end
end
end
function route_groupid_interrogate(t)
local group = false
for g in Session:route_groups():iter() do
for r in g:route_list():iter() do
if r:name() == t:name() then group = g:to_stateful():id():to_s() end
end
end return group
end
function route_group_interrogate(t)
for g in Session:route_groups():iter() do
for r in g:route_list():iter() do
if r:name() == t:name() then return g end
end
end
end
function recall(debug, path, dry_run)
local file = io.open(path, "r")
assert(file, "File not found!")
local bypass_routes = {}
local i = 0
for l in file:lines() do
--print(i, l)
local create_groups = dry_run["create_groups"]
local skip_line = false
local plugin, route, group = false, false, false
local f = load(l)
if debug then
--print('create_groups ' .. tostring(create_groups))
print(i, string.sub(l, 0, 29), f)
end
if f then f() end
if instance["route_id"] then route = true end
if instance["plugin_id"] then plugin = true end
if instance["group_id"] then group = true end
if group then
local g_id = instance["group_id"]
local routes = instance["routes"]
local name = instance["name"]
local group = group_by_id(g_id)
if not(group) then
if create_groups then
local group = Session:new_route_group(name)
for _, v in pairs(routes) do
local rt = Session:route_by_id(PBD.ID(v))
if rt:isnil() then rt = Session:route_by_name(name) end
if not(rt:isnil()) then group:add(rt) end
end
end
end
end
if route then
local substitution = tonumber(dry_run["destination-"..i])
if substitution == 0 then
bypass_routes[#bypass_routes + 1] = instance["route_id"]
goto nextline
end
local old_order = ARDOUR.ProcessorList()
local route_id = instance["route_id"]
local r_id = PBD.ID(instance["route_id"])
local muted, soloed = instance["muted"], instance["soloed"]
local order = instance["order"]
local cache = instance["cache"]
local group = instance["group"]
local group_name = instance["group_name"]
local name = instance["route_name"]
local gc, tc, pc = instance["gain_control"], instance["trim_control"], instance["pan_control"]
local sends = instance["sends"]
if not(substitution == instance["route_id"]) then
print('SUBSTITUTION FOR: ', name, substitution, Session:route_by_id(PBD.ID(substitution)):name())
--bypass_routes[#bypass_routes + 1] = route_id
was_subbed = true
r_id = PBD.ID(substitution)
end
local rt = Session:route_by_id(r_id)
if rt:isnil() then rt = Session:route_by_name(name) end
if rt:isnil() then goto nextline end
if sends then
for i, data in pairs(sends) do
i = i-1
for j, ctrl in pairs({
rt:send_level_controllable(i),
rt:send_enable_controllable(i),
rt:send_pan_azimuth_controllable(i),
rt:send_pan_azimuth_enable_controllable(i),
}) do
if not(ctrl:isnil()) then
local value = data[j]
if value then
if debug then
print("Setting " .. ctrl:name() .. " to value " .. value)
end
ctrl:set_value(value, PBD.GroupControlDisposition.NoGroup)
end
end
end
end
end
local cur_group_id = route_groupid_interrogate(rt)
if not(group) and (cur_group_id) then
local g = group_by_id(cur_group_id)
if g then g:remove(rt) end
end
local rt_group = group_by_name(group_name)
if rt_group then rt_group:add(rt) end
well_known = {'PRE', 'Trim', 'EQ', 'Comp', 'Fader', 'POST'}
protected_instrument = false
for k, v in pairs(order) do
local proc = Session:processor_by_id(PBD.ID(1))
if not(was_subbed) then
proc = Session:processor_by_id(PBD.ID(v))
end
if proc:isnil() then
for id, sub_tbl in pairs(cache) do
local name = sub_tbl[1]
local type = sub_tbl[2]
if v == id then
proc = new_plugin(name, type)
for _, control in pairs(well_known) do
if name == control then
proc = get_processor_by_name(rt, control)
invalidate[v] = proc:to_stateful():id():to_s()
goto nextproc
end
end
if not(proc) then goto nextproc end
if not(proc:isnil()) then
rt:add_processor_by_index(proc, 0, nil, true)
invalidate[v] = proc:to_stateful():id():to_s()
end
end
end
end
::nextproc::
if proc and not(proc:isnil()) then old_order:push_back(proc) end
if not(old_order:empty()) and not(protected_instrument) then
if not(rt:to_track():to_midi_track():isnil()) then
if not(rt:the_instrument():isnil()) then
protected_instrument = true
old_order:push_back(rt:the_instrument())
end
end
end
end
rt:reorder_processors(old_order, nil)
if muted then rt:mute_control():set_value(1, 1) else rt:mute_control():set_value(0, 1) end
if soloed then rt:solo_control():set_value(1, 1) else rt:solo_control():set_value(0, 1) end
rt:gain_control():set_value(gc, 1)
rt:trim_control():set_value(tc, 1)
if pc ~= false and not(rt:is_master()) then rt:pan_azimuth_control():set_value(pc, 1) end
end
if plugin then
--if the plugin is owned by a route
--we decided not to use, skip it
for _, v in pairs(bypass_routes) do
if instance["owned_by_route_id"] == v then
goto nextline
end
end
local enable = {}
local params = instance["parameters"]
local p_id = instance["plugin_id"]
local act = instance["active"]
for k, v in pairs(invalidate) do --invalidate any deleted plugin's id
if p_id == k then
p_id = v
end
end
local proc = Session:processor_by_id(PBD.ID(p_id))
if proc:isnil() then goto nextline end
local plug = proc:to_insert():plugin(0)
for k, v in pairs(params) do
local label = plug:parameter_label(k)
if string.find(label, "Assign") or string.find(label, "Enable") then --@ToDo: Check Plugin type == LADSPA or VST?
enable[k] = v --queue any assignments/enables for after the initial parameter recalling to duck the 'in-on-change' feature
end
print(string.format("%s (Port: %s) -> %s", label, k, v))
ARDOUR.LuaAPI.set_processor_param(proc, k, v)
end
for k, v in pairs(enable) do
ARDOUR.LuaAPI.set_processor_param(proc, k, v)
end
if act then proc:activate() else proc:deactivate() end
end
::nextline::
i = i + 1
end
end
function dry_run(debug, path)
--returns a dialog-able table of
--everything we do (logically)
--in the recall function
local route_values = {['----'] = "0"}
for r in Session:get_routes():iter() do
route_values[r:name()] = r:to_stateful():id():to_s()
end
local i = 0
local dry_table = {
{type = "label", align="right", key="col-1-title", col=0, colspan=1, title = 'Source:'},
{type = "label", align="left", key="col-2-title", col=1, colspan=1, title = 'Destination:'},
}
local file = io.open(path, "r")
assert(file, "File not found!")
pad = 0
for l in file:lines() do
local do_plugin, do_route, do_group = false, false, false
local f = load(l)
if debug then
print(i, string.sub(l, 0, 29), f)
end
if f then f() end
if instance["route_id"] then do_route = true end
if instance["plugin_id"] then do_plugin = true end
if instance["group_id"] then do_group = true end
if do_group then
local group_id = instance["group_id"]
local group_name = instance["name"]
local dlg_title, action_title = "", ""
local group_ptr = group_by_id(group_id)
if not(group_ptr) then
dlg_title = string.format("(Group) %s.", group_name)
--action_title = "will create and use settings"
else
dlg_title = string.format("(Group) %s.", group_ptr:name())
--action_title = "will use group settings"
end
table.insert(dry_table, {
order=pad, type = "label", align="right", key = "group-"..i , col = 0, colspan = 1, title = dlg_title
})
pad = pad + 1
end
if do_route then
local route_id = instance["route_id"]
local route_name = instance["route_name"]
local dlg_title = ""
local route_ptr = Session:route_by_id(PBD.ID(route_id))
if route_ptr:isnil() then
route_ptr = Session:route_by_name(route_name)
if not(route_ptr:isnil()) then
dlg_title = string.format("%s", route_ptr:name())
--action_title = "will use route settings"
else
dlg_title = string.format("%s", route_name)
--action_title = "will be ignored"
end
else
dlg_title = string.format("%s", route_ptr:name())
--action_title = "will use route settings"
end
if route_ptr:isnil() then name = route_name else name = route_ptr:name() end
table.insert(dry_table, {
order=instance['pi_order']+pad, type = "label", align="right", key = "route-"..i , col = 0, colspan = 1, title = dlg_title
})
table.insert(dry_table, {
type = "dropdown", align="left", key = "destination-"..i, col = 1, colspan = 1, title = "", values = route_values, default = name or "----"
})
end
i = i + 1
end
table.insert(dry_table, {
type = "checkbox", col=0, colspan=2, align="left", key = "create_groups", default = true, title = "Create Groups if necessary?"
})
return dry_table
end
local global_vs_local_dlg = {
{ type = "label", col=0, colspan=20, align="left", title = "" },
{
type = "radio", col=0, colspan=20, align="left", key = "recall-dir", title = "", values =
{
['Pick from Global Settings'] = 1, ['Pick from Local Settings'] = 2, ['Last Used Recall File'] = 3,
},
default = 'Last Used Recall File'
},
{ type = "label", col=0, colspan=20, align="left", title = ""},
}
local recall_options = {
{ type = "label", col=0, colspan=10, align="left", title = "" },
{ type = "file", col=0, colspan=15, align="left", key = "file", title = "Select a Settings File", path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "params.lua") },
{ type = "label", col=0, colspan=10, align="left", title = "" },
}
local gvld = LuaDialog.Dialog("Recall Mixer Settings:", global_vs_local_dlg):run()
if not(gvld) then
return
else
if gvld['recall-dir'] == 1 then
local global_ok = isdir(global_path)
local global_default_path = ARDOUR.LuaAPI.build_filename(global_path, string.format("FactoryDefault-%s.lua", whoami()))
print(global_default_path)
if global_ok then
recall_options[2]['path'] = global_default_path
local rv = LuaDialog.Dialog("Recall Mixer Settings:", recall_options):run()
if not(rv) then return end
local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, rv['file'])):run()
if dry_return then
acoraida_monicas_last_used_recall_file = rv['file']
recall(false, rv['file'], dry_return)
else
return
end
else
LuaDialog.Message ("Recall Mixer Settings:",
global_path .. ' does not exist!\nPlease run Store Mixer Settings first.',
LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
end
end
if gvld['recall-dir'] == 2 then
local local_ok = isdir(local_path)
local local_default_path = ARDOUR.LuaAPI.build_filename(local_path, 'asdf')
print(local_default_path)
if local_ok then
recall_options[2]['path'] = local_default_path
local rv = LuaDialog.Dialog("Recall Mixer Settings:", recall_options):run()
if not(rv) then return end
local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, rv['file'])):run()
if dry_return then
acoraida_monicas_last_used_recall_file = rv['file']
recall(true, rv['file'], dry_return)
else
return
end
else
LuaDialog.Message ("Recall Mixer Settings:",
local_path .. 'does not exist!\nPlease run Store Mixer Settings first.',
LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
end
end
if gvld['recall-dir'] == 3 then
if acoraida_monicas_last_used_recall_file then
local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, acoraida_monicas_last_used_recall_file)):run()
if dry_return then
recall(true, acoraida_monicas_last_used_recall_file, dry_return)
else
return
end
else
LuaDialog.Message ("Script has no record of last used file:",
'Please pick a recall file and then this option will be available',
LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run()
end
end
end
end end

View file

@ -1,383 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Store Mixer Settings",
author = "Mixbus Team",
description = [[
Stores the current Mixer state as a file
that can be read and recalled arbitrarily
by it's companion script, Recall Mixer Settings.
Supports: processor settings, grouping,
mute, solo, gain, trim, pan and processor ordering,
plus re-adding certain deleted plugins.
]]
}
function factory () return function ()
local user_cfg = ARDOUR.user_config_directory(-1)
local local_path = ARDOUR.LuaAPI.build_filename(Session:path(), 'mixer_settings')
local global_path = ARDOUR.LuaAPI.build_filename(user_cfg, 'mixer_settings')
function exists(file)
local ok, err, code = os.rename(file, file)
if not ok then
if code == 13 then -- Permission denied, but it exists
return true
end
end return ok, err
end
function whoami()
if not pcall(function() local first_check = Session:get_mixbus(0) end) then
return "Ardour"
else
local second_check = Session:get_mixbus(11)
if second_check:isnil() then
return "Mixbus"
else
return "32C"
end
end
end
function isdir(path)
return exists(path.."/")
end
function setup_paths()
local global_ok, local_ok = false, false
if not(isdir(global_path)) then
global_ok, _, _ = os.execute('mkdir '.. global_path)
if global_ok == 0 then
global_ok = true
end
else
global_ok = true
end
if not(isdir(local_path)) then
local_ok, _, _ = os.execute('mkdir '.. local_path)
if local_ok == 0 then
local_ok = true
end
else
local_ok = true
end
return global_ok, local_ok
end
function get_processor_by_name(track, name)
local i = 0
local proc = track:nth_processor(i)
repeat
if(proc:display_name() == name) then
return proc
else
i = i + 1
end
proc = track:nth_processor(i)
until proc:isnil()
end
function group_by_id(id)
local id = tonumber(id)
for g in Session:route_groups():iter() do
local group_id = tonumber(g:to_stateful():id():to_s())
if group_id == id then return g end
end
end
function group_by_name(name)
for g in Session:route_groups():iter() do
if g:name() == name then return g end
end
end
function route_groupid_interrogate(t)
local group = false
for g in Session:route_groups():iter() do
for r in g:route_list():iter() do
if r:name() == t:name() then group = g:to_stateful():id():to_s() end
end
end return group
end
function route_group_interrogate(t)
for g in Session:route_groups():iter() do
for r in g:route_list():iter() do
if r:name() == t:name() then return g end
end
end
end
function empty_last_store(path) --empty current file from last run
local file = io.open(path, "w")
--file:write(string.format("instance = { whoami = '%s' }", whoami())
file:write("")
file:close()
end
function mark_tracks(selected, path)
empty_last_store(path)
local route_string = [[instance = {
route_id = %d,
route_name = '%s',
gain_control = %s,
trim_control = %s,
pan_control = %s,
sends = {%s},
muted = %s,
soloed = %s,
order = {%s},
cache = {%s},
group = %s,
group_name = '%s',
pi_order = %d
}]]
local group_string = [[instance = {
group_id = %s,
name = '%s',
routes = {%s},
}]]
local processor_string = [[instance = {
plugin_id = %d,
type = %d,
display_name = '%s',
owned_by_route_name = '%s',
owned_by_route_id = %d,
parameters = {%s},
active = %s,
}]]
local group_route_string = " [%d] = %s,"
local proc_order_string = " [%d] = %d,"
local proc_cache_string = " [%d] = {'%s', %d},"
local params_string = " [%d] = %s,"
--ensure easy-to-read formatting doesn't make it through
local route_string = string.gsub(route_string, "[\n\t%s]", "")
local group_string = string.gsub(group_string, "[\n\t%s]", "")
local processor_string = string.gsub(processor_string, "[\n\t%s]", "")
local sel = Editor:get_selection ()
local groups_to_write = {}
local i = 0
local tracks = Session:get_stripables()
if selected then tracks = sel.tracks:routelist() end
for r in tracks:iter() do
local group = route_group_interrogate(r)
if group then
local already_there = false
for _, v in pairs(groups_to_write) do
if group == v then
already_there = true
end
end
if not(already_there) then
groups_to_write[#groups_to_write + 1] = group
end
end
end
for _, g in pairs(groups_to_write) do
local tmp_str = ""
for t in g:route_list():iter() do
tmp_str = tmp_str .. string.format(group_route_string, i, t:to_stateful():id():to_s())
i = i + 1
end
local group_str = string.format(
group_string,
g:to_stateful():id():to_s(),
g:name(),
tmp_str
)
file = io.open(path, "a")
file:write(group_str, "\r\n")
file:close()
end
for r in tracks:iter() do
if r:is_monitor () or r:is_auditioner () or not(r:to_vca():isnil()) then goto nextroute end -- skip special routes
r = r:to_route()
if r:isnil() then goto nextroute end
local order = ARDOUR.ProcessorList()
local x = 0
repeat
local proc = r:nth_processor(x)
if not proc:isnil() then
order:push_back(proc)
end
x = x + 1
until proc:isnil()
local route_group = route_group_interrogate(r)
if route_group then route_group = route_group:name() else route_group = "" end
local rid = r:to_stateful():id():to_s()
local pan = r:pan_azimuth_control()
if pan:isnil() then pan = false else pan = pan:get_value() end --sometimes a route doesn't have pan, like the master.
-- Get send information, if any.
local send_string = ""
local i = 0
repeat
local fmt = "{%s, %s, %s, %s}"
string.gsub(fmt, "[\n\t]", "")
local values = {}
for j, ctrl in pairs({
r:send_level_controllable(i),
r:send_enable_controllable(i),
r:send_pan_azimuth_controllable(i),
r:send_pan_azimuth_enable_controllable(i),
}) do
if not(ctrl:isnil()) then
values[#values + 1] = ctrl:get_value()
else
values[#values + 1] = "nil"
end
end
send_string = send_string .. string.format(fmt, table.unpack(values))
send_string = send_string .. ","
i = i + 1
until r:send_enable_controllable(i):isnil()
print(send_string)
local order_nmbr = 0
local tmp_order_str, tmp_cache_str = "", ""
for p in order:iter() do
local ptype
if not(p:to_insert():isnil()) then
ptype = p:to_insert():plugin(0):get_info().type
else
ptype = 99
end
local pid = p:to_stateful():id():to_s()
if not(string.find(p:display_name(), "latcomp")) then
tmp_order_str = tmp_order_str .. string.format(proc_order_string, order_nmbr, pid)
tmp_cache_str = tmp_cache_str .. string.format(proc_cache_string, pid, p:display_name(), ptype)
end
order_nmbr = order_nmbr + 1
end
local route_str = string.format(
route_string,
rid,
r:name(),
ARDOUR.LuaAPI.ascii_dtostr(r:gain_control():get_value()),
ARDOUR.LuaAPI.ascii_dtostr(r:trim_control():get_value()),
tostring(pan),
send_string,
r:muted(),
r:soloed(),
tmp_order_str,
tmp_cache_str,
route_groupid_interrogate(r),
route_group,
r:presentation_info_ptr():order()
)
file = io.open(path, "a")
file:write(route_str, "\n")
file:close()
local i = 0
while true do
local params = {}
local proc = r:nth_plugin (i)
if proc:isnil () then break end
local active = proc:active()
local id = proc:to_stateful():id():to_s()
local plug = proc:to_insert ():plugin (0)
local ptype = proc:to_insert():plugin(0):get_info().type
local n = 0 -- count control-ports
for j = 0, plug:parameter_count () - 1 do -- iterate over all plugin parameters
if plug:parameter_is_control (j) then
local label = plug:parameter_label (j)
if plug:parameter_is_input (j) and label ~= "hidden" and label:sub (1,1) ~= "#" then
--local _, _, pd = ARDOUR.LuaAPI.plugin_automation(proc, n)
local val = ARDOUR.LuaAPI.get_processor_param(proc, j, true)
print(r:name(), "->", proc:display_name(), label, val)
params[j] = val
end
n = n + 1
end
end
i = i + 1
local tmp_params_str = ""
for k, v in pairs(params) do
tmp_params_str = tmp_params_str .. string.format(params_string, k, ARDOUR.LuaAPI.ascii_dtostr(v))
end
local proc_str = string.format(
processor_string,
id,
ptype,
proc:display_name(),
r:name(),
r:to_stateful():id():to_s(),
tmp_params_str,
active
)
file = io.open(path, "a")
file:write(proc_str, "\n")
file:close()
end
::nextroute::
end
end
local store_options = {
{ type = "label", col=0, colspan=1, align="right", title = "Name:" },
{ type = "entry", col=1, colspan=1, align="left" , key = "filename", default = Session:name(), title=""},
{ type = "label", col=0, colspan=1, align="right", title = "Location:" },
{
type = "radio", col=1, colspan=3, align="left", key = "store-dir", title = "", values =
{
['Global (accessible from any session)'] = 1, ['Local (this session only)'] = 2
},
default = 'Locally (this session only)'
},
{ type = "hseparator", title="", col=0, colspan = 3},
{ type = "label", col=0, colspan=1, align="right", title = "Selected Tracks Only:" },
{ type = "checkbox", col=1, colspan=1, align="left", key = "selected", default = false, title = ""},
--{ type = "label", col=0, colspan=2, align="left", title = ''},
--{ type = "label", col=0, colspan=2, align="left", title = "Global Path: " .. global_path},
--{ type = "label", col=0, colspan=2, align="left", title = "Local Path: " .. local_path},
}
local global_ok, local_ok = setup_paths()
if global_ok and local_ok then
local rv = LuaDialog.Dialog("Store Mixer Settings:", store_options):run()
if not(rv) then return end
local filename = rv['filename']
if rv['store-dir'] == 1 then
local store_path = ARDOUR.LuaAPI.build_filename(global_path, string.format("%s-%s.lua", filename, whoami()))
local selected = rv['selected']
mark_tracks(selected, store_path)
end
if rv['store-dir'] == 2 then
local store_path = ARDOUR.LuaAPI.build_filename(local_path, string.format("%s-%s.lua", filename, whoami()))
print(store_path)
local selected = rv['selected']
mark_tracks(selected, store_path)
end
end
end end

View file

@ -1,21 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Mute All Tracks",
license = "MIT",
author = "Ardour Team",
description = [[Mute All Tracks in the Session]]
}
function factory () return function ()
local ctrls = ARDOUR.ControlListPtr () -- create a list of controls to change
for r in Session:get_tracks ():iter () do -- iterate over all tracks in the session
ctrls:push_back (r:mute_control()) -- add the track's mute-control to the list of of controls to be changed
end
-- of more than one control is to be changed..
if ctrls:size() > 0 then
-- do it (queue change in realtime-context) set to "1" ; here 'muted'
Session:set_controls (ctrls, 1, PBD.GroupControlDisposition.NoGroup)
end
end end

View file

@ -1,17 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "New Playlist",
license = "MIT",
author = "Ardour Lua Taskforce",
description = [[Prompts and builds a new playlist for every track in the session.]]
}
function factory () return function ()
for r in Session:get_tracks():iter() do
local rtav = Editor:rtav_from_route(r) -- lookup RTAV
Editor:new_playlists(rtav:to_timeaxisview())
end
collectgarbage()
end end

View file

@ -1,111 +0,0 @@
ardour {
["type"] = "dsp",
name = "NoiseGen",
category = "Instrument",
license = "MIT",
author = "Ardour Team",
description = [[Noise Generator (v-1.02)]]
}
function dsp_params ()
return
{
{ ["type"] = "input", name = "White/Pink", min = 0, max = 1, default = 0, toggled = true },
{ ["type"] = "input", name = "Gain", min = -60, max = 0, default = -18, unit="dB" },
}
end
function dsp_ioconfig ()
return { [1] = { audio_in = -1, audio_out = -1}, }
end
local sr = 0
function dsp_init (rate)
sr = rate
end
local ao = 0
local draw = 0
function dsp_run (ins, outs, n_samples)
local a = {} -- init array
local ctrl = CtrlPorts:array ()
local noise = ctrl[1] or 0
local amplitude = ARDOUR.DSP.dB_to_coefficient (ctrl[2]) or ARDOUR.DSP.dB_to_coefficient (-18)
local b0 = 0.0
local b1 = 0.0
local b2 = 0.0
local b3 = 0.0
local b4 = 0.0
local b5 = 0.0
local b6 = 0.0
--Pink noise generation courtesy of Paul Kellet's refined method
--http://www.musicdsp.org/files/pink.txt
--If 'white' consists of uniform random numbers,
--the pink noise will have an almost gaussian distribution.
for s = 1, n_samples do
if noise == 0 then
a[s] = amplitude * 2 * (math.random() - 0.5)
end
if noise == 1 then
white = (amplitude * 0.25) * 2 * (math.random() - 0.5)
b0 = 0.99886 * b0 + white * 0.0555179;
b1 = 0.99332 * b1 + white * 0.0750759;
b2 = 0.96900 * b2 + white * 0.1538520;
b3 = 0.86650 * b3 + white * 0.3104856;
b4 = 0.55000 * b4 + white * 0.5329522;
b5 = -0.7616 * b5 - white * 0.0168980;
b6 = white * 0.115926;
a[s] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
end
end
if (draw > (sr/15)) then
self:queue_draw()
draw = 0
end
-- passes array a {} into buffer
for c = 1,#outs do
outs[c]:set_table(a, n_samples)
end
draw = draw + n_samples
end
function render_inline (ctx, w, max_h) --inline display
local ctrl = CtrlPorts:array()
h = 30
p = 0
inc = 0
ycy = 0.5
pink = false
local amplitude = ARDOUR.DSP.dB_to_coefficient(ctrl[2])
if ctrl[1] == 1 then pink = true end
if pink then inc = 0.7/w end
--draw rectangle
ctx:rectangle(0, 0, w, h)
ctx:set_source_rgba(0, 0, 0, 1.0)
ctx:fill()
ctx:set_line_width(1.5)
ctx:set_source_rgba(0.8, 0.8, 0.8, 1.0)
l_x = 0
l_y = 0
for x = 0,w do
if pink then ycy = 0.3 else ycy = 0.5 end --slant slightly like an actual pink noise spectrum
y = math.log(20^amplitude) * (math.random() - 0.5) - p
yc = ycy * h + ((-0.5 * h) * y)
ctx:move_to (x, yc + 3)
ctx:line_to (l_x, l_y + 3)
l_x = x
l_y = yc
ctx:stroke()
p = p + inc
end
return {w, h + 6}
end

View file

@ -1,58 +0,0 @@
ardour { ["type"] = "EditorAction",
name = "Normalize All Tracks",
license = "MIT",
author = "Ardour Team",
description = [[Normalize all regions using a common gain-factor per track.]]
}
function factory () return function ()
-- target values -- TODO: use a LuaDialog.Dialog and ask..
local target_peak = -1 --dBFS
local target_rms = -18 --dBFS/RMS
-- prepare undo operation
Session:begin_reversible_command ("Normalize Tracks")
local add_undo = false -- keep track if something has changed
-- loop over all tracks in the session
for track in Session:get_tracks():iter() do
local norm = 0 -- per track gain
-- loop over all regions on track
for r in track:to_track():playlist():region_list():iter() do
-- test if it's an audio region
local ar = r:to_audioregion ()
if ar:isnil () then goto next end
local peak = ar:maximum_amplitude (nil)
local rms = ar:rms (nil)
-- check if region is silent
if (peak > 0) then
local f_rms = rms / 10 ^ (.05 * target_rms)
local f_peak = peak / 10 ^ (.05 * target_peak)
local tg = (f_peak > f_rms) and f_peak or f_rms -- max (f_peak, f_rms)
norm = (tg > norm) and tg or norm -- max (tg, norm)
end
::next::
end
-- apply same gain to all regions on track
if norm > 0 then
for r in track:to_track():playlist():region_list():iter() do
local ar = r:to_audioregion ()
if ar:isnil () then goto skip end
ar:to_stateful ():clear_changes ()
ar:set_scale_amplitude (1 / norm)
add_undo = true
::skip::
end
end
end
-- all done. now commit the combined undo operation
if add_undo then
-- the 'nil' command here means to use all collected diffs
Session:commit_reversible_command (nil)
else
Session:abort_reversible_command ()
end
end end

View file

@ -1,125 +0,0 @@
ardour {
["type"] = "dsp",
name = "a-Notch Bank",
category = "Filter",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[Notch Filter Bank; useful to remove noise with a harmonic spectum (e.g, mains hum, GSM signals, charge-pump noise, etc).
Note: this plugin is not suitable to be automated, it is intended for static noise only.]]
}
------------------------------------------------------------------
-- this is a quick/dirty example filter: no de-click, no de-zipper
-------------------------------------------------------------------
-- configuration
local max_stages = 100
-- plugin i/o ports
function dsp_ioconfig ()
return
{
-- allow any number of I/O as long as port-count matches
{ audio_in = -1, audio_out = -1},
}
end
-- plugin control ports
function dsp_params ()
return
{
{ ["type"] = "input", name = "Base Freq", min = 10, max = 2000, default = 100, unit="Hz", logarithmic = true },
{ ["type"] = "input", name = "Quality", min = 1.0, max = 100.0, default = 8.0, logarithmic = true },
{ ["type"] = "input", name = "Stages", min = 1.0, max = max_stages, default = 8.0, integer = true },
}
end
-- plugin instance state
local filters = {} -- the biquad filter instances
local chn = 0 -- configured channel count
local sample_rate = 0 -- configured sample-rate
local limit = 0 -- max number of stages (given freq & sample-rate)
-- cached control ports (keep track of changed)
local freq = 0
local qual = 0
-- dsp_init is called once when instantiating the plugin
function dsp_init (rate)
-- remember the sample-rate
sample_rate = rate
end
-- dsp_configure is called every time when the channel-count
-- changes, and at least once at the beginning.
function dsp_configure (ins, outs)
assert (ins:n_audio () == outs:n_audio ())
-- explicit cleanup
filters = {}
collectgarbage ()
-- remember audio-channels
chn = ins:n_audio ()
-- set up filter instances for all channels
for c = 1, chn do
filters[c] = {}
for i = 1, max_stages do
filters[c][i] = ARDOUR.DSP.Biquad (sample_rate)
end
end
end
-- the actual process function, called every cycle
-- ins, outs are audio-data arrays
-- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
-- n_samples are the number of samples to process
function dsp_run (ins, outs, n_samples)
-- make sure input and output count matches...
assert (#ins == #outs)
-- ...and matches the configured number of channels
assert (#ins == chn)
local ctrl = CtrlPorts:array () -- get control parameters as array
-- ctrl[] .. correspond to the parameters given in in dsp_params()
-- test if the plugin-parameters have changed
if freq ~= ctrl[1] or qual ~= ctrl[2] then
-- remember current settings
freq = ctrl[1]
qual = ctrl[2]
-- calc max number of states to configure/process
limit = math.floor (sample_rate / (2 * freq)) -- at most up to SR / 2
if limit > max_stages then limit = max_stages end
-- re-compute the filter coefficients for all filters
for c = 1, chn do -- for each channel
for i = 1, limit do -- and for each filter stage
-- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:Biquad
-- and for a list of available types, see
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR.DSP.Biquad.Type
-- the parameters are type, frequency, quality(bandwidth), gain
filters[c][i]:compute (ARDOUR.DSP.BiquadType.Notch, freq * i, qual * i, 0)
end
end
end
-- limit the number of process stages
local stages = math.floor (ctrl['3']) -- current user-set parameter
if stages < 1 then stages = 1 end -- at least one stage...
if stages > limit then stages = limit end
-- process all channels
for c = 1, chn do
-- when not processing in-place, copy the data from input to output first
if ins[c] ~= outs[c] then
ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples)
end
-- run all stages, in-place on the output buffer
for i = 1, stages do
filters[c][i]:run (outs[c], n_samples)
end
end
end

View file

@ -1,47 +0,0 @@
ardour {
["type"] = "EditorHook",
name = "Periodically Save Snapshot",
author = "Ardour Lua Task Force",
description = "Save a session-snapshot peridocally (every 15mins) named after the current date-time",
}
-- subscribe to signals
-- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal
function signals ()
return LuaSignal.Set():add ({[LuaSignal.LuaTimerS] = true})
end
-- create callback function
function factory ()
local _last_snapshot_time = 0 -- persistent variable
local _snapshot_interval = 60 * 15 -- 15 minutes
-- callback function which invoked when signal is emitted, every 100ms
return function (signal, ref, ...)
local now = os.time (); -- unix-time, seconds since 1970
-- skip initial save when script is loaded
if (_last_snapshot_time == 0) then
_last_snapshot_time = now;
end
-- every 15 mins
if (now > _last_snapshot_time + _snapshot_interval) then
-- don't save while recording, may interfere with recording
if Session:actively_recording() then
-- queue 30 sec after rec-stop
_last_snapshot_time = now - _snapshot_interval + 30
return
end
_last_snapshot_time = now
-- format date-time (avoid colon)
local snapshot_name = os.date ("%Y-%m-%d %H.%M.%S", now)
-- save session -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session
Session:save_state ("backup " .. snapshot_name, false, false, false)
end
end
end

View file

@ -1,26 +0,0 @@
ardour {
["type"] = "EditorHook",
name = "Save Snapshot after Export",
author = "Ardour Lua Task Force",
description = "Save a session-snapshot on export, named after the export-timespan",
}
-- subscribe to signals
-- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal
function signals ()
s = LuaSignal.Set()
s:add ({[LuaSignal.Exported] = true})
return s
end
-- create callback functions
function factory ()
-- callback function which invoked when signal is emitted
return function (signal, ref, ...)
-- 'Exported' passes 2 strings: current time-span name, path to exported file
-- (see C++ libs/ardour/export_handler.cc Session::Exported )
local timespan_name, file_path = ...
-- save session -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session
Session:save_state ("export-" .. timespan_name, false, false, false)
end
end

View file

@ -1,82 +0,0 @@
--[[
# Example script to prepare the Ardour session for recording
Usually there's a certain state needed to actually start the recording. This example
script treats the situation of a podcast recording. When starting the recording the
following settings have to be ensured.
* Session has to be recenabled
* Tracks have to be recenabled
* Gain automation have to set on write in order to record events from mute buttons
* The playhead has to be at 00:00:00.000
* The last (failed) capture has to be cleared
* Location markers have to be cleared
So this script automizes away the task and lets the podcast moderator by just one
action (for example triggerd by a Wiimote) prepare the session for recording.
It can be used for example with the python script of the Linux podcasting hacks:
https://github.com/linux-podcasting-hacks/wiimote-recording-control
Not that this script is more meant as an demo script to demonstrate the
possibilities of the Lua interface.
--]]
ardour {
["type"] = "EditorAction",
name = "Prepare recording for podcast",
author = "Johannes Mueller",
description = [[
Prepares the Ardour session for podcast recording.
* Sets the gain automation to "Write" so that muting buttons work.
* Recenables all tracks.
* Clears all markers.
* Erases the last capture (assuming that it was a failed one)
* Rewinds the session to starting point.
* Recenables the session.
]]
}
function factory (unused) return function()
if Session:actively_recording() then
return end
for t in Session:get_tracks():iter() do
t:gain_control():set_automation_state(ARDOUR.AutoState.Write)
t:rec_enable_control():set_value(1, PBD.GroupControlDisposition.UseGroup)
end
for l in Session:locations():list():iter() do
if l:is_mark() then
Session:locations():remove(l)
end
end
Session:goto_start()
Editor:remove_last_capture()
Session:maybe_enable_record()
end end
function icon (params) return function (ctx, width, height)
local x = width * .5
local y = height * .5
local r = math.min (x, y) * .55
ctx:arc (x, y, r, 0, 2 * math.pi)
ctx:set_source_rgba (.9, .3, .3, 1.)
ctx:fill_preserve ()
ctx:set_source_rgba (0, 0, 0, .8)
ctx:set_line_width (1)
ctx:stroke ()
local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(r * 1.5) .. "px")
txt:set_text ("P")
local tw, th = txt:get_pixel_size ()
ctx:set_source_rgba (0, 0, 0, 1.0)
ctx:move_to (.5 * (width - tw), .5 * (height - th))
txt:show_in_cairo_context (ctx)
end end

View file

@ -1,44 +0,0 @@
ardour { ["type"] = "EditorAction", name = "Remove Unknown Plugins",
license = "MIT",
author = "Ardour Team",
description = [[Remove all unknown plugins/processors from all tracks and busses]]
}
function factory (params) return function ()
-- iterate over all tracks and busses (aka routes)
for route in Session:get_routes ():iter () do
-- route is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route
local i = 0;
-- we need to iterate one-by one, removing a processor invalidates the list
repeat
proc = route:nth_processor (i)
-- proc is a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Processor
-- try cast it to http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:UnknownProcessor
if not proc:isnil () and not proc:to_unknownprocessor ():isnil () then
route:remove_processor (proc, nil, true)
else
i = i + 1
end
until proc:isnil ()
end
end end
function icon (params) return function (ctx, width, height, fg)
local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil (math.min (width, height) * .5) .. "px")
txt:set_text ("Fx")
local tw, th = txt:get_pixel_size ()
ctx:move_to (.5 * (width - tw), .5 * (height - th))
txt:layout_cairo_path (ctx)
ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
ctx:set_line_width (3)
ctx:stroke_preserve ()
ctx:set_source_rgba (.8, .2, .2, 1)
ctx:set_line_width (2)
ctx:stroke_preserve ()
ctx:set_source_rgba (0, 0, 0, 1)
ctx:fill ()
end end

View file

@ -1,249 +0,0 @@
ardour {
["type"] = "EditorAction",
name = "Reset Mixer",
license = "MIT",
author = "Ben Loftis, Nikolaus Gullotta, Maxime Lecoq",
description = [[Resets key Mixer settings after user-prompt (warning: this cannot be undone)]]
}
function factory() return function()
local sp_radio_buttons = {Bypass="bypass", Remove="remove", Nothing=false}
local dlg = {
{type="label", align="left", colspan="3", title="Please select below the items you want to reset:" },
{type="label", align="left", colspan="3", title="(Warning: this cannot be undone!)\n" },
{type="heading", align ="center", colspan="3", title = "Common Controls:" },
{type="checkbox", key="fader", default=true, title="Fader" },
{type="checkbox", key="mute", default=true, title="Mute" },
{type="checkbox", key="solo", default=true, title="Solo" },
{type="checkbox", key="trim", default=true, title="Trim" },
{type="checkbox", key="pan", default=true, title="Pan (All)" },
{type="checkbox", key="phase", default=true, title="Phase" },
{type="checkbox", key="sends", default=true, title="Sends" },
{type="checkbox", key="eq", default=true, title="EQ" },
{type="checkbox", key="comp", default=true, title="Compressor" },
{type="heading", align="center", colspan="3", title="Processors:" },
{type="radio", key="plugins", title="Plug-ins", values=sp_radio_buttons, default="Bypass" },
{type="radio", key="io", title="Sends/Inserts", values=sp_radio_buttons, default="Bypass" },
{type="hseparator", title=""},
{type="heading", align="center", colspan="3", title="Misc." },
{type="checkbox", key="auto", colspan="3", title = "Automation (switch to manual mode)" },
{type="checkbox", key="rec", colspan="3", title = "Disable Record" },
{type="checkbox", key="groups", colspan="3", title = "Groups" },
{type="checkbox", key="vcas", colspan="3", title = "VCAs (unassign all)" },
}
function reset(ctrl, disp, auto)
local disp = disp or PBD.GroupControlDisposition.NoGroup
if not(ctrl:isnil()) then
local pd = ctrl:desc()
ctrl:set_value(pd.normal, disp)
if auto then
ctrl:set_automation_state(auto)
end
end
end
function reset_eq_controls(route, disp, auto)
if route:isnil() then
return
end
local disp = disp or PBD.GroupControlDisposition.NoGroup
reset(route:eq_enable_controllable(), disp, auto)
local i = 0
repeat
for _,ctrl in pairs({
route:eq_freq_controllable(i),
route:eq_gain_controllable(i),
route:eq_q_controllable(i),
}) do
reset(ctrl, disp, auto)
end
i = i + 1
until route:eq_freq_controllable(i):isnil()
end
function reset_comp_controls(route, disp, auto)
if route:isnil() then
return
end
local disp = disp or PBD.GroupControlDisposition.NoGroup
for _,ctrl in pairs({
route:comp_enable_controllable(),
route:comp_makeup_controllable(),
route:comp_mode_controllable(),
route:comp_speed_controllable(),
route:comp_threshold_controllable(),
}) do
reset(ctrl, disp, auto)
end
end
function reset_send_controls(route, disp, auto)
if route:isnil() then
return
end
local disp = disp or PBD.GroupControlDisposition.NoGroup
local i = 0
repeat
for _,ctrl in pairs({
route:send_level_controllable(i),
route:send_enable_controllable(i),
route:send_pan_azimuth_controllable(i),
route:send_pan_azimuth_enable_controllable(i),
}) do
reset(ctrl, disp, auto)
end
i = i + 1
until route:send_enable_controllable(i):isnil()
end
function reset_plugin_automation(plugin, state)
if plugin:to_insert():isnil() then
return
end
local plugin = plugin:to_insert()
local pc = plugin:plugin(0):parameter_count()
for c = 0, pc do
local ac = plugin:to_automatable():automation_control(Evoral.Parameter(ARDOUR.AutomationType.PluginAutomation, 0, c), false)
if not(ac:isnil()) then
ac:set_automation_state(state)
end
end
end
function reset_plugins(route, prefs, auto)
if route:isnil() then
return
end
local i = 0
local queue = {}
repeat
-- Plugins are queued to not invalidate this loop
local proc = route:nth_processor(i)
if not(proc:isnil()) then
if prefs["auto"] then
reset_plugin_automation(proc, auto)
end
if prefs["plugins"] then
local insert = proc:to_insert()
if not(insert:isnil()) then
if insert:is_channelstrip() or not(insert:display_to_user()) then
ARDOUR.LuaAPI.reset_processor_to_default(insert)
else
queue[#queue + 1] = proc
end
end
end
if prefs["io"] then
local io_proc = proc:to_ioprocessor()
if not(io_proc:isnil()) then
queue[#queue + 1] = proc
end
end
end
i = i + 1
until proc:isnil()
-- Deal with queue now
for _, proc in pairs(queue) do
if not(proc:to_insert():isnil()) then
if prefs["plugins"] == "remove" then
route:remove_processor(proc, nil, true)
elseif prefs["plugins"] == "bypass" then
proc:deactivate()
end
end
if not(proc:to_ioprocessor():isnil()) then
if prefs["io"] == "remove" then
route:remove_processor(proc, nil, true)
elseif prefs["io"] == "bypass" then
proc:deactivate()
end
end
end
end
local pref = LuaDialog.Dialog("Reset Mixer", dlg):run()
if not(pref) then goto pass_script end
assert(pref, "Dialog box was cancelled or is nil")
for route in Session:get_routes():iter() do
local disp = PBD.GroupControlDisposition.NoGroup
local auto = nil
if pref["auto"] then
auto = ARDOUR.AutoState.Off
end
if pref["eq"] then reset_eq_controls(route, disp, auto) end
if pref["comp"] then reset_comp_controls(route, disp, auto) end
if pref["sends"] then reset_send_controls(route, disp, auto) end
reset_plugins(route, pref, auto)
if pref["rec"] then
reset(route:rec_enable_control(), disp, auto)
reset(route:rec_safe_control(), disp, auto)
end
if pref["fader"] then
reset(route:gain_control(), disp, auto)
end
if pref["phase"] then
reset(route:phase_control(), disp, auto)
end
if pref["trim"] then
reset(route:trim_control(), disp, auto)
end
if pref["mute"] then
reset(route:mute_control(), disp, auto)
end
if pref["solo"] then
reset(route:solo_control(), disp, auto)
end
if pref["pan"] then
reset(route:pan_azimuth_control(), disp, auto)
reset(route:pan_elevation_control(), disp, auto)
reset(route:pan_frontback_control(), disp, auto)
reset(route:pan_lfe_control(), disp, auto)
reset(route:pan_width_control(), disp, auto)
end
if pref["vcas"] then
local slave = route:to_slavable()
if not(slave:isnil()) then
for vca in Session:vca_manager():vcas():iter() do
slave:unassign(vca)
end
end
end
end
if pref["groups"] then
for group in Session:route_groups():iter() do
Session:remove_route_group(group)
end
end
::pass_script::
collectgarbage()
end end

View file

@ -1,34 +0,0 @@
ardour { ["type"] = "Snippet", name = "plugin channel-map dev" }
function factory () return function ()
-- first track needs to be stereo and have a stereo plugin
-- (x42-eq with spectrum display, per channel processing,
-- and pre/post visualization is very handy here)
function checksetup (r)
-- fail if Route ID 1 is not present or not stereo
assert (r and not r:isnil())
assert (r:n_inputs():n_audio() == 2)
-- check first Plugin and make sure it is a "Plugin Insert"
if not r:nth_plugin(0):isnil() and not r:nth_plugin(0):to_insert():isnil() then return end
-- insert x42-eq at the top.
local proc = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/fil4#stereo", ARDOUR.PluginType.LV2, "");
r:add_processor_by_index(proc, 0, nil, true)
end
r = Session:get_remote_nth_route(1)
checksetup (r)
pi = r:nth_plugin(0):to_insert()
pi:set_no_inplace (true)
cm = ARDOUR.ChanMapping()
--cm:set(ARDOUR.DataType("Audio"), 0, 0)
cm:set(ARDOUR.DataType("Audio"), 1, 0)
pi:set_input_map (0, cm)
cm = ARDOUR.ChanMapping()
--cm:set(ARDOUR.DataType("Audio"), 0, 0)
cm:set(ARDOUR.DataType("Audio"), 1, 0)
pi:set_output_map (0, cm)
end end

View file

@ -1,58 +0,0 @@
ardour { ["type"] = "Snippet", name = "Fader Automation" }
function factory () return function ()
local playhead = Session:transport_sample ()
local samplerate = Session:nominal_sample_rate ()
-- get selected tracks
rl = Editor:get_selection ().tracks:routelist ()
-- prepare undo operation
Session:begin_reversible_command ("Fancy Fade Out")
local add_undo = false -- keep track if something has changed
-- iterate over selected tracks
for r in rl:iter () do
local ac = r:amp ():gain_control () -- ARDOUR:AutomationControl
local al = ac:alist () -- ARDOUR:AutomationList (state, high-level)
-- set automation state to "Touch"
ac:set_automation_state (ARDOUR.AutoState.Touch)
-- query the value at the playhead position
local g = al:eval (playhead)
-- get state for undo
local before = al:get_state ()
-- delete all events after the playhead...
al:truncate_end (playhead)
-- ...and generate some new ones.
for i=0,50 do
-- use a sqrt fade-out (the shape is recognizable, and otherwise
-- not be possible to achieve with existing ardour fade shapes)
al:add (playhead + i * samplerate / 50,
g * (1 - math.sqrt (i / 50)),
false, true)
end
-- remove dense events
al:thin (20)
-- save undo
local after = al:get_state ()
Session:add_command (al:memento_command (before, after))
add_undo = true
::out::
end
-- all done, commit the combined Undo Operation
if add_undo then
-- the 'nil' Commend here mean to use the collected diffs added above
Session:commit_reversible_command (nil)
else
Session:abort_reversible_command ()
end
end end

View file

@ -1,10 +0,0 @@
ardour { ["type"] = "Snippet", name = "Foreach Track" }
function factory () return function ()
for r in Session:get_tracks():iter() do
print (r:name())
-- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Track
-- for available methods e.g.
r:set_active (true, nil)
end
end end

View file

@ -1,11 +0,0 @@
ardour { ["type"] = "Snippet", name = "Randomize Group Colors" }
function factory () return function ()
for grb in Session:route_groups ():iter () do
local r = math.random (0, 255)
local g = math.random (0, 255)
local b = math.random (0, 255)
local rgba = (r << 24) + (g << 16) + (b << 8) + 0xff
grp:set_rgba(rgba)
end
end end

View file

@ -1,14 +0,0 @@
ardour { ["type"] = "Snippet", name = "Import File(s) Example" }
function factory (params) return function ()
local files = C.StringVector();
files:push_back("/tmp/test.wav")
local pos = -1
Editor:do_import (files,
Editing.ImportDistinctFiles, Editing.ImportAsTrack, ARDOUR.SrcQuality.SrcBest,
ARDOUR.MidiTrackNameSource.SMFTrackName, ARDOUR.MidiTempoMapDisposition.SMFTempoIgnore,
pos, ARDOUR.PluginInfo())
end end

View file

@ -1,43 +0,0 @@
ardour { ["type"] = "Snippet", name = "Plugin automation" }
function factory () return function ()
-- query playhead position and session sample-rate
local playhead = Session:transport_sample ()
local samplerate = Session:nominal_sample_rate ()
-- get Track/Bus with RID 3
local r = Session:get_remote_nth_route(3)
-- make sure the track object exists
assert (not r:isnil ())
-- get AutomationList, ControlList and ParameterDescriptor
-- of the first plugin's first parameter
-- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI
local al, cl, pd = ARDOUR.LuaAPI.plugin_automation (r:nth_plugin (0), 0)
if not al:isnil () then
print ("Parameter Range", pd.lower, pd.upper)
print ("Current value", cl:eval (playhead))
-- prepare undo operation
Session:begin_reversible_command ("Automatix")
-- remember current AutomationList state
local before = al:get_state()
-- remove future automation
cl:truncate_end (playhead)
-- add new data points after the playhead 1 sec, min..max
-- without guard-points, but with initial (..., false, true)
for i=0,10 do
cl:add (playhead + i * samplerate / 10,
pd.lower + math.sqrt (i / 10) * (pd.upper - pd.lower),
false, true)
end
-- save undo
local after = al:get_state()
Session:add_command (al:memento_command(before, after))
Session:commit_reversible_command (nil)
end
end end

View file

@ -1,23 +0,0 @@
ardour { ["type"] = "Snippet", name = "Plugin Order Reverse" }
function factory () return function ()
local sel = Editor:get_selection ()
-- for each selected track/bus
for r in sel.tracks:routelist ():iter () do
print ("Route:", r:name ())
local neworder = ARDOUR.ProcessorList(); -- create a PluginList
local i = 0;
repeat -- iterate over all plugins/processors
local proc = r:nth_processor (i)
if not proc:isnil () then
-- append plugin to list
neworder:push_back(proc)
end
i = i + 1
until proc:isnil ()
-- reverse list
neworder:reverse()
-- and set new order
r:reorder_processors (neworder, nil)
end
end end

View file

@ -1,57 +0,0 @@
ardour { ["type"] = "Snippet", name = "Plugin Utils" }
function factory () return function ()
-------------------------------------------------------------------------------
-- List all Plugins
for p in ARDOUR.LuaAPI.list_plugins():iter() do
print (p.name, p.unique_id, p.type)
local psets = p:get_presets()
if not empty:empty() then
for pset in psets:iter() do
print (" - ", pset.label)
end
end
end
-------------------------------------------------------------------------------
-- add a Plugin (here LV2) to all mono tracks that contain the pattern "dru"
-- and load a plugin-preset (if it exists)
for r in Session:get_routes():iter() do
if (string.match (r:name(), "dru") and r:n_inputs():n_audio() == 1) then
local proc = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/fil4#mono", ARDOUR.PluginType.LV2, "cutbass");
assert (not proc:isnil())
r:add_processor_by_index(proc, 0, nil, true)
end
end
-------------------------------------------------------------------------------
-- load a plugin preset
route = Session:get_remote_nth_route(2)
assert (route)
-- to 4th plugin (from top), ardour starts counting at zero
plugin = route:nth_plugin(3):to_insert():plugin(0)
assert (not plugin:isnil())
ps = plugin:preset_by_label("cutbass") -- get preset by name
assert (ps)
print (ps.uri)
plugin:load_preset (ps)
-------------------------------------------------------------------------------
-- add a LuaProcessor (here "Scope") to all tracks
for t in Session:get_tracks():iter() do
local pos = 0 -- insert at the top
-- the following two lines are equivalent
--local proc = ARDOUR.LuaAPI.new_luaproc(Session, "a-Inline Scope");
local proc = ARDOUR.LuaAPI.new_plugin (Session, "a-Inline Scope", ARDOUR.PluginType.Lua, "");
assert (not proc:isnil())
t:add_processor_by_index(proc, pos, nil, true)
-- optionally set some parameters
ARDOUR.LuaAPI.set_processor_param (proc, 0, 5) -- timescale 5sec
end
end end

View file

@ -1,35 +0,0 @@
ardour { ["type"] = "Snippet", name = "portengine" }
function factory () return function ()
local a = Session:engine()
print ("----- Port objects from Ardour's engine ----");
_, t = a:get_ports (ARDOUR.DataType("audio"), ARDOUR.PortList())
-- table 't' holds argument references. t[2] is the PortList
for p in t[2]:iter() do
local lp = p:get_connected_latency_range (ARDOUR.LatencyRange(), true)
local lc = p:get_connected_latency_range (ARDOUR.LatencyRange(), false)
print (p:name(), " -- Play lat.", lp[1].min, lp[1].max, "Capt lat.", lc[1].min, lc[1].max)
end
print ("----- Port names queries from the backend ----");
_, t = a:get_backend_ports ("", ARDOUR.DataType("audio"), 0, C.StringVector())
-- table 't' holds argument references. t[4] is the StringVector
for n in t[4]:iter() do
print (n)
end
print ("----- Connections from the backend ----");
_, t = a:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput, C.StringVector())
for n in t[4]:iter() do
local printed_name = false;
local _, ct = a:get_connections (n, C.StringVector())
for c in ct[2]:iter() do
if (not printed_name) then
printed_name = true;
print (n)
end
print (" ->", c)
end
end
end end

View file

@ -1,82 +0,0 @@
ardour { ["type"] = "Snippet", name = "Set Region Gain" }
function factory () return function ()
-- get Editor GUI Selection
-- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection
local sel = Editor:get_selection ()
-- allocate a buffer (float* in C)
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:DspShm
local cmem = ARDOUR.DSP.DspShm (8192)
-- prepare undo operation
Session:begin_reversible_command ("Lua Region Gain")
local add_undo = false -- keep track if something has changed
-- iterate over selected regions
-- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection
for r in sel.regions:regionlist ():iter () do
-- test if it's an audio region
if r:to_audioregion ():isnil () then
goto next
end
-- to read the Region data, we use the Readable interface of the Region
-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable
local rd = r:to_readable ()
local n_samples = rd:readable_length ()
local n_channels = rd:n_channels ()
local peak = 0 -- the audio peak to be calculated
-- iterate over all channels in Audio Region
for c = 0, n_channels -1 do
local pos = 0
repeat
-- read at most 8K samples of channel 'c' starting at 'pos'
local s = rd:read (cmem:to_float (0), pos, 8192, c)
pos = pos + s
-- access the raw audio data
-- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray
local d = cmem:to_float (0):array()
-- iterate over the audio sample data
for i = 0, s do
if math.abs (d[i]) > peak then
peak = math.abs (d[i])
end
end
until s < 8192
assert (pos == n_samples)
end
if (peak > 0) then
print ("Region:", r:name (), "peak:", 20 * math.log (peak) / math.log(10), "dBFS")
else
print ("Region:", r:name (), " is silent")
end
-- normalize region
if (peak > 0) then
-- prepare for undo
r:to_stateful ():clear_changes ()
-- apply gain
r:to_audioregion (): set_scale_amplitude (1 / peak)
-- save changes (if any) to undo command
if not Session:add_stateful_diff_command (r:to_statefuldestructible ()):empty () then
add_undo = true
end
end
::next::
end
-- all done. now commit the combined undo operation
if add_undo then
-- the 'nil' command here means to use all collected diffs
Session:commit_reversible_command (nil)
else
Session:abort_reversible_command ()
end
end end

Some files were not shown because too many files have changed in this diff Show more