Delete trailing whitespace
[ardour.git] / gtk2_ardour / midi_tracer.cc
1 /*
2     Copyright (C) 2010 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <stdint.h>
21
22 #include <sstream>
23 #include <sys/time.h>
24 #include <time.h>
25
26 #include "midi++/parser.h"
27 #include "midi++/manager.h"
28
29 #include "midi_tracer.h"
30 #include "gui_thread.h"
31 #include "i18n.h"
32
33 using namespace Gtk;
34 using namespace std;
35 using namespace MIDI;
36 using namespace Glib;
37
38 MidiTracer::MidiTracer ()
39         : ArdourDialog (_("MIDI Tracer"))
40         , parser (0)
41         , line_count_adjustment (200, 1, 2000, 1, 10)
42         , line_count_spinner (line_count_adjustment)
43         , line_count_label (_("Store this many lines: "))
44         , autoscroll (true)
45         , show_hex (true)
46         , collect (true)
47         , _update_queued (0)
48         , fifo (1024)
49         , buffer_pool ("miditracer", buffer_size, 1024) // 1024 256 byte buffers
50         , autoscroll_button (_("Auto-Scroll"))
51         , base_button (_("Decimal"))
52         , collect_button (_("Enabled"))
53 {
54         get_vbox()->set_spacing (4);
55
56         Manager::instance()->PortsChanged.connect (_manager_connection, invalidator (*this), boost::bind (&MidiTracer::ports_changed, this), gui_context());
57
58         HBox* pbox = manage (new HBox);
59         pbox->pack_start (*manage (new Label (_("Port:"))), false, false);
60
61         _port_combo.signal_changed().connect (sigc::mem_fun (*this, &MidiTracer::port_changed));
62         pbox->pack_start (_port_combo);
63         pbox->show_all ();
64         get_vbox()->pack_start (*pbox, false, false);
65
66         scroller.add (text);
67         get_vbox()->set_border_width (12);
68         get_vbox()->pack_start (scroller, true, true);
69
70         text.show ();
71         text.set_name ("MidiTracerTextView");
72         scroller.show ();
73         scroller.set_size_request (400, 400);
74
75         collect_button.set_active (true);
76         base_button.set_active (false);
77         autoscroll_button.set_active (true);
78
79         line_count_box.set_spacing (6);
80         line_count_box.pack_start (line_count_label, false, false);
81         line_count_box.pack_start (line_count_spinner, false, false);
82
83         line_count_spinner.show ();
84         line_count_label.show ();
85         line_count_box.show ();
86
87         get_action_area()->add (line_count_box);
88
89         HBox* bbox = manage (new HBox);
90         bbox->add (base_button);
91         bbox->add (collect_button);
92         bbox->add (autoscroll_button);
93         bbox->show ();
94
95         get_action_area()->add (*bbox);
96
97         base_button.signal_toggled().connect (sigc::mem_fun (*this, &MidiTracer::base_toggle));
98         collect_button.signal_toggled().connect (sigc::mem_fun (*this, &MidiTracer::collect_toggle));
99         autoscroll_button.signal_toggled().connect (sigc::mem_fun (*this, &MidiTracer::autoscroll_toggle));
100
101         base_button.show ();
102         collect_button.show ();
103         autoscroll_button.show ();
104
105         ports_changed ();
106         port_changed ();
107 }
108
109
110 MidiTracer::~MidiTracer()
111 {
112 }
113
114 void
115 MidiTracer::ports_changed ()
116 {
117         string const c = _port_combo.get_active_text ();
118         _port_combo.clear ();
119
120         Manager::PortList const & p = Manager::instance()->get_midi_ports ();
121         for (Manager::PortList::const_iterator i = p.begin(); i != p.end(); ++i) {
122                 _port_combo.append_text ((*i)->name());
123         }
124
125         _port_combo.set_active_text (c);
126 }
127
128 void
129 MidiTracer::port_changed ()
130 {
131         disconnect ();
132
133         Port* p = Manager::instance()->port (_port_combo.get_active_text());
134
135         if (p) {
136                 p->parser()->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
137         }
138 }
139
140 void
141 MidiTracer::disconnect ()
142 {
143         _parser_connection.disconnect ();
144 }
145
146 void
147 MidiTracer::tracer (Parser&, byte* msg, size_t len)
148 {
149         stringstream ss;
150         struct timeval tv;
151         char* buf;
152         struct tm now;
153         size_t bufsize;
154         size_t s;
155
156         gettimeofday (&tv, 0);
157         localtime_r (&tv.tv_sec, &now);
158
159         buf = (char *) buffer_pool.alloc ();
160         bufsize = buffer_size;
161
162         s = strftime (buf, bufsize, "%H:%M:%S", &now);
163         bufsize -= s;
164         s += snprintf (&buf[s], bufsize, ".%06" PRId64, (int64_t) tv.tv_usec);
165         bufsize -= s;
166
167         switch ((eventType) msg[0]&0xf0) {
168         case off:
169                 if (show_hex) {
170                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x %02x\n", "NoteOff", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
171                 } else {
172                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d %-3d\n", "NoteOff", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
173                 }
174                 break;
175
176         case on:
177                 if (show_hex) {
178                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x %02x\n", "NoteOn", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
179                 } else {
180                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d %-3d\n", "NoteOn", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
181                 }
182                 break;
183
184         case polypress:
185                 if (show_hex) {
186                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "PolyPressure", (msg[0]&0xf)+1, (int) msg[1]);
187                 } else {
188                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "PolyPressure", (msg[0]&0xf)+1, (int) msg[1]);
189                 }
190                 break;
191
192         case MIDI::controller:
193                 if (show_hex) {
194                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x %02x\n", "Controller", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
195                 } else {
196                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %2d %-3d\n", "Controller", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
197                 }
198                 break;
199
200         case program:
201                 if (show_hex) {
202                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "Program Change", (msg[0]&0xf)+1, (int) msg[1]);
203                 } else {
204                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "Program Change", (msg[0]&0xf)+1, (int) msg[1]);
205                 }
206                 break;
207
208         case chanpress:
209                 if (show_hex) {
210                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x/%-3d\n", "Channel Pressure", (msg[0]&0xf)+1, (int) msg[1], (int) msg[1]);
211                 } else {
212                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x/%-3d\n", "Channel Pressure", (msg[0]&0xf)+1, (int) msg[1], (int) msg[1]);
213                 }
214                 break;
215
216         case MIDI::pitchbend:
217                 if (show_hex) {
218                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "Pitch Bend", (msg[0]&0xf)+1, (int) msg[1]);
219                 } else {
220                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "Pitch Bend", (msg[0]&0xf)+1, (int) msg[1]);
221                 }
222                 break;
223
224         case MIDI::sysex:
225                 if (len == 1) {
226                         switch (msg[0]) {
227                         case 0xf8:
228                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Clock");
229                                 break;
230                         case 0xfa:
231                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Start");
232                                 break;
233                         case 0xfb:
234                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Continue");
235                                 break;
236                         case 0xfc:
237                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Stop");
238                                 break;
239                         case 0xfe:
240                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Active Sense");
241                                 break;
242                         case 0xff:
243                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Reset");
244                                 break;
245                         default:
246                                 s += snprintf (&buf[s], bufsize, "%16s %02x\n", "Sysex", (int) msg[1]);
247                                 break;
248                         }
249
250                 } else if (len > 5 && msg[0] == 0xf0 && msg[1] == 0x7f && msg[3] == 0x06) {
251                         /* MMC */
252                         int cmd = msg[4];
253                         if (cmd == 0x44 && msg[5] == 0x06 && msg[6] == 0x01) {
254                                 s += snprintf (
255                                         &buf[s], bufsize, " MMC locate to %02d:%02d:%02d:%02d.%02d\n",
256                                         msg[7], msg[8], msg[9], msg[10], msg[11]
257                                         );
258                         } else {
259                                 std::string name;
260                                 if (cmd == 0x1) {
261                                         name = "STOP";
262                                 } else if (cmd == 0x3) {
263                                         name = "DEFERRED PLAY";
264                                 } else if (cmd == 0x6) {
265                                         name = "RECORD STROBE";
266                                 } else if (cmd == 0x7) {
267                                         name = "RECORD EXIT";
268                                 } else if (cmd == 0x8) {
269                                         name = "RECORD PAUSE";
270                                 }
271                                 if (!name.empty()) {
272                                         s += snprintf (&buf[s], bufsize, " MMC command %s\n", name.c_str());
273                                 } else {
274                                         s += snprintf (&buf[s], bufsize, " MMC command %02x\n", cmd);
275                                 }
276                         }
277
278                 } else if (len == 10 && msg[0] == 0xf0 && msg[1] == 0x7f && msg[9] == 0xf7)  {
279
280                         /* MTC full frame */
281                         s += snprintf (
282                                 &buf[s], bufsize, " MTC full frame to %02d:%02d:%02d:%02d\n", msg[5] & 0x1f, msg[6], msg[7], msg[8]
283                                 );
284
285                 } else {
286
287                         /* other sys-ex */
288
289                         s += snprintf (&buf[s], bufsize, "%16s (%d) = [", "Sysex", (int) len);
290                         bufsize -= s;
291
292                         for (unsigned int i = 0; i < len && bufsize > 3; ++i) {
293                                 if (i > 0) {
294                                         s += snprintf (&buf[s], bufsize, " %02x", msg[i]);
295                                 } else {
296                                         s += snprintf (&buf[s], bufsize, "%02x", msg[i]);
297                                 }
298                                 bufsize -= s;
299                         }
300                         s += snprintf (&buf[s], bufsize, "]\n");
301                 }
302                 break;
303
304         case MIDI::song:
305                 s += snprintf (&buf[s], bufsize, "%16s\n", "Song");
306                 break;
307
308         case MIDI::tune:
309                 s += snprintf (&buf[s], bufsize, "%16s\n", "Tune");
310                 break;
311
312         case MIDI::eox:
313                 s += snprintf (&buf[s], bufsize, "%16s\n", "EOX");
314                 break;
315
316         case MIDI::timing:
317                 s += snprintf (&buf[s], bufsize, "%16s\n", "Timing");
318                 break;
319
320         case MIDI::start:
321                 s += snprintf (&buf[s], bufsize, "%16s\n", "Start");
322                 break;
323
324         case MIDI::stop:
325                 s += snprintf (&buf[s], bufsize, "%16s\n", "Stop");
326                 break;
327
328         case MIDI::contineu:
329                 s += snprintf (&buf[s], bufsize, "%16s\n", "Continue");
330                 break;
331
332         case active:
333                 s += snprintf (&buf[s], bufsize, "%16s\n", "Active Sense");
334                 break;
335
336         default:
337                 s += snprintf (&buf[s], bufsize, "%16s\n", "Unknown");
338                 break;
339         }
340
341         // If you want to append more to the line, uncomment this first
342         // bufsize -= s;
343
344         fifo.write (&buf, 1);
345
346         if (g_atomic_int_get (&_update_queued) == 0) {
347                 gui_context()->call_slot (invalidator (*this), boost::bind (&MidiTracer::update, this));
348                 g_atomic_int_inc (&_update_queued);
349         }
350 }
351
352 void
353 MidiTracer::update ()
354 {
355         bool updated = false;
356         g_atomic_int_dec_and_test (&_update_queued);
357
358         RefPtr<TextBuffer> buf (text.get_buffer());
359
360         int excess = buf->get_line_count() - line_count_adjustment.get_value();
361
362         if (excess > 0) {
363                 buf->erase (buf->begin(), buf->get_iter_at_line (excess));
364         }
365
366         char *str;
367
368         while (fifo.read (&str, 1)) {
369                 buf->insert (buf->end(), string (str));
370                 buffer_pool.release (str);
371                 updated = true;
372         }
373
374         if (updated && autoscroll) {
375                 scroller.get_vadjustment()->set_value (scroller.get_vadjustment()->get_upper());
376         }
377 }
378
379 void
380 MidiTracer::base_toggle ()
381 {
382         show_hex = !base_button.get_active();
383 }
384
385 void
386 MidiTracer::collect_toggle ()
387 {
388         if (collect_button.get_active ()) {
389                 port_changed ();
390         } else {
391                 disconnect ();
392         }
393 }
394
395 void
396 MidiTracer::autoscroll_toggle ()
397 {
398         autoscroll = autoscroll_button.get_active ();
399 }