5 author = "Robin Gareus",
6 email = "robin@gareus.org",
7 site = "http://gareus.org",
8 description = [[An Example DSP Plugin to display the waveform on the mixer strip]]
11 -- return possible i/o configurations
12 function dsp_ioconfig ()
13 -- -1, -1 = any number of channels as long as input and output count matches
14 return { [1] = { audio_in = -1, audio_out = -1}, }
17 function dsp_params ()
20 { ["type"] = "input", name = "Timescale", min = .1, max = 5, default = 2, unit="sec", logarithmic = true },
21 { ["type"] = "input", name = "Logscale", min = 0, max = 1, default = 0, toggled = true },
22 { ["type"] = "input", name = "Height", min = 0, max = 3, default = 1, unit="dB", enum = true, scalepoints =
34 function dsp_init (rate)
35 -- global variables (DSP part only)
42 function dsp_configure (ins, outs)
43 -- store configuration in global variable
44 audio_ins = ins:n_audio ()
45 local audio_outs = outs:n_audio ()
46 assert (audio_ins == audio_outs)
47 -- allocate shared memory area
48 -- this is used to speed up DSP computaton (using a C array)
49 -- and to share data with the GUI
50 self:shmem ():allocate (4 + bufsiz * audio_ins)
51 self:shmem ():clear ()
52 self:shmem ():atomic_set_int (0, 0)
53 local cfg = self:shmem ():to_int (1):array ()
59 function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
60 local shmem = self:shmem ()
61 local write_ptr = shmem:atomic_get_int (0)
63 for c = 1,audio_ins do
64 -- Note: lua starts counting at 1, ardour's ChanMapping::get() at 0
65 local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped input buffer for given cannel
66 assert (ib ~= ARDOUR.ChanMapping.Invalid)
67 local chn_off = 4 + bufsiz * (c - 1)
68 if (write_ptr + n_samples < bufsiz) then
69 ARDOUR.DSP.copy_vector (shmem:to_float (write_ptr + chn_off), bufs:get_audio (ib):data (offset), n_samples)
71 local w0 = bufsiz - write_ptr;
72 ARDOUR.DSP.copy_vector (shmem:to_float (write_ptr + chn_off), bufs:get_audio (ib):data (offset), w0)
73 ARDOUR.DSP.copy_vector (shmem:to_float (chn_off) , bufs:get_audio (ib):data (offset), n_samples - w0)
77 write_ptr = (write_ptr + n_samples) % bufsiz
78 shmem:atomic_set_int (0, write_ptr)
80 -- emit QueueDraw every FPS
81 dpy_wr = dpy_wr + n_samples
82 if (dpy_wr > dpy_hz) then
83 dpy_wr = dpy_wr % dpy_hz;
89 -- helper function for drawing symmetric grid
90 function gridline (ctx, x, xr, h, val)
91 ctx:move_to (math.floor (.5 + x + val * xr) -.5, 1)
92 ctx:line_to (math.floor (.5 + x + val * xr) -.5, h - 1)
94 ctx:move_to (math.floor (.5 + x - val * xr) -.5, 1)
95 ctx:line_to (math.floor (.5 + x - val * xr) -.5, h - 1)
99 function render_inline (ctx, w, max_h)
100 local ctrl = CtrlPorts:array () -- get control port array (read/write)
101 local shmem = self:shmem () -- get shared memory region
102 local cfg = shmem:to_int (1):array () -- "cast" into lua-table
104 local buf_size = cfg[2]
108 local timescale = ctrl[1] or 1.0 -- display size in seconds
109 local logscale = ctrl[2] or 0; logscale = logscale > 0 -- logscale
110 local hmode = ctrl[3] or 1 -- height mode
114 h = math.ceil (w * 10 / 16)
118 elseif (hmode == 2) then
120 elseif (hmode == 3) then
123 h = math.ceil (w * 10 / 16)
131 local spp = math.floor (timescale * rate / (h - 2)) -- samples per pixel
132 local spl = spp * (h - 1) -- total number of audio samples to read
133 local read_ptr = (shmem:atomic_get_int (0) + buf_size - spl - 1) % buf_size -- read pointer
134 local xr = math.ceil ((w - 2) * (0.47 / n_chn)) -- x-axis range (per channel)
137 ctx:rectangle (0, 0, w, h)
138 ctx:set_source_rgba (.2, .2, .2, 1.0)
142 ctx:set_line_width (1.0)
143 local dash3 = C.DoubleVector ()
145 local dash4 = C.DoubleVector ()
148 -- plot every channel
150 local x = math.floor ((w - 2) * (c - .5) / n_chn) + 1.5 -- x-axis center for given channel
153 ctx:set_source_rgba (.5, .5, .5, 1.0)
154 ctx:move_to (x, 1) ctx:line_to (x, h - 1) ctx:stroke ()
156 ctx:set_dash (dash4, 2)
157 ctx:set_source_rgba (.4, .4, .4, 1.0)
159 gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-18))
160 gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-6))
161 ctx:set_dash (dash3, 2)
162 ctx:set_source_rgba (.5, .1, .1, 1.0)
163 gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-3))
165 gridline (ctx, x, xr, h, .1258)
166 gridline (ctx, x, xr, h, .5)
167 ctx:set_dash (dash3, 2)
168 ctx:set_source_rgba (.5, .1, .1, 1.0)
169 gridline (ctx, x, xr, h, .7079)
172 ctx:set_source_rgba (.5, .1, .1, 0.7)
173 gridline (ctx, x, xr, h, 1)
176 -- prepare waveform display drawing
177 ctx:set_source_rgba (.8, .8, .8, .7)
179 ctx:rectangle (math.floor (x - xr), 0, math.ceil (2 * xr), h)
182 local chn_off = 4 + buf_size * (c - 1)
183 local buf_off = read_ptr;
185 -- iterate over every y-axis pixel
189 -- calc min/max values for given range
190 if (buf_off + spp < buf_size) then
191 _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, spp))
193 local r0 = buf_size - buf_off;
194 _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, r0))
195 _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off) , s_min, s_max, spp - r0))
197 buf_off = (buf_off + spp) % buf_size;
200 s_max = ARDOUR.DSP.log_meter_coeff (s_max)
201 s_min = - ARDOUR.DSP.log_meter_coeff (-s_min)
204 ctx:move_to (x + s_min * xr, h - y + .5)
205 ctx:line_to (x + s_max * xr, h - y + .5)