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.
26 #include "pbd/error.h"
27 #include "pbd/enumwriter.h"
28 #include "midi++/events.h"
30 #include "ardour/midi_model.h"
31 #include "ardour/midi_source.h"
32 #include "ardour/midi_state_tracker.h"
33 #include "ardour/smf_source.h"
34 #include "ardour/types.h"
35 #include "ardour/session.h"
38 using namespace ARDOUR;
41 MidiModel::MidiModel(MidiSource* s)
42 : AutomatableSequence<TimeType>(s->session())
48 /** Start a new Diff command.
50 * This has no side-effects on the model or Session, the returned command
51 * can be held on to for as long as the caller wishes, or discarded without
52 * formality, until apply_command is called and ownership is taken.
54 MidiModel::DiffCommand*
55 MidiModel::new_diff_command(const string name)
57 DiffCommand* cmd = new DiffCommand(_midi_source->model(), name);
63 * Ownership of cmd is taken, it must not be deleted by the caller.
64 * The command will constitute one item on the undo stack.
67 MidiModel::apply_command(Session& session, Command* cmd)
69 session.begin_reversible_command(cmd->name());
71 session.commit_reversible_command(cmd);
75 /** Apply a command as part of a larger reversible transaction
77 * Ownership of cmd is taken, it must not be deleted by the caller.
78 * The command will constitute one item on the undo stack.
81 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
84 session.add_command(cmd);
88 /************** DIFF COMMAND ********************/
90 #define DIFF_COMMAND_ELEMENT "DiffCommand"
91 #define DIFF_NOTES_ELEMENT "ChangedNotes"
92 #define ADDED_NOTES_ELEMENT "AddedNotes"
93 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
94 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
96 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
104 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
108 set_state(node, Stateful::loading_state_version);
112 MidiModel::DiffCommand::add(const NotePtr note)
114 _removed_notes.remove(note);
115 _added_notes.push_back(note);
119 MidiModel::DiffCommand::remove(const NotePtr note)
121 _added_notes.remove(note);
122 _removed_notes.push_back(note);
126 MidiModel::DiffCommand::side_effect_remove(const NotePtr note)
128 side_effect_removals.insert (note);
132 MidiModel::DiffCommand::change(const NotePtr note, Property prop,
139 if (new_value == note->note()) {
142 change.old_value = note->note();
145 if (new_value == note->velocity()) {
148 change.old_value = note->velocity();
151 if (new_value == note->channel()) {
154 change.old_value = note->channel();
159 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
163 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
169 change.property = prop;
170 change.new_value = new_value;
172 _changes.push_back (change);
176 MidiModel::DiffCommand::change(const NotePtr note, Property prop,
185 fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
189 if (Evoral::musical_time_equal (note->time(), new_time)) {
192 change.old_time = note->time();
195 if (Evoral::musical_time_equal (note->length(), new_time)) {
198 change.old_time = note->length();
203 change.property = prop;
204 change.new_time = new_time;
206 _changes.push_back (change);
209 MidiModel::DiffCommand&
210 MidiModel::DiffCommand::operator+= (const DiffCommand& other)
212 if (this == &other) {
216 if (_model != other._model) {
220 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
221 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
222 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
223 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
229 MidiModel::DiffCommand::operator()()
232 MidiModel::WriteLock lock(_model->edit_lock());
234 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
235 if (!_model->add_note_unlocked(*i)) {
236 /* failed to add it, so don't leave it in the removed list, to
237 avoid apparent errors on undo.
239 _removed_notes.remove (*i);
243 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
244 _model->remove_note_unlocked(*i);
247 /* notes we modify in a way that requires remove-then-add to maintain ordering */
248 set<NotePtr> temporary_removals;
250 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
251 Property prop = i->property;
254 if (temporary_removals.find (i->note) == temporary_removals.end()) {
255 _model->remove_note_unlocked (i->note);
256 temporary_removals.insert (i->note);
258 i->note->set_note (i->new_value);
262 if (temporary_removals.find (i->note) == temporary_removals.end()) {
263 _model->remove_note_unlocked (i->note);
264 temporary_removals.insert (i->note);
267 i->note->set_time (i->new_time);
271 if (temporary_removals.find (i->note) == temporary_removals.end()) {
272 _model->remove_note_unlocked (i->note);
273 temporary_removals.insert (i->note);
275 i->note->set_channel (i->new_value);
278 /* no remove-then-add required for these properties, since we do not index them
282 i->note->set_velocity (i->new_value);
286 i->note->set_length (i->new_time);
293 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
294 DiffCommand side_effects (model(), "side effects");
295 _model->add_note_unlocked (*i, &side_effects);
296 *this += side_effects;
299 if (!side_effect_removals.empty()) {
301 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
302 cerr << "\t" << *i << ' ' << **i << endl;
307 _model->ContentsChanged(); /* EMIT SIGNAL */
311 MidiModel::DiffCommand::undo()
314 MidiModel::WriteLock lock(_model->edit_lock());
316 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
317 _model->remove_note_unlocked(*i);
320 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
321 _model->add_note_unlocked(*i);
324 /* notes we modify in a way that requires remove-then-add to maintain ordering */
325 set<NotePtr> temporary_removals;
327 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
328 Property prop = i->property;
331 if (temporary_removals.find (i->note) == temporary_removals.end()) {
332 _model->remove_note_unlocked (i->note);
333 temporary_removals.insert (i->note);
335 i->note->set_note (i->old_value);
338 i->note->set_velocity (i->old_value);
341 if (temporary_removals.find (i->note) == temporary_removals.end()) {
342 _model->remove_note_unlocked (i->note);
343 temporary_removals.insert (i->note);
345 i->note->set_time (i->old_time);
348 i->note->set_length (i->old_time);
351 if (temporary_removals.find (i->note) == temporary_removals.end()) {
352 _model->remove_note_unlocked (i->note);
353 temporary_removals.insert (i->note);
355 i->note->set_channel (i->old_value);
360 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
361 _model->add_note_unlocked (*i);
364 /* finally add back notes that were removed by the "do". we don't care
365 about side effects here since the model should be back to its original
366 state once this is done.
369 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
370 _model->add_note_unlocked (*i);
374 _model->ContentsChanged(); /* EMIT SIGNAL */
378 MidiModel::DiffCommand::marshal_note(const NotePtr note)
380 XMLNode* xml_note = new XMLNode("note");
383 ostringstream id_str(ios::ate);
384 id_str << int(note->id());
385 xml_note->add_property("id", id_str.str());
389 ostringstream note_str(ios::ate);
390 note_str << int(note->note());
391 xml_note->add_property("note", note_str.str());
395 ostringstream channel_str(ios::ate);
396 channel_str << int(note->channel());
397 xml_note->add_property("channel", channel_str.str());
401 ostringstream time_str(ios::ate);
402 time_str << note->time();
403 xml_note->add_property("time", time_str.str());
407 ostringstream length_str(ios::ate);
408 length_str << note->length();
409 xml_note->add_property("length", length_str.str());
413 ostringstream velocity_str(ios::ate);
414 velocity_str << (unsigned int) note->velocity();
415 xml_note->add_property("velocity", velocity_str.str());
421 Evoral::Sequence<MidiModel::TimeType>::NotePtr
422 MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
426 unsigned int channel;
429 unsigned int velocity;
432 if ((prop = xml_note->property("id")) != 0) {
433 istringstream id_str(prop->value());
436 error << "note information missing ID value" << endmsg;
440 if ((prop = xml_note->property("note")) != 0) {
441 istringstream note_str(prop->value());
444 warning << "note information missing note value" << endmsg;
448 if ((prop = xml_note->property("channel")) != 0) {
449 istringstream channel_str(prop->value());
450 channel_str >> channel;
452 warning << "note information missing channel" << endmsg;
456 if ((prop = xml_note->property("time")) != 0) {
457 istringstream time_str(prop->value());
460 warning << "note information missing time" << endmsg;
464 if ((prop = xml_note->property("length")) != 0) {
465 istringstream length_str(prop->value());
466 length_str >> length;
468 warning << "note information missing length" << endmsg;
472 if ((prop = xml_note->property("velocity")) != 0) {
473 istringstream velocity_str(prop->value());
474 velocity_str >> velocity;
476 warning << "note information missing velocity" << endmsg;
480 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
481 note_ptr->set_id (id);
487 MidiModel::DiffCommand::marshal_change(const NoteChange& change)
489 XMLNode* xml_change = new XMLNode("Change");
491 /* first, the change itself */
493 xml_change->add_property ("property", enum_2_string (change.property));
496 ostringstream old_value_str (ios::ate);
497 if (change.property == StartTime || change.property == Length) {
498 old_value_str << change.old_time;
500 old_value_str << (unsigned int) change.old_value;
502 xml_change->add_property ("old", old_value_str.str());
506 ostringstream new_value_str (ios::ate);
507 if (change.property == StartTime || change.property == Length) {
508 new_value_str << change.new_time;
510 new_value_str << (unsigned int) change.new_value;
512 xml_change->add_property ("new", new_value_str.str());
515 ostringstream id_str;
516 id_str << change.note->id();
517 xml_change->add_property ("id", id_str.str());
522 MidiModel::DiffCommand::NoteChange
523 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
528 if ((prop = xml_change->property("property")) != 0) {
529 change.property = (Property) string_2_enum (prop->value(), change.property);
531 fatal << "!!!" << endmsg;
535 if ((prop = xml_change->property ("id")) == 0) {
536 error << _("No NoteID found for note property change - ignored") << endmsg;
540 gint note_id = atoi (prop->value().c_str());
542 if ((prop = xml_change->property ("old")) != 0) {
543 istringstream old_str (prop->value());
544 if (change.property == StartTime || change.property == Length) {
545 old_str >> change.old_time;
547 int integer_value_so_that_istream_does_the_right_thing;
548 old_str >> integer_value_so_that_istream_does_the_right_thing;
549 change.old_value = integer_value_so_that_istream_does_the_right_thing;
552 fatal << "!!!" << endmsg;
556 if ((prop = xml_change->property ("new")) != 0) {
557 istringstream new_str (prop->value());
558 if (change.property == StartTime || change.property == Length) {
559 new_str >> change.new_time;
561 int integer_value_so_that_istream_does_the_right_thing;
562 new_str >> integer_value_so_that_istream_does_the_right_thing;
563 change.new_value = integer_value_so_that_istream_does_the_right_thing;
566 fatal << "!!!" << endmsg;
570 /* we must point at the instance of the note that is actually in the model.
571 so go look for it ...
574 change.note = _model->find_note (note_id);
577 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
585 MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
587 if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
593 _added_notes.clear();
594 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
596 XMLNodeList notes = added_notes->children();
597 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
598 boost::bind (&DiffCommand::unmarshal_note, this, _1));
604 _removed_notes.clear();
605 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
607 XMLNodeList notes = removed_notes->children();
608 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
609 boost::bind (&DiffCommand::unmarshal_note, this, _1));
617 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
620 XMLNodeList notes = changed_notes->children();
621 transform (notes.begin(), notes.end(), back_inserter(_changes),
622 boost::bind (&DiffCommand::unmarshal_change, this, _1));
626 /* side effect removals caused by changes */
628 side_effect_removals.clear();
630 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
632 if (side_effect_notes) {
633 XMLNodeList notes = side_effect_notes->children();
634 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
635 side_effect_removals.insert (unmarshal_note (*n));
643 MidiModel::DiffCommand::get_state ()
645 XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
646 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
648 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
649 for_each(_changes.begin(), _changes.end(),
651 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
652 boost::bind (&DiffCommand::marshal_change, this, _1)));
654 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
655 for_each(_added_notes.begin(), _added_notes.end(),
657 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
658 boost::bind (&DiffCommand::marshal_note, this, _1)));
660 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
661 for_each(_removed_notes.begin(), _removed_notes.end(),
663 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
664 boost::bind (&DiffCommand::marshal_note, this, _1)));
666 /* if this command had side-effects, store that state too
669 if (!side_effect_removals.empty()) {
670 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
671 for_each(side_effect_removals.begin(), side_effect_removals.end(),
673 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
674 boost::bind (&DiffCommand::marshal_note, this, _1)));
677 return *diff_command;
681 /** Write all of the model to a MidiSource (i.e. save the model).
682 * This is different from manually using read to write to a source in that
683 * note off events are written regardless of the track mode. This is so the
684 * user can switch a recorded track (with note durations from some instrument)
685 * to percussive, save, reload, then switch it back to sustained without
686 * destroying the original note durations.
688 * Similarly, control events are written without interpolation (as with the
692 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
694 ReadLock lock(read_lock());
696 const bool old_percussive = percussive();
697 set_percussive(false);
699 source->drop_model();
700 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
702 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
703 source->append_event_unlocked_beats(*i);
706 set_percussive(old_percussive);
707 source->mark_streaming_write_completed();
714 /** very similar to ::write_to() but writes to the model's own
715 existing midi_source, without making it call MidiSource::drop_model().
716 the caller is a MidiSource that needs to catch up with the state
720 MidiModel::sync_to_source ()
722 ReadLock lock(read_lock());
724 const bool old_percussive = percussive();
725 set_percussive(false);
727 _midi_source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
729 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
730 _midi_source->append_event_unlocked_beats(*i);
733 set_percussive (old_percussive);
734 _midi_source->mark_streaming_write_completed ();
741 /** Write part or all of the model to a MidiSource (i.e. save the model).
742 * This is different from manually using read to write to a source in that
743 * note off events are written regardless of the track mode. This is so the
744 * user can switch a recorded track (with note durations from some instrument)
745 * to percussive, save, reload, then switch it back to sustained without
746 * destroying the original note durations.
749 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
751 ReadLock lock(read_lock());
752 MidiStateTracker mst;
753 Evoral::MusicalTime extra_note_on_time = end_time;
755 const bool old_percussive = percussive();
756 set_percussive(false);
758 source->drop_model();
759 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
761 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
762 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
764 if (ev.time() >= begin_time && ev.time() < end_time) {
766 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
767 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
774 if (mev->is_note_off()) {
776 if (!mst.active (mev->note(), mev->channel())) {
778 /* add a note-on at the start of the range we're writing
779 to the file. velocity is just an arbitary reasonable value.
782 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
783 on.set_type (mev->type());
784 on.set_note (mev->note());
785 on.set_channel (mev->channel());
786 on.set_velocity (mev->velocity());
788 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
789 source->append_event_unlocked_beats (on);
790 mst.add (on.note(), on.channel());
792 extra_note_on_time += 1.0/128.0;
795 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
796 source->append_event_unlocked_beats (*i);
797 mst.remove (mev->note(), mev->channel());
800 } else if (mev->is_note_on()) {
801 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
802 mst.add (mev->note(), mev->channel());
803 source->append_event_unlocked_beats(*i);
806 cerr << "MIDI other event type\n";
807 source->append_event_unlocked_beats(*i);
812 mst.resolve_notes (*source, end_time);
814 set_percussive(old_percussive);
815 source->mark_streaming_write_completed();
823 MidiModel::get_state()
825 XMLNode *node = new XMLNode("MidiModel");
829 Evoral::Sequence<MidiModel::TimeType>::NotePtr
830 MidiModel::find_note (NotePtr other)
832 Notes::iterator l = notes().lower_bound(other);
834 if (l != notes().end()) {
835 for (; (*l)->time() == other->time(); ++l) {
836 /* NB: compare note contents, not note pointers.
837 If "other" was a ptr to a note already in
838 the model, we wouldn't be looking for it,
850 Evoral::Sequence<MidiModel::TimeType>::NotePtr
851 MidiModel::find_note (gint note_id)
853 /* used only for looking up notes when reloading history from disk,
854 so we don't care about performance *too* much.
857 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
858 if ((*l)->id() == note_id) {
866 /** Lock and invalidate the source.
867 * This should be used by commands and editing things
870 MidiModel::edit_lock()
872 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
873 _midi_source->invalidate(); // Release cached iterator's read lock on model
874 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
877 /** Lock just the model, the source lock must already be held.
878 * This should only be called from libardour/evoral places
881 MidiModel::write_lock()
883 assert(!_midi_source->mutex().trylock());
884 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
888 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
890 using namespace Evoral;
892 if (_writing || insert_merge_policy() == InsertMergeRelax) {
896 DiffCommand* cmd = static_cast<DiffCommand*>(arg);
898 TimeType sa = note->time();
899 TimeType ea = note->end_time();
901 const Pitches& p (pitches (note->channel()));
902 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
903 set<NotePtr> to_be_deleted;
904 bool set_note_length = false;
905 bool set_note_time = false;
906 TimeType note_time = note->time();
907 TimeType note_length = note->length();
909 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
911 for (Pitches::const_iterator i = p.lower_bound (search_note);
912 i != p.end() && (*i)->note() == note->note(); ++i) {
914 TimeType sb = (*i)->time();
915 TimeType eb = (*i)->end_time();
916 OverlapType overlap = OverlapNone;
919 if ((sb > sa) && (eb <= ea)) {
920 overlap = OverlapInternal;
921 } else if ((eb >= sa) && (eb <= ea)) {
922 overlap = OverlapStart;
923 } else if ((sb > sa) && (sb <= ea)) {
924 overlap = OverlapEnd;
925 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
926 overlap = OverlapExternal;
932 DEBUG_TRACE (DEBUG::Sequence, string_compose ("\toverlap is %1 for (%2,%3) vs (%4,%5)\n", enum_2_string(overlap),
935 if (insert_merge_policy() == InsertMergeReject) {
936 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
942 cerr << "OverlapStart\n";
943 /* existing note covers start of new note */
944 switch (insert_merge_policy()) {
945 case InsertMergeReplace:
946 to_be_deleted.insert (*i);
948 case InsertMergeTruncateExisting:
950 cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
952 (*i)->set_length (note->time() - (*i)->time());
954 case InsertMergeTruncateAddition:
955 set_note_time = true;
956 set_note_length = true;
957 note_time = (*i)->time() + (*i)->length();
958 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
960 case InsertMergeExtend:
962 cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
964 (*i)->set_length (note->end_time() - (*i)->time());
965 return -1; /* do not add the new note */
975 cerr << "OverlapEnd\n";
976 /* existing note covers end of new note */
977 switch (insert_merge_policy()) {
978 case InsertMergeReplace:
979 to_be_deleted.insert (*i);
982 case InsertMergeTruncateExisting:
983 /* resetting the start time of the existing note
984 is a problem because of time ordering.
988 case InsertMergeTruncateAddition:
989 set_note_length = true;
990 note_length = min (note_length, ((*i)->time() - note->time()));
993 case InsertMergeExtend:
994 /* we can't reset the time of the existing note because
995 that will corrupt time ordering. So remove the
996 existing note and change the position/length
997 of the new note (which has not been added yet)
999 to_be_deleted.insert (*i);
1000 set_note_length = true;
1001 note_length = min (note_length, (*i)->end_time() - note->time());
1010 case OverlapExternal:
1011 cerr << "OverlapExt\n";
1012 /* existing note overlaps all the new note */
1013 switch (insert_merge_policy()) {
1014 case InsertMergeReplace:
1015 to_be_deleted.insert (*i);
1017 case InsertMergeTruncateExisting:
1018 case InsertMergeTruncateAddition:
1019 case InsertMergeExtend:
1020 /* cannot add in this case */
1029 case OverlapInternal:
1030 cerr << "OverlapInt\n";
1031 /* new note fully overlaps an existing note */
1032 switch (insert_merge_policy()) {
1033 case InsertMergeReplace:
1034 case InsertMergeTruncateExisting:
1035 case InsertMergeTruncateAddition:
1036 case InsertMergeExtend:
1037 /* delete the existing note, the new one will cover it */
1038 to_be_deleted.insert (*i);
1054 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1055 remove_note_unlocked (*i);
1058 cmd->side_effect_remove (*i);
1062 if (set_note_time) {
1064 cmd->change (note, DiffCommand::StartTime, note_time);
1066 note->set_time (note_time);
1069 if (set_note_length) {
1071 cmd->change (note, DiffCommand::Length, note_length);
1073 note->set_length (note_length);
1080 MidiModel::insert_merge_policy () const
1082 /* XXX ultimately this should be a per-track or even per-model policy */
1084 return _midi_source->session().config.get_insert_merge_policy();
1088 MidiModel::set_midi_source (MidiSource* s)
1091 _midi_source->invalidate ();
1094 _midi_source_connections.drop_connections ();
1098 _midi_source->InterpolationChanged.connect_same_thread (
1099 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1102 _midi_source->AutomationStateChanged.connect_same_thread (
1103 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1107 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1108 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1109 * appropriate ControlList.
1111 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1112 * or the other is listened to by the GUI.
1115 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1117 Glib::Mutex::Lock lm (_control_lock);
1118 control(p)->list()->set_interpolation (s);
1121 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1122 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1125 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1127 _midi_source->set_interpolation_of (p, s);
1131 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1133 Glib::Mutex::Lock lm (_control_lock);
1134 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1135 al->set_automation_state (s);
1139 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1141 _midi_source->set_automation_state_of (p, s);
1144 boost::shared_ptr<Evoral::Control>
1145 MidiModel::control_factory (Evoral::Parameter const & p)
1147 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1149 /* Set up newly created control's lists to the appropriate interpolation and
1150 automation state from our source.
1153 assert (_midi_source);
1155 c->list()->set_interpolation (_midi_source->interpolation_of (p));
1157 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1160 al->set_automation_state (_midi_source->automation_state_of (p));