}
}
+ assert(_midi_driver_option != _("None"));
+
std::map<std::string, std::string> devices;
- get_alsa_rawmidi_device_names(devices);
+ if (_midi_driver_option == _("ALSA raw devices")) {
+ get_alsa_rawmidi_device_names(devices);
+ } else {
+ get_alsa_sequencer_names (devices);
+ }
+
for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
if (i->first == name) {
_midi_devices[name] = new AlsaMidiDeviceInfo();
std::vector<std::string> m;
m.push_back (_("None"));
m.push_back (_("ALSA raw devices"));
+ m.push_back (_("ALSA sequencer"));
return m;
}
AlsaAudioBackend::enumerate_midi_devices () const
{
std::vector<AudioBackend::DeviceStatus> s;
- if (_midi_driver_option == _("None")) {
+ std::map<std::string, std::string> devices;
+
+ if (_midi_driver_option == _("ALSA raw devices")) {
+ get_alsa_rawmidi_device_names (devices);
+ }
+ else if (_midi_driver_option == _("ALSA sequencer")) {
+ get_alsa_sequencer_names (devices);
+ } else {
return s;
}
- std::map<std::string, std::string> devices;
- get_alsa_rawmidi_device_names(devices);
-
for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
s.push_back (DeviceStatus (i->first, true));
}
int
AlsaAudioBackend::set_midi_option (const std::string& opt)
{
- if (opt != _("None") && opt != _("ALSA raw devices")) {
+ if (opt != _("None") && opt != _("ALSA raw devices") && opt != _("ALSA sequencer")) {
return -1;
}
_midi_driver_option = opt;
}
while (!_rmidi_out.empty ()) {
- AlsaRawMidiIO *m = _rmidi_out.back ();
+ AlsaMidiIO *m = _rmidi_out.back ();
m->stop();
_rmidi_out.pop_back ();
delete m;
}
while (!_rmidi_in.empty ()) {
- AlsaRawMidiIO *m = _rmidi_in.back ();
+ AlsaMidiIO *m = _rmidi_in.back ();
m->stop();
_rmidi_in.pop_back ();
delete m;
if (_midi_driver_option == _("None")) {
return 0;
+ } else if (_midi_driver_option == _("ALSA raw devices")) {
+ get_alsa_rawmidi_device_names(devices);
+ } else {
+ get_alsa_sequencer_names (devices);
}
- get_alsa_rawmidi_device_names(devices);
for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
struct AlsaMidiDeviceInfo * nfo = midi_device_info(i->first);
if (!nfo) continue;
if (!nfo->enabled) continue;
- AlsaRawMidiOut *mout = new AlsaRawMidiOut (i->second.c_str());
+ AlsaMidiOut *mout;
+ if (_midi_driver_option == _("ALSA raw devices")) {
+ mout = new AlsaRawMidiOut (i->second.c_str());
+ } else {
+ mout = new AlsaSeqMidiOut (i->second.c_str());
+ }
+
if (mout->state ()) {
PBD::warning << string_compose (
- _("AlsaRawMidiOut: failed to open midi device '%1'."), i->second)
+ _("AlsaMidiOut: failed to open midi device '%1'."), i->second)
<< endmsg;
delete mout;
} else {
mout->sync_time (g_get_monotonic_time());
if (mout->start ()) {
PBD::warning << string_compose (
- _("AlsaRawMidiOut: failed to start midi device '%1'."), i->second)
+ _("AlsaMidiOut: failed to start midi device '%1'."), i->second)
<< endmsg;
delete mout;
} else {
}
}
- AlsaRawMidiIn *midin = new AlsaRawMidiIn (i->second.c_str());
+ AlsaMidiIn *midin;
+ if (_midi_driver_option == _("ALSA raw devices")) {
+ midin = new AlsaRawMidiIn (i->second.c_str());
+ } else {
+ midin = new AlsaSeqMidiIn (i->second.c_str());
+ }
+
if (midin->state ()) {
PBD::warning << string_compose (
- _("AlsaRawMidiIn: failed to open midi device '%1'."), i->second)
+ _("AlsaMidiIn: failed to open midi device '%1'."), i->second)
<< endmsg;
delete midin;
} else {
midin->sync_time (g_get_monotonic_time());
if (midin->start ()) {
PBD::warning << string_compose (
- _("AlsaRawMidiIn: failed to start midi device '%1'."), i->second)
+ _("AlsaMidiIn: failed to start midi device '%1'."), i->second)
<< endmsg;
delete midin;
} else {
i = 0;
for (std::vector<AlsaPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it, ++i) {
assert (_rmidi_in.size() > i);
- AlsaRawMidiIn *rm = static_cast<AlsaRawMidiIn*>(_rmidi_in.at(i));
+ AlsaMidiIn *rm = static_cast<AlsaMidiIn*>(_rmidi_in.at(i));
void *bptr = (*it)->get_buffer(0);
pframes_t time;
- uint8_t data[64]; // match MaxAlsaRawEventSize in alsa_rawmidi.cc
+ uint8_t data[64]; // match MaxAlsaEventSize in alsa_rawmidi.cc
size_t size = sizeof(data);
midi_clear(bptr);
while (rm->recv_event (time, data, size)) {
for (std::vector<AlsaPort*>::const_iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) {
assert (_rmidi_out.size() > i);
const AlsaMidiBuffer src = static_cast<const AlsaMidiPort*>(*it)->const_buffer();
- AlsaRawMidiOut *rm = static_cast<AlsaRawMidiOut*>(_rmidi_out.at(i));
+ AlsaMidiOut *rm = static_cast<AlsaMidiOut*>(_rmidi_out.at(i));
rm->sync_time (clock1);
for (AlsaMidiBuffer::const_iterator mit = src.begin (); mit != src.end (); ++mit) {
rm->send_event ((*mit)->timestamp(), (*mit)->data(), (*mit)->size());
#include "zita-alsa-pcmi.h"
#include "alsa_rawmidi.h"
+#include "alsa_sequencer.h"
namespace ARDOUR {
std::vector<AlsaPort *> _system_midi_in;
std::vector<AlsaPort *> _system_midi_out;
- std::vector<AlsaRawMidiOut *> _rmidi_out;
- std::vector<AlsaRawMidiIn *> _rmidi_in;
+ std::vector<AlsaMidiOut *> _rmidi_out;
+ std::vector<AlsaMidiIn *> _rmidi_in;
struct PortConnectData {
std::string a;
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <unistd.h>
+
+#include <glibmm.h>
+
+#include "alsa_midi.h"
+#include "rt_thread.h"
+
+#include "pbd/error.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+#ifndef NDEBUG
+#define _DEBUGPRINT(STR) fprintf(stderr, STR);
+#else
+#define _DEBUGPRINT(STR) ;
+#endif
+
+AlsaMidiIO::AlsaMidiIO ()
+ : _state (-1)
+ , _running (false)
+ , _pfds (0)
+ , _sample_length_us (1e6 / 48000.0)
+ , _period_length_us (1.024e6 / 48000.0)
+ , _samples_per_period (1024)
+ , _rb (0)
+{
+ pthread_mutex_init (&_notify_mutex, 0);
+ pthread_cond_init (&_notify_ready, 0);
+
+ // MIDI (hw port) 31.25 kbaud
+ // worst case here is 8192 SPP and 8KSPS for which we'd need
+ // 4000 bytes sans MidiEventHeader.
+ // since we're not always in sync, let's use 4096.
+ _rb = new RingBuffer<uint8_t>(4096 + 4096 * sizeof(MidiEventHeader));
+}
+
+AlsaMidiIO::~AlsaMidiIO ()
+{
+ delete _rb;
+ pthread_mutex_destroy (&_notify_mutex);
+ pthread_cond_destroy (&_notify_ready);
+ free (_pfds);
+}
+
+static void * pthread_process (void *arg)
+{
+ AlsaMidiIO *d = static_cast<AlsaMidiIO *>(arg);
+ d->main_process_thread ();
+ pthread_exit (0);
+ return 0;
+}
+
+int
+AlsaMidiIO::start ()
+{
+ if (_realtime_pthread_create (SCHED_FIFO, -21, 100000,
+ &_main_thread, pthread_process, this))
+ {
+ if (pthread_create (&_main_thread, NULL, pthread_process, this)) {
+ PBD::error << _("AlsaMidiIO: Failed to create process thread.") << endmsg;
+ return -1;
+ } else {
+ PBD::warning << _("AlsaMidiIO: Cannot acquire realtime permissions.") << endmsg;
+ }
+ }
+ int timeout = 5000;
+ while (!_running && --timeout > 0) { Glib::usleep (1000); }
+ if (timeout == 0 || !_running) {
+ return -1;
+ }
+ return 0;
+}
+
+int
+AlsaMidiIO::stop ()
+{
+ void *status;
+ if (!_running) {
+ return 0;
+ }
+
+ _running = false;
+
+ pthread_mutex_lock (&_notify_mutex);
+ pthread_cond_signal (&_notify_ready);
+ pthread_mutex_unlock (&_notify_mutex);
+
+ if (pthread_join (_main_thread, &status)) {
+ PBD::error << _("AlsaMidiIO: Failed to terminate.") << endmsg;
+ return -1;
+ }
+ return 0;
+}
+
+void
+AlsaMidiIO::setup_timing (const size_t samples_per_period, const float samplerate)
+{
+ _period_length_us = (double) samples_per_period * 1e6 / samplerate;
+ _sample_length_us = 1e6 / samplerate;
+ _samples_per_period = samples_per_period;
+}
+
+void
+AlsaMidiIO::sync_time (const uint64_t tme)
+{
+ // TODO consider a PLL, if this turns out to be the bottleneck for jitter
+ // also think about using
+ // snd_pcm_status_get_tstamp() and snd_rawmidi_status_get_tstamp()
+ // instead of monotonic clock.
+#ifdef DEBUG_TIMING
+ double tdiff = (_clock_monotonic + _period_length_us - tme) / 1000.0;
+ if (abs(tdiff) >= .05) {
+ printf("AlsaMidiIO MJ: %.1f ms\n", tdiff);
+ }
+#endif
+ _clock_monotonic = tme;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaMidiOut::AlsaMidiOut ()
+ : AlsaMidiIO ()
+{
+}
+
+int
+AlsaMidiOut::send_event (const pframes_t time, const uint8_t *data, const size_t size)
+{
+ const uint32_t buf_size = sizeof (MidiEventHeader) + size;
+ if (_rb->write_space() < buf_size) {
+ _DEBUGPRINT("AlsaMidiOut: ring buffer overflow\n");
+ return -1;
+ }
+ struct MidiEventHeader h (_clock_monotonic + time * _sample_length_us, size);
+ _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
+ _rb->write (data, size);
+
+ if (pthread_mutex_trylock (&_notify_mutex) == 0) {
+ pthread_cond_signal (&_notify_ready);
+ pthread_mutex_unlock (&_notify_mutex);
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaMidiIn::AlsaMidiIn ()
+ : AlsaMidiIO ()
+{
+}
+
+size_t
+AlsaMidiIn::recv_event (pframes_t &time, uint8_t *data, size_t &size)
+{
+ const uint32_t read_space = _rb->read_space();
+ struct MidiEventHeader h(0,0);
+
+ if (read_space <= sizeof(MidiEventHeader)) {
+ return 0;
+ }
+
+ RingBuffer<uint8_t>::rw_vector vector;
+ _rb->get_read_vector(&vector);
+ if (vector.len[0] >= sizeof(MidiEventHeader)) {
+ memcpy((uint8_t*)&h, vector.buf[0], sizeof(MidiEventHeader));
+ } else {
+ if (vector.len[0] > 0) {
+ memcpy ((uint8_t*)&h, vector.buf[0], vector.len[0]);
+ }
+ memcpy (((uint8_t*)&h) + vector.len[0], vector.buf[1], sizeof(MidiEventHeader) - vector.len[0]);
+ }
+
+ if (h.time >= _clock_monotonic + _period_length_us ) {
+#ifdef DEBUG_TIMING
+ printf("AlsaMidiIn DEBUG: POSTPONE EVENT TO NEXT CYCLE: %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
+#endif
+ return 0;
+ }
+ _rb->increment_read_idx (sizeof(MidiEventHeader));
+
+ assert (h.size > 0);
+ if (h.size > size) {
+ _DEBUGPRINT("AlsaMidiIn::recv_event MIDI event too large!\n");
+ _rb->increment_read_idx (h.size);
+ return 0;
+ }
+ if (_rb->read (&data[0], h.size) != h.size) {
+ _DEBUGPRINT("AlsaMidiIn::recv_event Garbled MIDI EVENT DATA!!\n");
+ return 0;
+ }
+ if (h.time < _clock_monotonic) {
+#ifdef DEBUG_TIMING
+ printf("AlsaMidiIn DEBUG: MIDI TIME < 0 %.1f spl\n", ((_clock_monotonic - h.time) / -_sample_length_us));
+#endif
+ time = 0;
+ } else if (h.time >= _clock_monotonic + _period_length_us ) {
+#ifdef DEBUG_TIMING
+ printf("AlsaMidiIn DEBUG: MIDI TIME > PERIOD %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
+#endif
+ time = _samples_per_period - 1;
+ } else {
+ time = floor ((h.time - _clock_monotonic) / _sample_length_us);
+ }
+ assert(time < _samples_per_period);
+ size = h.size;
+ return h.size;
+}
+
+int
+AlsaMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
+ const uint32_t buf_size = sizeof(MidiEventHeader) + size;
+
+ if (size == 0) {
+ return -1;
+ }
+ if (_rb->write_space() < buf_size) {
+ _DEBUGPRINT("AlsaMidiIn: ring buffer overflow\n");
+ return -1;
+ }
+ struct MidiEventHeader h (time, size);
+ _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
+ _rb->write (data, size);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __libbackend_alsa_midi_h__
+#define __libbackend_alsa_midi_h__
+
+#include <stdint.h>
+#include <poll.h>
+#include <pthread.h>
+
+#include "pbd/ringbuffer.h"
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class AlsaMidiIO {
+public:
+ AlsaMidiIO ();
+ virtual ~AlsaMidiIO ();
+
+ int state (void) const { return _state; }
+ int start ();
+ int stop ();
+
+ void setup_timing (const size_t samples_per_period, const float samplerate);
+ void sync_time(uint64_t);
+
+ virtual void* main_process_thread () = 0;
+
+protected:
+ pthread_t _main_thread;
+ pthread_mutex_t _notify_mutex;
+ pthread_cond_t _notify_ready;
+
+ int _state;
+ bool _running;
+
+ int _npfds;
+ struct pollfd *_pfds;
+
+ double _sample_length_us;
+ double _period_length_us;
+ size_t _samples_per_period;
+ uint64_t _clock_monotonic;
+
+ struct MidiEventHeader {
+ uint64_t time;
+ size_t size;
+ MidiEventHeader(const uint64_t t, const size_t s)
+ : time(t)
+ , size(s) {}
+ };
+
+ RingBuffer<uint8_t>* _rb;
+
+protected:
+ virtual void init (const char *device_name, const bool input) = 0;
+
+};
+
+class AlsaMidiOut : virtual public AlsaMidiIO
+{
+public:
+ AlsaMidiOut ();
+
+ int send_event (const pframes_t, const uint8_t *, const size_t);
+};
+
+class AlsaMidiIn : virtual public AlsaMidiIO
+{
+public:
+ AlsaMidiIn ();
+
+ size_t recv_event (pframes_t &, uint8_t *, size_t &);
+
+protected:
+ int queue_event (const uint64_t, const uint8_t *, const size_t);
+};
+
+} // namespace
+
+#endif
*/
#include <unistd.h>
-
#include <glibmm.h>
+#include "select_sleep.h"
#include "alsa_rawmidi.h"
-#include "rt_thread.h"
#include "pbd/error.h"
#include "i18n.h"
#endif
AlsaRawMidiIO::AlsaRawMidiIO (const char *device, const bool input)
- : _state (-1)
- , _running (false)
+ : AlsaMidiIO()
, _device (0)
- , _pfds (0)
- , _sample_length_us (1e6 / 48000.0)
- , _period_length_us (1.024e6 / 48000.0)
- , _samples_per_period (1024)
- , _rb (0)
{
- pthread_mutex_init (&_notify_mutex, 0);
- pthread_cond_init (&_notify_ready, 0);
init (device, input);
}
snd_rawmidi_close (_device);
_device = 0;
}
- delete _rb;
- pthread_mutex_destroy (&_notify_mutex);
- pthread_cond_destroy (&_notify_ready);
- free (_pfds);
}
void
_pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
snd_rawmidi_poll_descriptors (_device, _pfds, _npfds);
- // MIDI (hw port) 31.25 kbaud
- // worst case here is 8192 SPP and 8KSPS for which we'd need
- // 4000 bytes sans MidiEventHeader.
- // since we're not always in sync, let's use 4096.
- _rb = new RingBuffer<uint8_t>(4096 + 4096 * sizeof(MidiEventHeader));
-
#if 0
_state = 0;
#else
return;
}
-static void * pthread_process (void *arg)
-{
- AlsaRawMidiIO *d = static_cast<AlsaRawMidiIO *>(arg);
- d->main_process_thread ();
- pthread_exit (0);
- return 0;
-}
-
-int
-AlsaRawMidiIO::start ()
-{
- if (_realtime_pthread_create (SCHED_FIFO, -21, 100000,
- &_main_thread, pthread_process, this))
- {
- if (pthread_create (&_main_thread, NULL, pthread_process, this)) {
- PBD::error << _("AlsaRawMidiIO: Failed to create process thread.") << endmsg;
- return -1;
- } else {
- PBD::warning << _("AlsaRawMidiIO: Cannot acquire realtime permissions.") << endmsg;
- }
- }
- int timeout = 5000;
- while (!_running && --timeout > 0) { Glib::usleep (1000); }
- if (timeout == 0 || !_running) {
- return -1;
- }
- return 0;
-}
-
-int
-AlsaRawMidiIO::stop ()
-{
- void *status;
- if (!_running) {
- return 0;
- }
-
- _running = false;
-
- pthread_mutex_lock (&_notify_mutex);
- pthread_cond_signal (&_notify_ready);
- pthread_mutex_unlock (&_notify_mutex);
-
- if (pthread_join (_main_thread, &status)) {
- PBD::error << _("AlsaRawMidiIO: Failed to terminate.") << endmsg;
- return -1;
- }
- return 0;
-}
-
-void
-AlsaRawMidiIO::setup_timing (const size_t samples_per_period, const float samplerate)
-{
- _period_length_us = (double) samples_per_period * 1e6 / samplerate;
- _sample_length_us = 1e6 / samplerate;
- _samples_per_period = samples_per_period;
-}
-
-void
-AlsaRawMidiIO::sync_time (const uint64_t tme)
-{
- // TODO consider a PLL, if this turns out to be the bottleneck for jitter
- // also think about using
- // snd_pcm_status_get_tstamp() and snd_rawmidi_status_get_tstamp()
- // instead of monotonic clock.
-#ifdef DEBUG_TIMING
- double tdiff = (_clock_monotonic + _period_length_us - tme) / 1000.0;
- if (abs(tdiff) >= .05) {
- printf("AlsaRawMidiIO MJ: %.1f ms\n", tdiff);
- }
-#endif
- _clock_monotonic = tme;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-// select sleeps _at most_ (compared to usleep() which sleeps at least)
-static void select_sleep (uint32_t usec) {
- if (usec <= 10) return;
- fd_set fd;
- int max_fd=0;
- struct timeval tv;
- tv.tv_sec = usec / 1000000;
- tv.tv_usec = usec % 1000000;
- FD_ZERO (&fd);
- select (max_fd, &fd, NULL, NULL, &tv);
-}
-
///////////////////////////////////////////////////////////////////////////////
AlsaRawMidiOut::AlsaRawMidiOut (const char *device)
: AlsaRawMidiIO (device, false)
+ , AlsaMidiOut ()
{
}
-
-int
-AlsaRawMidiOut::send_event (const pframes_t time, const uint8_t *data, const size_t size)
-{
- const uint32_t buf_size = sizeof (MidiEventHeader) + size;
- if (_rb->write_space() < buf_size) {
- _DEBUGPRINT("AlsaRawMidiOut: ring buffer overflow\n");
- return -1;
- }
- struct MidiEventHeader h (_clock_monotonic + time * _sample_length_us, size);
- _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
- _rb->write (data, size);
-
- if (pthread_mutex_trylock (&_notify_mutex) == 0) {
- pthread_cond_signal (&_notify_ready);
- pthread_mutex_unlock (&_notify_mutex);
- }
- return 0;
-}
-
void *
AlsaRawMidiOut::main_process_thread ()
{
AlsaRawMidiIn::AlsaRawMidiIn (const char *device)
: AlsaRawMidiIO (device, true)
+ , AlsaMidiIn ()
, _event(0,0)
, _first_time(true)
, _unbuffered_bytes(0)
{
}
-size_t
-AlsaRawMidiIn::recv_event (pframes_t &time, uint8_t *data, size_t &size)
-{
- const uint32_t read_space = _rb->read_space();
- struct MidiEventHeader h(0,0);
-
- if (read_space <= sizeof(MidiEventHeader)) {
- return 0;
- }
-
-#if 1
- // check if event is in current cycle
- RingBuffer<uint8_t>::rw_vector vector;
- _rb->get_read_vector(&vector);
- if (vector.len[0] >= sizeof(MidiEventHeader)) {
- memcpy((uint8_t*)&h, vector.buf[0], sizeof(MidiEventHeader));
- } else {
- if (vector.len[0] > 0) {
- memcpy ((uint8_t*)&h, vector.buf[0], vector.len[0]);
- }
- memcpy (((uint8_t*)&h) + vector.len[0], vector.buf[1], sizeof(MidiEventHeader) - vector.len[0]);
- }
-
- if (h.time >= _clock_monotonic + _period_length_us ) {
-#ifdef DEBUG_TIMING
- printf("AlsaRawMidiIn DEBUG: POSTPONE EVENT TO NEXT CYCLE: %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
-#endif
- return 0;
- }
- _rb->increment_read_idx (sizeof(MidiEventHeader));
-#else
- if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
- _DEBUGPRINT("AlsaRawMidiIn::recv_event Garbled MIDI EVENT HEADER!!\n");
- return 0;
- }
-#endif
- assert (h.size > 0);
- if (h.size > size) {
- _DEBUGPRINT("AlsaRawMidiIn::recv_event MIDI event too large!\n");
- _rb->increment_read_idx (h.size);
- return 0;
- }
- if (_rb->read (&data[0], h.size) != h.size) {
- _DEBUGPRINT("AlsaRawMidiIn::recv_event Garbled MIDI EVENT DATA!!\n");
- return 0;
- }
- if (h.time < _clock_monotonic) {
-#ifdef DEBUG_TIMING
- printf("AlsaRawMidiIn DEBUG: MIDI TIME < 0 %.1f spl\n", ((_clock_monotonic - h.time) / -_sample_length_us));
-#endif
- time = 0;
- } else if (h.time >= _clock_monotonic + _period_length_us ) {
-#ifdef DEBUG_TIMING
- printf("AlsaRawMidiIn DEBUG: MIDI TIME > PERIOD %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
-#endif
- time = _samples_per_period - 1;
- } else {
- time = floor ((h.time - _clock_monotonic) / _sample_length_us);
- }
- assert(time < _samples_per_period);
- size = h.size;
- return h.size;
-}
-
void *
AlsaRawMidiIn::main_process_thread ()
{
int
AlsaRawMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
- const uint32_t buf_size = sizeof(MidiEventHeader) + size;
_event._pending = false;
-
- if (size == 0) {
- return -1;
- }
- if (_rb->write_space() < buf_size) {
- _DEBUGPRINT("AlsaRawMidiIn: ring buffer overflow\n");
- return -1;
- }
- struct MidiEventHeader h (time, size);
- _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
- _rb->write (data, size);
- return 0;
+ return AlsaMidiIn::queue_event(time, data, size);
}
void
#include "pbd/ringbuffer.h"
#include "ardour/types.h"
+#include "alsa_midi.h"
namespace ARDOUR {
-class AlsaRawMidiIO {
+class AlsaRawMidiIO : virtual public AlsaMidiIO {
public:
AlsaRawMidiIO (const char *device, const bool input);
virtual ~AlsaRawMidiIO ();
- int state (void) const { return _state; }
- int start ();
- int stop ();
-
- void setup_timing (const size_t samples_per_period, const float samplerate);
- void sync_time(uint64_t);
-
- virtual void* main_process_thread () = 0;
-
protected:
- pthread_t _main_thread;
- pthread_mutex_t _notify_mutex;
- pthread_cond_t _notify_ready;
-
- int _state;
- bool _running;
-
snd_rawmidi_t *_device;
- int _npfds;
- struct pollfd *_pfds;
-
- double _sample_length_us;
- double _period_length_us;
- size_t _samples_per_period;
- uint64_t _clock_monotonic;
-
- struct MidiEventHeader {
- uint64_t time;
- size_t size;
- MidiEventHeader(const uint64_t t, const size_t s)
- : time(t)
- , size(s) {}
- };
-
- RingBuffer<uint8_t>* _rb;
private:
void init (const char *device_name, const bool input);
-
};
-class AlsaRawMidiOut : public AlsaRawMidiIO
+class AlsaRawMidiOut : public AlsaRawMidiIO, public AlsaMidiOut
{
public:
AlsaRawMidiOut (const char *device);
-
void* main_process_thread ();
- int send_event (const pframes_t, const uint8_t *, const size_t);
};
-class AlsaRawMidiIn : public AlsaRawMidiIO
+class AlsaRawMidiIn : public AlsaRawMidiIO, public AlsaMidiIn
{
public:
AlsaRawMidiIn (const char *device);
void* main_process_thread ();
- size_t recv_event (pframes_t &, uint8_t *, size_t &);
-
-private:
+protected:
int queue_event (const uint64_t, const uint8_t *, const size_t);
+private:
void parse_events (const uint64_t, const uint8_t *, const size_t);
bool process_byte (const uint64_t, const uint8_t);
*/
#include <unistd.h>
-
#include <glibmm.h>
+#include "select_sleep.h"
#include "alsa_sequencer.h"
-#include "rt_thread.h"
#include "pbd/error.h"
#include "i18n.h"
using namespace ARDOUR;
+/* max bytes per individual midi-event
+ * events larger than this are ignored */
+#define MaxAlsaSeqEventSize (64)
+
#ifndef NDEBUG
#define _DEBUGPRINT(STR) fprintf(stderr, STR);
#else
#endif
AlsaSeqMidiIO::AlsaSeqMidiIO (const char *device, const bool input)
- : _state (-1)
- , _running (false)
+ : AlsaMidiIO()
, _seq (0)
- , _pfds (0)
- , _sample_length_us (1e6 / 48000.0)
- , _period_length_us (1.024e6 / 48000.0)
- , _samples_per_period (1024)
- , _rb (0)
{
- pthread_mutex_init (&_notify_mutex, 0);
- pthread_cond_init (&_notify_ready, 0);
init (device, input);
}
snd_seq_close (_seq);
_seq = 0;
}
- delete _rb;
- pthread_mutex_destroy (&_notify_mutex);
- pthread_cond_destroy (&_notify_ready);
- free (_pfds);
}
void
if (input) {
if (snd_seq_connect_from (_seq, _port, port.client, port.port) < 0) {
- _DEBUGPRINT("AlsaSeqMidiIO: cannot connect port.\n");
+ _DEBUGPRINT("AlsaSeqMidiIO: cannot connect input port.\n");
goto initerr;
}
} else {
if (snd_seq_connect_to (_seq, _port, port.client, port.port) < 0) {
- _DEBUGPRINT("AlsaSeqMidiIO: cannot connect port.\n");
+ _DEBUGPRINT("AlsaSeqMidiIO: cannot connect output port.\n");
goto initerr;
}
}
snd_seq_nonblock(_seq, 1);
- // MIDI (hw port) 31.25 kbaud
- // worst case here is 8192 SPP and 8KSPS for which we'd need
- // 4000 bytes sans MidiEventHeader.
- // since we're not always in sync, let's use 4096.
- _rb = new RingBuffer<uint8_t>(4096 + 4096 * sizeof(MidiEventHeader));
-
_state = 0;
return;
return;
}
-static void * pthread_process (void *arg)
-{
- AlsaSeqMidiIO *d = static_cast<AlsaSeqMidiIO *>(arg);
- d->main_process_thread ();
- pthread_exit (0);
- return 0;
-}
-
-int
-AlsaSeqMidiIO::start ()
-{
- if (_realtime_pthread_create (SCHED_FIFO, -21, 100000,
- &_main_thread, pthread_process, this))
- {
- if (pthread_create (&_main_thread, NULL, pthread_process, this)) {
- PBD::error << _("AlsaSeqMidiIO: Failed to create process thread.") << endmsg;
- return -1;
- } else {
- PBD::warning << _("AlsaSeqMidiIO: Cannot acquire realtime permissions.") << endmsg;
- }
- }
- int timeout = 5000;
- while (!_running && --timeout > 0) { Glib::usleep (1000); }
- if (timeout == 0 || !_running) {
- return -1;
- }
- return 0;
-}
-
-int
-AlsaSeqMidiIO::stop ()
-{
- void *status;
- if (!_running) {
- return 0;
- }
-
- _running = false;
-
- pthread_mutex_lock (&_notify_mutex);
- pthread_cond_signal (&_notify_ready);
- pthread_mutex_unlock (&_notify_mutex);
-
- if (pthread_join (_main_thread, &status)) {
- PBD::error << _("AlsaSeqMidiIO: Failed to terminate.") << endmsg;
- return -1;
- }
- return 0;
-}
-
-void
-AlsaSeqMidiIO::setup_timing (const size_t samples_per_period, const float samplerate)
-{
- _period_length_us = (double) samples_per_period * 1e6 / samplerate;
- _sample_length_us = 1e6 / samplerate;
- _samples_per_period = samples_per_period;
-}
-
-void
-AlsaSeqMidiIO::sync_time (const uint64_t tme)
-{
- // TODO consider a PLL, if this turns out to be the bottleneck for jitter
- // also think about using
- // snd_pcm_status_get_tstamp() and snd_rawmidi_status_get_tstamp()
- // instead of monotonic clock.
-#ifdef DEBUG_TIMING
- double tdiff = (_clock_monotonic + _period_length_us - tme) / 1000.0;
- if (abs(tdiff) >= .05) {
- printf("AlsaSeqMidiIO MJ: %.1f ms\n", tdiff);
- }
-#endif
- _clock_monotonic = tme;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-// select sleeps _at most_ (compared to usleep() which sleeps at least)
-static void select_sleep (uint32_t usec) {
- if (usec <= 10) return;
- fd_set fd;
- int max_fd=0;
- struct timeval tv;
- tv.tv_sec = usec / 1000000;
- tv.tv_usec = usec % 1000000;
- FD_ZERO (&fd);
- select (max_fd, &fd, NULL, NULL, &tv);
-}
-
///////////////////////////////////////////////////////////////////////////////
AlsaSeqMidiOut::AlsaSeqMidiOut (const char *device)
: AlsaSeqMidiIO (device, false)
+ , AlsaMidiOut ()
{
}
-
-int
-AlsaSeqMidiOut::send_event (const pframes_t time, const uint8_t *data, const size_t size)
-{
- const uint32_t buf_size = sizeof (MidiEventHeader) + size;
- if (_rb->write_space() < buf_size) {
- _DEBUGPRINT("AlsaSeqMidiOut: ring buffer overflow\n");
- return -1;
- }
- struct MidiEventHeader h (_clock_monotonic + time * _sample_length_us, size);
- _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
- _rb->write (data, size);
-
- if (pthread_mutex_trylock (&_notify_mutex) == 0) {
- pthread_cond_signal (&_notify_ready);
- pthread_mutex_unlock (&_notify_mutex);
- }
- return 0;
-}
-
-#define MaxAlsaSeqEventSize 64
-
void *
AlsaSeqMidiOut::main_process_thread ()
{
AlsaSeqMidiIn::AlsaSeqMidiIn (const char *device)
: AlsaSeqMidiIO (device, true)
+ , AlsaMidiIn ()
{
}
-size_t
-AlsaSeqMidiIn::recv_event (pframes_t &time, uint8_t *data, size_t &size)
-{
- const uint32_t read_space = _rb->read_space();
- struct MidiEventHeader h(0,0);
-
- if (read_space <= sizeof(MidiEventHeader)) {
- return 0;
- }
-
-#if 1
- // check if event is in current cycle
- RingBuffer<uint8_t>::rw_vector vector;
- _rb->get_read_vector(&vector);
- if (vector.len[0] >= sizeof(MidiEventHeader)) {
- memcpy((uint8_t*)&h, vector.buf[0], sizeof(MidiEventHeader));
- } else {
- if (vector.len[0] > 0) {
- memcpy ((uint8_t*)&h, vector.buf[0], vector.len[0]);
- }
- memcpy (((uint8_t*)&h) + vector.len[0], vector.buf[1], sizeof(MidiEventHeader) - vector.len[0]);
- }
-
- if (h.time >= _clock_monotonic + _period_length_us ) {
-#ifdef DEBUG_TIMING
- printf("AlsaSeqMidiIn DEBUG: POSTPONE EVENT TO NEXT CYCLE: %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
-#endif
- return 0;
- }
- _rb->increment_read_idx (sizeof(MidiEventHeader));
-#else
- if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
- _DEBUGPRINT("AlsaSeqMidiIn::recv_event Garbled MIDI EVENT HEADER!!\n");
- return 0;
- }
-#endif
- assert (h.size > 0);
- if (h.size > size) {
- _DEBUGPRINT("AlsaSeqMidiIn::recv_event MIDI event too large!\n");
- _rb->increment_read_idx (h.size);
- return 0;
- }
- if (_rb->read (&data[0], h.size) != h.size) {
- _DEBUGPRINT("AlsaSeqMidiIn::recv_event Garbled MIDI EVENT DATA!!\n");
- return 0;
- }
- if (h.time < _clock_monotonic) {
-#ifdef DEBUG_TIMING
- printf("AlsaSeqMidiIn DEBUG: MIDI TIME < 0 %.1f spl\n", ((_clock_monotonic - h.time) / -_sample_length_us));
-#endif
- time = 0;
- } else if (h.time >= _clock_monotonic + _period_length_us ) {
-#ifdef DEBUG_TIMING
- printf("AlsaSeqMidiIn DEBUG: MIDI TIME > PERIOD %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
-#endif
- time = _samples_per_period - 1;
- } else {
- time = floor ((h.time - _clock_monotonic) / _sample_length_us);
- }
- assert(time < _samples_per_period);
- size = h.size;
- return h.size;
-}
-
-int
-AlsaSeqMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
- const uint32_t buf_size = sizeof(MidiEventHeader) + size;
-
- if (size == 0) {
- return -1;
- }
- if (_rb->write_space() < buf_size) {
- _DEBUGPRINT("AlsaSeqMidiIn: ring buffer overflow\n");
- return -1;
- }
- struct MidiEventHeader h (time, size);
- _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
- _rb->write (data, size);
- return 0;
-}
-
void *
AlsaSeqMidiIn::main_process_thread ()
{
#include "pbd/ringbuffer.h"
#include "ardour/types.h"
+#include "alsa_midi.h"
namespace ARDOUR {
-class AlsaSeqMidiIO {
+class AlsaSeqMidiIO : virtual public AlsaMidiIO {
public:
AlsaSeqMidiIO (const char *port_name, const bool input);
virtual ~AlsaSeqMidiIO ();
- int state (void) const { return _state; }
- int start ();
- int stop ();
-
- void setup_timing (const size_t samples_per_period, const float samplerate);
- void sync_time(uint64_t);
-
- virtual void* main_process_thread () = 0;
-
protected:
- pthread_t _main_thread;
- pthread_mutex_t _notify_mutex;
- pthread_cond_t _notify_ready;
-
- int _state;
- bool _running;
-
snd_seq_t *_seq;
- //snd_seq_addr_t _port;
int _port;
- int _npfds;
- struct pollfd *_pfds;
-
- double _sample_length_us;
- double _period_length_us;
- size_t _samples_per_period;
- uint64_t _clock_monotonic;
-
- struct MidiEventHeader {
- uint64_t time;
- size_t size;
- MidiEventHeader(const uint64_t t, const size_t s)
- : time(t)
- , size(s) {}
- };
-
- RingBuffer<uint8_t>* _rb;
-
private:
void init (const char *device_name, const bool input);
};
-class AlsaSeqMidiOut : public AlsaSeqMidiIO
+class AlsaSeqMidiOut : public AlsaSeqMidiIO, public AlsaMidiOut
{
public:
AlsaSeqMidiOut (const char *port_name);
-
void* main_process_thread ();
- int send_event (const pframes_t, const uint8_t *, const size_t);
};
-class AlsaSeqMidiIn : public AlsaSeqMidiIO
+class AlsaSeqMidiIn : public AlsaSeqMidiIO, public AlsaMidiIn
{
public:
AlsaSeqMidiIn (const char *port_name);
void* main_process_thread ();
- size_t recv_event (pframes_t &, uint8_t *, size_t &);
-
-private:
- int queue_event (const uint64_t, const uint8_t *, const size_t);
};
} // namespace
--- /dev/null
+/*
+ * Copyright (C) 2004,2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <sys/select.h>
+
+/* select() sleeps _at most_ a given time.
+ * (compared to usleep() or nanosleep() which sleep at least a given time)
+ */
+static void select_sleep (uint64_t usec) {
+ if (usec <= 10) return;
+ fd_set fd;
+ int max_fd=0;
+ struct timeval tv;
+ tv.tv_sec = usec / 1000000;
+ tv.tv_usec = usec % 1000000;
+ FD_ZERO (&fd);
+ select (max_fd, &fd, NULL, NULL, &tv);
+ // on Linux, tv reflects the actual time slept.
+}
obj = bld(features = 'cxx cxxshlib')
obj.source = [
'alsa_audiobackend.cc',
+ 'alsa_midi.cc',
'alsa_rawmidi.cc',
'alsa_sequencer.cc',
'zita-alsa-pcmi.cc',