Use checkbuttons rather than togglebuttons the midi tracer as it seems more logical...
[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 (false)
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, ".%-9" 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 PropertyChange", (msg[0]&0xf)+1, (int) msg[1]);
152                 } else {
153                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "Program PropertyChange", (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                         s += snprintf (&buf[s], bufsize, "%16s (%d) = [", "Sysex", (int) len);
202                         bufsize -= s;
203
204                         for (unsigned int i = 0; i < len && bufsize > 3; ++i) {
205                                 if (i > 0) {
206                                         s += snprintf (&buf[s], bufsize, " %02x", msg[i]);
207                                 } else {
208                                         s += snprintf (&buf[s], bufsize, "%02x", msg[i]);
209                                 }
210                                 bufsize -= s;
211                         }
212                         s += snprintf (&buf[s], bufsize, "]\n");
213                 }
214                 break;
215             
216         case MIDI::song:
217                 s += snprintf (&buf[s], bufsize, "%16s\n", "Song");
218                 break;
219             
220         case MIDI::tune:
221                 s += snprintf (&buf[s], bufsize, "%16s\n", "Tune");
222                 break;
223             
224         case MIDI::eox:
225                 s += snprintf (&buf[s], bufsize, "%16s\n", "EOX");
226                 break;
227             
228         case MIDI::timing:
229                 s += snprintf (&buf[s], bufsize, "%16s\n", "Timing");
230                 break;
231             
232         case MIDI::start:
233                 s += snprintf (&buf[s], bufsize, "%16s\n", "Start");
234                 break;
235             
236         case MIDI::stop:
237                 s += snprintf (&buf[s], bufsize, "%16s\n", "Stop");
238                 break;
239             
240         case MIDI::contineu:
241                 s += snprintf (&buf[s], bufsize, "%16s\n", "Continue");
242                 break;
243             
244         case active:
245                 s += snprintf (&buf[s], bufsize, "%16s\n", "Active Sense");
246                 break;
247             
248         default:
249                 s += snprintf (&buf[s], bufsize, "%16s\n", "Unknown");
250                 break;
251         }
252
253         // If you want to append more to the line, uncomment this first
254         // bufsize -= s;
255
256         fifo.write (&buf, 1);
257
258         if (!update_queued) {
259                 gui_context()->call_slot (boost::bind (&MidiTracer::update, this));
260                 update_queued = true;
261         }
262 }
263
264 void
265 MidiTracer::update ()
266 {
267         bool updated = false;
268         update_queued = false;
269
270         RefPtr<TextBuffer> buf (text.get_buffer());
271
272         int excess = buf->get_line_count() - line_count_adjustment.get_value();
273
274         if (excess > 0) {
275                 buf->erase (buf->begin(), buf->get_iter_at_line (excess));
276         }
277
278         char *str;
279
280         while (fifo.read (&str, 1)) {
281                 buf->insert (buf->end(), string (str));
282                 buffer_pool.release (str);
283                 updated = true;
284         }
285
286         if (updated && autoscroll) {
287                 scroller.get_vadjustment()->set_value (scroller.get_vadjustment()->get_upper());
288         }
289 }
290
291 void
292 MidiTracer::base_toggle ()
293 {
294         show_hex = !base_button.get_active();
295 }
296
297 void
298 MidiTracer::collect_toggle ()
299 {
300         if (collect_button.get_active ()) {
301                 connect ();
302         } else {
303                 disconnect ();
304         }
305 }
306
307 void
308 MidiTracer::autoscroll_toggle ()
309 {
310         autoscroll = autoscroll_button.get_active ();
311 }