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())
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 cerr << "This undo has " << side_effect_removals.size() << " SER's to be re-added\n";
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 cerr << "Reconstruct DiffCommand with " << notes.size() << " SER's\n";
713 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
714 side_effect_removals.insert (unmarshal_note (*n));
722 MidiModel::DiffCommand::get_state ()
724 XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
725 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
727 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
728 for_each(_changes.begin(), _changes.end(),
730 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
731 boost::bind (&DiffCommand::marshal_change, this, _1)));
733 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
734 for_each(_added_notes.begin(), _added_notes.end(),
736 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
737 boost::bind (&DiffCommand::marshal_note, this, _1)));
739 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
740 for_each(_removed_notes.begin(), _removed_notes.end(),
742 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
743 boost::bind (&DiffCommand::marshal_note, this, _1)));
745 /* if this command had side-effects, store that state too
748 if (!side_effect_removals.empty()) {
749 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
750 for_each(side_effect_removals.begin(), side_effect_removals.end(),
752 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
753 boost::bind (&DiffCommand::marshal_note, this, _1)));
756 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.
767 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
769 ReadLock lock(read_lock());
771 const bool old_percussive = percussive();
772 set_percussive(false);
774 source->drop_model();
775 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
777 for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
778 source->append_event_unlocked_beats(*i);
781 set_percussive(old_percussive);
782 source->mark_streaming_write_completed();
789 /** Write part or all of the model to a MidiSource (i.e. save the model).
790 * This is different from manually using read to write to a source in that
791 * note off events are written regardless of the track mode. This is so the
792 * user can switch a recorded track (with note durations from some instrument)
793 * to percussive, save, reload, then switch it back to sustained without
794 * destroying the original note durations.
797 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
799 ReadLock lock(read_lock());
800 MidiStateTracker mst;
801 Evoral::MusicalTime extra_note_on_time = end_time;
803 const bool old_percussive = percussive();
804 set_percussive(false);
806 source->drop_model();
807 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
809 for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
810 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
812 if (ev.time() >= begin_time && ev.time() < end_time) {
814 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
815 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
822 if (mev->is_note_off()) {
824 if (!mst.active (mev->note(), mev->channel())) {
826 /* add a note-on at the start of the range we're writing
827 to the file. velocity is just an arbitary reasonable value.
830 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
831 on.set_type (mev->type());
832 on.set_note (mev->note());
833 on.set_channel (mev->channel());
834 on.set_velocity (mev->velocity());
836 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
837 source->append_event_unlocked_beats (on);
838 mst.add (on.note(), on.channel());
840 extra_note_on_time += 1.0/128.0;
843 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
844 source->append_event_unlocked_beats (*i);
845 mst.remove (mev->note(), mev->channel());
848 } else if (mev->is_note_on()) {
849 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
850 mst.add (mev->note(), mev->channel());
851 source->append_event_unlocked_beats(*i);
854 cerr << "MIDI other event type\n";
855 source->append_event_unlocked_beats(*i);
860 mst.resolve_notes (*source, end_time);
862 set_percussive(old_percussive);
863 source->mark_streaming_write_completed();
871 MidiModel::get_state()
873 XMLNode *node = new XMLNode("MidiModel");
877 Evoral::Sequence<MidiModel::TimeType>::NotePtr
878 MidiModel::find_note (NotePtr other)
880 Notes::iterator l = notes().lower_bound(other);
882 if (l != notes().end()) {
883 for (; (*l)->time() == other->time(); ++l) {
884 /* NB: compare note contents, not note pointers.
885 If "other" was a ptr to a note already in
886 the model, we wouldn't be looking for it,
898 /** Lock and invalidate the source.
899 * This should be used by commands and editing things
902 MidiModel::edit_lock()
904 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
905 _midi_source->invalidate(); // Release cached iterator's read lock on model
906 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
909 /** Lock just the model, the source lock must already be held.
910 * This should only be called from libardour/evoral places
913 MidiModel::write_lock()
915 assert(!_midi_source->mutex().trylock());
916 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
920 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
922 using namespace Evoral;
924 if (_writing || insert_merge_policy() == InsertMergeRelax) {
928 DiffCommand* cmd = static_cast<DiffCommand*>(arg);
930 TimeType sa = note->time();
931 TimeType ea = note->end_time();
933 const Pitches& p (pitches (note->channel()));
934 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
935 set<NotePtr> to_be_deleted;
936 bool set_note_length = false;
937 bool set_note_time = false;
938 TimeType note_time = note->time();
939 TimeType note_length = note->length();
941 for (Pitches::const_iterator i = p.lower_bound (search_note);
942 i != p.end() && (*i)->note() == note->note(); ++i) {
944 TimeType sb = (*i)->time();
945 TimeType eb = (*i)->end_time();
946 OverlapType overlap = OverlapNone;
948 if ((sb > sa) && (eb <= ea)) {
949 overlap = OverlapInternal;
950 } else if ((eb >= sa) && (eb <= ea)) {
951 overlap = OverlapStart;
952 } else if ((sb > sa) && (sb <= ea)) {
953 overlap = OverlapEnd;
954 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
955 overlap = OverlapExternal;
961 if (insert_merge_policy() == InsertMergeReject) {
967 cerr << "OverlapStart\n";
968 /* existing note covers start of new note */
969 switch (insert_merge_policy()) {
970 case InsertMergeReplace:
971 to_be_deleted.insert (*i);
973 case InsertMergeTruncateExisting:
975 cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
977 (*i)->set_length (note->time() - (*i)->time());
979 case InsertMergeTruncateAddition:
980 set_note_time = true;
981 set_note_length = true;
982 note_time = (*i)->time() + (*i)->length();
983 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
985 case InsertMergeExtend:
987 cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
989 (*i)->set_length (note->end_time() - (*i)->time());
990 return -1; /* do not add the new note */
1000 cerr << "OverlapEnd\n";
1001 /* existing note covers end of new note */
1002 switch (insert_merge_policy()) {
1003 case InsertMergeReplace:
1004 to_be_deleted.insert (*i);
1007 case InsertMergeTruncateExisting:
1008 /* resetting the start time of the existing note
1009 is a problem because of time ordering.
1013 case InsertMergeTruncateAddition:
1014 set_note_length = true;
1015 note_length = min (note_length, ((*i)->time() - note->time()));
1018 case InsertMergeExtend:
1019 /* we can't reset the time of the existing note because
1020 that will corrupt time ordering. So remove the
1021 existing note and change the position/length
1022 of the new note (which has not been added yet)
1024 to_be_deleted.insert (*i);
1025 set_note_length = true;
1026 note_length = min (note_length, (*i)->end_time() - note->time());
1035 case OverlapExternal:
1036 cerr << "OverlapExt\n";
1037 /* existing note overlaps all the new note */
1038 switch (insert_merge_policy()) {
1039 case InsertMergeReplace:
1040 to_be_deleted.insert (*i);
1042 case InsertMergeTruncateExisting:
1043 case InsertMergeTruncateAddition:
1044 case InsertMergeExtend:
1045 /* cannot add in this case */
1054 case OverlapInternal:
1055 cerr << "OverlapInt\n";
1056 /* new note fully overlaps an existing note */
1057 switch (insert_merge_policy()) {
1058 case InsertMergeReplace:
1059 case InsertMergeTruncateExisting:
1060 case InsertMergeTruncateAddition:
1061 case InsertMergeExtend:
1062 /* delete the existing note, the new one will cover it */
1063 to_be_deleted.insert (*i);
1079 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1080 remove_note_unlocked (*i);
1083 cmd->side_effect_remove (*i);
1087 if (set_note_time) {
1089 cmd->change (note, DiffCommand::StartTime, note_time);
1091 note->set_time (note_time);
1094 if (set_note_length) {
1096 cmd->change (note, DiffCommand::Length, note_length);
1098 note->set_length (note_length);
1105 MidiModel::insert_merge_policy () const
1107 /* XXX ultimately this should be a per-track or even per-model policy */
1109 return _midi_source->session().config.get_insert_merge_policy();
1113 MidiModel::set_midi_source (MidiSource* s)
1115 _midi_source->invalidate ();