detect buffer "overflow" when delivering immediate events and queue remainder for...
authorPaul Davis <paul@linuxaudiosystems.com>
Sun, 29 May 2011 17:35:21 +0000 (17:35 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Sun, 29 May 2011 17:35:21 +0000 (17:35 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@9629 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/midi_ring_buffer.h
libs/ardour/midi_ring_buffer.cc
libs/ardour/midi_track.cc

index 309f03280de3cef99fe653da430390f56a45730e..567f375bc3f9a6f68b0ddec5580b0edbd29cd2a4 100644 (file)
@@ -50,7 +50,7 @@ public:
        inline bool read_prefix(T* time, Evoral::EventType* type, uint32_t* size);
        inline bool read_contents(uint32_t size, uint8_t* buf);
 
-       size_t read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset=0);
+       size_t read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset=0, bool stop_on_overflow_in_destination=false);
        void dump(std::ostream& dst);
 
        /** Set the channel filtering mode.
index 850313cc12c7c83177d85c329bc96acc2790967f..824d3bad8436f829a567d0ae738c01256bea783f 100644 (file)
@@ -34,7 +34,7 @@ using namespace PBD;
  */
 template<typename T>
 size_t
-MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset)
+MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset, bool stop_on_overflow_in_dst)
 {
        if (this->read_space() == 0) {
                return 0;
@@ -74,9 +74,22 @@ MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, frame
 
        size_t count = 0;
 
-       while (this->read_space() >= sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t)) {
+       const size_t prefix_size = sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t);
 
-               this->peek ((uint8_t*) &ev_time, sizeof (T));
+       while (this->read_space() >= prefix_size) {
+
+               uint8_t peekbuf[prefix_size];
+               bool success;
+
+               success = this->peek (peekbuf, prefix_size);
+               /* this cannot fail, because we've already verified that there
+                  is prefix_space to read
+               */
+               assert (success);
+
+               ev_time = *((T*) peekbuf);
+               ev_type = *((Evoral::EventType*)(peekbuf + sizeof (T)));
+               ev_size = *((uint32_t*)(peekbuf + sizeof(T) + sizeof (Evoral::EventType)));
 
                if (ev_time + loop_offset >= end) {
                        DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 past end @ %2\n", ev_time, end));
@@ -87,13 +100,33 @@ MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, frame
                } else {
                        DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 in range %2 .. %3\n", ev_time, start, end));
                }
+               
+               /* lets see if we are going to be able to write this event into dst.
+                */
+
+               assert(ev_time >= start);
+               
+               ev_time -= start;
+               ev_time += offset;
 
-               bool success = read_prefix(&ev_time, &ev_type, &ev_size);
-               if (!success) {
-                       cerr << "WARNING: error reading event prefix from MIDI ring" << endl;
+               // write the timestamp to address (write_loc - 1)
+               uint8_t* write_loc = dst.reserve(ev_time, ev_size);
+               if (write_loc == NULL) {
+                       if (stop_on_overflow_in_dst) {
+                               DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MidiRingBuffer: overflow in destination MIDI buffer, stopped after %1 events\n", count));
+                               break;
+                       }
+                       cerr << "MRB: Unable to reserve space in buffer, event skipped";
+                       this->increment_read_ptr (prefix_size + ev_size); // Advance read pointer to next event
                        continue;
                }
 
+               /* we're good to go ahead and read the data now but since we
+                * have the prefix data already, just skip over that
+                */
+
+               this->increment_read_ptr (prefix_size);
+
                // This event marks a loop end (i.e. the next event's timestamp will be non-monotonic)
                if (ev_type == LoopEventType) {
                        assert (ev_size == sizeof (framepos_t));
@@ -120,19 +153,6 @@ MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, frame
                        }
                }
 
-               assert(ev_time >= start);
-               
-               ev_time -= start;
-               ev_time += offset;
-
-               // write the timestamp to address (write_loc - 1)
-               uint8_t* write_loc = dst.reserve(ev_time, ev_size);
-               if (write_loc == NULL) {
-                       cerr << "MRB: Unable to reserve space in buffer, event skipped";
-                       this->increment_read_ptr (ev_size); // Advance read pointer to next event
-                       continue;
-               }
-
                // write MIDI buffer contents
                success = read_contents (ev_size, write_loc);
 
index 47c992466c627327733aafdab7155dbd6c287fd6..07b6d2ac6d127cb2c178bbc36700d526b3b05db6 100644 (file)
@@ -441,13 +441,26 @@ MidiTrack::push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes)
 void
 MidiTrack::write_out_of_band_data (BufferSet& bufs, framepos_t /*start*/, framepos_t /*end*/, framecnt_t nframes)
 {
-       // Append immediate events
        MidiBuffer& buf (bufs.get_midi (0));
+
+       // Append immediate events
+
        if (_immediate_events.read_space()) {
+
                DEBUG_TRACE (DEBUG::MidiIO, string_compose ("%1 has %2 of immediate events to deliver\n", 
                                                            name(), _immediate_events.read_space()));
+
+               /* write as many of the immediate events as we can, but give "true" as
+                * the last argument ("stop on overflow in destination") so that we'll
+                * ship the rest out next time.
+                *
+                * the (nframes-1) argument puts all these events at the last
+                * possible position of the output buffer, so that we do not 
+                * violate monotonicity when writing.
+                */
+               
+               _immediate_events.read (buf, 0, 1, nframes-1, true);
        }
-       _immediate_events.read (buf, 0, 1, nframes-1); // all stamps = 0
 
        // MIDI thru: send incoming data "through" output
        if (_midi_thru && _session.transport_speed() != 0.0f && _input->n_ports().n_midi()) {