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
This commit is contained in:
Paul Davis 2011-05-29 17:35:21 +00:00
parent 36ccf83049
commit 0a9f5423f5
3 changed files with 55 additions and 22 deletions

View 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.

View 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.
*/
bool success = read_prefix(&ev_time, &ev_type, &ev_size);
if (!success) {
cerr << "WARNING: error reading event prefix from MIDI ring" << endl;
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) {
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);

View 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()) {