add a lua inline scope processor
authorRobin Gareus <robin@gareus.org>
Fri, 18 Mar 2016 20:34:22 +0000 (21:34 +0100)
committerRobin Gareus <robin@gareus.org>
Fri, 18 Mar 2016 20:34:22 +0000 (21:34 +0100)
scripts/scope.lua [new file with mode: 0644]

diff --git a/scripts/scope.lua b/scripts/scope.lua
new file mode 100644 (file)
index 0000000..961670a
--- /dev/null
@@ -0,0 +1,211 @@
+ardour {
+       ["type"]    = "dsp",
+       name        = "Inline Scope",
+       license     = "GPLv2",
+       author      = "Robin Gareus",
+       email       = "robin@gareus.org",
+       site        = "http://gareus.org",
+       description = [[An Example DSP Plugin to display the waveform on the mixer strip]]
+}
+
+-- 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
+
+function dsp_params ()
+       return
+       {
+               { ["type"] = "input", name = "timescale", min = .1, max = 5, default = 2, unit="sec", logarithmic = true },
+               { ["type"] = "input", name = "loscale", min = 0, max = 1, default = 0, toggled = true },
+               { ["type"] = "input", name = "height", min = 0, max = 3, default = 1, unit="dB", enum = true, scalepoints =
+                       {
+                               ["Min"] = 0,
+                               ["16:10"] = 1,
+                               ["1:1"] = 2,
+                               ["Max"] = 3
+                       }
+               },
+       }
+end
+
+
+function dsp_init (rate)
+       -- global variables (DSP part only)
+       samplerate = rate
+       bufsiz = 6 * rate
+       dpy_hz = rate / 25
+       dpy_wr = 0
+end
+
+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)
+       -- allocate shared memory area
+       -- this is used to speed up DSP computaton (using a C array)
+       -- and to share data with the GUI
+       self:shmem ():allocate (4 + bufsiz * audio_ins)
+       self:shmem ():clear ()
+       self:shmem ():atomic_set_int (0, 0)
+       local cfg = self:shmem ():to_int (1):array ()
+       cfg[1] = samplerate
+       cfg[2] = bufsiz
+       cfg[3] = audio_ins
+end
+
+function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
+       local shmem = self:shmem ()
+       local write_ptr = shmem:atomic_get_int (0)
+
+       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
+               assert (ib ~= ARDOUR.ChanMapping.Invalid)
+               local chn_off = 4 + bufsiz * (c - 1)
+               if (write_ptr + n_samples < bufsiz) then
+                       ARDOUR.DSP.copy_vector (shmem:to_float (write_ptr + chn_off), bufs:get_audio (ib):data (offset), n_samples)
+               else
+                       local w0 = bufsiz - write_ptr;
+                       ARDOUR.DSP.copy_vector (shmem:to_float (write_ptr + chn_off), bufs:get_audio (ib):data (offset), w0)
+                       ARDOUR.DSP.copy_vector (shmem:to_float (chn_off)            , bufs:get_audio (ib):data (offset), n_samples - w0)
+               end
+       end
+
+       write_ptr = (write_ptr + n_samples) % bufsiz
+       shmem:atomic_set_int (0, write_ptr)
+
+       -- emit QueueDraw every FPS
+       dpy_wr = dpy_wr + n_samples
+       if (dpy_wr > dpy_hz) then
+               dpy_wr = dpy_wr % dpy_hz;
+               self:queue_draw ()
+       end
+end
+
+
+-- helper function for drawing symmetric grid
+function gridline (ctx, x, xr, h, val)
+       ctx:move_to (math.floor (.5 + x + val * xr) -.5, 1)
+       ctx:line_to (math.floor (.5 + x + val * xr) -.5, h - 1)
+       ctx:stroke ()
+       ctx:move_to (math.floor (.5 + x - val * xr) -.5, 1)
+       ctx:line_to (math.floor (.5 + x - val * xr) -.5, h - 1)
+       ctx:stroke ()
+end
+
+function render_inline (ctx, w, max_h)
+       local ctrl = CtrlPorts:array () -- get control port array (read/write)
+       local shmem = self:shmem () -- get shared memory region
+       local cfg = shmem:to_int (1):array () -- "cast" into lua-table
+       local rate = cfg[1]
+       local buf_size = cfg[2]
+       local n_chn = cfg[3]
+
+       -- get settings
+       local timescale = ctrl[1] or 1.0 -- display size in seconds
+       local logscale = ctrl[2] or 0; logscale = logscale > 0 -- logscale
+       local hmode = ctrl[3] or 1 -- height mode
+
+       -- calc height
+       if hmode == 0 then
+               h = math.ceil (w * 10 / 16)
+               if (h > 44) then
+                       h = 44
+               end
+       elseif (hmode == 2) then
+               h = w
+       elseif (hmode == 3) then
+               h = max_h
+       else
+               h = math.ceil (w * 10 / 16)
+       end
+
+       if (h > max_h) then
+               h = max_h
+       end
+
+       -- display settings
+       local spp = math.floor (timescale * rate / (h - 2)) -- samples per pixel
+       local spl = spp * (h - 1) -- total number of audio samples to read
+       local read_ptr = (shmem:atomic_get_int (0) + buf_size - spl - 1) % buf_size -- read pointer
+       local xr = math.ceil ((w - 2) * (0.47 / n_chn)) -- x-axis range (per channel)
+
+       -- clear background
+       ctx:rectangle (0, 0, w, h)
+       ctx:set_source_rgba (.2, .2, .2, 1.0)
+       ctx:fill ()
+
+       -- prepare drawing
+       ctx:set_line_width (1.0)
+       local dash3 = ARDOUR.DoubleVector ()
+       dash3:add ({1, 3})
+       local dash4 = ARDOUR.DoubleVector ()
+       dash4:add ({1, 4})
+
+       -- plot every channel
+       for c = 1,n_chn do
+               local x = math.floor ((w - 2) * (c - .5) / n_chn) + 1.5  -- x-axis center for given channel
+
+               -- draw grid --
+               ctx:set_source_rgba (.5, .5, .5, 1.0)
+               ctx:move_to (x, 1) ctx:line_to (x, h - 1) ctx:stroke ()
+
+               ctx:set_dash (dash4, 2)
+               ctx:set_source_rgba (.4, .4, .4, 1.0)
+               if (logscale) then
+                       gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-18))
+                       gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-6))
+                       ctx:set_dash (dash3, 2)
+                       ctx:set_source_rgba (.5, .1, .1, 1.0)
+                       gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-3))
+               else
+                       gridline (ctx, x, xr, h, .1258)
+                       gridline (ctx, x, xr, h, .5)
+                       ctx:set_dash (dash3, 2)
+                       ctx:set_source_rgba (.5, .1, .1, 1.0)
+                       gridline (ctx, x, xr, h, .7079)
+               end
+               ctx:unset_dash ()
+               ctx:set_source_rgba (.5, .1, .1, 0.7)
+               gridline (ctx, x, xr, h, 1)
+
+
+               -- prepare waveform display drawing
+               ctx:set_source_rgba (.8, .8, .8, .7)
+               ctx:save ()
+               ctx:rectangle (math.floor (x - xr), 0, math.ceil (2 * xr), h)
+               ctx:clip ()
+
+               local chn_off = 4 + buf_size * (c - 1)
+               local buf_off = read_ptr;
+
+               -- iterate over every y-axis pixel
+               for y = 1, h - 1 do
+                       local s_min = 0
+                       local s_max = 0
+                       -- calc min/max values for given range
+                       if (buf_off + spp < buf_size) then
+                               _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, spp))
+                       else
+                               local r0 = buf_size - buf_off;
+                               _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, r0))
+                               _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off)          , s_min, s_max, spp - r0))
+                       end
+                       buf_off = (buf_off + spp) % buf_size;
+
+                       if (logscale) then
+                               s_max = ARDOUR.DSP.log_meter_coeff (s_max)
+                               s_min = - ARDOUR.DSP.log_meter_coeff (-s_min)
+                       end
+
+                       ctx:move_to (x + s_min * xr, h - y + .5)
+                       ctx:line_to (x + s_max * xr, h - y + .5)
+               end
+               ctx:stroke ()
+               ctx:restore ()
+       end
+       return {w, h}
+end