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 = 0, audio_out = 0}, }
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_run (_, _, n_samples)
57 assert (type(midiin) == "table")
58 assert (type(midiout) == "table")
60 local pos = self:shmem():atomic_get_int(0)
61 local buffer = self:shmem():to_int(1):array()
63 -- passthrough midi data, and fill the event buffer
64 for i, d in pairs(midiin) do
66 midiout[i] = { time = d["time"], data = ev }
67 pos = pos % ringsize + 1
68 for j = 1, math.min(#ev,evlen) do
69 buffer[(pos-1)*evlen + j] = ev[j]
71 for j = #ev+1, evlen do
72 buffer[(pos-1)*evlen + j] = 0
76 self:shmem():atomic_set_int(0, pos)
81 local txt = nil -- a pango context
86 function show_midi(ctx, x, y, buffer, event)
87 local base = (event - 1) * evlen
88 if buffer[base+1] == -1 then return end
89 local evtype = buffer[base + 1] >> 4
90 local channel = (buffer[base + 1] & 15) + 1 -- for System Common Messages this has no use
92 txt:set_text(string.format("%02u \u{2669}Off" .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
93 elseif evtype == 9 then
94 txt:set_text(string.format("%02u \u{2669}On " .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
95 elseif evtype == 10 then
96 txt:set_text(string.format("%02u \u{2669}KP " .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
97 elseif evtype == 11 then
98 txt:set_text(string.format("%02u CC " .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
99 elseif evtype == 12 then
100 txt:set_text(string.format("%02u PRG " .. hex, channel, buffer[base+2]))
101 elseif evtype == 13 then
102 txt:set_text(string.format("%02u KP " .. hex, channel, buffer[base+2]))
103 elseif evtype == 14 then
104 txt:set_text(string.format("%02u PBnd" .. hex, channel, buffer[base+2] | buffer[base+3] << 7))
105 elseif show_scm > 0 then -- System Common Message
106 local message = buffer[base + 1] & 15
108 txt:set_text("-- SysEx")
109 elseif message == 1 then
110 txt:set_text(string.format("-- Time Code" .. hex, buffer[base+2]))
111 elseif message == 2 then
112 txt:set_text(string.format("-- Song Pos" .. hex, buffer[base+2] | buffer[base+3] << 7))
113 elseif message == 3 then
114 txt:set_text(string.format("-- Select Song" .. hex, buffer[base+2]))
115 elseif message == 6 then
116 txt:set_text("-- Tune Rq")
117 elseif message == 8 then
118 txt:set_text("-- Timing")
119 elseif message == 10 then
120 txt:set_text("-- Start")
121 elseif message == 11 then
122 txt:set_text("-- Continue")
123 elseif message == 12 then
124 txt:set_text("-- Stop")
125 elseif message == 14 then
126 txt:set_text("-- Active")
127 elseif message == 15 then
128 txt:set_text("-- Reset")
132 txt:show_in_cairo_context (ctx)
135 function render_inline (ctx, displaywidth, max_h)
136 local ctrl = CtrlPorts:array ()
137 local pos = self:shmem():atomic_get_int(0)
138 local buffer = self:shmem():to_int(1):array()
139 local count = ctrl[2]
141 if not txt or cursize ~= ctrl[1] then
142 cursize = math.floor(ctrl[1])
143 txt = Cairo.PangoLayout (ctx, "Mono " .. cursize)
146 if ctrl[3] > 0 then hex = " %2X" else hex = " %3u" end
149 -- compute the size of the display
151 local _, lineheight = txt:get_pixel_size()
152 local displayheight = math.min(vpadding + (lineheight + vpadding) * count, max_h)
154 -- compute starting position (pango anchors text at north-west corner)
155 local x, y = hpadding, displayheight - lineheight - vpadding
158 ctx:rectangle (0, 0, displaywidth, displayheight)
159 ctx:set_source_rgba (.2, .2, .2, 1.0)
162 -- print latest event
163 ctx:set_source_rgba (1.0, 1.0, 1.0, 1.0)
164 show_midi(ctx, x, y, buffer, pos)
165 y = y - lineheight - vpadding
167 -- and remaining events
168 ctx:set_source_rgba (.8, .8, .8, 1.0)
169 for i = pos-1, 1, -1 do
170 if y < 0 then break end
171 show_midi(ctx, x, y, buffer, i)
172 y = y - lineheight - vpadding
174 for i = ringsize, pos+1, -1 do
175 if y < 0 then break end
176 show_midi(ctx, x, y, buffer, i)
177 y = y - lineheight - vpadding
180 return {displaywidth, displayheight}