improve efficiency of MIDI tracer window with a lock-free FIFO and lock-free msg...
[ardour2.git] / gtk2_ardour / midi_tracer.cc
blobe78271e01d47707290711f12d5d4ce60e2f952ca
1 #define __STDC_FORMAT_MACROS 1
2 #include <stdint.h>
4 #include <sstream>
5 #include <sys/time.h>
6 #include <time.h>
8 #include "midi++/parser.h"
10 #include "midi_tracer.h"
11 #include "gui_thread.h"
12 #include "i18n.h"
14 using namespace Gtk;
15 using namespace std;
16 using namespace MIDI;
17 using namespace Glib;
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"))
35 scroller.add (text);
36 get_vbox()->set_border_width (12);
37 get_vbox()->pack_start (scroller, true, true);
39 text.show ();
40 text.set_name ("MidiTracerTextView");
41 scroller.show ();
42 scroller.set_size_request (400, 400);
44 collect_button.set_active (true);
45 base_button.set_active (false);
46 autoscroll_button.set_active (true);
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);
52 line_count_spinner.show ();
53 line_count_label.show ();
54 line_count_box.show ();
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);
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));
65 base_button.show ();
66 collect_button.show ();
67 autoscroll_button.show ();
69 connect ();
73 MidiTracer::~MidiTracer()
77 void
78 MidiTracer::connect ()
80 disconnect ();
81 parser.any.connect_same_thread (connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
84 void
85 MidiTracer::disconnect ()
87 connection.disconnect ();
90 void
91 MidiTracer::tracer (Parser&, byte* msg, size_t len)
93 stringstream ss;
94 struct timeval tv;
95 char* buf;
96 struct tm now;
97 size_t bufsize;
98 size_t s;
100 gettimeofday (&tv, 0);
101 localtime_r (&tv.tv_sec, &now);
103 buf = (char *) buffer_pool.alloc ();
104 bufsize = buffer_size;
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;
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]);
118 break;
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]);
126 break;
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]);
134 break;
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]);
142 break;
144 case program:
145 if (show_hex) {
146 s += snprintf (&buf[s], bufsize, "%16s chn %2d %02x\n", "Program Change", (msg[0]&0xf)+1, (int) msg[1]);
147 } else {
148 s += snprintf (&buf[s], bufsize, "%16s chn %2d %-3d\n", "Program Change", (msg[0]&0xf)+1, (int) msg[1]);
150 break;
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]);
158 break;
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]);
166 break;
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;
193 bufsize -= s;
194 } else {
195 s += snprintf (&buf[s], bufsize, " %16s (%d) = [", "Sysex", (int) len);
196 bufsize -= s;
198 for (unsigned int i = 0; i < len && s < sizeof (buf)-3; ++i) {
199 if (i > 0) {
200 s += snprintf (&buf[s], bufsize, " %02x", msg[i]);
201 } else {
202 s += snprintf (&buf[s], bufsize, "%02x", msg[i]);
204 bufsize = sizeof (buf) - s;
206 s += snprintf (&buf[s], bufsize, "]\n");
208 break;
210 case MIDI::song:
211 s += snprintf (&buf[s], bufsize, "%16s\n", "Song");
212 break;
214 case MIDI::tune:
215 s += snprintf (&buf[s], bufsize, "%16s\n", "Tune");
216 break;
218 case MIDI::eox:
219 s += snprintf (&buf[s], bufsize, "%16s\n", "EOX");
220 break;
222 case MIDI::timing:
223 s += snprintf (&buf[s], bufsize, "%16s\n", "Timing");
224 break;
226 case MIDI::start:
227 s += snprintf (&buf[s], bufsize, "%16s\n", "Start");
228 break;
230 case MIDI::stop:
231 s += snprintf (&buf[s], bufsize, "%16s\n", "Stop");
232 break;
234 case MIDI::contineu:
235 s += snprintf (&buf[s], bufsize, "%16s\n", "Continue");
236 break;
238 case active:
239 s += snprintf (&buf[s], bufsize, "%16s\n", "Active Sense");
240 break;
242 default:
243 s += snprintf (&buf[s], bufsize, "%16s\n", "Unknown");
244 break;
247 // If you want to append more to the line, uncomment this first
248 // bufsize -= s;
250 fifo.write (&buf, 1);
252 if (!update_queued) {
253 gui_context()->call_slot (boost::bind (&MidiTracer::update, this));
254 update_queued = true;
258 void
259 MidiTracer::update ()
261 bool updated = false;
262 update_queued = false;
264 RefPtr<TextBuffer> buf (text.get_buffer());
266 int excess = buf->get_line_count() - line_count_adjustment.get_value();
268 if (excess > 0) {
269 buf->erase (buf->begin(), buf->get_iter_at_line (excess));
272 char *str;
274 while (fifo.read (&str, 1)) {
275 buf->insert (buf->end(), string (str));
276 buffer_pool.release (str);
277 updated = true;
280 if (updated && autoscroll) {
281 scroller.get_vadjustment()->set_value (scroller.get_vadjustment()->get_upper());
285 void
286 MidiTracer::base_toggle ()
288 show_hex = !base_button.get_active();
291 void
292 MidiTracer::collect_toggle ()
294 if (collect_button.get_active ()) {
295 connect ();
296 } else {
297 disconnect ();
301 void
302 MidiTracer::autoscroll_toggle ()
304 autoscroll = autoscroll_button.get_active ();