4 category = "Visualization",
6 author = "Robin Gareus",
7 email = "robin@gareus.org",
8 site = "http://gareus.org",
9 description = [[An Example DSP Plugin to display the waveform on the mixer strip]]
12 -- return possible i/o configurations
13 function dsp_ioconfig ()
14 -- -1, -1 = any number of channels as long as input and output count matches
15 return { [1] = { audio_in = -1, audio_out = -1}, }
18 function dsp_params ()
21 { ["type"] = "input", name = "Timescale", min = .1, max = 5, default = 2, unit="sec", logarithmic = true },
22 { ["type"] = "input", name = "Logscale", min = 0, max = 1, default = 0, toggled = true },
23 { ["type"] = "input", name = "Height", min = 0, max = 3, default = 1, enum = true, scalepoints =
35 function dsp_init (rate)
36 -- global variables (DSP part only)
43 function dsp_configure (ins, outs)
44 -- store configuration in global variable
45 audio_ins = ins:n_audio ()
46 -- allocate shared memory area
47 -- this is used to speed up DSP computaton (using a C array)
48 -- and to share data with the GUI
49 self:shmem ():allocate (4 + bufsiz * audio_ins)
50 self:shmem ():clear ()
51 self:shmem ():atomic_set_int (0, 0)
52 local cfg = self:shmem ():to_int (1):array ()
58 function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
59 local shmem = self:shmem ()
60 local write_ptr = shmem:atomic_get_int (0)
62 for c = 1,audio_ins do
63 -- Note: lua starts counting at 1, ardour's ChanMapping::get() at 0
64 local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped input buffer for given cannel
65 local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped output buffer for given cannel
66 local chn_off = 4 + bufsiz * (c - 1)
67 if (ib ~= ARDOUR.ChanMapping.Invalid) then
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)
75 if (ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob) then
76 ARDOUR.DSP.copy_vector (bufs:get_audio (ob):data (offset), bufs:get_audio (ib):data (offset), n_samples)
79 if (write_ptr + n_samples < bufsiz) then
80 ARDOUR.DSP.memset (shmem:to_float (write_ptr + chn_off), 0, n_samples)
82 local w0 = bufsiz - write_ptr;
83 ARDOUR.DSP.memset (shmem:to_float (write_ptr + chn_off), 0, w0)
84 ARDOUR.DSP.memset (shmem:to_float (chn_off) , 0, n_samples - w0)
88 -- clear unconnected inplace buffers
89 for c = 1,audio_ins do
90 local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped input buffer for given cannel
91 local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped output buffer for given cannel
92 if (ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid) then
93 bufs:get_audio (ob):silence (n_samples, offset)
97 write_ptr = (write_ptr + n_samples) % bufsiz
98 shmem:atomic_set_int (0, write_ptr)
100 -- emit QueueDraw every FPS
101 dpy_wr = dpy_wr + n_samples
102 if (dpy_wr > dpy_hz) then
103 dpy_wr = dpy_wr % dpy_hz;
109 -- helper function for drawing symmetric grid
110 function gridline (ctx, x, xr, h, val)
111 ctx:move_to (math.floor (.5 + x + val * xr) -.5, 1)
112 ctx:line_to (math.floor (.5 + x + val * xr) -.5, h - 1)
114 ctx:move_to (math.floor (.5 + x - val * xr) -.5, 1)
115 ctx:line_to (math.floor (.5 + x - val * xr) -.5, h - 1)
119 function render_inline (ctx, w, max_h)
120 local ctrl = CtrlPorts:array () -- get control port array (read/write)
121 local shmem = self:shmem () -- get shared memory region
122 local cfg = shmem:to_int (1):array () -- "cast" into lua-table
124 local buf_size = cfg[2]
128 local timescale = ctrl[1] or 1.0 -- display size in seconds
129 local logscale = ctrl[2] or 0; logscale = logscale > 0 -- logscale
130 local hmode = ctrl[3] or 1 -- height mode
134 h = math.ceil (w * 10 / 16)
138 elseif (hmode == 2) then
140 elseif (hmode == 3) then
143 h = math.ceil (w * 10 / 16)
151 local spp = math.floor (timescale * rate / (h - 2)) -- samples per pixel
152 local spl = spp * (h - 1) -- total number of audio samples to read
153 local read_ptr = (shmem:atomic_get_int (0) + buf_size - spl - 1) % buf_size -- read pointer
154 local xr = math.ceil ((w - 2) * (0.47 / n_chn)) -- x-axis range (per channel)
157 ctx:rectangle (0, 0, w, h)
158 ctx:set_source_rgba (.2, .2, .2, 1.0)
162 ctx:set_line_width (1.0)
163 local dash3 = C.DoubleVector ()
165 local dash4 = C.DoubleVector ()
168 -- plot every channel
170 local x = math.floor ((w - 2) * (c - .5) / n_chn) + 1.5 -- x-axis center for given channel
173 ctx:set_source_rgba (.5, .5, .5, 1.0)
174 ctx:move_to (x, 1) ctx:line_to (x, h - 1) ctx:stroke ()
176 ctx:set_dash (dash4, 2)
177 ctx:set_source_rgba (.4, .4, .4, 1.0)
179 gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-18))
180 gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-6))
181 ctx:set_dash (dash3, 2)
182 ctx:set_source_rgba (.5, .1, .1, 1.0)
183 gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-3))
185 gridline (ctx, x, xr, h, .1258)
186 gridline (ctx, x, xr, h, .5)
187 ctx:set_dash (dash3, 2)
188 ctx:set_source_rgba (.5, .1, .1, 1.0)
189 gridline (ctx, x, xr, h, .7079)
192 ctx:set_source_rgba (.5, .1, .1, 0.7)
193 gridline (ctx, x, xr, h, 1)
196 -- prepare waveform display drawing
197 ctx:set_source_rgba (.8, .8, .8, .7)
199 ctx:rectangle (math.floor (x - xr), 0, math.ceil (2 * xr), h)
202 local chn_off = 4 + buf_size * (c - 1)
203 local buf_off = read_ptr;
205 -- iterate over every y-axis pixel
209 -- calc min/max values for given range
210 if (buf_off + spp < buf_size) then
211 _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, spp))
213 local r0 = buf_size - buf_off;
214 _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, r0))
215 _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off) , s_min, s_max, spp - r0))
217 buf_off = (buf_off + spp) % buf_size;
220 s_max = ARDOUR.DSP.log_meter_coeff (s_max)
221 s_min = - ARDOUR.DSP.log_meter_coeff (-s_min)
224 ctx:move_to (x + s_min * xr, h - y + .5)
225 ctx:line_to (x + s_max * xr, h - y + .5)