2 ["type"] = "EditorAction",
3 name = "Send Raw MIDI from File",
5 author = "Ardour Team",
6 description = [[Read raw binary midi (.syx) from file and send it to a control port]]
9 function factory () return function ()
13 local a = Session:engine()
14 local _, t = a:get_ports (ARDOUR.DataType("midi"), ARDOUR.PortList())
15 for p in t[2]:iter() do
16 local amp = p:to_asyncmidiport ()
17 if amp:isnil() or not amp:sends_output() then goto continue end
19 print (amp:name(), amp:sends_output())
25 local dialog_options = {
26 { type = "file", key = "file", title = "Select .syx MIDI file" },
27 { type = "dropdown", key = "port", title = "Target Port", values = portlist () }
30 local rv = LuaDialog.Dialog ("Select Taget", dialog_options):run ()
31 dialog_options = nil -- drop references (ports, shared ptr)
32 collectgarbage () -- and release the references immediately
34 if not rv then return end -- user cancelled
36 local f = io.open (rv["file"], "rb")
39 LuaDialog.Message ("Raw MIDI Tx", "File Not Found", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run ()
43 do -- scope for 'local'
44 local size = f:seek("end") -- determine file size
47 if size > 1048576 then
48 local ok = LuaDialog.Message ("Raw MIDI Tx",
49 string.format ("File is larger than 1MB.\nFile-size = %.1f kB\n\nContinue?", size / 1024),
50 LuaDialog.MessageType.Question, LuaDialog.ButtonType.Yes_No):run ()
51 if ok ~= LuaDialog.Response.Yes then
58 do -- scope for 'local'
59 local midi_byte_count = 0
61 local message_count = 0
62 local long_message = false
64 local async_midi_port = rv["port"] -- reference to port
65 local parser = ARDOUR.RawMidiParser () -- construct a MIDI parser
68 -- read file in 64byte chunks
69 local bytes = f:read (64)
70 if not bytes then break end
71 total_read = total_read + #bytes
73 -- parse MIDI data byte-by-byte
75 if parser:process_byte (bytes:byte (i)) then
76 if parser:buffer_size () > 255 then
78 print ("WARNING -- single large message > 255, bytes: ", parser:buffer_size ())
80 -- parsed complete normalized MIDI message, send it
81 async_midi_port:write (parser:midi_buffer (), parser:buffer_size (), 0)
83 -- Physical MIDI is sent at 31.25kBaud.
84 -- Every message is sent as 10bit message on the wire,
85 -- so every MIDI byte needs 320usec.
86 ARDOUR.LuaAPI.usleep (400 * parser:buffer_size ())
88 -- count msgs and valid bytes sent
89 midi_byte_count = midi_byte_count + parser:buffer_size ()
90 message_count = message_count + 1
91 if 0 == message_count % 50 then
92 -- print() wakes up the GUI, prevent stalling the event loop
93 print ("Sent", message_count, "messages, bytes so far: ", midi_byte_count)
100 print ("Sent", message_count, "messages, total bytes: ", midi_byte_count, "/", total_read)
103 LuaDialog.Message ("Raw MIDI Tx", "Dataset contained messages longer than 127 bytes. Which may or may not have been transmitted successfully.", LuaDialog.MessageType.Warning, LuaDialog.ButtonType.Close):run ()
112 function icon (params) return function (ctx, width, height, fg)
113 ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
114 local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .5) .. "px")
117 txt:show_in_cairo_context (ctx)
120 local tw, th = txt:get_pixel_size ()
121 ctx:move_to (.5 * (width - tw), .5 * (height - th))
122 txt:show_in_cairo_context (ctx)
125 tw, th = txt:get_pixel_size ()
126 ctx:move_to ((width - tw - 1), (height - th -1))
127 txt:show_in_cairo_context (ctx)