Add some lua scripts
authorRobin Gareus <robin@gareus.org>
Tue, 23 Feb 2016 14:44:41 +0000 (15:44 +0100)
committerRobin Gareus <robin@gareus.org>
Tue, 23 Feb 2016 14:44:41 +0000 (15:44 +0100)
13 files changed:
scripts/amp1.lua [new file with mode: 0644]
scripts/amp2.lua [new file with mode: 0644]
scripts/amp3.lua [new file with mode: 0644]
scripts/editor_test.lua [new file with mode: 0644]
scripts/hook_test.lua [new file with mode: 0644]
scripts/rewind.lua [new file with mode: 0644]
scripts/session_test.lua [new file with mode: 0644]
scripts/synth1.lua [new file with mode: 0644]
scripts/voice_activate.lua [new file with mode: 0644]
scripts/wscript [new file with mode: 0644]
tools/linux_packaging/build
tools/osx_packaging/osx_build
wscript

diff --git a/scripts/amp1.lua b/scripts/amp1.lua
new file mode 100644 (file)
index 0000000..da3dd87
--- /dev/null
@@ -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 (file)
index 0000000..f6328ff
--- /dev/null
@@ -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 (file)
index 0000000..2f246a2
--- /dev/null
@@ -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 (file)
index 0000000..323d243
--- /dev/null
@@ -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 (file)
index 0000000..a4676c5
--- /dev/null
@@ -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 (file)
index 0000000..09dc3c8
--- /dev/null
@@ -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 (file)
index 0000000..dbe4390
--- /dev/null
@@ -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 (file)
index 0000000..d0c6ad7
--- /dev/null
@@ -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 (file)
index 0000000..eb9ef96
--- /dev/null
@@ -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 (file)
index 0000000..acf8ae7
--- /dev/null
@@ -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
index f25cd2fc4ac57063ae405ed8f285c8edd5779f7c..be26342e1a581daef1bc521f3cf5734895931434 100755 (executable)
@@ -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
index f7226bc33202a0b57123bccc2a6176bf3c74a22d..04d2fd1ad06325c218d2f09106f3ed59e45f3281 100755 (executable)
@@ -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 30a710afe6ec86bccb3c3a67a77b65a68bb657a5..98a76092901b6aff9e55170d6b8da8ace1daf39d 100644 (file)
--- a/wscript
+++ b/wscript
@@ -223,6 +223,7 @@ children = [
         'midi_maps',
         'mcp',
         'patchfiles',
+        'scripts',
         'headless',
         'session_utils',
         # shared helper binaries (plugin-scanner, exec-wrapper)