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 char *device, const bool input)
48 AlsaRawMidiIO::~AlsaRawMidiIO ()
51 snd_rawmidi_drain (_device);
52 snd_rawmidi_close (_device);
58 AlsaRawMidiIO::init (const char *device_name, const bool input)
60 if (snd_rawmidi_open (
61 input ? &_device : NULL,
62 input ? NULL : &_device,
63 device_name, SND_RAWMIDI_NONBLOCK) < 0) {
67 _npfds = snd_rawmidi_poll_descriptors_count (_device);
69 _DEBUGPRINT("AlsaRawMidiIO: no poll descriptor(s).\n");
70 snd_rawmidi_close (_device);
74 _pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
75 snd_rawmidi_poll_descriptors (_device, _pfds, _npfds);
80 snd_rawmidi_params_t *params;
81 if (snd_rawmidi_params_malloc (¶ms)) {
84 if (snd_rawmidi_params_current (_device, params)) {
87 if (snd_rawmidi_params_set_avail_min (_device, params, 1)) {
90 if ( snd_rawmidi_params_set_buffer_size (_device, params, 64)) {
93 if (snd_rawmidi_params_set_no_active_sensing (_device, params, 1)) {
101 _DEBUGPRINT("AlsaRawMidiIO: parameter setup error\n");
102 snd_rawmidi_close (_device);
108 ///////////////////////////////////////////////////////////////////////////////
110 AlsaRawMidiOut::AlsaRawMidiOut (const char *device)
111 : AlsaRawMidiIO (device, false)
117 AlsaRawMidiOut::main_process_thread ()
120 pthread_mutex_lock (&_notify_mutex);
121 bool need_drain = false;
123 bool have_data = false;
124 struct MidiEventHeader h(0,0);
125 uint8_t data[MaxAlsaRawEventSize];
127 const uint32_t read_space = _rb->read_space();
129 if (read_space > sizeof(MidiEventHeader)) {
130 if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
131 _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT HEADER!!\n");
134 assert (read_space >= h.size);
135 if (h.size > MaxAlsaRawEventSize) {
136 _rb->increment_read_idx (h.size);
137 _DEBUGPRINT("AlsaRawMidiOut: MIDI event too large!\n");
140 if (_rb->read (&data[0], h.size) != h.size) {
141 _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT DATA!!\n");
149 snd_rawmidi_drain (_device);
152 pthread_cond_wait (&_notify_ready, &_notify_mutex);
156 uint64_t now = g_get_monotonic_time();
157 while (h.time > now + 500) {
159 snd_rawmidi_drain (_device);
162 select_sleep(h.time - now);
164 now = g_get_monotonic_time();
168 int perr = poll (_pfds, _npfds, 10 /* ms */);
170 PBD::error << _("AlsaRawMidiOut: Error polling device. Terminating Midi Thread.") << endmsg;
174 _DEBUGPRINT("AlsaRawMidiOut: poll() timed out.\n");
178 unsigned short revents = 0;
179 if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
180 PBD::error << _("AlsaRawMidiOut: Failed to poll device. Terminating Midi Thread.") << endmsg;
184 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
185 PBD::error << _("AlsaRawMidiOut: poll error. Terminating Midi Thread.") << endmsg;
189 if (!(revents & POLLOUT)) {
190 _DEBUGPRINT("AlsaRawMidiOut: POLLOUT not ready.\n");
195 ssize_t err = snd_rawmidi_write (_device, data, h.size);
197 if ((err == -EAGAIN)) {
198 snd_rawmidi_drain (_device);
201 if (err == -EWOULDBLOCK) {
206 PBD::error << _("AlsaRawMidiOut: write failed. Terminating Midi Thread.") << endmsg;
209 if ((size_t) err < h.size) {
210 _DEBUGPRINT("AlsaRawMidiOut: short write\n");
211 memmove(&data[0], &data[err], err);
218 pthread_mutex_unlock (&_notify_mutex);
219 _DEBUGPRINT("AlsaRawMidiOut: MIDI OUT THREAD STOPPED\n");
224 ///////////////////////////////////////////////////////////////////////////////
226 AlsaRawMidiIn::AlsaRawMidiIn (const char *device)
227 : AlsaRawMidiIO (device, true)
231 , _unbuffered_bytes(0)
239 AlsaRawMidiIn::main_process_thread ()
243 unsigned short revents = 0;
245 int perr = poll (_pfds, _npfds, 100 /* ms */);
247 PBD::error << _("AlsaRawMidiIn: Error polling device. Terminating Midi Thread.") << endmsg;
254 if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
255 PBD::error << _("AlsaRawMidiIn: Failed to poll device. Terminating Midi Thread.") << endmsg;
259 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
260 PBD::error << _("AlsaRawMidiIn: poll error. Terminating Midi Thread.") << endmsg;
264 if (!(revents & POLLIN)) {
265 _DEBUGPRINT("AlsaRawMidiOut: POLLIN not ready.\n");
270 uint8_t data[MaxAlsaRawEventSize];
271 uint64_t time = g_get_monotonic_time();
272 ssize_t err = snd_rawmidi_read (_device, data, sizeof(data));
274 if ((err == -EAGAIN) || (err == -EWOULDBLOCK)) {
278 PBD::error << _("AlsaRawMidiIn: read error. Terminating Midi") << endmsg;
282 _DEBUGPRINT("AlsaRawMidiIn: zero read\n");
287 queue_event (time, data, err);
289 parse_events (time, data, err);
293 _DEBUGPRINT("AlsaRawMidiIn: MIDI IN THREAD STOPPED\n");
298 AlsaRawMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
299 _event._pending = false;
300 return AlsaMidiIn::queue_event(time, data, size);
304 AlsaRawMidiIn::parse_events (const uint64_t time, const uint8_t *data, const size_t size) {
305 if (_event._pending) {
306 _DEBUGPRINT("AlsaRawMidiIn: queue pending event\n");
307 if (queue_event (_event._time, _parser_buffer, _event._size)) {
311 for (size_t i = 0; i < size; ++i) {
312 if (_first_time && !(data[i] & 0x80)) {
315 _first_time = false; /// TODO optimize e.g. use fn pointer to different parse_events()
316 if (process_byte(time, data[i])) {
317 if (queue_event (_event._time, _parser_buffer, _event._size)) {
324 // based on JackMidiRawInputWriteQueue by Devin Anderson //
326 AlsaRawMidiIn::process_byte(const uint64_t time, const uint8_t byte)
333 _parser_buffer[0] = byte;
334 prepare_byte_event(time, byte);
339 if (_status_byte == 0xf0) {
341 return prepare_buffered_event(time);
344 _unbuffered_bytes = 0;
350 // Non-realtime status byte
352 _DEBUGPRINT("AlsaRawMidiIn: discarded bogus midi message\n");
354 for (size_t i=0; i < _total_bytes; ++i) {
355 printf("%02x ", _parser_buffer[i]);
360 _unbuffered_bytes = 0;
363 switch (byte & 0xf0) {
369 // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
374 // Program Change, Channel Pressure
385 // MTC Quarter Frame, Song Select
400 prepare_byte_event(time, byte);
410 if (! _status_byte) {
411 // Data bytes without a status will be discarded.
416 if (! _total_bytes) {
417 _DEBUGPRINT("AlsaRawMidiIn: apply running status\n");
418 record_byte(_status_byte);
421 return (_total_bytes == _expected_bytes) ? prepare_buffered_event(time) : false;