2 * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
3 * Copyright (C) 2010 Devin Anderson
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "select_sleep.h"
24 #include "alsa_rawmidi.h"
26 #include "pbd/error.h"
29 using namespace ARDOUR;
32 #define _DEBUGPRINT(STR) fprintf(stderr, STR);
34 #define _DEBUGPRINT(STR) ;
37 AlsaRawMidiIO::AlsaRawMidiIO (const std::string &name, const char *device, const bool input)
45 AlsaRawMidiIO::~AlsaRawMidiIO ()
48 snd_rawmidi_drain (_device);
49 snd_rawmidi_close (_device);
55 AlsaRawMidiIO::init (const char *device_name, const bool input)
57 if (snd_rawmidi_open (
58 input ? &_device : NULL,
59 input ? NULL : &_device,
60 device_name, SND_RAWMIDI_NONBLOCK) < 0) {
64 _npfds = snd_rawmidi_poll_descriptors_count (_device);
66 _DEBUGPRINT("AlsaRawMidiIO: no poll descriptor(s).\n");
67 snd_rawmidi_close (_device);
71 _pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
72 snd_rawmidi_poll_descriptors (_device, _pfds, _npfds);
77 snd_rawmidi_params_t *params;
78 if (snd_rawmidi_params_malloc (¶ms)) {
81 if (snd_rawmidi_params_current (_device, params)) {
84 if (snd_rawmidi_params_set_avail_min (_device, params, 1)) {
87 if (snd_rawmidi_params_set_buffer_size (_device, params, 64)) {
90 if (snd_rawmidi_params_set_no_active_sensing (_device, params, 1)) {
98 _DEBUGPRINT("AlsaRawMidiIO: parameter setup error\n");
99 snd_rawmidi_close (_device);
105 ///////////////////////////////////////////////////////////////////////////////
107 AlsaRawMidiOut::AlsaRawMidiOut (const std::string &name, const char *device)
108 : AlsaRawMidiIO (name, device, false)
114 AlsaRawMidiOut::main_process_thread ()
117 pthread_mutex_lock (&_notify_mutex);
118 unsigned int need_drain = 0;
120 bool have_data = false;
121 struct MidiEventHeader h(0,0);
122 uint8_t data[MaxAlsaMidiEventSize];
124 const uint32_t read_space = _rb->read_space();
126 if (read_space > sizeof(MidiEventHeader)) {
127 if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
128 _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT HEADER!!\n");
131 assert (read_space >= h.size);
132 if (h.size > MaxAlsaMidiEventSize) {
133 _rb->increment_read_idx (h.size);
134 _DEBUGPRINT("AlsaRawMidiOut: MIDI event too large!\n");
137 if (_rb->read (&data[0], h.size) != h.size) {
138 _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT DATA!!\n");
145 if (need_drain > 0) {
146 snd_rawmidi_drain (_device);
149 pthread_cond_wait (&_notify_ready, &_notify_mutex);
153 uint64_t now = g_get_monotonic_time();
154 while (h.time > now + 500) {
155 if (need_drain > 0) {
156 snd_rawmidi_drain (_device);
159 select_sleep(h.time - now);
161 now = g_get_monotonic_time();
165 int perr = poll (_pfds, _npfds, 10 /* ms */);
167 PBD::error << _("AlsaRawMidiOut: Error polling device. Terminating Midi Thread.") << endmsg;
171 _DEBUGPRINT("AlsaRawMidiOut: poll() timed out.\n");
175 unsigned short revents = 0;
176 if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
177 PBD::error << _("AlsaRawMidiOut: Failed to poll device. Terminating Midi Thread.") << endmsg;
181 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
182 PBD::error << _("AlsaRawMidiOut: poll error. Terminating Midi Thread.") << endmsg;
186 if (!(revents & POLLOUT)) {
187 _DEBUGPRINT("AlsaRawMidiOut: POLLOUT not ready.\n");
192 ssize_t err = snd_rawmidi_write (_device, data, h.size);
194 #if 0 // DEBUG -- not rt-safe
195 printf("TX [%ld | %ld]", h.size, err);
196 for (size_t i = 0; i < h.size; ++i) {
197 printf (" %02x", data[i]);
202 if ((err == -EAGAIN)) {
203 snd_rawmidi_drain (_device);
206 if (err == -EWOULDBLOCK) {
211 PBD::error << _("AlsaRawMidiOut: write failed. Terminating Midi Thread.") << endmsg;
214 if ((size_t) err < h.size) {
215 _DEBUGPRINT("AlsaRawMidiOut: short write\n");
216 memmove(&data[0], &data[err], err);
221 if ((need_drain += h.size) >= 64) {
222 snd_rawmidi_drain (_device);
227 pthread_mutex_unlock (&_notify_mutex);
228 _DEBUGPRINT("AlsaRawMidiOut: MIDI OUT THREAD STOPPED\n");
233 ///////////////////////////////////////////////////////////////////////////////
235 AlsaRawMidiIn::AlsaRawMidiIn (const std::string &name, const char *device)
236 : AlsaRawMidiIO (name, device, true)
240 , _unbuffered_bytes(0)
248 AlsaRawMidiIn::main_process_thread ()
252 unsigned short revents = 0;
254 int perr = poll (_pfds, _npfds, 100 /* ms */);
256 PBD::error << _("AlsaRawMidiIn: Error polling device. Terminating Midi Thread.") << endmsg;
263 if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
264 PBD::error << _("AlsaRawMidiIn: Failed to poll device. Terminating Midi Thread.") << endmsg;
268 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
269 PBD::error << _("AlsaRawMidiIn: poll error. Terminating Midi Thread.") << endmsg;
273 if (!(revents & POLLIN)) {
274 _DEBUGPRINT("AlsaRawMidiOut: POLLIN not ready.\n");
279 uint8_t data[MaxAlsaMidiEventSize];
280 uint64_t time = g_get_monotonic_time();
281 ssize_t err = snd_rawmidi_read (_device, data, sizeof(data));
283 #if EAGAIN != EWOULDBLOCK
284 if ((err == -EAGAIN) || (err == -EWOULDBLOCK)) {
286 if (err == -EAGAIN) {
291 PBD::error << _("AlsaRawMidiIn: read error. Terminating Midi") << endmsg;
295 _DEBUGPRINT("AlsaRawMidiIn: zero read\n");
300 queue_event (time, data, err);
302 parse_events (time, data, err);
306 _DEBUGPRINT("AlsaRawMidiIn: MIDI IN THREAD STOPPED\n");
311 AlsaRawMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
312 _event._pending = false;
313 return AlsaMidiIn::queue_event(time, data, size);
317 AlsaRawMidiIn::parse_events (const uint64_t time, const uint8_t *data, const size_t size) {
318 if (_event._pending) {
319 _DEBUGPRINT("AlsaRawMidiIn: queue pending event\n");
320 if (queue_event (_event._time, _parser_buffer, _event._size)) {
324 for (size_t i = 0; i < size; ++i) {
325 if (_first_time && !(data[i] & 0x80)) {
328 _first_time = false; /// TODO optimize e.g. use fn pointer to different parse_events()
329 if (process_byte(time, data[i])) {
330 if (queue_event (_event._time, _parser_buffer, _event._size)) {
337 // based on JackMidiRawInputWriteQueue by Devin Anderson //
339 AlsaRawMidiIn::process_byte(const uint64_t time, const uint8_t byte)
346 _parser_buffer[0] = byte;
347 prepare_byte_event(time, byte);
352 if (_status_byte == 0xf0) {
354 return prepare_buffered_event(time);
357 _unbuffered_bytes = 0;
363 // Non-realtime status byte
365 _DEBUGPRINT("AlsaRawMidiIn: discarded bogus midi message\n");
367 for (size_t i=0; i < _total_bytes; ++i) {
368 printf("%02x ", _parser_buffer[i]);
373 _unbuffered_bytes = 0;
376 switch (byte & 0xf0) {
382 // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
387 // Program Change, Channel Pressure
398 // MTC Quarter Frame, Song Select
413 prepare_byte_event(time, byte);
423 if (! _status_byte) {
424 // Data bytes without a status will be discarded.
429 if (! _total_bytes) {
430 _DEBUGPRINT("AlsaRawMidiIn: apply running status\n");
431 record_byte(_status_byte);
434 return (_total_bytes == _expected_bytes) ? prepare_buffered_event(time) : false;