fix some plugin control name typos
[ardour.git] / scripts / scope.lua
1 ardour {
2         ["type"]    = "dsp",
3         name        = "Inline Scope",
4         license     = "GPLv2",
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]]
9 }
10
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}, }
15 end
16
17 function dsp_params ()
18         return
19         {
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 =
23                         {
24                                 ["Min"] = 0,
25                                 ["16:10"] = 1,
26                                 ["1:1"] = 2,
27                                 ["Max"] = 3
28                         }
29                 },
30         }
31 end
32
33
34 function dsp_init (rate)
35         -- global variables (DSP part only)
36         samplerate = rate
37         bufsiz = 6 * rate
38         dpy_hz = rate / 25
39         dpy_wr = 0
40 end
41
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 ()
54         cfg[1] = samplerate
55         cfg[2] = bufsiz
56         cfg[3] = audio_ins
57 end
58
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)
62
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)
70                 else
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)
74                 end
75         end
76
77         write_ptr = (write_ptr + n_samples) % bufsiz
78         shmem:atomic_set_int (0, write_ptr)
79
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;
84                 self:queue_draw ()
85         end
86 end
87
88
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)
93         ctx:stroke ()
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)
96         ctx:stroke ()
97 end
98
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
103         local rate = cfg[1]
104         local buf_size = cfg[2]
105         local n_chn = cfg[3]
106
107         -- get settings
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
111
112         -- calc height
113         if hmode == 0 then
114                 h = math.ceil (w * 10 / 16)
115                 if (h > 44) then
116                         h = 44
117                 end
118         elseif (hmode == 2) then
119                 h = w
120         elseif (hmode == 3) then
121                 h = max_h
122         else
123                 h = math.ceil (w * 10 / 16)
124         end
125
126         if (h > max_h) then
127                 h = max_h
128         end
129
130         -- display settings
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)
135
136         -- clear background
137         ctx:rectangle (0, 0, w, h)
138         ctx:set_source_rgba (.2, .2, .2, 1.0)
139         ctx:fill ()
140
141         -- prepare drawing
142         ctx:set_line_width (1.0)
143         local dash3 = ARDOUR.DoubleVector ()
144         dash3:add ({1, 3})
145         local dash4 = ARDOUR.DoubleVector ()
146         dash4:add ({1, 4})
147
148         -- plot every channel
149         for c = 1,n_chn do
150                 local x = math.floor ((w - 2) * (c - .5) / n_chn) + 1.5  -- x-axis center for given channel
151
152                 -- draw grid --
153                 ctx:set_source_rgba (.5, .5, .5, 1.0)
154                 ctx:move_to (x, 1) ctx:line_to (x, h - 1) ctx:stroke ()
155
156                 ctx:set_dash (dash4, 2)
157                 ctx:set_source_rgba (.4, .4, .4, 1.0)
158                 if (logscale) then
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))
164                 else
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)
170                 end
171                 ctx:unset_dash ()
172                 ctx:set_source_rgba (.5, .1, .1, 0.7)
173                 gridline (ctx, x, xr, h, 1)
174
175
176                 -- prepare waveform display drawing
177                 ctx:set_source_rgba (.8, .8, .8, .7)
178                 ctx:save ()
179                 ctx:rectangle (math.floor (x - xr), 0, math.ceil (2 * xr), h)
180                 ctx:clip ()
181
182                 local chn_off = 4 + buf_size * (c - 1)
183                 local buf_off = read_ptr;
184
185                 -- iterate over every y-axis pixel
186                 for y = 1, h - 1 do
187                         local s_min = 0
188                         local s_max = 0
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))
192                         else
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))
196                         end
197                         buf_off = (buf_off + spp) % buf_size;
198
199                         if (logscale) then
200                                 s_max = ARDOUR.DSP.log_meter_coeff (s_max)
201                                 s_min = - ARDOUR.DSP.log_meter_coeff (-s_min)
202                         end
203
204                         ctx:move_to (x + s_min * xr, h - y + .5)
205                         ctx:line_to (x + s_max * xr, h - y + .5)
206                 end
207                 ctx:stroke ()
208                 ctx:restore ()
209         end
210         return {w, h}
211 end