the Properties & 64bit region commit
[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         get_action_area()->add (base_button);
58         get_action_area()->add(collect_button);
59         get_action_area()->add (autoscroll_button);
60
61         base_button.signal_toggled().connect (sigc::mem_fun (*this, &MidiTracer::base_toggle));
62         collect_button.signal_toggled().connect (sigc::mem_fun (*this, &MidiTracer::collect_toggle));
63         autoscroll_button.signal_toggled().connect (sigc::mem_fun (*this, &MidiTracer::autoscroll_toggle));
64
65         base_button.show ();
66         collect_button.show ();
67         autoscroll_button.show ();
68
69         connect ();
70 }
71
72
73 MidiTracer::~MidiTracer()
74 {
75 }
76
77 void
78 MidiTracer::connect ()
79 {
80         disconnect ();
81         parser.any.connect_same_thread (connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
82 }
83
84 void
85 MidiTracer::disconnect ()
86 {
87         connection.disconnect ();
88 }
89
90 void
91 MidiTracer::tracer (Parser&, byte* msg, size_t len)
92 {
93         stringstream ss;
94         struct timeval tv;
95         char* buf;
96         struct tm now;
97         size_t bufsize;
98         size_t s;
99
100         gettimeofday (&tv, 0);
101         localtime_r (&tv.tv_sec, &now);
102
103         buf = (char *) buffer_pool.alloc ();
104         bufsize = buffer_size;
105
106         s = strftime (buf, bufsize, "%H:%M:%S", &now);
107         bufsize -= s;
108         s += snprintf (&buf[s], bufsize, ".%-9" PRId64, (int64_t) tv.tv_usec);
109         bufsize -= s;
110
111         switch ((eventType) msg[0]&0xf0) {
112         case off:
113                 if (show_hex) {
114                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x %02x\n", "NoteOff", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
115                 } else {
116                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d %-3d\n", "NoteOff", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
117                 }
118                 break;
119                 
120         case on:
121                 if (show_hex) {
122                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x %02x\n", "NoteOn", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
123                 } else {
124                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d %-3d\n", "NoteOn", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
125                 }
126                 break;
127             
128         case polypress:
129                 if (show_hex) {
130                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "PolyPressure", (msg[0]&0xf)+1, (int) msg[1]);
131                 } else {
132                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "PolyPressure", (msg[0]&0xf)+1, (int) msg[1]);
133                 }
134                 break;
135             
136         case MIDI::controller:
137                 if (show_hex) {
138                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x %02x\n", "Controller", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
139                 } else {
140                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %2d %-3d\n", "Controller", (msg[0]&0xf)+1, (int) msg[1], (int) msg[2]);
141                 }
142                 break;
143                 
144         case program:
145                 if (show_hex) {
146                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "Program PropertyChange", (msg[0]&0xf)+1, (int) msg[1]);
147                 } else {
148                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "Program PropertyChange", (msg[0]&0xf)+1, (int) msg[1]);
149                 }
150                 break;
151                 
152         case chanpress:
153                 if (show_hex) {
154                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x/%-3d\n", "Channel Pressure", (msg[0]&0xf)+1, (int) msg[1], (int) msg[1]);
155                 } else {
156                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x/%-3d\n", "Channel Pressure", (msg[0]&0xf)+1, (int) msg[1], (int) msg[1]);
157                 }
158                 break;
159             
160         case MIDI::pitchbend:
161                 if (show_hex) {
162                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "Pitch Bend", (msg[0]&0xf)+1, (int) msg[1]);
163                 } else {
164                         s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "Pitch Bend", (msg[0]&0xf)+1, (int) msg[1]);
165                 }
166                 break;
167             
168         case MIDI::sysex:
169                 if (len == 1) {
170                         switch (msg[0]) {
171                         case 0xf8:
172                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Clock");
173                                 break;
174                         case 0xfa:
175                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Start");
176                                 break;
177                         case 0xfb:
178                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Continue");
179                                 break;
180                         case 0xfc:
181                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Stop");
182                                 break;
183                         case 0xfe:
184                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Active Sense");
185                                 break;
186                         case 0xff:
187                                 s += snprintf (&buf[s], bufsize, "%16s\n", "Reset");
188                                 break;
189                         default:
190                                 s += snprintf (&buf[s], bufsize, "%16s %02x\n", "Sysex", (int) msg[1]);
191                                 break;
192                         } 
193
194                 } else {
195
196                         s += snprintf (&buf[s], bufsize, "%16s (%d) = [", "Sysex", (int) len);
197                         bufsize -= s;
198
199                         for (unsigned int i = 0; i < len && bufsize > 3; ++i) {
200                                 if (i > 0) {
201                                         s += snprintf (&buf[s], bufsize, " %02x", msg[i]);
202                                 } else {
203                                         s += snprintf (&buf[s], bufsize, "%02x", msg[i]);
204                                 }
205                                 bufsize -= s;
206                         }
207                         s += snprintf (&buf[s], bufsize, "]\n");
208                 }
209                 break;
210             
211         case MIDI::song:
212                 s += snprintf (&buf[s], bufsize, "%16s\n", "Song");
213                 break;
214             
215         case MIDI::tune:
216                 s += snprintf (&buf[s], bufsize, "%16s\n", "Tune");
217                 break;
218             
219         case MIDI::eox:
220                 s += snprintf (&buf[s], bufsize, "%16s\n", "EOX");
221                 break;
222             
223         case MIDI::timing:
224                 s += snprintf (&buf[s], bufsize, "%16s\n", "Timing");
225                 break;
226             
227         case MIDI::start:
228                 s += snprintf (&buf[s], bufsize, "%16s\n", "Start");
229                 break;
230             
231         case MIDI::stop:
232                 s += snprintf (&buf[s], bufsize, "%16s\n", "Stop");
233                 break;
234             
235         case MIDI::contineu:
236                 s += snprintf (&buf[s], bufsize, "%16s\n", "Continue");
237                 break;
238             
239         case active:
240                 s += snprintf (&buf[s], bufsize, "%16s\n", "Active Sense");
241                 break;
242             
243         default:
244                 s += snprintf (&buf[s], bufsize, "%16s\n", "Unknown");
245                 break;
246         }
247
248         // If you want to append more to the line, uncomment this first
249         // bufsize -= s;
250
251         fifo.write (&buf, 1);
252
253         if (!update_queued) {
254                 gui_context()->call_slot (boost::bind (&MidiTracer::update, this));
255                 update_queued = true;
256         }
257 }
258
259 void
260 MidiTracer::update ()
261 {
262         bool updated = false;
263         update_queued = false;
264
265         RefPtr<TextBuffer> buf (text.get_buffer());
266
267         int excess = buf->get_line_count() - line_count_adjustment.get_value();
268
269         if (excess > 0) {
270                 buf->erase (buf->begin(), buf->get_iter_at_line (excess));
271         }
272
273         char *str;
274
275         while (fifo.read (&str, 1)) {
276                 buf->insert (buf->end(), string (str));
277                 buffer_pool.release (str);
278                 updated = true;
279         }
280
281         if (updated && autoscroll) {
282                 scroller.get_vadjustment()->set_value (scroller.get_vadjustment()->get_upper());
283         }
284 }
285
286 void
287 MidiTracer::base_toggle ()
288 {
289         show_hex = !base_button.get_active();
290 }
291
292 void
293 MidiTracer::collect_toggle ()
294 {
295         if (collect_button.get_active ()) {
296                 connect ();
297         } else {
298                 disconnect ();
299         }
300 }
301
302 void
303 MidiTracer::autoscroll_toggle ()
304 {
305         autoscroll = autoscroll_button.get_active ();
306 }