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;
31 /* max bytes per individual midi-event
32 * events larger than this are ignored */
33 #define MaxAlsaRawEventSize (64)
36 #define _DEBUGPRINT(STR) fprintf(stderr, STR);
38 #define _DEBUGPRINT(STR) ;
41 AlsaRawMidiIO::AlsaRawMidiIO (const std::string &name, const char *device, const bool input)
49 AlsaRawMidiIO::~AlsaRawMidiIO ()
52 snd_rawmidi_drain (_device);
53 snd_rawmidi_close (_device);
59 AlsaRawMidiIO::init (const char *device_name, const bool input)
61 if (snd_rawmidi_open (
62 input ? &_device : NULL,
63 input ? NULL : &_device,
64 device_name, SND_RAWMIDI_NONBLOCK) < 0) {
68 _npfds = snd_rawmidi_poll_descriptors_count (_device);
70 _DEBUGPRINT("AlsaRawMidiIO: no poll descriptor(s).\n");
71 snd_rawmidi_close (_device);
75 _pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
76 snd_rawmidi_poll_descriptors (_device, _pfds, _npfds);
81 snd_rawmidi_params_t *params;
82 if (snd_rawmidi_params_malloc (¶ms)) {
85 if (snd_rawmidi_params_current (_device, params)) {
88 if (snd_rawmidi_params_set_avail_min (_device, params, 1)) {
91 if ( snd_rawmidi_params_set_buffer_size (_device, params, 64)) {
94 if (snd_rawmidi_params_set_no_active_sensing (_device, params, 1)) {
102 _DEBUGPRINT("AlsaRawMidiIO: parameter setup error\n");
103 snd_rawmidi_close (_device);
109 ///////////////////////////////////////////////////////////////////////////////
111 AlsaRawMidiOut::AlsaRawMidiOut (const std::string &name, const char *device)
112 : AlsaRawMidiIO (name, device, false)
118 AlsaRawMidiOut::main_process_thread ()
121 pthread_mutex_lock (&_notify_mutex);
122 bool need_drain = false;
124 bool have_data = false;
125 struct MidiEventHeader h(0,0);
126 uint8_t data[MaxAlsaRawEventSize];
128 const uint32_t read_space = _rb->read_space();
130 if (read_space > sizeof(MidiEventHeader)) {
131 if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
132 _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT HEADER!!\n");
135 assert (read_space >= h.size);
136 if (h.size > MaxAlsaRawEventSize) {
137 _rb->increment_read_idx (h.size);
138 _DEBUGPRINT("AlsaRawMidiOut: MIDI event too large!\n");
141 if (_rb->read (&data[0], h.size) != h.size) {
142 _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT DATA!!\n");
150 snd_rawmidi_drain (_device);
153 pthread_cond_wait (&_notify_ready, &_notify_mutex);
157 uint64_t now = g_get_monotonic_time();
158 while (h.time > now + 500) {
160 snd_rawmidi_drain (_device);
163 select_sleep(h.time - now);
165 now = g_get_monotonic_time();
169 int perr = poll (_pfds, _npfds, 10 /* ms */);
171 PBD::error << _("AlsaRawMidiOut: Error polling device. Terminating Midi Thread.") << endmsg;
175 _DEBUGPRINT("AlsaRawMidiOut: poll() timed out.\n");
179 unsigned short revents = 0;
180 if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
181 PBD::error << _("AlsaRawMidiOut: Failed to poll device. Terminating Midi Thread.") << endmsg;
185 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
186 PBD::error << _("AlsaRawMidiOut: poll error. Terminating Midi Thread.") << endmsg;
190 if (!(revents & POLLOUT)) {
191 _DEBUGPRINT("AlsaRawMidiOut: POLLOUT not ready.\n");
196 ssize_t err = snd_rawmidi_write (_device, data, h.size);
198 if ((err == -EAGAIN)) {
199 snd_rawmidi_drain (_device);
202 if (err == -EWOULDBLOCK) {
207 PBD::error << _("AlsaRawMidiOut: write failed. Terminating Midi Thread.") << endmsg;
210 if ((size_t) err < h.size) {
211 _DEBUGPRINT("AlsaRawMidiOut: short write\n");
212 memmove(&data[0], &data[err], err);
219 pthread_mutex_unlock (&_notify_mutex);
220 _DEBUGPRINT("AlsaRawMidiOut: MIDI OUT THREAD STOPPED\n");
225 ///////////////////////////////////////////////////////////////////////////////
227 AlsaRawMidiIn::AlsaRawMidiIn (const std::string &name, const char *device)
228 : AlsaRawMidiIO (name, device, true)
232 , _unbuffered_bytes(0)
240 AlsaRawMidiIn::main_process_thread ()
244 unsigned short revents = 0;
246 int perr = poll (_pfds, _npfds, 100 /* ms */);
248 PBD::error << _("AlsaRawMidiIn: Error polling device. Terminating Midi Thread.") << endmsg;
255 if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
256 PBD::error << _("AlsaRawMidiIn: Failed to poll device. Terminating Midi Thread.") << endmsg;
260 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
261 PBD::error << _("AlsaRawMidiIn: poll error. Terminating Midi Thread.") << endmsg;
265 if (!(revents & POLLIN)) {
266 _DEBUGPRINT("AlsaRawMidiOut: POLLIN not ready.\n");
271 uint8_t data[MaxAlsaRawEventSize];
272 uint64_t time = g_get_monotonic_time();
273 ssize_t err = snd_rawmidi_read (_device, data, sizeof(data));
275 if ((err == -EAGAIN) || (err == -EWOULDBLOCK)) {
279 PBD::error << _("AlsaRawMidiIn: read error. Terminating Midi") << endmsg;
283 _DEBUGPRINT("AlsaRawMidiIn: zero read\n");
288 queue_event (time, data, err);
290 parse_events (time, data, err);
294 _DEBUGPRINT("AlsaRawMidiIn: MIDI IN THREAD STOPPED\n");
299 AlsaRawMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
300 _event._pending = false;
301 return AlsaMidiIn::queue_event(time, data, size);
305 AlsaRawMidiIn::parse_events (const uint64_t time, const uint8_t *data, const size_t size) {
306 if (_event._pending) {
307 _DEBUGPRINT("AlsaRawMidiIn: queue pending event\n");
308 if (queue_event (_event._time, _parser_buffer, _event._size)) {
312 for (size_t i = 0; i < size; ++i) {
313 if (_first_time && !(data[i] & 0x80)) {
316 _first_time = false; /// TODO optimize e.g. use fn pointer to different parse_events()
317 if (process_byte(time, data[i])) {
318 if (queue_event (_event._time, _parser_buffer, _event._size)) {
325 // based on JackMidiRawInputWriteQueue by Devin Anderson //
327 AlsaRawMidiIn::process_byte(const uint64_t time, const uint8_t byte)
334 _parser_buffer[0] = byte;
335 prepare_byte_event(time, byte);
340 if (_status_byte == 0xf0) {
342 return prepare_buffered_event(time);
345 _unbuffered_bytes = 0;
351 // Non-realtime status byte
353 _DEBUGPRINT("AlsaRawMidiIn: discarded bogus midi message\n");
355 for (size_t i=0; i < _total_bytes; ++i) {
356 printf("%02x ", _parser_buffer[i]);
361 _unbuffered_bytes = 0;
364 switch (byte & 0xf0) {
370 // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
375 // Program Change, Channel Pressure
386 // MTC Quarter Frame, Song Select
401 prepare_byte_event(time, byte);
411 if (! _status_byte) {
412 // Data bytes without a status will be discarded.
417 if (! _total_bytes) {
418 _DEBUGPRINT("AlsaRawMidiIn: apply running status\n");
419 record_byte(_status_byte);
422 return (_total_bytes == _expected_bytes) ? prepare_buffered_event(time) : false;