enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / backends / alsa / alsa_rawmidi.cc
index 00fc39a4cf5f8fa14c12d268f8b99badcb1bf887..0702c63158c8bb94f1ae32f6076058c6fabfb831 100644 (file)
  */
 
 #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"
+#include "pbd/i18n.h"
 
 using namespace ARDOUR;
 
@@ -39,31 +38,21 @@ using namespace ARDOUR;
 #define _DEBUGPRINT(STR) ;
 #endif
 
-AlsaRawMidiIO::AlsaRawMidiIO (const char *device, const bool input)
-       : _state (-1)
-       , _running (false)
+AlsaRawMidiIO::AlsaRawMidiIO (const std::string &name, const char *device, const bool input)
+       : 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);
+       _name = name;
        init (device, input);
 }
 
 AlsaRawMidiIO::~AlsaRawMidiIO ()
 {
        if (_device) {
+               snd_rawmidi_drain (_device);
                snd_rawmidi_close (_device);
                _device = 0;
        }
-       delete _rb;
-       pthread_mutex_destroy (&_notify_mutex);
-       pthread_cond_destroy (&_notify_ready);
-       free (_pfds);
 }
 
 void
@@ -86,12 +75,6 @@ AlsaRawMidiIO::init (const char *device_name, const bool input)
        _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
@@ -123,126 +106,20 @@ initerr:
        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)
+AlsaRawMidiOut::AlsaRawMidiOut (const std::string &name, const char *device)
+               : AlsaRawMidiIO (name, 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 ()
 {
        _running = true;
        pthread_mutex_lock (&_notify_mutex);
+       bool need_drain = false;
        while (_running) {
                bool have_data = false;
                struct MidiEventHeader h(0,0);
@@ -269,13 +146,22 @@ AlsaRawMidiOut::main_process_thread ()
                }
 
                if (!have_data) {
+                       if (need_drain) {
+                               snd_rawmidi_drain (_device);
+                               need_drain = false;
+                       }
                        pthread_cond_wait (&_notify_ready, &_notify_mutex);
                        continue;
                }
 
                uint64_t now = g_get_monotonic_time();
                while (h.time > now + 500) {
-                       select_sleep(h.time - now);
+                       if (need_drain) {
+                               snd_rawmidi_drain (_device);
+                               need_drain = false;
+                       } else {
+                               select_sleep(h.time - now);
+                       }
                        now = g_get_monotonic_time();
                }
 
@@ -309,7 +195,11 @@ retry:
 
                ssize_t err = snd_rawmidi_write (_device, data, h.size);
 
-               if ((err == -EAGAIN) || (err == -EWOULDBLOCK)) {
+               if ((err == -EAGAIN)) {
+                       snd_rawmidi_drain (_device);
+                       goto retry;
+               }
+               if (err == -EWOULDBLOCK) {
                        select_sleep (1000);
                        goto retry;
                }
@@ -323,7 +213,7 @@ retry:
                        h.size -= err;
                        goto retry;
                }
-               snd_rawmidi_drain (_device);
+               need_drain = true;
        }
 
        pthread_mutex_unlock (&_notify_mutex);
@@ -334,8 +224,9 @@ retry:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-AlsaRawMidiIn::AlsaRawMidiIn (const char *device)
-               : AlsaRawMidiIO (device, true)
+AlsaRawMidiIn::AlsaRawMidiIn (const std::string &name, const char *device)
+               : AlsaRawMidiIO (name, device, true)
+               , AlsaMidiIn ()
                , _event(0,0)
                , _first_time(true)
                , _unbuffered_bytes(0)
@@ -345,70 +236,6 @@ AlsaRawMidiIn::AlsaRawMidiIn (const char *device)
 {
 }
 
-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 ()
 {
@@ -470,24 +297,14 @@ 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
 AlsaRawMidiIn::parse_events (const uint64_t time, const uint8_t *data, const size_t size) {
        if (_event._pending) {
+               _DEBUGPRINT("AlsaRawMidiIn: queue pending event\n");
                if (queue_event (_event._time, _parser_buffer, _event._size)) {
                        return;
                }
@@ -533,6 +350,13 @@ AlsaRawMidiIn::process_byte(const uint64_t time, const uint8_t byte)
        if (byte >= 0x80) {
                // Non-realtime status byte
                if (_total_bytes) {
+                       _DEBUGPRINT("AlsaRawMidiIn: discarded bogus midi message\n");
+#if 0
+                       for (size_t i=0; i < _total_bytes; ++i) {
+                               printf("%02x ", _parser_buffer[i]);
+                       }
+                       printf("\n");
+#endif
                        _total_bytes = 0;
                        _unbuffered_bytes = 0;
                }
@@ -591,7 +415,7 @@ AlsaRawMidiIn::process_byte(const uint64_t time, const uint8_t byte)
                return false;
        }
        if (! _total_bytes) {
-               // Apply running status.
+               _DEBUGPRINT("AlsaRawMidiIn: apply running status\n");
                record_byte(_status_byte);
        }
        record_byte(byte);