From 0a9f5423f5b3faede68fe7c384c5fe32db2e23bf Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sun, 29 May 2011 17:35:21 +0000 Subject: [PATCH 1/1] detect buffer "overflow" when delivering immediate events and queue remainder for delivery next time (though without actually requeing - just leave ringbuffer ptrs/indexes where they are. required some deep but minor changes in how MidiRingBuffer::read() works, so that we can detect if we're going to be able to deliver an event before we actually read any of its data. Peek FTW! git-svn-id: svn://localhost/ardour2/branches/3.0@9629 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/midi_ring_buffer.h | 2 +- libs/ardour/midi_ring_buffer.cc | 58 ++++++++++++++++++--------- libs/ardour/midi_track.cc | 17 +++++++- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index 309f03280d..567f375bc3 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -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. diff --git a/libs/ardour/midi_ring_buffer.cc b/libs/ardour/midi_ring_buffer.cc index 850313cc12..824d3bad84 100644 --- a/libs/ardour/midi_ring_buffer.cc +++ b/libs/ardour/midi_ring_buffer.cc @@ -34,7 +34,7 @@ using namespace PBD; */ template size_t -MidiRingBuffer::read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset) +MidiRingBuffer::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::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::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::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); diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 47c992466c..07b6d2ac6d 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -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()) { -- 2.30.2