From 1473c2d36460707f6484fe5a0f1d913fbb8a77e0 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 23 Feb 2016 15:44:41 +0100 Subject: [PATCH] Add some lua scripts --- scripts/amp1.lua | 48 ++++++++++++++++ scripts/amp2.lua | 43 +++++++++++++++ scripts/amp3.lua | 45 +++++++++++++++ scripts/editor_test.lua | 16 ++++++ scripts/hook_test.lua | 40 ++++++++++++++ scripts/rewind.lua | 10 ++++ scripts/session_test.lua | 38 +++++++++++++ scripts/synth1.lua | 100 ++++++++++++++++++++++++++++++++++ scripts/voice_activate.lua | 49 +++++++++++++++++ scripts/wscript | 16 ++++++ tools/linux_packaging/build | 8 +++ tools/osx_packaging/osx_build | 8 +++ wscript | 1 + 13 files changed, 422 insertions(+) create mode 100644 scripts/amp1.lua create mode 100644 scripts/amp2.lua create mode 100644 scripts/amp3.lua create mode 100644 scripts/editor_test.lua create mode 100644 scripts/hook_test.lua create mode 100644 scripts/rewind.lua create mode 100644 scripts/session_test.lua create mode 100644 scripts/synth1.lua create mode 100644 scripts/voice_activate.lua create mode 100644 scripts/wscript diff --git a/scripts/amp1.lua b/scripts/amp1.lua new file mode 100644 index 0000000000..da3dd87eca --- /dev/null +++ b/scripts/amp1.lua @@ -0,0 +1,48 @@ +ardour { + ["type"] = "dsp", + name = "Simple Amp", + license = "MIT", + author = "Robin Gareus", + email = "robin@gareus.org", + site = "http://gareus.org", + 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 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 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 diff --git a/scripts/amp2.lua b/scripts/amp2.lua new file mode 100644 index 0000000000..f6328ff404 --- /dev/null +++ b/scripts/amp2.lua @@ -0,0 +1,43 @@ +ardour { + ["type"] = "dsp", + name = "Simple Amp II", + license = "MIT", + author = "Robin Gareus", + email = "robin@gareus.org", + site = "http://gareus.org", + 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 + local b = in_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped buffer + local a = bufs:get_audio(b):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 diff --git a/scripts/amp3.lua b/scripts/amp3.lua new file mode 100644 index 0000000000..2f246a252d --- /dev/null +++ b/scripts/amp3.lua @@ -0,0 +1,45 @@ +ardour { + ["type"] = "dsp", + name = "Simple Amp III", + license = "MIT", + author = "Robin Gareus", + email = "robin@gareus.org", + site = "http://gareus.org", + 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 letting ardour to +-- the channel and offset mapping. + +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 + for c = 1,#ins do + --for c in pairs (ins) do -- also works, slightly less effective + ARDOUR.DSP.apply_gain_to_buffer (ins[c], n_samples, gain); -- process in-place + end +end diff --git a/scripts/editor_test.lua b/scripts/editor_test.lua new file mode 100644 index 0000000000..323d243d97 --- /dev/null +++ b/scripts/editor_test.lua @@ -0,0 +1,16 @@ +ardour { + ["type"] = "EditorAction", + name = "Action Test", + license = "MIT", + author = "Robin Gareus", + email = "robin@gareus.org", + site = "http://gareus.org", + description = [[ An Example Ardour Editor Action Plugin.]] +} + +function factory (params) + return function () + for n in pairs(_G) do print(n) end + print ("----") + end +end diff --git a/scripts/hook_test.lua b/scripts/hook_test.lua new file mode 100644 index 0000000000..a4676c5135 --- /dev/null +++ b/scripts/hook_test.lua @@ -0,0 +1,40 @@ +ardour { + ["type"] = "EditorHook", + name = "Callback Example", + 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:containsFramePos (ARDOUR.Properties.Start)), + tostring (pch:containsFramePos (ARDOUR.Properties.Length)) + )) + io.close (file) + end + end +end diff --git a/scripts/rewind.lua b/scripts/rewind.lua new file mode 100644 index 0000000000..09dc3c8617 --- /dev/null +++ b/scripts/rewind.lua @@ -0,0 +1,10 @@ +ardour { + ["type"] = "EditorAction", + name = "Rewind", +} + +function factory (params) + return function () + Session:goto_start() + end +end diff --git a/scripts/session_test.lua b/scripts/session_test.lua new file mode 100644 index 0000000000..dbe4390b5e --- /dev/null +++ b/scripts/session_test.lua @@ -0,0 +1,38 @@ +ardour { + ["type"] = "session", + name = "Example", + license = "MIT", + author = "Robin Gareus", + email = "robin@gareus.org", + site = "http://gareus.org", + description = [[ + An Example Ardour Session Process Plugin. + Install a 'hook' that is called on every process cycle + (before doing any processing). + This example stops the transport after rolling for a specific time.]] +} + +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:frame_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:frame_rate()) then + Session:request_transport_speed(0.0, true) + end + end +end diff --git a/scripts/synth1.lua b/scripts/synth1.lua new file mode 100644 index 0000000000..d0c6ad7a52 --- /dev/null +++ b/scripts/synth1.lua @@ -0,0 +1,100 @@ +ardour { + ["type"] = "dsp", + name = "Simple Synth", + license = "MIT", + author = "Robin Gareus", + email = "robin@gareus.org", + site = "http://gareus.org", + description = [[An Example Synth for prototyping.]] +} + +function dsp_ioconfig () + return + { + { audio_in = 0, audio_out = 1}, + } +end + +function dsp_midi_input () + return true +end + + +local note_table = {} +local active_notes = {} +local phases = {} +local env = .01; + +function dsp_init (rate) + for n = 1,128 do + note_table [n] = (440 / 32) * 2^((n - 10.0) / 12.0) / rate + end + env = 100 / rate +end + +function dsp_run (ins, outs, n_samples) + -- initialize output buffer + assert (#outs == 1) + local a = {} + for s = 1, n_samples do a[s] = 0 end + + + -- very basic synth, simple sine, basic envelope + local function synth (s_start, s_end) + for n,v in pairs (active_notes) do + local vel = v["vel"] or 0 + local tgt = v["tvel"]; + for s = s_start,s_end do + local phase = phases[n] or 0 + vel = vel + env * (tgt - vel) + a[s] = a[s] + math.sin(6.283185307 * phase) * vel / 167 + phase = phase + note_table[n] + if (phase > 1.0) then + phases[n] = phase - 2.0 + else + phases[n] = phase + end + end + if vel < 1 and tgt == 0 then + active_notes[n] = nil + else + active_notes[n]["vel"] = vel; + end + end + end + + local tme = 1 + -- parse midi messages + assert (type(mididata) == "table") -- global table of midi events (for now) + for _,b in pairs (mididata) do + local t = b["time"] -- t = [ 1 .. n_samples ] + + -- synth sound until event + synth(tme, t) + tme = t + 1 + + local d = b["data"] -- get midi-event + -- we ignore the midi channel + if (#d == 3 and bit32.band (d[1], 240) == 144) then -- note on + local n = 1 + d[2]; + active_notes[n] = active_notes[n] or {} + active_notes[n]["tvel"] = d[3] + end + if (#d == 3 and bit32.band (d[1], 240) == 128) then -- note off + local n = 1 + d[2]; + active_notes[n] = active_notes[n] or {} + active_notes[n]["tvel"] = 0 + end + if (#d == 3 and bit32.band (d[1], 240) == 176) then -- CC + if (d[2] == 120 or d[2] == 123) then -- panic + active_notes = {} + end + end + end + + -- synth rest of cycle + synth(tme, n_samples) + + -- copy + outs[1]:set_table(a, n_samples) +end diff --git a/scripts/voice_activate.lua b/scripts/voice_activate.lua new file mode 100644 index 0000000000..eb9ef962f0 --- /dev/null +++ b/scripts/voice_activate.lua @@ -0,0 +1,49 @@ +ardour { + ["type"] = "dsp", + name = "Voice/Level Activate", + license = "MIT", + author = "Robin Gareus", + authoremail = "robin@gareus.org", + site = "http://gareus.org", + description = [[ + An Example Audio Plugin that rolls the transport + when the signal level on the plugin's input a given threshold.]] +} + +function dsp_ioconfig () + return + { + { audio_in = -1, audio_out = -1}, + } +end + +function dsp_params () + return + { + { ["type"] = "input", name = "Threshold", min = -20, max = 0, default = -6, doc = "Threshold in dBFS for all channels" }, + { ["type"] = "output", name = "Level", min = -120, max = 0 }, + } +end + +function dsp_configure (ins, outs) + n_channels = ins:n_audio(); +end + +-- use ardour's vectorized functions +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + local ctrl = CtrlPorts:array() -- get control port array (read/write) + if Session:transport_rolling() then ctrl[2] = -math.huge return end + local threshold = 10 ^ (.05 * ctrl[1]) -- dBFS to coefficient + local level = -math.huge + for c = 1,n_channels do + local b = in_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of buffer for given cannel + if b ~= ARDOUR.ChanMapping.Invalid then + local a = ARDOUR.DSP.compute_peak(bufs:get_audio(b):data(offset), n_samples, 0) + if a > threshold then + Session:request_transport_speed(1.0, true) + end + if a > level then level = a end + end + end + ctrl[2] = ARDOUR.DSP.accurate_coefficient_to_dB (level) +end diff --git a/scripts/wscript b/scripts/wscript new file mode 100644 index 0000000000..acf8ae76b4 --- /dev/null +++ b/scripts/wscript @@ -0,0 +1,16 @@ +#!/usr/bin/python + +import os + +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + scripts = bld.path.ant_glob ('*.lua') + bld.install_files (os.path.join(bld.env['DATADIR'], 'scripts'), scripts) + +def options(opt): + pass diff --git a/tools/linux_packaging/build b/tools/linux_packaging/build index f25cd2fc4a..be26342e1a 100755 --- a/tools/linux_packaging/build +++ b/tools/linux_packaging/build @@ -177,6 +177,7 @@ ExportFormats=$Shared/export Locale=$Shared/locale MidiMaps=$Shared/midi_maps PatchFiles=$Shared/patchfiles +LuaScripts=$Shared/scripts MackieControl=$Shared/mcp if [ x$PRINT_SYSDEPS != x ] ; then @@ -209,6 +210,7 @@ mkdir -p $Locale mkdir -p $Surfaces mkdir -p $MidiMaps mkdir -p $PatchFiles +mkdir -p $LuaScripts mkdir -p $MackieControl mkdir -p $ExportFormats mkdir -p $Panners @@ -372,6 +374,12 @@ for x in $BUILD_ROOT/../patchfiles/*.midnam ; do cp "$x" $PatchFiles done +# Lua Scripts Files +# got to be careful with names here +for x in $BUILD_ROOT/../scripts/*.lua ; do + cp "$x" $LuaScripts +done + # MackieControl data # got to be careful with names here for x in $BUILD_ROOT/../mcp/*.device $BUILD_ROOT/../mcp/*.profile ; do diff --git a/tools/osx_packaging/osx_build b/tools/osx_packaging/osx_build index f7226bc332..04d2fd1ad0 100755 --- a/tools/osx_packaging/osx_build +++ b/tools/osx_packaging/osx_build @@ -124,6 +124,7 @@ MidiMaps=$Shared/midi_maps ExportFormats=$Shared/export Templates=$Shared/templates PatchFiles=$Shared/patchfiles +LuaScripts=$Shared/scripts MackieControl=$Shared/mcp if [ x$PRINT_SYSDEPS != x ] ; then @@ -161,6 +162,7 @@ mkdir -p $Frameworks/modules mkdir -p $Etc mkdir -p $MackieControl mkdir -p $PatchFiles +mkdir -p $LuaScripts # maybe set variables env="" @@ -373,6 +375,12 @@ for x in $BUILD_ROOT/../patchfiles/*.midnam ; do cp "$x" $PatchFiles done +# Lua Script Files +# got to be careful with names here +for x in $BUILD_ROOT/../scripts/*.lua ; do + cp "$x" $LuaScripts +done + # MackieControl data # got to be careful with names here for x in $BUILD_ROOT/../mcp/*.device $BUILD_ROOT/../mcp/*.profile ; do diff --git a/wscript b/wscript index 30a710afe6..98a7609290 100644 --- a/wscript +++ b/wscript @@ -223,6 +223,7 @@ children = [ 'midi_maps', 'mcp', 'patchfiles', + 'scripts', 'headless', 'session_utils', # shared helper binaries (plugin-scanner, exec-wrapper)