X-Git-Url: https://main.carlh.net/gitweb/?p=ardour.git;a=blobdiff_plain;f=libs%2Fardour%2Fasync_midi_port.cc;h=bbcc0aab85e7e086d9a081e6d062e15df18bda2a;hp=5bc8e2283357c225a256d1ccb5f6043fa6eb5429;hb=c8c6bca6587450ff64303dbc994a4cd28d6ce7aa;hpb=4729bbde5f5f6833b878155da5a395d5ecd9e565 diff --git a/libs/ardour/async_midi_port.cc b/libs/ardour/async_midi_port.cc index 5bc8e22833..bbcc0aab85 100644 --- a/libs/ardour/async_midi_port.cc +++ b/libs/ardour/async_midi_port.cc @@ -1,6 +1,6 @@ /* Copyright (C) 1998 Paul Barton-Davis - + 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 @@ -19,6 +19,9 @@ */ #include +#include + +#include #include "pbd/error.h" #include "pbd/stacktrace.h" @@ -34,10 +37,6 @@ using namespace ARDOUR; using namespace std; using namespace PBD; -namespace Evoral { - template class EventRingBuffer; -} - pthread_t AsyncMIDIPort::_process_thread; #define port_engine AudioEngine::instance()->port_engine() @@ -47,9 +46,10 @@ AsyncMIDIPort::AsyncMIDIPort (string const & name, PortFlags flags) , MIDI::Port (name, MIDI::Port::Flags (0)) , _currently_in_cycle (false) , _last_write_timestamp (0) - , output_fifo (512) + , have_timer (false) + , output_fifo (2048) , input_fifo (1024) - , xthread (true) + , _xthread (true) { } @@ -58,38 +58,57 @@ AsyncMIDIPort::~AsyncMIDIPort () } void -AsyncMIDIPort::flush_output_fifo (pframes_t nframes) +AsyncMIDIPort::set_timer (boost::function& f) +{ + timer = f; + have_timer = true; +} + +void +AsyncMIDIPort::flush_output_fifo (MIDI::pframes_t nframes) { RingBuffer< Evoral::Event >::rw_vector vec = { { 0, 0 }, { 0, 0 } }; - size_t written; + size_t written = 0; output_fifo.get_read_vector (&vec); MidiBuffer& mb (get_midi_buffer (nframes)); - + if (vec.len[0]) { Evoral::Event* evp = vec.buf[0]; - + + assert (evp->size()); + assert (evp->buffer()); + for (size_t n = 0; n < vec.len[0]; ++n, ++evp) { - mb.push_back (evp->time(), evp->size(), evp->buffer()); + if (mb.push_back (evp->time(), evp->size(), evp->buffer())) { + written++; + } } } - + if (vec.len[1]) { Evoral::Event* evp = vec.buf[1]; + assert (evp->size()); + assert (evp->buffer()); + for (size_t n = 0; n < vec.len[1]; ++n, ++evp) { - mb.push_back (evp->time(), evp->size(), evp->buffer()); + if (mb.push_back (evp->time(), evp->size(), evp->buffer())) { + written++; + } } } - - if ((written = vec.len[0] + vec.len[1]) != 0) { - output_fifo.increment_read_idx (written); - } + + /* do this "atomically" after we're done pushing events into the + * MidiBuffer + */ + + output_fifo.increment_read_idx (written); } void -AsyncMIDIPort::cycle_start (pframes_t nframes) +AsyncMIDIPort::cycle_start (MIDI::pframes_t nframes) { _currently_in_cycle = true; MidiPort::cycle_start (nframes); @@ -100,29 +119,37 @@ AsyncMIDIPort::cycle_start (pframes_t nframes) if (ARDOUR::Port::sends_output()) { flush_output_fifo (nframes); - } - + } + /* copy incoming data from the port buffer into the input FIFO and if necessary wakeup the reader */ if (ARDOUR::Port::receives_input()) { MidiBuffer& mb (get_midi_buffer (nframes)); - pframes_t when = AudioEngine::instance()->sample_time_at_cycle_start(); + framecnt_t when; + + if (have_timer) { + when = timer (); + } else { + when = AudioEngine::instance()->sample_time_at_cycle_start(); + } for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) { + if (!have_timer) { + when += (*b).time(); + } input_fifo.write (when, (Evoral::EventType) 0, (*b).size(), (*b).buffer()); } - + if (!mb.empty()) { - xthread.wakeup (); + _xthread.wakeup (); } } - } void -AsyncMIDIPort::cycle_end (pframes_t nframes) +AsyncMIDIPort::cycle_end (MIDI::pframes_t nframes) { if (ARDOUR::Port::sends_output()) { /* move any additional data from output FIFO into the port @@ -141,7 +168,7 @@ AsyncMIDIPort::cycle_end (pframes_t nframes) * Cannot be called from a processing thread. */ void -AsyncMIDIPort::drain (int check_interval_usecs) +AsyncMIDIPort::drain (int check_interval_usecs, int total_usecs_to_wait) { RingBuffer< Evoral::Event >::rw_vector vec = { { 0, 0 }, { 0, 0} }; @@ -156,17 +183,21 @@ AsyncMIDIPort::drain (int check_interval_usecs) return; } - while (1) { + microseconds_t now = get_microseconds (); + microseconds_t end = now + total_usecs_to_wait; + + while (now < end) { output_fifo.get_write_vector (&vec); if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) { break; } - usleep (check_interval_usecs); + Glib::usleep (check_interval_usecs); + now = get_microseconds(); } } int -AsyncMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp) +AsyncMIDIPort::write (const MIDI::byte * msg, size_t msglen, MIDI::timestamp_t timestamp) { int ret = 0; @@ -179,7 +210,7 @@ AsyncMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp) /* this is the best estimate of "when" this MIDI data is being * delivered */ - + _parser->set_timestamp (AudioEngine::instance()->sample_time() + timestamp); for (size_t n = 0; n < msglen; ++n) { _parser->scanner (msg[n]); @@ -187,7 +218,7 @@ AsyncMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp) Glib::Threads::Mutex::Lock lm (output_fifo_lock); RingBuffer< Evoral::Event >::rw_vector vec = { { 0, 0 }, { 0, 0} }; - + output_fifo.get_write_vector (&vec); if (vec.len[0] + vec.len[1] < 1) { @@ -196,19 +227,28 @@ AsyncMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp) } if (vec.len[0]) { - if (!vec.buf[0]->owns_buffer()) { + /* force each event inside the ringbuffer to own its + own buffer, but let that be null and of zero size + initially. When ::set() is called, the buffer will + be allocated to hold a *copy* of the data we're + storing, and then that buffer will be used over and + over, occasionally being upwardly resized as + necessary. + */ + if (!vec.buf[0]->owns_buffer()) { vec.buf[0]->set_buffer (0, 0, true); } vec.buf[0]->set (msg, msglen, timestamp); } else { - if (!vec.buf[1]->owns_buffer()) { + /* see comment in previous branch of if() statement */ + if (!vec.buf[1]->owns_buffer()) { vec.buf[1]->set_buffer (0, 0, true); } vec.buf[1]->set (msg, msglen, timestamp); } output_fifo.increment_write_idx (1); - + ret = msglen; } else { @@ -219,7 +259,7 @@ AsyncMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp) } if (timestamp >= _cycle_nframes) { - std::cerr << "attempting to write MIDI event of " << msglen << " bytes at time " + std::cerr << "attempting to write MIDI event of " << msglen << " MIDI::bytes at time " << timestamp << " of " << _cycle_nframes << " (this will not work - needs a code fix)" << std::endl; @@ -234,11 +274,11 @@ AsyncMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp) if (_currently_in_cycle) { MidiBuffer& mb (get_midi_buffer (_cycle_nframes)); - + if (timestamp == 0) { timestamp = _last_write_timestamp; - } - + } + if (mb.push_back (timestamp, msglen, msg)) { ret = msglen; _last_write_timestamp = timestamp; @@ -264,13 +304,13 @@ AsyncMIDIPort::read (MIDI::byte *, size_t) if (!ARDOUR::Port::receives_input()) { return 0; } - + timestamp_t time; Evoral::EventType type; uint32_t size; - byte buffer[input_fifo.capacity()]; + vector buffer(input_fifo.capacity()); - while (input_fifo.read (&time, &type, &size, buffer)) { + while (input_fifo.read (&time, &type, &size, &buffer[0])) { _parser->set_timestamp (time); for (uint32_t i = 0; i < size; ++i) { _parser->scanner (buffer[i]); @@ -281,7 +321,7 @@ AsyncMIDIPort::read (MIDI::byte *, size_t) } void -AsyncMIDIPort::parse (framecnt_t) +AsyncMIDIPort::parse (MIDI::framecnt_t) { MIDI::byte buf[1];