fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / async_midi_port.cc
index 22b5438fe084cf11de01543c9181b133166a5e72..bbcc0aab85e7e086d9a081e6d062e15df18bda2a 100644 (file)
@@ -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 <iostream>
+#include <vector>
+
+#include <glibmm/timer.h>
 
 #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<timestamp_t>;
-}
-
 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<MIDI::framecnt_t (void)>& f)
+{
+       timer = f;
+       have_timer = true;
+}
+
+void
+AsyncMIDIPort::flush_output_fifo (MIDI::pframes_t nframes)
 {
        RingBuffer< Evoral::Event<double> >::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<double>* 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<double>* 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,26 +168,36 @@ 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<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
 
+       if (!AudioEngine::instance()->running() || AudioEngine::instance()->session() == 0) {
+               /* no more process calls - it will never drain */
+               return;
+       }
+
+
        if (is_process_thread()) {
                error << "Process thread called MIDI::AsyncMIDIPort::drain() - this cannot work" << endmsg;
                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;
 
@@ -173,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]);
@@ -181,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<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
-               
+
                output_fifo.get_write_vector (&vec);
 
                if (vec.len[0] + vec.len[1] < 1) {
@@ -190,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 {
@@ -213,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;
@@ -228,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;
@@ -258,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<MIDI::byte> 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]);
@@ -275,7 +321,7 @@ AsyncMIDIPort::read (MIDI::byte *, size_t)
 }
 
 void
-AsyncMIDIPort::parse (framecnt_t)
+AsyncMIDIPort::parse (MIDI::framecnt_t)
 {
        MIDI::byte buf[1];