Add example Lua script to send raw MIDI (sysex) from file
authorRobin Gareus <robin@gareus.org>
Tue, 5 Dec 2017 15:09:19 +0000 (16:09 +0100)
committerRobin Gareus <robin@gareus.org>
Tue, 5 Dec 2017 15:09:52 +0000 (16:09 +0100)
scripts/_tx_raw_midi_from_file.lua [new file with mode: 0644]

diff --git a/scripts/_tx_raw_midi_from_file.lua b/scripts/_tx_raw_midi_from_file.lua
new file mode 100644 (file)
index 0000000..36cbdc1
--- /dev/null
@@ -0,0 +1,110 @@
+ardour {
+       ["type"]    = "EditorAction",
+       name        = "Send Raw MIDI from File",
+       license     = "MIT",
+       author      = "Ardour Team",
+       description = [[Read raw binary midi (.syx) from file and send it to a control port]]
+}
+
+function factory () return function ()
+
+       function portlist ()
+               local rv = {}
+               local a = Session:engine()
+               local _, t = a:get_ports (ARDOUR.DataType("midi"), ARDOUR.PortList())
+               for p in t[2]:iter() do
+                       local amp = p:to_asyncmidiport ()
+                       if amp:isnil() or not amp:sends_output() then goto continue end
+                       rv[amp:name()] = amp
+                       print (amp:name(), amp:sends_output())
+                       ::continue::
+               end
+               return rv
+       end
+
+       local dialog_options = {
+               { type = "file", key = "file", title = "Select .syx MIDI file" },
+               { type = "dropdown", key = "port", title = "Target Port", values = portlist () }
+       }
+
+       local rv = LuaDialog.Dialog ("Select Taget", dialog_options):run ()
+       dialog_options = nil -- drop references (ports, shared ptr)
+       collectgarbage () -- and release the references immediately
+
+       if not rv then return end -- user cancelled
+
+       local f = io.open (rv["file"], "rb")
+
+       if not f then
+               LuaDialog.Message ("Raw MIDI Tx", "File Not Found", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run ()
+               goto out
+       end
+
+       do  -- scope for 'local'
+               local size = f:seek("end") -- determine file size
+               f:seek("set", 0)
+
+               if size > 1048576 then
+                       local ok = LuaDialog.Message ("Raw MIDI Tx",
+                               string.format ("File is larger than 1MB.\nFile-size = %.1f kB\n\nContinue?", size / 1024),
+                               LuaDialog.MessageType.Question, LuaDialog.ButtonType.Yes_No):run ()
+                       if ok ~= LuaDialog.Response.Yes then
+                               f:close ()
+                               goto out
+                       end
+               end
+       end
+
+       do -- scope for 'local'
+               local midi_byte_count = 0
+               local total_read = 0
+               local message_count = 0
+               local long_message = false
+
+               local async_midi_port = rv["port"] -- reference to port
+               local parser = ARDOUR.RawMidiParser () -- construct a MIDI parser
+
+               while true do
+                       -- read file in 64byte chunks
+                       local bytes = f:read (64)
+                       if not bytes then break end
+                       total_read = total_read + #bytes
+
+                       -- parse MIDI data byte-by-byte
+                       for i = 1, #bytes do
+                               if parser:process_byte (bytes:byte (i)) then
+                                       if parser:buffer_size () > 127 then
+                                               long_message = true
+                                               print ("WARNING -- single large message > 127, bytes: ", parser:buffer_size ())
+                                       end
+                                       -- parsed complete normalized MIDI message, send it
+                                       async_midi_port:write (parser:midi_buffer (), parser:buffer_size (), 0)
+
+                                       -- Physical MIDI is sent at 31.25kBaud.
+                                       -- Every message is sent as 10bit message on the wire,
+                                       -- so every MIDI byte needs 320usec.
+                                       ARDOUR.LuaAPI.usleep (400 * parser:buffer_size ())
+
+                                       -- count msgs and valid bytes sent
+                                       midi_byte_count = midi_byte_count + parser:buffer_size ()
+                                       message_count = message_count + 1
+                                       if 0 == message_count % 50 then
+                                               -- print() wakes up the GUI, prevent stalling the event loop
+                                               print ("Sent", message_count, "messages, bytes so far: ", midi_byte_count)
+                                       end
+                               end
+                       end
+               end
+
+               f:close ()
+               print ("Sent", message_count, "messages, total bytes: ", midi_byte_count, "/", total_read)
+
+               if long_message then
+                       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 ()
+               end
+       end
+
+       ::out::
+       rv = nil
+       collectgarbage ()
+end end