summaryrefslogtreecommitdiff
path: root/scripts/midimon.lua
diff options
context:
space:
mode:
authorJulien "_FrnchFrgg_" RIVAUD <frnchfrgg@free.fr>2016-07-11 15:29:56 +0200
committerJulien "_FrnchFrgg_" RIVAUD <frnchfrgg@free.fr>2016-07-11 15:29:56 +0200
commit1f08503ada66ea3a699140adcbafddd71eb9ad95 (patch)
tree7978f3424816bca19c9054c993f2020e5e816c38 /scripts/midimon.lua
parent2f535e3410475bc5159b0ad063e0fae1e1554980 (diff)
Add a new MIDI monitor plugin
This plugin lets through all incoming MIDI events, and also shows the latest ones in a human-readable format directly on the mixer strip. The user can choose the font size and the number of recent events displayed, as well as whether to print values in decimal or hexadecimal, and whether to print system events.
Diffstat (limited to 'scripts/midimon.lua')
-rw-r--r--scripts/midimon.lua181
1 files changed, 181 insertions, 0 deletions
diff --git a/scripts/midimon.lua b/scripts/midimon.lua
new file mode 100644
index 0000000000..9c8df8bf8c
--- /dev/null
+++ b/scripts/midimon.lua
@@ -0,0 +1,181 @@
+ardour {
+ ["type"] = "dsp",
+ name = "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 { { audio_in = 0, audio_out = 0}, }
+end
+
+function dsp_has_midi_input () return true end
+function dsp_has_midi_output () return true 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 }
+ }
+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_run (_, _, n_samples)
+ assert (type(midiin) == "table")
+ assert (type(midiout) == "table")
+
+ local pos = self:shmem():atomic_get_int(0)
+ local buffer = self:shmem():to_int(1):array()
+
+ -- passthrough midi data, and fill the event buffer
+ for i, d in pairs(midiin) do
+ local ev = d["data"]
+ midiout[i] = { time = d["time"], data = ev }
+ pos = pos % ringsize + 1
+ for j = 1, math.min(#ev,evlen) do
+ buffer[(pos-1)*evlen + j] = ev[j]
+ end
+ for j = #ev+1, evlen do
+ buffer[(pos-1)*evlen + j] = 0
+ 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 show_scm = nil
+
+function show_midi(ctx, x, y, buffer, event)
+ local base = (event - 1) * evlen
+ if buffer[base+1] == -1 then return 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" .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
+ elseif evtype == 9 then
+ txt:set_text(string.format("%02u \u{2669}On " .. hex .. hex, channel, buffer[base+2], buffer[base+3]))
+ elseif evtype == 10 then
+ txt:set_text(string.format("%02u \u{2669}KP " .. hex .. hex, channel, 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")
+ end
+ end
+ ctx:move_to (x, y)
+ txt:show_in_cairo_context (ctx)
+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 = " %2X" else hex = " %3u" end
+ show_scm = ctrl[4]
+
+ -- 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 ()
+
+ -- print latest event
+ ctx:set_source_rgba (1.0, 1.0, 1.0, 1.0)
+ show_midi(ctx, x, y, buffer, pos)
+ y = y - lineheight - vpadding
+
+ -- and remaining events
+ ctx:set_source_rgba (.8, .8, .8, 1.0)
+ for i = pos-1, 1, -1 do
+ if y < 0 then break end
+ show_midi(ctx, x, y, buffer, i)
+ y = y - lineheight - vpadding
+ end
+ for i = ringsize, pos+1, -1 do
+ if y < 0 then break end
+ show_midi(ctx, x, y, buffer, i)
+ y = y - lineheight - vpadding
+ end
+
+ return {displaywidth, displayheight}
+end