2 * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "select_sleep.h"
23 #include "alsa_sequencer.h"
25 #include "pbd/error.h"
28 using namespace ARDOUR;
30 /* max bytes per individual midi-event
31 * events larger than this are ignored */
32 #define MaxAlsaSeqEventSize (64)
35 #define _DEBUGPRINT(STR) fprintf(stderr, STR);
37 #define _DEBUGPRINT(STR) ;
40 AlsaSeqMidiIO::AlsaSeqMidiIO (const std::string &name, const char *device, const bool input)
48 AlsaSeqMidiIO::~AlsaSeqMidiIO ()
57 AlsaSeqMidiIO::init (const char *device_name, const bool input)
59 if (snd_seq_open (&_seq, "hw",
60 input ? SND_SEQ_OPEN_INPUT : SND_SEQ_OPEN_OUTPUT, 0) < 0)
66 if (snd_seq_set_client_name (_seq, "Ardour")) {
67 _DEBUGPRINT("AlsaSeqMidiIO: cannot set client name.\n");
71 _port = snd_seq_create_simple_port (_seq, "port", SND_SEQ_PORT_CAP_NO_EXPORT |
72 (input ? SND_SEQ_PORT_CAP_WRITE : SND_SEQ_PORT_CAP_READ),
73 SND_SEQ_PORT_TYPE_APPLICATION);
76 _DEBUGPRINT("AlsaSeqMidiIO: cannot create port.\n");
80 _npfds = snd_seq_poll_descriptors_count (_seq, input ? POLLIN : POLLOUT);
82 _DEBUGPRINT("AlsaSeqMidiIO: no poll descriptor(s).\n");
85 _pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
86 snd_seq_poll_descriptors (_seq, _pfds, _npfds, input ? POLLIN : POLLOUT);
90 if (snd_seq_parse_address (_seq, &port, device_name) < 0) {
91 _DEBUGPRINT("AlsaSeqMidiIO: cannot resolve hardware port.\n");
96 if (snd_seq_connect_from (_seq, _port, port.client, port.port) < 0) {
97 _DEBUGPRINT("AlsaSeqMidiIO: cannot connect input port.\n");
101 if (snd_seq_connect_to (_seq, _port, port.client, port.port) < 0) {
102 _DEBUGPRINT("AlsaSeqMidiIO: cannot connect output port.\n");
107 snd_seq_nonblock(_seq, 1);
113 PBD::error << _("AlsaSeqMidiIO: Device initialization failed.") << endmsg;
114 snd_seq_close (_seq);
119 ///////////////////////////////////////////////////////////////////////////////
121 AlsaSeqMidiOut::AlsaSeqMidiOut (const std::string &name, const char *device)
122 : AlsaSeqMidiIO (name, device, false)
128 AlsaSeqMidiOut::main_process_thread ()
131 bool need_drain = false;
132 snd_midi_event_t *alsa_codec = NULL;
133 snd_midi_event_new (MaxAlsaSeqEventSize, &alsa_codec);
134 pthread_mutex_lock (&_notify_mutex);
136 bool have_data = false;
137 struct MidiEventHeader h(0,0);
138 uint8_t data[MaxAlsaSeqEventSize];
140 const uint32_t read_space = _rb->read_space();
142 if (read_space > sizeof(MidiEventHeader)) {
143 if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
144 _DEBUGPRINT("AlsaSeqMidiOut: Garbled MIDI EVENT HEADER!!\n");
147 assert (read_space >= h.size);
148 if (h.size > MaxAlsaSeqEventSize) {
149 _rb->increment_read_idx (h.size);
150 _DEBUGPRINT("AlsaSeqMidiOut: MIDI event too large!\n");
153 if (_rb->read (&data[0], h.size) != h.size) {
154 _DEBUGPRINT("AlsaSeqMidiOut: Garbled MIDI EVENT DATA!!\n");
162 snd_seq_drain_output (_seq);
165 pthread_cond_wait (&_notify_ready, &_notify_mutex);
169 snd_seq_event_t alsa_event;
170 snd_seq_ev_clear (&alsa_event);
171 snd_midi_event_reset_encode (alsa_codec);
172 if (!snd_midi_event_encode (alsa_codec, data, h.size, &alsa_event)) {
173 PBD::error << _("AlsaSeqMidiOut: Invalid Midi Event.") << endmsg;
177 snd_seq_ev_set_source (&alsa_event, _port);
178 snd_seq_ev_set_subs (&alsa_event);
179 snd_seq_ev_set_direct (&alsa_event);
181 uint64_t now = g_get_monotonic_time();
182 while (h.time > now + 500) {
184 snd_seq_drain_output (_seq);
187 select_sleep(h.time - now);
189 now = g_get_monotonic_time();
193 int perr = poll (_pfds, _npfds, 10 /* ms */);
195 PBD::error << _("AlsaSeqMidiOut: Error polling device. Terminating Midi Thread.") << endmsg;
199 _DEBUGPRINT("AlsaSeqMidiOut: poll() timed out.\n");
203 ssize_t err = snd_seq_event_output(_seq, &alsa_event);
205 if ((err == -EAGAIN)) {
206 snd_seq_drain_output (_seq);
209 if (err == -EWOULDBLOCK) {
214 PBD::error << _("AlsaSeqMidiOut: write failed. Terminating Midi Thread.") << endmsg;
220 pthread_mutex_unlock (&_notify_mutex);
223 snd_midi_event_free(alsa_codec);
225 _DEBUGPRINT("AlsaSeqMidiOut: MIDI OUT THREAD STOPPED\n");
229 ///////////////////////////////////////////////////////////////////////////////
231 AlsaSeqMidiIn::AlsaSeqMidiIn (const std::string &name, const char *device)
232 : AlsaSeqMidiIO (name, device, true)
238 AlsaSeqMidiIn::main_process_thread ()
242 snd_midi_event_t *alsa_codec = NULL;
243 snd_midi_event_new (MaxAlsaSeqEventSize, &alsa_codec);
248 snd_seq_poll_descriptors (_seq, _pfds, _npfds, POLLIN);
249 int perr = poll (_pfds, _npfds, 100 /* ms */);
252 PBD::error << _("AlsaSeqMidiIn: Error polling device. Terminating Midi Thread.") << endmsg;
260 snd_seq_event_t *event;
261 uint64_t time = g_get_monotonic_time();
262 ssize_t err = snd_seq_event_input (_seq, &event);
264 if ((err == -EAGAIN) || (err == -EWOULDBLOCK)) {
268 if (err == -ENOSPC) {
269 PBD::error << _("AlsaSeqMidiIn: FIFO overrun.") << endmsg;
274 PBD::error << _("AlsaSeqMidiIn: read error. Terminating Midi") << endmsg;
278 uint8_t data[MaxAlsaSeqEventSize];
279 snd_midi_event_reset_decode (alsa_codec);
280 ssize_t size = snd_midi_event_decode (alsa_codec, data, sizeof(data), event);
283 queue_event (time, data, size);
285 do_poll = (0 == err);
289 snd_midi_event_free(alsa_codec);
291 _DEBUGPRINT("AlsaSeqMidiIn: MIDI IN THREAD STOPPED\n");