|
1 <stránka |
|
2 xmlns="https://trac.frantovo.cz/xml-web-generator/wiki/xmlns/strana" |
|
3 xmlns:m="https://trac.frantovo.cz/xml-web-generator/wiki/xmlns/makro"> |
|
4 |
|
5 <nadpis>Monitoring MIDI messages using JACK</nadpis> |
|
6 <perex>examine events running through a JACK pipeline or bind MIDI keys to actions + bonus: Roland MT-32</perex> |
|
7 <m:pořadí-příkladu>04100</m:pořadí-příkladu> |
|
8 |
|
9 <text xmlns="http://www.w3.org/1999/xhtml"> |
|
10 |
|
11 <p> |
|
12 A powerful audio system called <a href="https://jackaudio.org/">JACK</a> allows us to |
|
13 build pipelines consisting of audio interfaces, players, recorders, filters and effects… |
|
14 and route sound streams (both PCM and MIDI) through them. |
|
15 MIDI messages can come from keyboards or other hardware MIDI controllers or from MIDI players and other software. |
|
16 Sometimes it is useful to check what is happening under the hood and examine particular MIDI messages |
|
17 instead of just playing them on a sound module or synthesizer. |
|
18 In this example we will show how to bridge two seemingly unrelated worlds: real-time audio and relational pipes. |
|
19 </p> |
|
20 |
|
21 <p> |
|
22 We can join the JACK graph with <code>relpipe-in-jack</code> command. |
|
23 It does not consume STDIN, it gets events from JACK instead, so no other input data are needed. |
|
24 </p> |
|
25 |
|
26 <m:pre jazyk="bash">relpipe-in-jack | relpipe-out-gui</m:pre> |
|
27 |
|
28 <p> |
|
29 We are connected to the JACK daemon now, but no data are routed to us. |
|
30 So we need to direct some MIDI streams to our input: |
|
31 </p> |
|
32 |
|
33 <m:img src="img/jack-connections-1.png"/> |
|
34 |
|
35 <p> |
|
36 And then incoming MIDI events will generate output like this: |
|
37 </p> |
|
38 |
|
39 <pre><![CDATA["event","channel","note_on","note_pitch","note_velocity","controller_id","controller_value","raw" |
|
40 "note","0","true","43","64","0","0","90 2b 40" |
|
41 "note","0","false","43","64","0","0","80 2b 40" |
|
42 "note","0","true","50","64","0","0","90 32 40" |
|
43 "note","0","false","50","64","0","0","80 32 40" |
|
44 "note","0","true","67","64","0","0","90 43 40" |
|
45 "note","0","false","67","64","0","0","80 43 40" |
|
46 "control","0","false","0","0","32","1","b0 20 01" |
|
47 "control","0","false","0","0","0","0","b0 00 00" |
|
48 "unknown","0","false","0","0","0","0","c0 00" |
|
49 "note","0","true","36","64","0","0","90 24 40" |
|
50 "note","0","false","36","64","0","0","80 24 40" |
|
51 "sysex","0","false","0","0","0","0","f0 41 10 16 12 20 00 00 61 68 6f 6a 3e f7" |
|
52 "note","0","true","60","64","0","0","90 3c 40" |
|
53 "note","0","false","60","64","0","0","80 3c 40"]]></pre> |
|
54 |
|
55 <p> |
|
56 CSV is not the only option. Once data are in the machine-readable relational format, |
|
57 we can convert them into any other format or process them using any relational transformation. |
|
58 For ad-hoc monitoring, we can use <code>relpipe-out-gui</code> and watch MIDI messages – as they come – |
|
59 in a table in GUI: |
|
60 </p> |
|
61 |
|
62 <m:img src="img/jack-monitoring-gui-1.png"/> |
|
63 |
|
64 <p> |
|
65 As we can see, there are raw MIDI data and also some decoded values. |
|
66 Future versions of <code>relpipe-in-jack</code> should provide improved decoder and other features like timing information. |
|
67 </p> |
|
68 |
|
69 <p> |
|
70 We can use <code>relpipe-in-jack</code> not only for monitoring |
|
71 but we can also write a simple script that binds particular MIDI events |
|
72 to actions to be executed – like starting a program or running a script on background. |
|
73 So we can map some keys/buttons/knobs on our piano, mixer or other controller: |
|
74 </p> |
|
75 |
|
76 <m:pre jazyk="bash"><![CDATA[#!/bin/bash |
|
77 |
|
78 read_nullbyte() { local IFS=; for v in "$@"; do export "$v"; read -r -d '' "$v"; done } |
|
79 |
|
80 relpipe-in-jack \ |
|
81 | relpipe-out-nullbyte \ |
|
82 | while read_nullbyte event channel note_on note_pitch velocity c_id c_value raw; do |
|
83 if [[ "$note_pitch" == "21" && "$note_on" == "true" ]]; then kcalc & |
|
84 elif [[ "$note_pitch" == "23" && "$note_on" == "true" ]]; then dolphin & |
|
85 fi |
|
86 done]]></m:pre> |
|
87 |
|
88 <p> |
|
89 Many audio applications have similar feature, |
|
90 but we can leave this script running on the background |
|
91 even when we do not want to have loaded a complex software like a DAW (Digital audio workstation). |
|
92 </p> |
|
93 |
|
94 |
|
95 <h2>Bonus: display messages on Roland MT-32</h2> |
|
96 |
|
97 <p> |
|
98 Maybe you remember games or MIDI files that <i>magically</i> displayed text messages on your |
|
99 <a href="https://en.wikipedia.org/wiki/Roland_MT-32">MT-32</a> unit |
|
100 which you used to generate wonderful sounds and music from the MIDI signal. |
|
101 Let us look inside and check how this <i>magic</i> works. |
|
102 </p> |
|
103 |
|
104 <p> |
|
105 We have to route the MIDI signal through JACK and fork it there (similarly to <code>tee</code> in shell) |
|
106 so it will flow both to the MT-32 and the <code>relpipe-in-jack</code> process. |
|
107 </p> |
|
108 |
|
109 <p> |
|
110 Given that we have running the JACK daemon (can be started from GUI application like QjackCtl) |
|
111 and <code>a2j</code> which bridges ALSA-MIDI and JACK-MIDI together. |
|
112 We set up the routing (the fork) and then play a SMF (Standard MIDI file) file: |
|
113 </p> |
|
114 |
|
115 <pre>aplaymidi --port=14:0 test.mid</pre> |
|
116 |
|
117 <p>Proper port number could be found here:</p> |
|
118 |
|
119 <pre><![CDATA[$ aplaymidi -l |
|
120 Port Client name Port name |
|
121 14:0 Midi Through Midi Through Port-0 |
|
122 24:0 Rubix44 Rubix44 MIDI 1]]></pre> |
|
123 |
|
124 <p> |
|
125 Data sent to the <i>Midi Through</i> port in the ALSA-MIDI emerges in the <code>a2j</code> in the JACK-MIDI graph |
|
126 and then flows to <code>relpipe-in-jack</code>. |
|
127 So the MT-32 will play the sounds and display messages (if any) and we will get copy of everything in |
|
128 <code>relpipe-in-jack</code> that passes data to command like <code>relpipe-out-gui</code> |
|
129 or <code>relpipe-out-csv</code> where we see decoded messages and also raw MIDI data. |
|
130 </p> |
|
131 |
|
132 <p> |
|
133 n.b. the <i>Midi Through</i> in the ALSA-MIDI graph should be connected to our audio interface |
|
134 (e.g. <a href="https://blog.frantovo.cz/c/365/Roland%20Rubix44%20%E2%80%93%20extern%C3%AD%20zvukov%C3%A1%20karta">Rubix44</a>) |
|
135 which is linked to the MT-32 using a MIDI cable. |
|
136 </p> |
|
137 |
|
138 <p> |
|
139 Messages that cause text to be showed on the MT-32 display are so called <i>System Exclusive (SysEx) messages</i>. |
|
140 After executing the <code>aplaymidi</code> we see for example: |
|
141 </p> |
|
142 |
|
143 <m:img src="img/mt-32-display-1.jpeg"/> |
|
144 |
|
145 <p> |
|
146 And <code>relpipe-in-jack</code> captured and reported this SysEx message: |
|
147 </p> |
|
148 |
|
149 <pre>f0 41 10 16 12 20 00 00 <strong>52 65 6c 61 74 69 6f 6e 61 6c 20 70 69 70 65 73</strong> 14 f7</pre> |
|
150 |
|
151 <p> |
|
152 The highlighted part (52 … 73) is easy – it is just the <code>Relational pipes</code> ASCII bytes in HEX. |
|
153 But what does the rest mean? |
|
154 </p> |
|
155 |
|
156 <ul> |
|
157 <li><code>f0</code> = start of SysEx message</li> |
|
158 <li><code>41</code> = Roland <a href="https://www.midi.org/specifications-old/item/manufacturer-id-numbers">manufacturer ID</a>. This is important because SysEx messages are vendor-specific. So devices from other manufacturers (that might be in the same MIDI daisy-chain) will know, that this message is not for them and will ignore it.</li> |
|
159 <li><code>10</code> = device ID – if we have more units with same manufacturer and model ID, we should assign them different device IDs, so we can address them individually.</li> |
|
160 <li><code>16</code> = model ID</li> |
|
161 <li><code>12</code> = command ID, 12 is set, 11 is get</li> |
|
162 <li><code>20 00 00</code> = the address of the value we want to set, 20 00 00 is the text to be displayed</li> |
|
163 <li><code>14</code> = checksum computed from the <code>20 00 00 … 73</code> part</li> |
|
164 <li><code>f7</code> = end of the SysEx message</li> |
|
165 </ul> |
|
166 |
|
167 <p> |
|
168 Only the start and end of the SysEx message and the manufacturer ID are part of the MIDI standard. |
|
169 The rest of the bytes is Roland specific. SysEx messages can do much more than controlling the display – consult the documentation for particular device. |
|
170 </p> |
|
171 |
|
172 |
|
173 <p> |
|
174 We can generate our own messages using the <a href="https://hg.frantovo.cz/midi/mt-32-display/">mt-32-display</a> tool. |
|
175 It produces SysEx messages for Roland MT-32 in hex format which can be passed directly to the <code>amidi</code> command (see examples in the code). |
|
176 Unfortunately <code>amidi</code> works only with hardware ports and is unable to route through JACK. |
|
177 But there is another tool called <a href="https://hg.frantovo.cz/midi/sysex2smf/">sysex2smf</a> |
|
178 which translates raw SysEx data to Standard MIDI file (<code>*.mid</code>). |
|
179 And SMF can be played using the <code>aplaymidi</code> which can be routed through JACK and our pipelines. |
|
180 So the 48 lines of C++ code (both tools together) is everything we need to enjoy this nice feature of our MT-32 unit. |
|
181 </p> |
|
182 |
|
183 <video src="https://blog.frantovo.cz/s/1581/mt-32.webm" poster="img/mt-32-video-1.jpeg" controls="controls" width="820px">Maybe you often ask: What would MT-32 do?</video> |
|
184 |
|
185 |
|
186 </text> |
|
187 |
|
188 </stránka> |