2 Copyright (C) 2007 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #define __STDC_LIMIT_MACROS 1
27 #include "pbd/error.h"
28 #include "pbd/enumwriter.h"
29 #include "midi++/events.h"
31 #include "ardour/midi_model.h"
32 #include "ardour/midi_source.h"
33 #include "ardour/midi_state_tracker.h"
34 #include "ardour/smf_source.h"
35 #include "ardour/types.h"
36 #include "ardour/session.h"
39 using namespace ARDOUR;
42 MidiModel::MidiModel(MidiSource* s)
43 : AutomatableSequence<TimeType>(s->session())
49 /** Start a new Diff command.
51 * This has no side-effects on the model or Session, the returned command
52 * can be held on to for as long as the caller wishes, or discarded without
53 * formality, until apply_command is called and ownership is taken.
55 MidiModel::DiffCommand*
56 MidiModel::new_diff_command(const string name)
58 DiffCommand* cmd = new DiffCommand(_midi_source->model(), name);
64 * Ownership of cmd is taken, it must not be deleted by the caller.
65 * The command will constitute one item on the undo stack.
68 MidiModel::apply_command(Session& session, Command* cmd)
70 session.begin_reversible_command(cmd->name());
72 session.commit_reversible_command(cmd);
76 /** Apply a command as part of a larger reversible transaction
78 * Ownership of cmd is taken, it must not be deleted by the caller.
79 * The command will constitute one item on the undo stack.
82 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
85 session.add_command(cmd);
89 /************** DIFF COMMAND ********************/
91 #define DIFF_COMMAND_ELEMENT "DiffCommand"
92 #define DIFF_NOTES_ELEMENT "ChangedNotes"
93 #define ADDED_NOTES_ELEMENT "AddedNotes"
94 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
95 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
97 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
105 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
109 set_state(node, Stateful::loading_state_version);
113 MidiModel::DiffCommand::add(const NotePtr note)
115 _removed_notes.remove(note);
116 _added_notes.push_back(note);
120 MidiModel::DiffCommand::remove(const NotePtr note)
122 _added_notes.remove(note);
123 _removed_notes.push_back(note);
127 MidiModel::DiffCommand::side_effect_remove(const NotePtr note)
129 side_effect_removals.insert (note);
133 MidiModel::DiffCommand::change(const NotePtr note, Property prop,
140 if (new_value == note->note()) {
143 change.old_value = note->note();
146 if (new_value == note->velocity()) {
149 change.old_value = note->velocity();
152 if (new_value == note->channel()) {
155 change.old_value = note->channel();
160 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
164 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
170 change.property = prop;
171 change.new_value = new_value;
173 _changes.push_back (change);
177 MidiModel::DiffCommand::change(const NotePtr note, Property prop,
186 fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
190 if (Evoral::musical_time_equal (note->time(), new_time)) {
193 change.old_time = note->time();
196 if (Evoral::musical_time_equal (note->length(), new_time)) {
199 change.old_time = note->length();
204 change.property = prop;
205 change.new_time = new_time;
207 _changes.push_back (change);
210 MidiModel::DiffCommand&
211 MidiModel::DiffCommand::operator+= (const DiffCommand& other)
213 if (this == &other) {
217 if (_model != other._model) {
221 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
222 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
223 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
224 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
230 MidiModel::DiffCommand::operator()()
233 MidiModel::WriteLock lock(_model->edit_lock());
235 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
236 if (!_model->add_note_unlocked(*i)) {
237 /* failed to add it, so don't leave it in the removed list, to
238 avoid apparent errors on undo.
240 _removed_notes.remove (*i);
244 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
245 _model->remove_note_unlocked(*i);
248 /* notes we modify in a way that requires remove-then-add to maintain ordering */
249 set<NotePtr> temporary_removals;
251 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
252 Property prop = i->property;
255 if (temporary_removals.find (i->note) == temporary_removals.end()) {
256 _model->remove_note_unlocked (i->note);
257 temporary_removals.insert (i->note);
259 i->note->set_note (i->new_value);
263 if (temporary_removals.find (i->note) == temporary_removals.end()) {
264 _model->remove_note_unlocked (i->note);
265 temporary_removals.insert (i->note);
268 i->note->set_time (i->new_time);
272 if (temporary_removals.find (i->note) == temporary_removals.end()) {
273 _model->remove_note_unlocked (i->note);
274 temporary_removals.insert (i->note);
276 i->note->set_channel (i->new_value);
279 /* no remove-then-add required for these properties, since we do not index them
283 i->note->set_velocity (i->new_value);
287 i->note->set_length (i->new_time);
294 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
295 DiffCommand side_effects (model(), "side effects");
296 _model->add_note_unlocked (*i, &side_effects);
297 *this += side_effects;
300 if (!side_effect_removals.empty()) {
302 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
303 cerr << "\t" << *i << ' ' << **i << endl;
308 _model->ContentsChanged(); /* EMIT SIGNAL */
312 MidiModel::DiffCommand::undo()
315 MidiModel::WriteLock lock(_model->edit_lock());
317 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
318 _model->remove_note_unlocked(*i);
321 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
322 _model->add_note_unlocked(*i);
325 /* notes we modify in a way that requires remove-then-add to maintain ordering */
326 set<NotePtr> temporary_removals;
328 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
329 Property prop = i->property;
332 if (temporary_removals.find (i->note) == temporary_removals.end()) {
333 _model->remove_note_unlocked (i->note);
334 temporary_removals.insert (i->note);
336 i->note->set_note (i->old_value);
339 i->note->set_velocity (i->old_value);
342 if (temporary_removals.find (i->note) == temporary_removals.end()) {
343 _model->remove_note_unlocked (i->note);
344 temporary_removals.insert (i->note);
346 i->note->set_time (i->old_time);
349 i->note->set_length (i->old_time);
352 if (temporary_removals.find (i->note) == temporary_removals.end()) {
353 _model->remove_note_unlocked (i->note);
354 temporary_removals.insert (i->note);
356 i->note->set_channel (i->old_value);
361 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
362 _model->add_note_unlocked (*i);
365 /* finally add back notes that were removed by the "do". we don't care
366 about side effects here since the model should be back to its original
367 state once this is done.
370 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
371 _model->add_note_unlocked (*i);
375 _model->ContentsChanged(); /* EMIT SIGNAL */
379 MidiModel::DiffCommand::marshal_note(const NotePtr note)
381 XMLNode* xml_note = new XMLNode("note");
383 cerr << "Marshalling note: " << *note << endl;
385 ostringstream note_str(ios::ate);
386 note_str << int(note->note());
387 xml_note->add_property("note", note_str.str());
389 ostringstream channel_str(ios::ate);
390 channel_str << int(note->channel());
391 xml_note->add_property("channel", channel_str.str());
393 ostringstream time_str(ios::ate);
394 time_str << note->time();
395 xml_note->add_property("time", time_str.str());
397 ostringstream length_str(ios::ate);
398 length_str << note->length();
399 xml_note->add_property("length", length_str.str());
401 ostringstream velocity_str(ios::ate);
402 velocity_str << (unsigned int) note->velocity();
403 xml_note->add_property("velocity", velocity_str.str());
408 Evoral::Sequence<MidiModel::TimeType>::NotePtr
409 MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
413 unsigned int channel;
416 unsigned int velocity;
418 if ((prop = xml_note->property("note")) != 0) {
419 istringstream note_str(prop->value());
422 warning << "note information missing note value" << endmsg;
426 if ((prop = xml_note->property("channel")) != 0) {
427 istringstream channel_str(prop->value());
428 channel_str >> channel;
430 warning << "note information missing channel" << endmsg;
434 if ((prop = xml_note->property("time")) != 0) {
435 istringstream time_str(prop->value());
438 warning << "note information missing time" << endmsg;
442 if ((prop = xml_note->property("length")) != 0) {
443 istringstream length_str(prop->value());
444 length_str >> length;
446 warning << "note information missing length" << endmsg;
450 if ((prop = xml_note->property("velocity")) != 0) {
451 istringstream velocity_str(prop->value());
452 velocity_str >> velocity;
454 warning << "note information missing velocity" << endmsg;
458 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
464 MidiModel::DiffCommand::marshal_change(const NoteChange& change)
466 XMLNode* xml_change = new XMLNode("change");
468 /* first, the change itself */
470 xml_change->add_property ("property", enum_2_string (change.property));
473 ostringstream old_value_str (ios::ate);
474 if (change.property == StartTime || change.property == Length) {
475 old_value_str << change.old_time;
477 old_value_str << (unsigned int) change.old_value;
479 xml_change->add_property ("old", old_value_str.str());
483 ostringstream new_value_str (ios::ate);
484 if (change.property == StartTime || change.property == Length) {
485 new_value_str << change.new_time;
487 new_value_str << (unsigned int) change.new_value;
489 xml_change->add_property ("new", new_value_str.str());
492 /* now the rest of the note */
494 const SMFSource* smf = dynamic_cast<const SMFSource*> (_model->midi_source());
496 if (change.property != NoteNumber) {
497 ostringstream note_str;
498 note_str << int(change.note->note());
499 xml_change->add_property("note", note_str.str());
502 if (change.property != Channel) {
503 ostringstream channel_str;
504 channel_str << int(change.note->channel());
505 xml_change->add_property("channel", channel_str.str());
508 if (change.property != StartTime) {
509 ostringstream time_str;
511 time_str << smf->round_to_file_precision (change.note->time());
513 time_str << change.note->time();
515 xml_change->add_property("time", time_str.str());
518 if (change.property != Length) {
519 ostringstream length_str;
521 length_str << smf->round_to_file_precision (change.note->length());
523 length_str << change.note->length();
525 xml_change->add_property ("length", length_str.str());
528 if (change.property != Velocity) {
529 ostringstream velocity_str;
530 velocity_str << int (change.note->velocity());
531 xml_change->add_property("velocity", velocity_str.str());
534 /* and now notes that were remove as a side-effect */
539 MidiModel::DiffCommand::NoteChange
540 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
545 unsigned int channel;
546 unsigned int velocity;
547 Evoral::MusicalTime time;
548 Evoral::MusicalTime length;
550 if ((prop = xml_change->property("property")) != 0) {
551 change.property = (Property) string_2_enum (prop->value(), change.property);
553 fatal << "!!!" << endmsg;
557 if ((prop = xml_change->property ("old")) != 0) {
558 istringstream old_str (prop->value());
559 if (change.property == StartTime || change.property == Length) {
560 old_str >> change.old_time;
562 int integer_value_so_that_istream_does_the_right_thing;
563 old_str >> integer_value_so_that_istream_does_the_right_thing;
564 change.old_value = integer_value_so_that_istream_does_the_right_thing;
567 fatal << "!!!" << endmsg;
571 if ((prop = xml_change->property ("new")) != 0) {
572 istringstream new_str (prop->value());
573 if (change.property == StartTime || change.property == Length) {
574 new_str >> change.new_time;
576 int integer_value_so_that_istream_does_the_right_thing;
577 new_str >> integer_value_so_that_istream_does_the_right_thing;
578 change.new_value = integer_value_so_that_istream_does_the_right_thing;
581 fatal << "!!!" << endmsg;
585 if (change.property != NoteNumber) {
586 if ((prop = xml_change->property("note")) != 0) {
587 istringstream note_str(prop->value());
590 warning << "note information missing note value" << endmsg;
594 note = change.new_value;
597 if (change.property != Channel) {
598 if ((prop = xml_change->property("channel")) != 0) {
599 istringstream channel_str(prop->value());
600 channel_str >> channel;
602 warning << "note information missing channel" << endmsg;
606 channel = change.new_value;
609 if (change.property != StartTime) {
610 if ((prop = xml_change->property("time")) != 0) {
611 istringstream time_str(prop->value());
614 warning << "note information missing time" << endmsg;
618 time = change.new_time;
621 if (change.property != Length) {
622 if ((prop = xml_change->property("length")) != 0) {
623 istringstream length_str(prop->value());
624 length_str >> length;
626 warning << "note information missing length" << endmsg;
630 length = change.new_time;
633 if (change.property != Velocity) {
634 if ((prop = xml_change->property("velocity")) != 0) {
635 istringstream velocity_str(prop->value());
636 velocity_str >> velocity;
638 warning << "note information missing velocity" << endmsg;
642 velocity = change.new_value;
645 /* we must point at the instance of the note that is actually in the model.
646 so go look for it ...
649 NotePtr new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
651 change.note = _model->find_note (new_note);
654 warning << "MIDI note " << *new_note << " not found in model - programmers should investigate this" << endmsg;
655 /* use the actual new note */
656 change.note = new_note;
663 MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
665 if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
671 _added_notes.clear();
672 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
674 XMLNodeList notes = added_notes->children();
675 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
676 boost::bind (&DiffCommand::unmarshal_note, this, _1));
682 _removed_notes.clear();
683 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
685 XMLNodeList notes = removed_notes->children();
686 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
687 boost::bind (&DiffCommand::unmarshal_note, this, _1));
695 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
698 XMLNodeList notes = changed_notes->children();
699 transform (notes.begin(), notes.end(), back_inserter(_changes),
700 boost::bind (&DiffCommand::unmarshal_change, this, _1));
704 /* side effect removals caused by changes */
706 side_effect_removals.clear();
708 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
710 if (side_effect_notes) {
711 XMLNodeList notes = side_effect_notes->children();
712 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
713 side_effect_removals.insert (unmarshal_note (*n));
721 MidiModel::DiffCommand::get_state ()
723 XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
724 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
726 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
727 for_each(_changes.begin(), _changes.end(),
729 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
730 boost::bind (&DiffCommand::marshal_change, this, _1)));
732 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
733 for_each(_added_notes.begin(), _added_notes.end(),
735 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
736 boost::bind (&DiffCommand::marshal_note, this, _1)));
738 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
739 for_each(_removed_notes.begin(), _removed_notes.end(),
741 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
742 boost::bind (&DiffCommand::marshal_note, this, _1)));
744 /* if this command had side-effects, store that state too
747 if (!side_effect_removals.empty()) {
748 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
749 for_each(side_effect_removals.begin(), side_effect_removals.end(),
751 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
752 boost::bind (&DiffCommand::marshal_note, this, _1)));
755 return *diff_command;
759 /** Write all of the model to a MidiSource (i.e. save the model).
760 * This is different from manually using read to write to a source in that
761 * note off events are written regardless of the track mode. This is so the
762 * user can switch a recorded track (with note durations from some instrument)
763 * to percussive, save, reload, then switch it back to sustained without
764 * destroying the original note durations.
766 * Similarly, control events are written without interpolation (as with the
770 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
772 ReadLock lock(read_lock());
774 const bool old_percussive = percussive();
775 set_percussive(false);
777 source->drop_model();
778 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
780 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
781 source->append_event_unlocked_beats(*i);
784 set_percussive(old_percussive);
785 source->mark_streaming_write_completed();
792 /** very similar to ::write_to() but writes to the model's own
793 existing midi_source, without making it call MidiSource::drop_model().
794 the caller is a MidiSource that needs to catch up with the state
798 MidiModel::sync_to_source ()
800 ReadLock lock(read_lock());
802 const bool old_percussive = percussive();
803 set_percussive(false);
805 _midi_source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
807 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
808 _midi_source->append_event_unlocked_beats(*i);
811 set_percussive (old_percussive);
812 _midi_source->mark_streaming_write_completed ();
819 /** Write part or all of the model to a MidiSource (i.e. save the model).
820 * This is different from manually using read to write to a source in that
821 * note off events are written regardless of the track mode. This is so the
822 * user can switch a recorded track (with note durations from some instrument)
823 * to percussive, save, reload, then switch it back to sustained without
824 * destroying the original note durations.
827 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
829 ReadLock lock(read_lock());
830 MidiStateTracker mst;
831 Evoral::MusicalTime extra_note_on_time = end_time;
833 const bool old_percussive = percussive();
834 set_percussive(false);
836 source->drop_model();
837 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
839 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
840 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
842 if (ev.time() >= begin_time && ev.time() < end_time) {
844 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
845 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
852 if (mev->is_note_off()) {
854 if (!mst.active (mev->note(), mev->channel())) {
856 /* add a note-on at the start of the range we're writing
857 to the file. velocity is just an arbitary reasonable value.
860 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
861 on.set_type (mev->type());
862 on.set_note (mev->note());
863 on.set_channel (mev->channel());
864 on.set_velocity (mev->velocity());
866 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
867 source->append_event_unlocked_beats (on);
868 mst.add (on.note(), on.channel());
870 extra_note_on_time += 1.0/128.0;
873 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
874 source->append_event_unlocked_beats (*i);
875 mst.remove (mev->note(), mev->channel());
878 } else if (mev->is_note_on()) {
879 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
880 mst.add (mev->note(), mev->channel());
881 source->append_event_unlocked_beats(*i);
884 cerr << "MIDI other event type\n";
885 source->append_event_unlocked_beats(*i);
890 mst.resolve_notes (*source, end_time);
892 set_percussive(old_percussive);
893 source->mark_streaming_write_completed();
901 MidiModel::get_state()
903 XMLNode *node = new XMLNode("MidiModel");
907 Evoral::Sequence<MidiModel::TimeType>::NotePtr
908 MidiModel::find_note (NotePtr other)
910 Notes::iterator l = notes().lower_bound(other);
912 if (l != notes().end()) {
913 for (; (*l)->time() == other->time(); ++l) {
914 /* NB: compare note contents, not note pointers.
915 If "other" was a ptr to a note already in
916 the model, we wouldn't be looking for it,
928 /** Lock and invalidate the source.
929 * This should be used by commands and editing things
932 MidiModel::edit_lock()
934 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
935 _midi_source->invalidate(); // Release cached iterator's read lock on model
936 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
939 /** Lock just the model, the source lock must already be held.
940 * This should only be called from libardour/evoral places
943 MidiModel::write_lock()
945 assert(!_midi_source->mutex().trylock());
946 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
950 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
952 using namespace Evoral;
954 if (_writing || insert_merge_policy() == InsertMergeRelax) {
958 DiffCommand* cmd = static_cast<DiffCommand*>(arg);
960 TimeType sa = note->time();
961 TimeType ea = note->end_time();
963 const Pitches& p (pitches (note->channel()));
964 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
965 set<NotePtr> to_be_deleted;
966 bool set_note_length = false;
967 bool set_note_time = false;
968 TimeType note_time = note->time();
969 TimeType note_length = note->length();
971 for (Pitches::const_iterator i = p.lower_bound (search_note);
972 i != p.end() && (*i)->note() == note->note(); ++i) {
974 TimeType sb = (*i)->time();
975 TimeType eb = (*i)->end_time();
976 OverlapType overlap = OverlapNone;
978 if ((sb > sa) && (eb <= ea)) {
979 overlap = OverlapInternal;
980 } else if ((eb >= sa) && (eb <= ea)) {
981 overlap = OverlapStart;
982 } else if ((sb > sa) && (sb <= ea)) {
983 overlap = OverlapEnd;
984 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
985 overlap = OverlapExternal;
991 if (insert_merge_policy() == InsertMergeReject) {
997 cerr << "OverlapStart\n";
998 /* existing note covers start of new note */
999 switch (insert_merge_policy()) {
1000 case InsertMergeReplace:
1001 to_be_deleted.insert (*i);
1003 case InsertMergeTruncateExisting:
1005 cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
1007 (*i)->set_length (note->time() - (*i)->time());
1009 case InsertMergeTruncateAddition:
1010 set_note_time = true;
1011 set_note_length = true;
1012 note_time = (*i)->time() + (*i)->length();
1013 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1015 case InsertMergeExtend:
1017 cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
1019 (*i)->set_length (note->end_time() - (*i)->time());
1020 return -1; /* do not add the new note */
1030 cerr << "OverlapEnd\n";
1031 /* existing note covers end of new note */
1032 switch (insert_merge_policy()) {
1033 case InsertMergeReplace:
1034 to_be_deleted.insert (*i);
1037 case InsertMergeTruncateExisting:
1038 /* resetting the start time of the existing note
1039 is a problem because of time ordering.
1043 case InsertMergeTruncateAddition:
1044 set_note_length = true;
1045 note_length = min (note_length, ((*i)->time() - note->time()));
1048 case InsertMergeExtend:
1049 /* we can't reset the time of the existing note because
1050 that will corrupt time ordering. So remove the
1051 existing note and change the position/length
1052 of the new note (which has not been added yet)
1054 to_be_deleted.insert (*i);
1055 set_note_length = true;
1056 note_length = min (note_length, (*i)->end_time() - note->time());
1065 case OverlapExternal:
1066 cerr << "OverlapExt\n";
1067 /* existing note overlaps all the new note */
1068 switch (insert_merge_policy()) {
1069 case InsertMergeReplace:
1070 to_be_deleted.insert (*i);
1072 case InsertMergeTruncateExisting:
1073 case InsertMergeTruncateAddition:
1074 case InsertMergeExtend:
1075 /* cannot add in this case */
1084 case OverlapInternal:
1085 cerr << "OverlapInt\n";
1086 /* new note fully overlaps an existing note */
1087 switch (insert_merge_policy()) {
1088 case InsertMergeReplace:
1089 case InsertMergeTruncateExisting:
1090 case InsertMergeTruncateAddition:
1091 case InsertMergeExtend:
1092 /* delete the existing note, the new one will cover it */
1093 to_be_deleted.insert (*i);
1109 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1110 remove_note_unlocked (*i);
1113 cmd->side_effect_remove (*i);
1117 if (set_note_time) {
1119 cmd->change (note, DiffCommand::StartTime, note_time);
1121 note->set_time (note_time);
1124 if (set_note_length) {
1126 cmd->change (note, DiffCommand::Length, note_length);
1128 note->set_length (note_length);
1135 MidiModel::insert_merge_policy () const
1137 /* XXX ultimately this should be a per-track or even per-model policy */
1139 return _midi_source->session().config.get_insert_merge_policy();
1143 MidiModel::set_midi_source (MidiSource* s)
1146 _midi_source->invalidate ();
1149 _midi_source_connections.drop_connections ();
1153 _midi_source->InterpolationChanged.connect_same_thread (
1154 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1158 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1159 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1160 * appropriate ControlList.
1162 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and the
1163 * MidiSource's InterpolationChanged signal is listened to by the GUI.
1166 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1168 Glib::Mutex::Lock lm (_control_lock);
1169 control(p)->list()->set_interpolation (s);
1172 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1173 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1176 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1178 _midi_source->set_interpolation_of (p, s);
1181 boost::shared_ptr<Evoral::Control>
1182 MidiModel::control_factory (Evoral::Parameter const & p)
1184 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1186 /* Set up newly created control's lists to the appropriate interpolation state
1190 assert (_midi_source);
1192 c->list()->set_interpolation (_midi_source->interpolation_of (p));