a12f756a0f9ea85d1b97dc6edda6837a66021128
[ardour.git] / gtk2_ardour / midi_tracer.cc
1 #define __STDC_FORMAT_MACROS 1
2 #include <stdint.h>
3
4 #include <sstream>
5 #include <sys/time.h>
6 #include <time.h>
7
8 #include "midi++/parser.h"
9
10 #include "midi_tracer.h"
11 #include "gui_thread.h"
12 #include "i18n.h"
13
14 using namespace Gtk;
15 using namespace std;
16 using namespace MIDI;
17 using namespace Glib;
18
19 MidiTracer::MidiTracer (const std::string& name, Parser& p)
20         : ArdourDialog (string_compose (_("MIDI Trace %1"), name))
21         , parser (p)
22         , line_count_adjustment (200, 1, 2000, 1, 10)
23         , line_count_spinner (line_count_adjustment)
24         , line_count_label (_("Store this many lines: "))
25         , autoscroll (true)
26         , show_hex (true)
27         , collect (true)
28         , _update_queued (0)
29         , fifo (1024)
30         , buffer_pool ("miditracer", buffer_size, 1024) // 1024 256 byte buffers
31         , autoscroll_button (_("Auto-Scroll"))
32         , base_button (_("Decimal"))
33         , collect_button (_("Enabled"))
34 {
35         scroller.add (text);
36         get_vbox()->set_border_width (12);
37         get_vbox()->pack_start (scroller, true, true);
38         
39         text.show ();
40         text.set_name ("MidiTracerTextView");
41         scroller.show ();
42         scroller.set_size_request (400, 400);
43
44         collect_button.set_active (true);
45         base_button.set_active (false);
46         autoscroll_button.set_active (true);
47
48         line_count_box.set_spacing (6);
49         line_count_box.pack_start (line_count_label, false, false);
50         line_count_box.pack_start (line_count_spinner, false, false);
51
52         line_count_spinner.show ();
53         line_count_label.show ();
54         line_count_box.show ();
55
56         get_action_area()->add (line_count_box);
57
58         HBox* bbox = manage (new HBox);
59         bbox->add (base_button);
60         bbox->add (collect_button);
61         bbox->add (autoscroll_button);
62         bbox->show ();
63         
64         get_action_area()->add (*bbox);
65
66         base_button.signal_toggled().connect (sigc::mem_fun (*this, &MidiTracer::base_toggle));
67         collect_button.signal_toggled().connect (sigc::mem_fun (*this, &MidiTracer::collect_toggle));
68         autoscroll_button.signal_toggled().connect (sigc::mem_fun (*this, &MidiTracer::autoscroll_toggle));
69
70         base_button.show ();
71         collect_button.show ();
72         autoscroll_button.show ();
73
74         connect ();
75 }
76
77
78 MidiTracer::~MidiTracer()
79 {
80 }
81
82 void
83 MidiTracer::connect ()
84 {
85         disconnect ();
86         parser.any.connect_same_thread (connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
87 }
88
89 void
90 MidiTracer::disconnect ()
91 {
92         connection.disconnect ();
93 }
94
95 void
96 MidiTracer::tracer (Parser&, byte* msg, size_t len)
97 {
98         stringstream ss;
99         struct timeval tv;
100         char* buf;
101         struct tm now;
102         size_t bufsize;
103         size_t s;
104
105         gettimeofday (&tv, 0);
106         localtime_r (&tv.tv_sec, &now);
107
108         buf = (char *) buffer_pool.alloc ();
109         bufsize = buffer_size;
110
111         s = strftime (buf, bufsize, "%H:%M:%S", &now);
112         bufsize -= s;
113         s += snprintf (&buf[s], bufsize, ".%06" PRId64, (int64_t) tv.tv_usec);
114         bufsize -= s;
115
116         switch ((eventType) msg[0]&0xf0) {
117         case off:
118                 if (show_hex) {
119                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x %02x\n", "NoteOff", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
120                 } else {
121                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d %-3d\n", "NoteOff", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
122                 }
123                 break;
124                 
125         case on:
126                 if (show_hex) {
127                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x %02x\n", "NoteOn", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
128                 } else {
129                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d %-3d\n", "NoteOn", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
130                 }
131                 break;
132             
133         case polypress:
134                 if (show_hex) {
135                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "PolyPressure", (msg[0]&0xf)+1, (int) msg[1]);
136                 } else {
137                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "PolyPressure", (msg[0]&0xf)+1, (int) msg[1]);
138                 }
139                 break;
140             
141         case MIDI::controller:
142                 if (show_hex) {
143                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x %02x\n", "Controller", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
144                 } else {
145                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %2d %-3d\n", "Controller", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
146                 }
147                 break;
148                 
149         case program:
150                 if (show_hex) {
151                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "Program Change", (msg[0]&0xf)+1, (int) msg[1]);
152                 } else {
153                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "Program Change", (msg[0]&0xf)+1, (int) msg[1]);
154                 }
155                 break;
156                 
157         case chanpress:
158                 if (show_hex) {
159                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x/%-3d\n", "Channel Pressure", (msg[0]&0xf)+1, (int) msg[1], (int) msg[1]);
160                 } else {
161                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x/%-3d\n", "Channel Pressure", (msg[0]&0xf)+1, (int) msg[1], (int) msg[1]);
162                 }
163                 break;
164             
165         case MIDI::pitchbend:
166                 if (show_hex) {
167                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "Pitch Bend", (msg[0]&0xf)+1, (int) msg[1]);
168                 } else {
169                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "Pitch Bend", (msg[0]&0xf)+1, (int) msg[1]);
170                 }
171                 break;
172             
173         case MIDI::sysex:
174                 if (len == 1) {
175                         switch (msg[0]) {
176                         case 0xf8:
177                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Clock");
178                                 break;
179                         case 0xfa:
180                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Start");
181                                 break;
182                         case 0xfb:
183                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Continue");
184                                 break;
185                         case 0xfc:
186                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Stop");
187                                 break;
188                         case 0xfe:
189                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Active Sense");
190                                 break;
191                         case 0xff:
192                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Reset");
193                                 break;
194                         default:
195                                 s += snprintf (&buf[s], bufsize, "%16s %02x\n", "Sysex", (int) msg[1]);
196                                 break;
197                         } 
198
199                 } else {
200
201                         if (len > 5 && msg[0] == 0xf0 && msg[1] == 0x7f && msg[3] == 0x06) {
202                                 /* MMC */
203                                 int cmd = msg[4];
204                                 if (cmd == 0x44 && msg[5] == 0x06 && msg[6] == 0x01) {
205                                         s += snprintf (
206                                                 &buf[s], bufsize, " MMC locate to %02d:%02d:%02d:%02d.%02d\n",
207                                                 msg[7], msg[8], msg[9], msg[10], msg[11]
208                                                 );
209                                 } else {
210                                         std::string name;
211                                         if (cmd == 0x1) {
212                                                 name = "STOP";
213                                         } else if (cmd == 0x3) {
214                                                 name = "DEFERRED PLAY";
215                                         } else if (cmd == 0x6) {
216                                                 name = "RECORD STROBE";
217                                         } else if (cmd == 0x7) {
218                                                 name = "RECORD EXIT";
219                                         } else if (cmd == 0x8) {
220                                                 name = "RECORD PAUSE";
221                                         }
222                                         if (!name.empty()) {
223                                                 s += snprintf (&buf[s], bufsize, " MMC command %s\n", name.c_str());
224                                         } else {
225                                                 s += snprintf (&buf[s], bufsize, " MMC command %02x\n", cmd);
226                                         }
227                                 }
228                                 
229                         } else {
230                                 /* other sys-ex */
231
232                                 s += snprintf (&buf[s], bufsize, "%16s (%d) = [", "Sysex", (int) len);
233                                 bufsize -= s;
234                                 
235                                 for (unsigned int i = 0; i < len && bufsize > 3; ++i) {
236                                         if (i > 0) {
237                                                 s += snprintf (&buf[s], bufsize, " %02x", msg[i]);
238                                         } else {
239                                                 s += snprintf (&buf[s], bufsize, "%02x", msg[i]);
240                                         }
241                                         bufsize -= s;
242                                 }
243                                 s += snprintf (&buf[s], bufsize, "]\n");
244                         }
245                 }
246                 break;
247             
248         case MIDI::song:
249                 s += snprintf (&buf[s], bufsize, "%16s\n", "Song");
250                 break;
251             
252         case MIDI::tune:
253                 s += snprintf (&buf[s], bufsize, "%16s\n", "Tune");
254                 break;
255             
256         case MIDI::eox:
257                 s += snprintf (&buf[s], bufsize, "%16s\n", "EOX");
258                 break;
259             
260         case MIDI::timing:
261                 s += snprintf (&buf[s], bufsize, "%16s\n", "Timing");
262                 break;
263             
264         case MIDI::start:
265                 s += snprintf (&buf[s], bufsize, "%16s\n", "Start");
266                 break;
267             
268         case MIDI::stop:
269                 s += snprintf (&buf[s], bufsize, "%16s\n", "Stop");
270                 break;
271             
272         case MIDI::contineu:
273                 s += snprintf (&buf[s], bufsize, "%16s\n", "Continue");
274                 break;
275             
276         case active:
277                 s += snprintf (&buf[s], bufsize, "%16s\n", "Active Sense");
278                 break;
279             
280         default:
281                 s += snprintf (&buf[s], bufsize, "%16s\n", "Unknown");
282                 break;
283         }
284
285         // If you want to append more to the line, uncomment this first
286         // bufsize -= s;
287
288         fifo.write (&buf, 1);
289
290         if (g_atomic_int_get (&_update_queued) == 0) {
291                 gui_context()->call_slot (invalidator (*this), boost::bind (&MidiTracer::update, this));
292                 g_atomic_int_inc (&_update_queued);
293         }
294 }
295
296 void
297 MidiTracer::update ()
298 {
299         bool updated = false;
300         g_atomic_int_dec_and_test (&_update_queued);
301
302         RefPtr<TextBuffer> buf (text.get_buffer());
303
304         int excess = buf->get_line_count() - line_count_adjustment.get_value();
305
306         if (excess > 0) {
307                 buf->erase (buf->begin(), buf->get_iter_at_line (excess));
308         }
309
310         char *str;
311
312         while (fifo.read (&str, 1)) {
313                 buf->insert (buf->end(), string (str));
314                 buffer_pool.release (str);
315                 updated = true;
316         }
317
318         if (updated && autoscroll) {
319                 scroller.get_vadjustment()->set_value (scroller.get_vadjustment()->get_upper());
320         }
321 }
322
323 void
324 MidiTracer::base_toggle ()
325 {
326         show_hex = !base_button.get_active();
327 }
328
329 void
330 MidiTracer::collect_toggle ()
331 {
332         if (collect_button.get_active ()) {
333                 connect ();
334         } else {
335                 disconnect ();
336         }
337 }
338
339 void
340 MidiTracer::autoscroll_toggle ()
341 {
342         autoscroll = autoscroll_button.get_active ();
343 }