2016-07-11 09:29:56 -04:00
|
|
|
|
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 ()
|
2016-08-04 11:47:38 -04:00
|
|
|
|
return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, }
|
2016-07-11 09:29:56 -04:00
|
|
|
|
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",
|
2016-07-12 17:02:30 -04:00
|
|
|
|
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 },
|
2016-07-11 09:29:56 -04:00
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
2016-07-11 18:13:01 -04:00
|
|
|
|
function dsp_runmap (bufs, in_map, out_map, n_samples, offset)
|
2016-07-11 09:29:56 -04:00
|
|
|
|
local pos = self:shmem():atomic_get_int(0)
|
|
|
|
|
local buffer = self:shmem():to_int(1):array()
|
|
|
|
|
|
2016-07-11 18:13:01 -04:00
|
|
|
|
-- 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
|
2016-07-11 09:29:56 -04:00
|
|
|
|
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
|
2016-07-12 17:02:30 -04:00
|
|
|
|
local format_note = nil
|
2016-07-11 09:29:56 -04:00
|
|
|
|
local show_scm = nil
|
|
|
|
|
|
2016-07-12 17:02:30 -04:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2016-07-11 09:29:56 -04:00
|
|
|
|
function show_midi(ctx, x, y, buffer, event)
|
|
|
|
|
local base = (event - 1) * evlen
|
2016-07-11 11:39:59 -04:00
|
|
|
|
if buffer[base+1] == -1 then return false end
|
2016-07-11 09:29:56 -04:00
|
|
|
|
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
|
2016-07-12 17:02:30 -04:00
|
|
|
|
txt:set_text(string.format("%02u \u{2669}Off%s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3]))
|
2016-07-11 09:29:56 -04:00
|
|
|
|
elseif evtype == 9 then
|
2016-07-12 17:02:30 -04:00
|
|
|
|
txt:set_text(string.format("%02u \u{2669}On %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3]))
|
2016-07-11 09:29:56 -04:00
|
|
|
|
elseif evtype == 10 then
|
2016-07-12 17:02:30 -04:00
|
|
|
|
txt:set_text(string.format("%02u \u{2669}KP %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3]))
|
2016-07-11 09:29:56 -04:00
|
|
|
|
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")
|
2016-07-11 11:39:59 -04:00
|
|
|
|
else
|
|
|
|
|
return false
|
2016-07-11 09:29:56 -04:00
|
|
|
|
end
|
2016-07-11 11:39:59 -04:00
|
|
|
|
else
|
|
|
|
|
return false
|
2016-07-11 09:29:56 -04:00
|
|
|
|
end
|
|
|
|
|
ctx:move_to (x, y)
|
|
|
|
|
txt:show_in_cairo_context (ctx)
|
2016-07-11 11:39:59 -04:00
|
|
|
|
return true
|
2016-07-11 09:29:56 -04:00
|
|
|
|
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
|
|
|
|
|
|
2016-07-11 11:39:59 -04:00
|
|
|
|
if ctrl[3] > 0 then hex = " %02X" else hex = " %3u" end
|
2016-07-11 09:29:56 -04:00
|
|
|
|
show_scm = ctrl[4]
|
|
|
|
|
|
2016-07-12 17:02:30 -04:00
|
|
|
|
if ctrl[5] > 0 then
|
|
|
|
|
format_note = format_note_num
|
|
|
|
|
else
|
|
|
|
|
format_note = format_note_name
|
|
|
|
|
end
|
|
|
|
|
|
2016-07-11 09:29:56 -04:00
|
|
|
|
-- 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 ()
|
|
|
|
|
|
2016-07-11 11:39:59 -04:00
|
|
|
|
-- color of latest event
|
2016-07-11 09:29:56 -04:00
|
|
|
|
ctx:set_source_rgba (1.0, 1.0, 1.0, 1.0)
|
|
|
|
|
|
2016-07-11 11:39:59 -04:00
|
|
|
|
-- print events
|
|
|
|
|
for i = pos, 1, -1 do
|
2016-07-11 09:29:56 -04:00
|
|
|
|
if y < 0 then break end
|
2016-07-11 11:39:59 -04:00
|
|
|
|
if show_midi(ctx, x, y, buffer, i) then
|
|
|
|
|
y = y - lineheight - vpadding
|
|
|
|
|
ctx:set_source_rgba (.8, .8, .8, 1.0)
|
|
|
|
|
end
|
2016-07-11 09:29:56 -04:00
|
|
|
|
end
|
|
|
|
|
for i = ringsize, pos+1, -1 do
|
|
|
|
|
if y < 0 then break end
|
2016-07-11 11:39:59 -04:00
|
|
|
|
if show_midi(ctx, x, y, buffer, i) then
|
|
|
|
|
y = y - lineheight - vpadding
|
|
|
|
|
ctx:set_source_rgba (.8, .8, .8, 1.0)
|
|
|
|
|
end
|
2016-07-11 09:29:56 -04:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return {displaywidth, displayheight}
|
|
|
|
|
end
|