mirror of
https://github.com/Ardour/ardour.git
synced 2026-01-06 05:35:47 +01:00
Also move Lua scripts to share subfolder
This commit is contained in:
parent
bf649cd68a
commit
180843f9bd
129 changed files with 2 additions and 2 deletions
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
119
scripts/amp4.lua
119
scripts/amp4.lua
|
|
@ -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
|
||||
--]]
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue