diff options
author | Robin Gareus <robin@gareus.org> | 2016-03-18 21:34:22 +0100 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2016-03-18 21:34:22 +0100 |
commit | 91207efbaac662599c02fb2e266e215dfc5404bc (patch) | |
tree | a11c379a8db0165228bbfc293c2e6fef42f8d892 | |
parent | 877325ea6ed7d31176c353f60e2dd04cd1b60eb0 (diff) |
add a lua inline scope processor
-rw-r--r-- | scripts/scope.lua | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/scripts/scope.lua b/scripts/scope.lua new file mode 100644 index 0000000000..961670a0ac --- /dev/null +++ b/scripts/scope.lua @@ -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 |