*/
#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;
#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
_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)
+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);
}
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();
}
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;
}
h.size -= err;
goto retry;
}
- snd_rawmidi_drain (_device);
+ need_drain = true;
}
pthread_mutex_unlock (&_notify_mutex);
///////////////////////////////////////////////////////////////////////////////
-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)
{
}
-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
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;
}
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;
}
return false;
}
if (! _total_bytes) {
- // Apply running status.
+ _DEBUGPRINT("AlsaRawMidiIn: apply running status\n");
record_byte(_status_byte);
}
record_byte(byte);