Fix MIDI CC record/playback crash.

This commit is contained in:
David Robillard 2014-12-30 14:45:11 -05:00
parent c35e94a3c8
commit 97d344f740
3 changed files with 119 additions and 138 deletions

View file

@ -313,6 +313,7 @@ MidiSource::mark_midi_streaming_write_completed (const Lock&
} }
} }
invalidate(lock);
_writing = false; _writing = false;
} }

View file

@ -217,7 +217,7 @@ private:
public: public:
/** Read iterator */ /** Read iterator */
class LIBEVORAL_API /* Added by JE - */ const_iterator { class LIBEVORAL_API const_iterator {
public: public:
const_iterator(); const_iterator();
const_iterator(const Sequence<Time>& seq, Time t, bool, std::set<Evoral::Parameter> const &); const_iterator(const Sequence<Time>& seq, Time t, bool, std::set<Evoral::Parameter> const &);
@ -242,6 +242,9 @@ public:
private: private:
friend class Sequence<Time>; friend class Sequence<Time>;
Time choose_next(Time earliest_t);
void set_event();
typedef std::vector<ControlIterator> ControlIterators; typedef std::vector<ControlIterator> ControlIterators;
enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX, PATCH_CHANGE }; enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX, PATCH_CHANGE };

View file

@ -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); assert (_patch_change_iter == seq.patch_changes().end() || (*_patch_change_iter)->time() >= t);
// Find first control event after t // Find first control event after t
ControlIterator earliest_control(boost::shared_ptr<ControlList>(), DBL_MAX, 0.0);
_control_iters.reserve(seq._controls.size()); _control_iters.reserve(seq._controls.size());
bool found = false; bool found = false;
size_t earliest_control_index = 0; 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) { for (Controls::const_iterator i = seq._controls.begin(); i != seq._controls.end(); ++i) {
if (filtered.find (i->first) != filtered.end()) { if (filtered.find (i->first) != filtered.end()) {
@ -155,82 +155,41 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
_control_iters.push_back(new_iter); _control_iters.push_back(new_iter);
// Found a new earliest_control // Found a new earliest_control
if (x < earliest_control.x) { if (x < earliest_control_x) {
earliest_control = new_iter; earliest_control_x = x;
earliest_control_index = _control_iters.size() - 1; earliest_control_index = _control_iters.size() - 1;
found = true; found = true;
} }
} }
if (found) { if (found) {
_control_iter = _control_iters.begin() + earliest_control_index; _control_iter = _control_iters.begin() + earliest_control_index;
assert(_control_iter != _control_iters.end()); assert(_control_iter != _control_iters.end());
assert(_control_iter->list);
} else { } else {
_control_iter = _control_iters.end(); _control_iter = _control_iters.end();
} }
// Now find the earliest event overall and point to it // Choose the earliest event overall to point to
Time earliest_t = t; choose_next(t);
if (_note_iter != seq.notes().end()) { // Allocate a new event for storing the current event in MIDI format
_type = NOTE_ON; _event = boost::shared_ptr< Event<Time> >(
earliest_t = (*_note_iter)->time(); new Event<Time>(0, Time(), 4, NULL, true));
}
if (_sysex_iter != seq.sysexes().end() // Set event from chosen sub-iterator
&& ((*_sysex_iter)->time() < earliest_t || _type == NIL)) { set_event();
_type = SYSEX;
earliest_t = (*_sysex_iter)->time();
}
if (_patch_change_iter != seq.patch_changes().end() && ((*_patch_change_iter)->time() < earliest_t || _type == NIL)) { if (_is_end) {
_type = PATCH_CHANGE; DEBUG_TRACE(DEBUG::Sequence,
earliest_t = (*_patch_change_iter)->time (); string_compose("Starting at end @ %1\n", t));
}
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);
}
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));
_active_notes.push(*_note_iter);
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));
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);
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));
break;
default:
break;
}
if (_type == NIL || !_event || _event->size() == 0) {
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at end @ %1\n", t));
_type = NIL;
_is_end = true;
} else { } else {
DEBUG_TRACE (DEBUG::Sequence, string_compose ("New iterator = 0x%1 : 0x%2 @ %3\n", DEBUG_TRACE(DEBUG::Sequence,
(int)_event->event_type(), string_compose("Starting at type 0x%1 : 0x%2 @ %3\n",
(int)((MIDIEvent<Time>*)_event.get())->type(), (int)_event->event_type(),
_event->time())); (int)((MIDIEvent<Time>*)_event.get())->type(),
_event->time()));
assert(midi_event_is_valid(_event->buffer(), _event->size()));
} }
} }
template<typename Time> template<typename Time>
@ -253,10 +212,100 @@ Sequence<Time>::const_iterator::invalidate()
_patch_change_iter = _seq->patch_changes().end(); _patch_change_iter = _seq->patch_changes().end();
_active_patch_change_message = 0; _active_patch_change_message = 0;
} }
_control_iters.clear();
_control_iter = _control_iters.end(); _control_iter = _control_iters.end();
_lock.reset(); _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();
}
// 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);
}
}
// 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();
}
}
// 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, "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, "iterator = sysex\n");
*_event = *(*_sysex_iter);
break;
case CONTROL:
DEBUG_TRACE(DEBUG::Sequence, "iterator = control\n");
_seq->control_to_midi_event(_event, *_control_iter);
break;
case PATCH_CHANGE:
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, "iterator = end\n");
_type = NIL;
_is_end = true;
} else {
assert(midi_event_is_valid(_event->buffer(), _event->size()));
}
}
template<typename Time> template<typename Time>
const typename Sequence<Time>::const_iterator& const typename Sequence<Time>::const_iterator&
Sequence<Time>::const_iterator::operator++() Sequence<Time>::const_iterator::operator++()
@ -332,83 +381,11 @@ Sequence<Time>::const_iterator::operator++()
assert(false); assert(false);
} }
// Now find the earliest event overall and point to it // Choose the earliest event overall to point to
_type = NIL; choose_next(std::numeric_limits<Time>::max());
Time earliest_t = std::numeric_limits<Time>::max();
// Next earliest note on // Set event from chosen sub-iterator
if (_note_iter != _seq->notes().end()) { set_event();
_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();
}
}
// 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;
}
assert(_is_end || (_event->size() > 0 && _event->buffer() && _event->buffer()[0] != '\0')); assert(_is_end || (_event->size() > 0 && _event->buffer() && _event->buffer()[0] != '\0'));