summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-12-30 14:45:11 -0500
committerDavid Robillard <d@drobilla.net>2014-12-30 14:45:11 -0500
commit97d344f740925de178655b850f68d44041cce046 (patch)
tree4754d7889e4d566083d5fcd800963e36972c02ab /libs
parentc35e94a3c83028220f6eb494fdfe9c1960aaf751 (diff)
Fix MIDI CC record/playback crash.
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/midi_source.cc1
-rw-r--r--libs/evoral/evoral/Sequence.hpp5
-rw-r--r--libs/evoral/src/Sequence.cpp249
3 files changed, 118 insertions, 137 deletions
diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc
index 1b1cf20c68..0c08adf4a5 100644
--- a/libs/ardour/midi_source.cc
+++ b/libs/ardour/midi_source.cc
@@ -313,6 +313,7 @@ MidiSource::mark_midi_streaming_write_completed (const Lock&
}
}
+ invalidate(lock);
_writing = false;
}
diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp
index 9aded0d9f0..4e6420fbb1 100644
--- a/libs/evoral/evoral/Sequence.hpp
+++ b/libs/evoral/evoral/Sequence.hpp
@@ -217,7 +217,7 @@ private:
public:
/** Read iterator */
- class LIBEVORAL_API /* Added by JE - */ const_iterator {
+ class LIBEVORAL_API const_iterator {
public:
const_iterator();
const_iterator(const Sequence<Time>& seq, Time t, bool, std::set<Evoral::Parameter> const &);
@@ -242,6 +242,9 @@ public:
private:
friend class Sequence<Time>;
+ Time choose_next(Time earliest_t);
+ void set_event();
+
typedef std::vector<ControlIterator> ControlIterators;
enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX, PATCH_CHANGE };
diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp
index 22f090beca..a77e3c94be 100644
--- a/libs/evoral/src/Sequence.cpp
+++ b/libs/evoral/src/Sequence.cpp
@@ -114,10 +114,10 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
assert (_patch_change_iter == seq.patch_changes().end() || (*_patch_change_iter)->time() >= t);
// Find first control event after t
- ControlIterator earliest_control(boost::shared_ptr<ControlList>(), DBL_MAX, 0.0);
_control_iters.reserve(seq._controls.size());
bool found = false;
size_t earliest_control_index = 0;
+ double earliest_control_x = DBL_MAX;
for (Controls::const_iterator i = seq._controls.begin(); i != seq._controls.end(); ++i) {
if (filtered.find (i->first) != filtered.end()) {
@@ -155,106 +155,155 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
_control_iters.push_back(new_iter);
// Found a new earliest_control
- if (x < earliest_control.x) {
- earliest_control = new_iter;
+ if (x < earliest_control_x) {
+ earliest_control_x = x;
earliest_control_index = _control_iters.size() - 1;
- found = true;
+ found = true;
}
}
if (found) {
_control_iter = _control_iters.begin() + earliest_control_index;
assert(_control_iter != _control_iters.end());
+ assert(_control_iter->list);
} else {
_control_iter = _control_iters.end();
}
- // Now find the earliest event overall and point to it
- Time earliest_t = t;
+ // Choose the earliest event overall to point to
+ choose_next(t);
- if (_note_iter != seq.notes().end()) {
- _type = NOTE_ON;
+ // Allocate a new event for storing the current event in MIDI format
+ _event = boost::shared_ptr< Event<Time> >(
+ new Event<Time>(0, Time(), 4, NULL, true));
+
+ // Set event from chosen sub-iterator
+ set_event();
+
+ if (_is_end) {
+ DEBUG_TRACE(DEBUG::Sequence,
+ string_compose("Starting at end @ %1\n", t));
+ } else {
+ DEBUG_TRACE(DEBUG::Sequence,
+ string_compose("Starting at type 0x%1 : 0x%2 @ %3\n",
+ (int)_event->event_type(),
+ (int)((MIDIEvent<Time>*)_event.get())->type(),
+ _event->time()));
+ }
+}
+
+template<typename Time>
+Sequence<Time>::const_iterator::~const_iterator()
+{
+}
+
+template<typename Time>
+void
+Sequence<Time>::const_iterator::invalidate()
+{
+ while (!_active_notes.empty()) {
+ _active_notes.pop();
+ }
+ _type = NIL;
+ _is_end = true;
+ if (_seq) {
+ _note_iter = _seq->notes().end();
+ _sysex_iter = _seq->sysexes().end();
+ _patch_change_iter = _seq->patch_changes().end();
+ _active_patch_change_message = 0;
+ }
+ _control_iters.clear();
+ _control_iter = _control_iters.end();
+ _lock.reset();
+}
+
+template<typename Time>
+Time
+Sequence<Time>::const_iterator::choose_next(Time earliest_t)
+{
+ _type = NIL;
+
+ // Next earliest note on
+ if (_note_iter != _seq->notes().end()) {
+ _type = NOTE_ON;
earliest_t = (*_note_iter)->time();
}
- if (_sysex_iter != seq.sysexes().end()
- && ((*_sysex_iter)->time() < earliest_t || _type == NIL)) {
- _type = SYSEX;
- earliest_t = (*_sysex_iter)->time();
+ // Use the next note off iff it's earlier or the same time as the note on
+ if ((!_active_notes.empty())) {
+ if (_type == NIL || _active_notes.top()->end_time() <= earliest_t) {
+ _type = NOTE_OFF;
+ earliest_t = _active_notes.top()->end_time();
+ }
+ }
+
+ // Use the next earliest controller iff it's earlier than the note event
+ if (_control_iter != _control_iters.end() &&
+ _control_iter->list && _control_iter->x != DBL_MAX) {
+ if (_type == NIL || _control_iter->x < earliest_t.to_double()) {
+ _type = CONTROL;
+ earliest_t = Time(_control_iter->x);
+ }
}
- if (_patch_change_iter != seq.patch_changes().end() && ((*_patch_change_iter)->time() < earliest_t || _type == NIL)) {
- _type = PATCH_CHANGE;
- earliest_t = (*_patch_change_iter)->time ();
+ // Use the next earliest SysEx iff it's earlier than the controller
+ if (_sysex_iter != _seq->sysexes().end()) {
+ if (_type == NIL || (*_sysex_iter)->time() < earliest_t) {
+ _type = SYSEX;
+ earliest_t = (*_sysex_iter)->time();
+ }
}
- if (_control_iter != _control_iters.end()
- && earliest_control.list && earliest_control.x >= t.to_double()
- && (earliest_control.x < earliest_t.to_double() || _type == NIL)) {
- _type = CONTROL;
- earliest_t = Time(earliest_control.x);
+ // Use the next earliest patch change iff it's earlier than the SysEx
+ if (_patch_change_iter != _seq->patch_changes().end()) {
+ if (_type == NIL || (*_patch_change_iter)->time() < earliest_t) {
+ _type = PATCH_CHANGE;
+ earliest_t = (*_patch_change_iter)->time();
+ }
}
+ return earliest_t;
+}
+template<typename Time>
+void
+Sequence<Time>::const_iterator::set_event()
+{
switch (_type) {
case NOTE_ON:
- DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at note on event @ %1\n", earliest_t));
- _event = boost::shared_ptr<Event<Time> > (new Event<Time> ((*_note_iter)->on_event(), true));
+ DEBUG_TRACE(DEBUG::Sequence, "iterator = note on\n");
+ *_event = (*_note_iter)->on_event();
_active_notes.push(*_note_iter);
break;
+ case NOTE_OFF:
+ DEBUG_TRACE(DEBUG::Sequence, "iterator = note off\n");
+ assert(!_active_notes.empty());
+ *_event = _active_notes.top()->off_event();
+ _active_notes.pop();
+ break;
case SYSEX:
- DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at sysex event @ %1\n", earliest_t));
- _event = boost::shared_ptr< Event<Time> >(
- new Event<Time>(*(*_sysex_iter), true));
+ DEBUG_TRACE(DEBUG::Sequence, "iterator = sysex\n");
+ *_event = *(*_sysex_iter);
break;
case CONTROL:
- DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at control event @ %1\n", earliest_t));
- seq.control_to_midi_event(_event, earliest_control);
+ DEBUG_TRACE(DEBUG::Sequence, "iterator = control\n");
+ _seq->control_to_midi_event(_event, *_control_iter);
break;
case PATCH_CHANGE:
- DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at patch change event @ %1\n", earliest_t));
- _event = boost::shared_ptr<Event<Time> > (new Event<Time> ((*_patch_change_iter)->message (_active_patch_change_message), true));
+ DEBUG_TRACE(DEBUG::Sequence, "iterator = program change\n");
+ *_event = (*_patch_change_iter)->message (_active_patch_change_message);
break;
default:
+ _is_end = true;
break;
}
if (_type == NIL || !_event || _event->size() == 0) {
- DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at end @ %1\n", t));
+ DEBUG_TRACE(DEBUG::Sequence, "iterator = end\n");
_type = NIL;
_is_end = true;
} else {
- DEBUG_TRACE (DEBUG::Sequence, string_compose ("New iterator = 0x%1 : 0x%2 @ %3\n",
- (int)_event->event_type(),
- (int)((MIDIEvent<Time>*)_event.get())->type(),
- _event->time()));
-
assert(midi_event_is_valid(_event->buffer(), _event->size()));
}
-
-}
-
-template<typename Time>
-Sequence<Time>::const_iterator::~const_iterator()
-{
-}
-
-template<typename Time>
-void
-Sequence<Time>::const_iterator::invalidate()
-{
- while (!_active_notes.empty()) {
- _active_notes.pop();
- }
- _type = NIL;
- _is_end = true;
- if (_seq) {
- _note_iter = _seq->notes().end();
- _sysex_iter = _seq->sysexes().end();
- _patch_change_iter = _seq->patch_changes().end();
- _active_patch_change_message = 0;
- }
- _control_iter = _control_iters.end();
- _lock.reset();
}
template<typename Time>
@@ -332,83 +381,11 @@ Sequence<Time>::const_iterator::operator++()
assert(false);
}
- // Now find the earliest event overall and point to it
- _type = NIL;
- Time earliest_t = std::numeric_limits<Time>::max();
-
- // Next earliest note on
- if (_note_iter != _seq->notes().end()) {
- _type = NOTE_ON;
- earliest_t = (*_note_iter)->time();
- }
-
- // Use the next note off iff it's earlier or the same time as the note on
-#ifdef PERCUSSIVE_IGNORE_NOTE_OFFS
- // issue 0005121 When in Percussive mode, all note offs go missing, which jams all MIDI instruments that they stop playing
- // remove this code since it drowns MIDI instruments by stealing all voices and crashes LinuxSampler
- if (!_seq->percussive() && (!_active_notes.empty())) {
-#else
- if ((!_active_notes.empty())) {
-#endif
- if (_type == NIL || _active_notes.top()->end_time() <= earliest_t) {
- _type = NOTE_OFF;
- earliest_t = _active_notes.top()->end_time();
- }
- }
-
- // Use the next earliest controller iff it's earlier than the note event
- if (_control_iter != _control_iters.end() &&
- _control_iter->list && _control_iter->x != DBL_MAX &&
- (_control_iter->x < earliest_t.to_double() || _type == NIL)) {
- _type = CONTROL;
- earliest_t = Time(_control_iter->x);
- }
-
- // Use the next earliest SysEx iff it's earlier than the controller
- if (_sysex_iter != _seq->sysexes().end()) {
- if (_type == NIL || (*_sysex_iter)->time() < earliest_t) {
- _type = SYSEX;
- earliest_t = (*_sysex_iter)->time();
- }
- }
+ // Choose the earliest event overall to point to
+ choose_next(std::numeric_limits<Time>::max());
- // Use the next earliest patch change iff it's earlier than the SysEx
- if (_patch_change_iter != _seq->patch_changes().end()) {
- if (_type == NIL || (*_patch_change_iter)->time() < earliest_t) {
- _type = PATCH_CHANGE;
- earliest_t = (*_patch_change_iter)->time();
- }
- }
-
- // Set event to reflect new position
- switch (_type) {
- case NOTE_ON:
- // DEBUG_TRACE(DEBUG::Sequence, "iterator = note on\n");
- *_event = (*_note_iter)->on_event();
- _active_notes.push(*_note_iter);
- break;
- case NOTE_OFF:
- // DEBUG_TRACE(DEBUG::Sequence, "iterator = note off\n");
- assert(!_active_notes.empty());
- *_event = _active_notes.top()->off_event();
- _active_notes.pop();
- break;
- case CONTROL:
- //DEBUG_TRACE(DEBUG::Sequence, "iterator = control\n");
- _seq->control_to_midi_event(_event, *_control_iter);
- break;
- case SYSEX:
- //DEBUG_TRACE(DEBUG::Sequence, "iterator = sysex\n");
- *_event = *(*_sysex_iter);
- break;
- case PATCH_CHANGE:
- //DEBUG_TRACE(DEBUG::Sequence, "iterator = patch change\n");
- *_event = (*_patch_change_iter)->message (_active_patch_change_message);
- break;
- default:
- //DEBUG_TRACE(DEBUG::Sequence, "iterator = end\n");
- _is_end = true;
- }
+ // Set event from chosen sub-iterator
+ set_event();
assert(_is_end || (_event->size() > 0 && _event->buffer() && _event->buffer()[0] != '\0'));