2016-03-18 16:34:22 -04:00
|
|
|
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
|
|
|
|
{
|
2016-03-18 19:31:04 -04:00
|
|
|
{ ["type"] = "input", name = "Timescale", min = .1, max = 5, default = 2, unit="sec", logarithmic = true },
|
|
|
|
{ ["type"] = "input", name = "Logscale", min = 0, max = 1, default = 0, toggled = true },
|
|
|
|
{ ["type"] = "input", name = "Height", min = 0, max = 3, default = 1, unit="dB", enum = true, scalepoints =
|
2016-03-18 16:34:22 -04:00
|
|
|
{
|
|
|
|
["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
|