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");
384 ostringstream id_str(ios::ate);
385 id_str << int(note->id());
386 xml_note->add_property("id", id_str.str());
390 ostringstream note_str(ios::ate);
391 note_str << int(note->note());
392 xml_note->add_property("note", note_str.str());
396 ostringstream channel_str(ios::ate);
397 channel_str << int(note->channel());
398 xml_note->add_property("channel", channel_str.str());
402 ostringstream time_str(ios::ate);
403 time_str << note->time();
404 xml_note->add_property("time", time_str.str());
408 ostringstream length_str(ios::ate);
409 length_str << note->length();
410 xml_note->add_property("length", length_str.str());
414 ostringstream velocity_str(ios::ate);
415 velocity_str << (unsigned int) note->velocity();
416 xml_note->add_property("velocity", velocity_str.str());
422 Evoral::Sequence<MidiModel::TimeType>::NotePtr
423 MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
427 unsigned int channel;
430 unsigned int velocity;
433 if ((prop = xml_note->property("id")) != 0) {
434 istringstream id_str(prop->value());
437 error << "note information missing ID value" << endmsg;
441 if ((prop = xml_note->property("note")) != 0) {
442 istringstream note_str(prop->value());
445 warning << "note information missing note value" << endmsg;
449 if ((prop = xml_note->property("channel")) != 0) {
450 istringstream channel_str(prop->value());
451 channel_str >> channel;
453 warning << "note information missing channel" << endmsg;
457 if ((prop = xml_note->property("time")) != 0) {
458 istringstream time_str(prop->value());
461 warning << "note information missing time" << endmsg;
465 if ((prop = xml_note->property("length")) != 0) {
466 istringstream length_str(prop->value());
467 length_str >> length;
469 warning << "note information missing length" << endmsg;
473 if ((prop = xml_note->property("velocity")) != 0) {
474 istringstream velocity_str(prop->value());
475 velocity_str >> velocity;
477 warning << "note information missing velocity" << endmsg;
481 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
482 note_ptr->set_id (id);
488 MidiModel::DiffCommand::marshal_change(const NoteChange& change)
490 XMLNode* xml_change = new XMLNode("Change");
492 /* first, the change itself */
494 xml_change->add_property ("property", enum_2_string (change.property));
497 ostringstream old_value_str (ios::ate);
498 if (change.property == StartTime || change.property == Length) {
499 old_value_str << change.old_time;
501 old_value_str << (unsigned int) change.old_value;
503 xml_change->add_property ("old", old_value_str.str());
507 ostringstream new_value_str (ios::ate);
508 if (change.property == StartTime || change.property == Length) {
509 new_value_str << change.new_time;
511 new_value_str << (unsigned int) change.new_value;
513 xml_change->add_property ("new", new_value_str.str());
516 ostringstream id_str;
517 id_str << change.note->id();
518 xml_change->add_property ("id", id_str.str());
523 MidiModel::DiffCommand::NoteChange
524 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
529 if ((prop = xml_change->property("property")) != 0) {
530 change.property = (Property) string_2_enum (prop->value(), change.property);
532 fatal << "!!!" << endmsg;
536 if ((prop = xml_change->property ("id")) == 0) {
537 error << _("No NoteID found for note property change - ignored") << endmsg;
541 gint note_id = atoi (prop->value().c_str());
543 if ((prop = xml_change->property ("old")) != 0) {
544 istringstream old_str (prop->value());
545 if (change.property == StartTime || change.property == Length) {
546 old_str >> change.old_time;
548 int integer_value_so_that_istream_does_the_right_thing;
549 old_str >> integer_value_so_that_istream_does_the_right_thing;
550 change.old_value = integer_value_so_that_istream_does_the_right_thing;
553 fatal << "!!!" << endmsg;
557 if ((prop = xml_change->property ("new")) != 0) {
558 istringstream new_str (prop->value());
559 if (change.property == StartTime || change.property == Length) {
560 new_str >> change.new_time;
562 int integer_value_so_that_istream_does_the_right_thing;
563 new_str >> integer_value_so_that_istream_does_the_right_thing;
564 change.new_value = integer_value_so_that_istream_does_the_right_thing;
567 fatal << "!!!" << endmsg;
571 /* we must point at the instance of the note that is actually in the model.
572 so go look for it ...
575 change.note = _model->find_note (note_id);
578 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
586 MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
588 if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
594 _added_notes.clear();
595 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
597 XMLNodeList notes = added_notes->children();
598 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
599 boost::bind (&DiffCommand::unmarshal_note, this, _1));
605 _removed_notes.clear();
606 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
608 XMLNodeList notes = removed_notes->children();
609 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
610 boost::bind (&DiffCommand::unmarshal_note, this, _1));
618 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
621 XMLNodeList notes = changed_notes->children();
622 transform (notes.begin(), notes.end(), back_inserter(_changes),
623 boost::bind (&DiffCommand::unmarshal_change, this, _1));
627 /* side effect removals caused by changes */
629 side_effect_removals.clear();
631 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
633 if (side_effect_notes) {
634 XMLNodeList notes = side_effect_notes->children();
635 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
636 side_effect_removals.insert (unmarshal_note (*n));
644 MidiModel::DiffCommand::get_state ()
646 XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
647 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
649 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
650 for_each(_changes.begin(), _changes.end(),
652 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
653 boost::bind (&DiffCommand::marshal_change, this, _1)));
655 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
656 for_each(_added_notes.begin(), _added_notes.end(),
658 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
659 boost::bind (&DiffCommand::marshal_note, this, _1)));
661 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
662 for_each(_removed_notes.begin(), _removed_notes.end(),
664 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
665 boost::bind (&DiffCommand::marshal_note, this, _1)));
667 /* if this command had side-effects, store that state too
670 if (!side_effect_removals.empty()) {
671 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
672 for_each(side_effect_removals.begin(), side_effect_removals.end(),
674 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
675 boost::bind (&DiffCommand::marshal_note, this, _1)));
678 return *diff_command;
682 /** Write all of the model to a MidiSource (i.e. save the model).
683 * This is different from manually using read to write to a source in that
684 * note off events are written regardless of the track mode. This is so the
685 * user can switch a recorded track (with note durations from some instrument)
686 * to percussive, save, reload, then switch it back to sustained without
687 * destroying the original note durations.
689 * Similarly, control events are written without interpolation (as with the
693 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
695 ReadLock lock(read_lock());
697 const bool old_percussive = percussive();
698 set_percussive(false);
700 source->drop_model();
701 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
703 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
704 source->append_event_unlocked_beats(*i);
707 set_percussive(old_percussive);
708 source->mark_streaming_write_completed();
715 /** very similar to ::write_to() but writes to the model's own
716 existing midi_source, without making it call MidiSource::drop_model().
717 the caller is a MidiSource that needs to catch up with the state
721 MidiModel::sync_to_source ()
723 ReadLock lock(read_lock());
725 const bool old_percussive = percussive();
726 set_percussive(false);
728 _midi_source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
730 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
731 _midi_source->append_event_unlocked_beats(*i);
734 set_percussive (old_percussive);
735 _midi_source->mark_streaming_write_completed ();
742 /** Write part or all of the model to a MidiSource (i.e. save the model).
743 * This is different from manually using read to write to a source in that
744 * note off events are written regardless of the track mode. This is so the
745 * user can switch a recorded track (with note durations from some instrument)
746 * to percussive, save, reload, then switch it back to sustained without
747 * destroying the original note durations.
750 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
752 ReadLock lock(read_lock());
753 MidiStateTracker mst;
754 Evoral::MusicalTime extra_note_on_time = end_time;
756 const bool old_percussive = percussive();
757 set_percussive(false);
759 source->drop_model();
760 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
762 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
763 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
765 if (ev.time() >= begin_time && ev.time() < end_time) {
767 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
768 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
775 if (mev->is_note_off()) {
777 if (!mst.active (mev->note(), mev->channel())) {
779 /* add a note-on at the start of the range we're writing
780 to the file. velocity is just an arbitary reasonable value.
783 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
784 on.set_type (mev->type());
785 on.set_note (mev->note());
786 on.set_channel (mev->channel());
787 on.set_velocity (mev->velocity());
789 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
790 source->append_event_unlocked_beats (on);
791 mst.add (on.note(), on.channel());
793 extra_note_on_time += 1.0/128.0;
796 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
797 source->append_event_unlocked_beats (*i);
798 mst.remove (mev->note(), mev->channel());
801 } else if (mev->is_note_on()) {
802 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
803 mst.add (mev->note(), mev->channel());
804 source->append_event_unlocked_beats(*i);
807 cerr << "MIDI other event type\n";
808 source->append_event_unlocked_beats(*i);
813 mst.resolve_notes (*source, end_time);
815 set_percussive(old_percussive);
816 source->mark_streaming_write_completed();
824 MidiModel::get_state()
826 XMLNode *node = new XMLNode("MidiModel");
830 Evoral::Sequence<MidiModel::TimeType>::NotePtr
831 MidiModel::find_note (NotePtr other)
833 Notes::iterator l = notes().lower_bound(other);
835 if (l != notes().end()) {
836 for (; (*l)->time() == other->time(); ++l) {
837 /* NB: compare note contents, not note pointers.
838 If "other" was a ptr to a note already in
839 the model, we wouldn't be looking for it,
851 Evoral::Sequence<MidiModel::TimeType>::NotePtr
852 MidiModel::find_note (gint note_id)
854 /* used only for looking up notes when reloading history from disk,
855 so we don't care about performance *too* much.
858 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
859 if ((*l)->id() == note_id) {
867 /** Lock and invalidate the source.
868 * This should be used by commands and editing things
871 MidiModel::edit_lock()
873 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
874 _midi_source->invalidate(); // Release cached iterator's read lock on model
875 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
878 /** Lock just the model, the source lock must already be held.
879 * This should only be called from libardour/evoral places
882 MidiModel::write_lock()
884 assert(!_midi_source->mutex().trylock());
885 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
889 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
891 using namespace Evoral;
893 if (_writing || insert_merge_policy() == InsertMergeRelax) {
897 DiffCommand* cmd = static_cast<DiffCommand*>(arg);
899 TimeType sa = note->time();
900 TimeType ea = note->end_time();
902 const Pitches& p (pitches (note->channel()));
903 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
904 set<NotePtr> to_be_deleted;
905 bool set_note_length = false;
906 bool set_note_time = false;
907 TimeType note_time = note->time();
908 TimeType note_length = note->length();
910 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
912 for (Pitches::const_iterator i = p.lower_bound (search_note);
913 i != p.end() && (*i)->note() == note->note(); ++i) {
915 TimeType sb = (*i)->time();
916 TimeType eb = (*i)->end_time();
917 OverlapType overlap = OverlapNone;
920 if ((sb > sa) && (eb <= ea)) {
921 overlap = OverlapInternal;
922 } else if ((eb >= sa) && (eb <= ea)) {
923 overlap = OverlapStart;
924 } else if ((sb > sa) && (sb <= ea)) {
925 overlap = OverlapEnd;
926 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
927 overlap = OverlapExternal;
933 DEBUG_TRACE (DEBUG::Sequence, string_compose ("\toverlap is %1 for (%2,%3) vs (%4,%5)\n", enum_2_string(overlap),
936 if (insert_merge_policy() == InsertMergeReject) {
937 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
943 cerr << "OverlapStart\n";
944 /* existing note covers start of new note */
945 switch (insert_merge_policy()) {
946 case InsertMergeReplace:
947 to_be_deleted.insert (*i);
949 case InsertMergeTruncateExisting:
951 cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
953 (*i)->set_length (note->time() - (*i)->time());
955 case InsertMergeTruncateAddition:
956 set_note_time = true;
957 set_note_length = true;
958 note_time = (*i)->time() + (*i)->length();
959 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
961 case InsertMergeExtend:
963 cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
965 (*i)->set_length (note->end_time() - (*i)->time());
966 return -1; /* do not add the new note */
976 cerr << "OverlapEnd\n";
977 /* existing note covers end of new note */
978 switch (insert_merge_policy()) {
979 case InsertMergeReplace:
980 to_be_deleted.insert (*i);
983 case InsertMergeTruncateExisting:
984 /* resetting the start time of the existing note
985 is a problem because of time ordering.
989 case InsertMergeTruncateAddition:
990 set_note_length = true;
991 note_length = min (note_length, ((*i)->time() - note->time()));
994 case InsertMergeExtend:
995 /* we can't reset the time of the existing note because
996 that will corrupt time ordering. So remove the
997 existing note and change the position/length
998 of the new note (which has not been added yet)
1000 to_be_deleted.insert (*i);
1001 set_note_length = true;
1002 note_length = min (note_length, (*i)->end_time() - note->time());
1011 case OverlapExternal:
1012 cerr << "OverlapExt\n";
1013 /* existing note overlaps all the new note */
1014 switch (insert_merge_policy()) {
1015 case InsertMergeReplace:
1016 to_be_deleted.insert (*i);
1018 case InsertMergeTruncateExisting:
1019 case InsertMergeTruncateAddition:
1020 case InsertMergeExtend:
1021 /* cannot add in this case */
1030 case OverlapInternal:
1031 cerr << "OverlapInt\n";
1032 /* new note fully overlaps an existing note */
1033 switch (insert_merge_policy()) {
1034 case InsertMergeReplace:
1035 case InsertMergeTruncateExisting:
1036 case InsertMergeTruncateAddition:
1037 case InsertMergeExtend:
1038 /* delete the existing note, the new one will cover it */
1039 to_be_deleted.insert (*i);
1055 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1056 remove_note_unlocked (*i);
1059 cmd->side_effect_remove (*i);
1063 if (set_note_time) {
1065 cmd->change (note, DiffCommand::StartTime, note_time);
1067 note->set_time (note_time);
1070 if (set_note_length) {
1072 cmd->change (note, DiffCommand::Length, note_length);
1074 note->set_length (note_length);
1081 MidiModel::insert_merge_policy () const
1083 /* XXX ultimately this should be a per-track or even per-model policy */
1085 return _midi_source->session().config.get_insert_merge_policy();
1089 MidiModel::set_midi_source (MidiSource* s)
1092 _midi_source->invalidate ();
1095 _midi_source_connections.drop_connections ();
1099 _midi_source->InterpolationChanged.connect_same_thread (
1100 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1103 _midi_source->AutomationStateChanged.connect_same_thread (
1104 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1108 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1109 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1110 * appropriate ControlList.
1112 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1113 * or the other is listened to by the GUI.
1116 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1118 Glib::Mutex::Lock lm (_control_lock);
1119 control(p)->list()->set_interpolation (s);
1122 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1123 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1126 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1128 _midi_source->set_interpolation_of (p, s);
1132 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1134 Glib::Mutex::Lock lm (_control_lock);
1135 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1136 al->set_automation_state (s);
1140 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1142 _midi_source->set_automation_state_of (p, s);
1145 boost::shared_ptr<Evoral::Control>
1146 MidiModel::control_factory (Evoral::Parameter const & p)
1148 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1150 /* Set up newly created control's lists to the appropriate interpolation and
1151 automation state from our source.
1154 assert (_midi_source);
1156 c->list()->set_interpolation (_midi_source->interpolation_of (p));
1158 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1161 al->set_automation_state (_midi_source->automation_state_of (p));