description changes and stop jumping instructions if proc:isnil()
[ardour.git] / scripts / _tx_raw_midi_from_file.lua
1 ardour {
2         ["type"]    = "EditorAction",
3         name        = "Send Raw MIDI from File",
4         license     = "MIT",
5         author      = "Ardour Team",
6         description = [[Read raw binary midi (.syx) from file and send it to a control port]]
7 }
8
9 function factory () return function ()
10
11         function portlist ()
12                 local rv = {}
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
18                         rv[amp:name()] = amp
19                         print (amp:name(), amp:sends_output())
20                         ::continue::
21                 end
22                 return rv
23         end
24
25         local dialog_options = {
26                 { type = "file", key = "file", title = "Select .syx MIDI file" },
27                 { type = "dropdown", key = "port", title = "Target Port", values = portlist () }
28         }
29
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
33
34         if not rv then return end -- user cancelled
35
36         local f = io.open (rv["file"], "rb")
37
38         if not f then
39                 LuaDialog.Message ("Raw MIDI Tx", "File Not Found", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run ()
40                 goto out
41         end
42
43         do  -- scope for 'local'
44                 local size = f:seek("end") -- determine file size
45                 f:seek("set", 0)
46
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
52                                 f:close ()
53                                 goto out
54                         end
55                 end
56         end
57
58         do -- scope for 'local'
59                 local midi_byte_count = 0
60                 local total_read = 0
61                 local message_count = 0
62                 local long_message = false
63
64                 local async_midi_port = rv["port"] -- reference to port
65                 local parser = ARDOUR.RawMidiParser () -- construct a MIDI parser
66
67                 while true do
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
72
73                         -- parse MIDI data byte-by-byte
74                         for i = 1, #bytes do
75                                 if parser:process_byte (bytes:byte (i)) then
76                                         if parser:buffer_size () > 255 then
77                                                 long_message = true
78                                                 print ("WARNING -- single large message > 255, bytes: ", parser:buffer_size ())
79                                         end
80                                         -- parsed complete normalized MIDI message, send it
81                                         async_midi_port:write (parser:midi_buffer (), parser:buffer_size (), 0)
82
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 ())
87
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)
94                                         end
95                                 end
96                         end
97                 end
98
99                 f:close ()
100                 print ("Sent", message_count, "messages, total bytes: ", midi_byte_count, "/", total_read)
101
102                 if long_message then
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 ()
104                 end
105         end
106
107         ::out::
108         rv = nil
109         collectgarbage ()
110 end end
111
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")
115         txt:set_text ("S")
116         ctx:move_to (1, 1)
117         txt:show_in_cairo_context (ctx)
118
119         txt:set_text ("Y")
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)
123
124         txt:set_text ("X")
125         tw, th = txt:get_pixel_size ()
126         ctx:move_to ((width - tw - 1), (height - th -1))
127         txt:show_in_cairo_context (ctx)
128 end end