1 /*
2  * Copyright (C) 2010 Devin Anderson <surfacepatterns@gmail.com>
3  * Copyright (C) 2014-2017 Robin Gareus <robin@gareus.org>
4  * Copyright (C) 2016-2017 Paul Davis <paul@linuxaudiosystems.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include <unistd.h>
22 #include <glibmm.h>
23 
24 #include "select_sleep.h"
25 #include "alsa_rawmidi.h"
26 
27 #include "pbd/error.h"
28 #include "pbd/i18n.h"
29 
30 using namespace ARDOUR;
31 
32 #ifndef NDEBUG
33 #define _DEBUGPRINT(STR) fprintf(stderr, STR);
34 #else
35 #define _DEBUGPRINT(STR) ;
36 #endif
37 
AlsaRawMidiIO(const std::string & name,const char * device,const bool input)38 AlsaRawMidiIO::AlsaRawMidiIO (const std::string &name, const char *device, const bool input)
39 	: AlsaMidiIO()
40 	, _device (0)
41 {
42 	_name = name;
43 	init (device, input);
44 }
45 
~AlsaRawMidiIO()46 AlsaRawMidiIO::~AlsaRawMidiIO ()
47 {
48 	if (_device) {
49 		snd_rawmidi_drain (_device);
50 		snd_rawmidi_close (_device);
51 		_device = 0;
52 	}
53 }
54 
55 void
init(const char * device_name,const bool input)56 AlsaRawMidiIO::init (const char *device_name, const bool input)
57 {
58 	if (snd_rawmidi_open (
59 				input ? &_device : NULL,
60 				input ? NULL : &_device,
61 				device_name, SND_RAWMIDI_NONBLOCK) < 0) {
62 		return;
63 	}
64 
65 	_npfds = snd_rawmidi_poll_descriptors_count (_device);
66 	if (_npfds < 1) {
67 		_DEBUGPRINT("AlsaRawMidiIO: no poll descriptor(s).\n");
68 		snd_rawmidi_close (_device);
69 		_device = 0;
70 		return;
71 	}
72 	_pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
73 	snd_rawmidi_poll_descriptors (_device, _pfds, _npfds);
74 
75 #if 0
76 	_state = 0;
77 #else
78 	snd_rawmidi_params_t *params;
79 	if (snd_rawmidi_params_malloc (&params)) {
80 		goto initerr;
81 	}
82 	if (snd_rawmidi_params_current (_device, params)) {
83 		goto initerr;
84 	}
85 	if (snd_rawmidi_params_set_avail_min (_device, params, 1)) {
86 		goto initerr;
87 	}
88 	if (snd_rawmidi_params_set_buffer_size (_device, params, 64)) {
89 		goto initerr;
90 	}
91 	if (snd_rawmidi_params_set_no_active_sensing (_device, params, 1)) {
92 		goto initerr;
93 	}
94 
95 	_state = 0;
96 	return;
97 
98 initerr:
99 	_DEBUGPRINT("AlsaRawMidiIO: parameter setup error\n");
100 	snd_rawmidi_close (_device);
101 	_device = 0;
102 #endif
103 	return;
104 }
105 
106 ///////////////////////////////////////////////////////////////////////////////
107 
AlsaRawMidiOut(const std::string & name,const char * device)108 AlsaRawMidiOut::AlsaRawMidiOut (const std::string &name, const char *device)
109 		: AlsaRawMidiIO (name, device, false)
110 		, AlsaMidiOut ()
111 {
112 }
113 
114 void *
main_process_thread()115 AlsaRawMidiOut::main_process_thread ()
116 {
117 	_running = true;
118 	pthread_mutex_lock (&_notify_mutex);
119 	unsigned int need_drain = 0;
120 	while (_running) {
121 		bool have_data = false;
122 		struct MidiEventHeader h(0,0);
123 		uint8_t data[MaxAlsaMidiEventSize];
124 
125 		const uint32_t read_space = _rb->read_space();
126 
127 		if (read_space > sizeof(MidiEventHeader)) {
128 			if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
129 				_DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT HEADER!!\n");
130 				break;
131 			}
132 			assert (read_space >= h.size);
133 			if (h.size > MaxAlsaMidiEventSize) {
134 				_rb->increment_read_idx (h.size);
135 				_DEBUGPRINT("AlsaRawMidiOut: MIDI event too large!\n");
136 				continue;
137 			}
138 			if (_rb->read (&data[0], h.size) != h.size) {
139 				_DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT DATA!!\n");
140 				break;
141 			}
142 			have_data = true;
143 		}
144 
145 		if (!have_data) {
146 			if (need_drain > 0) {
147 				snd_rawmidi_drain (_device);
148 				need_drain = 0;
149 			}
150 			pthread_cond_wait (&_notify_ready, &_notify_mutex);
151 			continue;
152 		}
153 
154 		uint64_t now = g_get_monotonic_time();
155 		while (h.time > now + 500) {
156 			if (need_drain > 0) {
157 				snd_rawmidi_drain (_device);
158 				need_drain = 0;
159 			} else {
160 				select_sleep(h.time - now);
161 			}
162 			now = g_get_monotonic_time();
163 		}
164 
165 retry:
166 		int perr = poll (_pfds, _npfds, 10 /* ms */);
167 		if (perr < 0) {
168 			PBD::error << _("AlsaRawMidiOut: Error polling device. Terminating Midi Thread.") << endmsg;
169 			break;
170 		}
171 		if (perr == 0) {
172 			_DEBUGPRINT("AlsaRawMidiOut: poll() timed out.\n");
173 			goto retry;
174 		}
175 
176 		unsigned short revents = 0;
177 		if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
178 			PBD::error << _("AlsaRawMidiOut: Failed to poll device. Terminating Midi Thread.") << endmsg;
179 			break;
180 		}
181 
182 		if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
183 			PBD::error << _("AlsaRawMidiOut: poll error. Terminating Midi Thread.") << endmsg;
184 			break;
185 		}
186 
187 		if (!(revents & POLLOUT)) {
188 			_DEBUGPRINT("AlsaRawMidiOut: POLLOUT not ready.\n");
189 			select_sleep (1000);
190 			goto retry;
191 		}
192 
193 		ssize_t err = snd_rawmidi_write (_device, data, h.size);
194 
195 #if 0 // DEBUG -- not rt-safe
196 		printf("TX [%ld | %ld]", h.size, err);
197 		for (size_t i = 0; i < h.size; ++i) {
198 			printf (" %02x", data[i]);
199 		}
200 		printf ("\n");
201 #endif
202 
203 		if (err == -EAGAIN) {
204 			snd_rawmidi_drain (_device);
205 			goto retry;
206 		}
207 		if (err == -EWOULDBLOCK) {
208 			select_sleep (1000);
209 			goto retry;
210 		}
211 		if (err < 0) {
212 			PBD::error << _("AlsaRawMidiOut: write failed. Terminating Midi Thread.") << endmsg;
213 			break;
214 		}
215 		if ((size_t) err < h.size) {
216 			_DEBUGPRINT("AlsaRawMidiOut: short write\n");
217 			memmove(&data[0], &data[err], err);
218 			h.size -= err;
219 			goto retry;
220 		}
221 
222 		if ((need_drain += h.size) >= 64) {
223 			snd_rawmidi_drain (_device);
224 			need_drain = 0;
225 		}
226 	}
227 
228 	pthread_mutex_unlock (&_notify_mutex);
229 	_DEBUGPRINT("AlsaRawMidiOut: MIDI OUT THREAD STOPPED\n");
230 	return 0;
231 }
232 
233 
234 ///////////////////////////////////////////////////////////////////////////////
235 
AlsaRawMidiIn(const std::string & name,const char * device)236 AlsaRawMidiIn::AlsaRawMidiIn (const std::string &name, const char *device)
237 		: AlsaRawMidiIO (name, device, true)
238 		, AlsaMidiIn ()
239 		, _event(0,0)
240 		, _first_time(true)
241 		, _unbuffered_bytes(0)
242 		, _total_bytes(0)
243 		, _expected_bytes(0)
244 		, _status_byte(0)
245 {
246 }
247 
248 void *
main_process_thread()249 AlsaRawMidiIn::main_process_thread ()
250 {
251 	_running = true;
252 	while (_running) {
253 		unsigned short revents = 0;
254 
255 		int perr = poll (_pfds, _npfds, 100 /* ms */);
256 		if (perr < 0) {
257 			PBD::error << _("AlsaRawMidiIn: Error polling device. Terminating Midi Thread.") << endmsg;
258 			break;
259 		}
260 		if (perr == 0) {
261 			continue;
262 		}
263 
264 		if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
265 			PBD::error << _("AlsaRawMidiIn: Failed to poll device. Terminating Midi Thread.") << endmsg;
266 			break;
267 		}
268 
269 		if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
270 			PBD::error << _("AlsaRawMidiIn: poll error. Terminating Midi Thread.") << endmsg;
271 			break;
272 		}
273 
274 		if (!(revents & POLLIN)) {
275 			_DEBUGPRINT("AlsaRawMidiOut: POLLIN not ready.\n");
276 			select_sleep (1000);
277 			continue;
278 		}
279 
280 		uint8_t data[MaxAlsaMidiEventSize];
281 		uint64_t time = g_get_monotonic_time();
282 		ssize_t err = snd_rawmidi_read (_device, data, sizeof(data));
283 
284 #if EAGAIN != EWOULDBLOCK
285 		if ((err == -EAGAIN) || (err == -EWOULDBLOCK))  {
286 #else
287 		if (err == -EAGAIN) {
288 #endif
289 		    continue;
290 		}
291 		if (err < 0) {
292 			PBD::error << _("AlsaRawMidiIn: read error. Terminating Midi") << endmsg;
293 			break;
294 		}
295 		if (err == 0) {
296 			_DEBUGPRINT("AlsaRawMidiIn: zero read\n");
297 			continue;
298 		}
299 
300 #if 0
301 		queue_event (time, data, err);
302 #else
303 		parse_events (time, data, err);
304 #endif
305 	}
306 
307 	_DEBUGPRINT("AlsaRawMidiIn: MIDI IN THREAD STOPPED\n");
308 	return 0;
309 }
310 
311 int
312 AlsaRawMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
313 	_event._pending = false;
314 	return AlsaMidiIn::queue_event(time, data, size);
315 }
316 
317 void
318 AlsaRawMidiIn::parse_events (const uint64_t time, const uint8_t *data, const size_t size) {
319 	if (_event._pending) {
320 		_DEBUGPRINT("AlsaRawMidiIn: queue pending event\n");
321 		if (queue_event (_event._time, _parser_buffer, _event._size)) {
322 			return;
323 		}
324 	}
325 	for (size_t i = 0; i < size; ++i) {
326 		if (_first_time && !(data[i] & 0x80)) {
327 			continue;
328 		}
329 		_first_time = false; /// TODO optimize e.g. use fn pointer to different parse_events()
330 		if (process_byte(time, data[i])) {
331 			if (queue_event (_event._time, _parser_buffer, _event._size)) {
332 				return;
333 			}
334 		}
335 	}
336 }
337 
338 // based on JackMidiRawInputWriteQueue by Devin Anderson //
339 bool
340 AlsaRawMidiIn::process_byte(const uint64_t time, const uint8_t byte)
341 {
342 	if (byte >= 0xf8) {
343 		// Realtime
344 		if (byte == 0xfd) {
345 			return false;
346 		}
347 		_parser_buffer[0] = byte;
348 		prepare_byte_event(time, byte);
349 		return true;
350 	}
351 	if (byte == 0xf7) {
352 		// Sysex end
353 		if (_status_byte == 0xf0) {
354 			record_byte(byte);
355 			return prepare_buffered_event(time);
356 		}
357     _total_bytes = 0;
358     _unbuffered_bytes = 0;
359 		_expected_bytes = 0;
360 		_status_byte = 0;
361 		return false;
362 	}
363 	if (byte >= 0x80) {
364 		// Non-realtime status byte
365 		if (_total_bytes) {
366 			_DEBUGPRINT("AlsaRawMidiIn: discarded bogus midi message\n");
367 #if 0
368 			for (size_t i=0; i < _total_bytes; ++i) {
369 				printf("%02x ", _parser_buffer[i]);
370 			}
371 			printf("\n");
372 #endif
373 			_total_bytes = 0;
374 			_unbuffered_bytes = 0;
375 		}
376 		_status_byte = byte;
377 		switch (byte & 0xf0) {
378 			case 0x80:
379 			case 0x90:
380 			case 0xa0:
381 			case 0xb0:
382 			case 0xe0:
383 				// Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
384 				_expected_bytes = 3;
385 				break;
386 			case 0xc0:
387 			case 0xd0:
388 				// Program Change, Channel Pressure
389 				_expected_bytes = 2;
390 				break;
391 			case 0xf0:
392 				switch (byte) {
393 					case 0xf0:
394 						// Sysex
395 						_expected_bytes = 0;
396 						break;
397 					case 0xf1:
398 					case 0xf3:
399 						// MTC Quarter Frame, Song Select
400 						_expected_bytes = 2;
401 						break;
402 					case 0xf2:
403 						// Song Position
404 						_expected_bytes = 3;
405 						break;
406 					case 0xf4:
407 					case 0xf5:
408 						// Undefined
409 						_expected_bytes = 0;
410 						_status_byte = 0;
411 						return false;
412 					case 0xf6:
413 						// Tune Request
414 						prepare_byte_event(time, byte);
415 						_expected_bytes = 0;
416 						_status_byte = 0;
417 						return true;
418 				}
419 		}
420 		record_byte(byte);
421 		return false;
422 	}
423 	// Data byte
424 	if (! _status_byte) {
425 		// Data bytes without a status will be discarded.
426 		_total_bytes++;
427 		_unbuffered_bytes++;
428 		return false;
429 	}
430 	if (! _total_bytes) {
431 		_DEBUGPRINT("AlsaRawMidiIn: apply running status\n");
432 		record_byte(_status_byte);
433 	}
434 	record_byte(byte);
435 	return (_total_bytes == _expected_bytes) ? prepare_buffered_event(time) : false;
436 }
437