summaryrefslogtreecommitdiff
path: root/share/scripts/midimon.lua
diff options
context:
space:
mode:
Diffstat (limited to 'share/scripts/midimon.lua')
-rw-r--r--share/scripts/midimon.lua213
1 files changed, 213 insertions, 0 deletions
diff --git a/share/scripts/midimon.lua b/share/scripts/midimon.lua
new file mode 100644
index 0000000000..7f58fd34de
--- /dev/null
+++ b/share/scripts/midimon.lua
@@ -0,0 +1,213 @@
+ardour {
+ ["type"] = "dsp",
+ name = "a-MIDI Monitor",
+ category = "Visualization",
+ license = "GPLv2",
+ author = "Ardour Team",
+ description = [[Display recent MIDIĀ events inline in the mixer strip]]
+}
+
+local maxevents = 20
+local ringsize = maxevents * 3
+local evlen = 3
+local hpadding, vpadding = 4, 2
+
+function dsp_ioconfig ()
+ return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, }
+end
+
+function dsp_params ()
+ return
+ {
+ { ["type"] = "input",
+ name = "Font size",
+ doc = "Text size used by the monitor to display midi events",
+ min = 4, max = 12, default = 7, integer = true },
+ { ["type"] = "input",
+ name = "Line count",
+ doc = "How many events will be shown at most",
+ min = 1, max = maxevents, default = 6, integer = true },
+ { ["type"] = "input",
+ name = "Hexadecimal",
+ doc = "If enabled, values will be printed in hexadecimal notation",
+ min = 0, max = 1, default = 0, toggled = true },
+ { ["type"] = "input",
+ name = "System messages",
+ doc = "If enabled, the monitor will show System Control and Real-Time messages",
+ min = 0, max = 1, default = 0, toggled = true },
+ { ["type"] = "input",
+ name = "Numeric Notes",
+ doc = "If enabled, note-events displayed numerically",
+ min = 0, max = 1, default = 0, toggled = true },
+ }
+end
+
+function dsp_init (rate)
+ -- create a shmem space to hold latest midi events
+ -- a int representing the index of the last event, and
+ -- a C-table as storage for events.
+ self:shmem():allocate(1 + ringsize*evlen)
+ self:shmem():atomic_set_int(0, 1)
+ local buffer = self:shmem():to_int(1):array()
+ for i = 1, ringsize*evlen do
+ buffer[i] = -1 -- sentinel for empty slot
+ end
+end
+
+function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
+ local pos = self:shmem():atomic_get_int(0)
+ local buffer = self:shmem():to_int(1):array()
+
+ -- passthrough all data
+ ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio"))
+ ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("midi"))
+
+ -- then fill the event buffer
+ local ib = in_map:get (ARDOUR.DataType ("midi"), 0) -- index of 1st midi input
+
+ if ib ~= ARDOUR.ChanMapping.Invalid then
+ local events = bufs:get_midi (ib):table () -- copy event list into a lua table
+
+ -- iterate over all MIDI events
+ for _, e in pairs (events) do
+ local ev = e:buffer():array()
+ pos = pos % ringsize + 1
+ -- copy the data
+ for j = 1, math.min(e:size(),evlen) do
+ buffer[(pos-1)*evlen + j] = ev[j]
+ end
+ -- zero unused slots
+ for j = e:size()+1, evlen do
+ buffer[(pos-1)*evlen + j] = 0
+ end
+ end
+ end
+
+ self:shmem():atomic_set_int(0, pos)
+
+ self:queue_draw ()
+end
+
+local txt = nil -- a pango context
+local cursize = 0
+local hex = nil
+local format_note = nil
+local show_scm = nil
+
+function format_note_name(b)
+ return string.format ("%5s", ARDOUR.ParameterDescriptor.midi_note_name (b))
+end
+
+function format_note_num(b)
+ return string.format (hex, b)
+end
+
+
+function show_midi(ctx, x, y, buffer, event)
+ local base = (event - 1) * evlen
+ if buffer[base+1] == -1 then return false end
+ local evtype = buffer[base + 1] >> 4
+ local channel = (buffer[base + 1] & 15) + 1 -- for System Common Messages this has no use
+ if evtype == 8 then
+ txt:set_text(string.format("%02u \u{2669}Off%s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3]))
+ elseif evtype == 9 then
+ txt:set_text(string.format("%02u \u{2669}On %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3]))
+ elseif evtype == 10 then
+ txt:set_text(string.format("%02u \u{2669}KP %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3]))
+ elseif evtype == 11 then
+ txt:set_text(string.format("%02u CC " .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
+ elseif evtype == 12 then
+ txt:set_text(string.format("%02u PRG " .. hex, channel, buffer[base+2]))
+ elseif evtype == 13 then
+ txt:set_text(string.format("%02u KP " .. hex, channel, buffer[base+2]))
+ elseif evtype == 14 then
+ txt:set_text(string.format("%02u PBnd" .. hex, channel, buffer[base+2] | buffer[base+3] << 7))
+ elseif show_scm > 0 then -- System Common Message
+ local message = buffer[base + 1] & 15
+ if message == 0 then
+ txt:set_text("-- SysEx")
+ elseif message == 1 then
+ txt:set_text(string.format("-- Time Code" .. hex, buffer[base+2]))
+ elseif message == 2 then
+ txt:set_text(string.format("-- Song Pos" .. hex, buffer[base+2] | buffer[base+3] << 7))
+ elseif message == 3 then
+ txt:set_text(string.format("-- Select Song" .. hex, buffer[base+2]))
+ elseif message == 6 then
+ txt:set_text("-- Tune Rq")
+ elseif message == 8 then
+ txt:set_text("-- Timing")
+ elseif message == 10 then
+ txt:set_text("-- Start")
+ elseif message == 11 then
+ txt:set_text("-- Continue")
+ elseif message == 12 then
+ txt:set_text("-- Stop")
+ elseif message == 14 then
+ txt:set_text("-- Active")
+ elseif message == 15 then
+ txt:set_text("-- Reset")
+ else
+ return false
+ end
+ else
+ return false
+ end
+ ctx:move_to (x, y)
+ txt:show_in_cairo_context (ctx)
+ return true
+end
+
+function render_inline (ctx, displaywidth, max_h)
+ local ctrl = CtrlPorts:array ()
+ local pos = self:shmem():atomic_get_int(0)
+ local buffer = self:shmem():to_int(1):array()
+ local count = ctrl[2]
+
+ if not txt or cursize ~= ctrl[1] then
+ cursize = math.floor(ctrl[1])
+ txt = Cairo.PangoLayout (ctx, "Mono " .. cursize)
+ end
+
+ if ctrl[3] > 0 then hex = " %02X" else hex = " %3u" end
+ show_scm = ctrl[4]
+
+ if ctrl[5] > 0 then
+ format_note = format_note_num
+ else
+ format_note = format_note_name
+ end
+
+ -- compute the size of the display
+ txt:set_text("0")
+ local _, lineheight = txt:get_pixel_size()
+ local displayheight = math.min(vpadding + (lineheight + vpadding) * count, max_h)
+
+ -- compute starting position (pango anchors text at north-west corner)
+ local x, y = hpadding, displayheight - lineheight - vpadding
+
+ -- clear background
+ ctx:rectangle (0, 0, displaywidth, displayheight)
+ ctx:set_source_rgba (.2, .2, .2, 1.0)
+ ctx:fill ()
+
+ -- color of latest event
+ ctx:set_source_rgba (1.0, 1.0, 1.0, 1.0)
+
+ -- print events
+ for i = pos, 1, -1 do
+ if y < 0 then break end
+ if show_midi(ctx, x, y, buffer, i) then
+ y = y - lineheight - vpadding
+ ctx:set_source_rgba (.8, .8, .8, 1.0)
+ end
+ end
+ for i = ringsize, pos+1, -1 do
+ if y < 0 then break end
+ if show_midi(ctx, x, y, buffer, i) then
+ y = y - lineheight - vpadding
+ ctx:set_source_rgba (.8, .8, .8, 1.0)
+ end
+ end
+
+ return {displaywidth, displayheight}
+end