4 category = "Visualization",
6 author = "Ardour Team",
7 description = [[Display recent MIDIĀ events inline in the mixer strip]]
11 local ringsize = maxevents * 3
13 local hpadding, vpadding = 4, 2
15 function dsp_ioconfig ()
16 return { { audio_in = -1, audio_out = -1}, }
19 function dsp_has_midi_input () return true end
20 function dsp_has_midi_output () return true end
22 function dsp_params ()
27 doc = "Text size used by the monitor to display midi events",
28 min = 4, max = 12, default = 7, integer = true },
31 doc = "How many events will be shown at most",
32 min = 1, max = maxevents, default = 6, integer = true },
35 doc = "If enabled, values will be printed in hexadecimal notation",
36 min = 0, max = 1, default = 0, toggled = true },
38 name = "System messages",
39 doc = "If enabled, the monitor will show System Control and Real-Time messages",
40 min = 0, max = 1, default = 0, toggled = true }
44 function dsp_init (rate)
45 -- create a shmem space to hold latest midi events
46 -- a int representing the index of the last event, and
47 -- a C-table as storage for events.
48 self:shmem():allocate(1 + ringsize*evlen)
49 self:shmem():atomic_set_int(0, 1)
50 local buffer = self:shmem():to_int(1):array()
51 for i = 1, ringsize*evlen do
52 buffer[i] = -1 -- sentinel for empty slot
56 function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
57 local pos = self:shmem():atomic_get_int(0)
58 local buffer = self:shmem():to_int(1):array()
60 -- passthrough all data
61 ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio"))
62 ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("midi"))
64 -- then fill the event buffer
65 local ib = in_map:get (ARDOUR.DataType ("midi"), 0) -- index of 1st midi input
67 if ib ~= ARDOUR.ChanMapping.Invalid then
68 local events = bufs:get_midi (ib):table () -- copy event list into a lua table
70 -- iterate over all MIDI events
71 for _, e in pairs (events) do
72 local ev = e:buffer():array()
73 pos = pos % ringsize + 1
75 for j = 1, math.min(e:size(),evlen) do
76 buffer[(pos-1)*evlen + j] = ev[j]
79 for j = e:size()+1, evlen do
80 buffer[(pos-1)*evlen + j] = 0
85 self:shmem():atomic_set_int(0, pos)
90 local txt = nil -- a pango context
95 function show_midi(ctx, x, y, buffer, event)
96 local base = (event - 1) * evlen
97 if buffer[base+1] == -1 then return false end
98 local evtype = buffer[base + 1] >> 4
99 local channel = (buffer[base + 1] & 15) + 1 -- for System Common Messages this has no use
101 txt:set_text(string.format("%02u \u{2669}Off" .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
102 elseif evtype == 9 then
103 txt:set_text(string.format("%02u \u{2669}On " .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
104 elseif evtype == 10 then
105 txt:set_text(string.format("%02u \u{2669}KP " .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
106 elseif evtype == 11 then
107 txt:set_text(string.format("%02u CC " .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
108 elseif evtype == 12 then
109 txt:set_text(string.format("%02u PRG " .. hex, channel, buffer[base+2]))
110 elseif evtype == 13 then
111 txt:set_text(string.format("%02u KP " .. hex, channel, buffer[base+2]))
112 elseif evtype == 14 then
113 txt:set_text(string.format("%02u PBnd" .. hex, channel, buffer[base+2] | buffer[base+3] << 7))
114 elseif show_scm > 0 then -- System Common Message
115 local message = buffer[base + 1] & 15
117 txt:set_text("-- SysEx")
118 elseif message == 1 then
119 txt:set_text(string.format("-- Time Code" .. hex, buffer[base+2]))
120 elseif message == 2 then
121 txt:set_text(string.format("-- Song Pos" .. hex, buffer[base+2] | buffer[base+3] << 7))
122 elseif message == 3 then
123 txt:set_text(string.format("-- Select Song" .. hex, buffer[base+2]))
124 elseif message == 6 then
125 txt:set_text("-- Tune Rq")
126 elseif message == 8 then
127 txt:set_text("-- Timing")
128 elseif message == 10 then
129 txt:set_text("-- Start")
130 elseif message == 11 then
131 txt:set_text("-- Continue")
132 elseif message == 12 then
133 txt:set_text("-- Stop")
134 elseif message == 14 then
135 txt:set_text("-- Active")
136 elseif message == 15 then
137 txt:set_text("-- Reset")
145 txt:show_in_cairo_context (ctx)
149 function render_inline (ctx, displaywidth, max_h)
150 local ctrl = CtrlPorts:array ()
151 local pos = self:shmem():atomic_get_int(0)
152 local buffer = self:shmem():to_int(1):array()
153 local count = ctrl[2]
155 if not txt or cursize ~= ctrl[1] then
156 cursize = math.floor(ctrl[1])
157 txt = Cairo.PangoLayout (ctx, "Mono " .. cursize)
160 if ctrl[3] > 0 then hex = " %02X" else hex = " %3u" end
163 -- compute the size of the display
165 local _, lineheight = txt:get_pixel_size()
166 local displayheight = math.min(vpadding + (lineheight + vpadding) * count, max_h)
168 -- compute starting position (pango anchors text at north-west corner)
169 local x, y = hpadding, displayheight - lineheight - vpadding
172 ctx:rectangle (0, 0, displaywidth, displayheight)
173 ctx:set_source_rgba (.2, .2, .2, 1.0)
176 -- color of latest event
177 ctx:set_source_rgba (1.0, 1.0, 1.0, 1.0)
180 for i = pos, 1, -1 do
181 if y < 0 then break end
182 if show_midi(ctx, x, y, buffer, i) then
183 y = y - lineheight - vpadding
184 ctx:set_source_rgba (.8, .8, .8, 1.0)
187 for i = ringsize, pos+1, -1 do
188 if y < 0 then break end
189 if show_midi(ctx, x, y, buffer, i) then
190 y = y - lineheight - vpadding
191 ctx:set_source_rgba (.8, .8, .8, 1.0)
195 return {displaywidth, displayheight}