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 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");
382 cerr << "Marshalling note: " << *note << endl;
384 ostringstream note_str(ios::ate);
385 note_str << int(note->note());
386 xml_note->add_property("note", note_str.str());
388 ostringstream channel_str(ios::ate);
389 channel_str << int(note->channel());
390 xml_note->add_property("channel", channel_str.str());
392 ostringstream time_str(ios::ate);
393 time_str << note->time();
394 xml_note->add_property("time", time_str.str());
396 ostringstream length_str(ios::ate);
397 length_str << note->length();
398 xml_note->add_property("length", length_str.str());
400 ostringstream velocity_str(ios::ate);
401 velocity_str << (unsigned int) note->velocity();
402 xml_note->add_property("velocity", velocity_str.str());
407 Evoral::Sequence<MidiModel::TimeType>::NotePtr
408 MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
412 unsigned int channel;
415 unsigned int velocity;
417 if ((prop = xml_note->property("note")) != 0) {
418 istringstream note_str(prop->value());
421 warning << "note information missing note value" << endmsg;
425 if ((prop = xml_note->property("channel")) != 0) {
426 istringstream channel_str(prop->value());
427 channel_str >> channel;
429 warning << "note information missing channel" << endmsg;
433 if ((prop = xml_note->property("time")) != 0) {
434 istringstream time_str(prop->value());
437 warning << "note information missing time" << endmsg;
441 if ((prop = xml_note->property("length")) != 0) {
442 istringstream length_str(prop->value());
443 length_str >> length;
445 warning << "note information missing length" << endmsg;
449 if ((prop = xml_note->property("velocity")) != 0) {
450 istringstream velocity_str(prop->value());
451 velocity_str >> velocity;
453 warning << "note information missing velocity" << endmsg;
457 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
463 MidiModel::DiffCommand::marshal_change(const NoteChange& change)
465 XMLNode* xml_change = new XMLNode("change");
467 /* first, the change itself */
469 xml_change->add_property ("property", enum_2_string (change.property));
472 ostringstream old_value_str (ios::ate);
473 if (change.property == StartTime || change.property == Length) {
474 old_value_str << change.old_time;
476 old_value_str << (unsigned int) change.old_value;
478 xml_change->add_property ("old", old_value_str.str());
482 ostringstream new_value_str (ios::ate);
483 if (change.property == StartTime || change.property == Length) {
484 new_value_str << change.new_time;
486 new_value_str << (unsigned int) change.new_value;
488 xml_change->add_property ("new", new_value_str.str());
491 /* now the rest of the note */
493 const SMFSource* smf = dynamic_cast<const SMFSource*> (_model->midi_source());
495 if (change.property != NoteNumber) {
496 ostringstream note_str;
497 note_str << int(change.note->note());
498 xml_change->add_property("note", note_str.str());
501 if (change.property != Channel) {
502 ostringstream channel_str;
503 channel_str << int(change.note->channel());
504 xml_change->add_property("channel", channel_str.str());
507 if (change.property != StartTime) {
508 ostringstream time_str;
510 time_str << smf->round_to_file_precision (change.note->time());
512 time_str << change.note->time();
514 xml_change->add_property("time", time_str.str());
517 if (change.property != Length) {
518 ostringstream length_str;
520 length_str << smf->round_to_file_precision (change.note->length());
522 length_str << change.note->length();
524 xml_change->add_property ("length", length_str.str());
527 if (change.property != Velocity) {
528 ostringstream velocity_str;
529 velocity_str << int (change.note->velocity());
530 xml_change->add_property("velocity", velocity_str.str());
533 /* and now notes that were remove as a side-effect */
538 MidiModel::DiffCommand::NoteChange
539 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
544 unsigned int channel;
545 unsigned int velocity;
546 Evoral::MusicalTime time;
547 Evoral::MusicalTime length;
549 if ((prop = xml_change->property("property")) != 0) {
550 change.property = (Property) string_2_enum (prop->value(), change.property);
552 fatal << "!!!" << endmsg;
556 if ((prop = xml_change->property ("old")) != 0) {
557 istringstream old_str (prop->value());
558 if (change.property == StartTime || change.property == Length) {
559 old_str >> change.old_time;
561 int integer_value_so_that_istream_does_the_right_thing;
562 old_str >> integer_value_so_that_istream_does_the_right_thing;
563 change.old_value = integer_value_so_that_istream_does_the_right_thing;
566 fatal << "!!!" << endmsg;
570 if ((prop = xml_change->property ("new")) != 0) {
571 istringstream new_str (prop->value());
572 if (change.property == StartTime || change.property == Length) {
573 new_str >> change.new_time;
575 int integer_value_so_that_istream_does_the_right_thing;
576 new_str >> integer_value_so_that_istream_does_the_right_thing;
577 change.new_value = integer_value_so_that_istream_does_the_right_thing;
580 fatal << "!!!" << endmsg;
584 if (change.property != NoteNumber) {
585 if ((prop = xml_change->property("note")) != 0) {
586 istringstream note_str(prop->value());
589 warning << "note information missing note value" << endmsg;
593 note = change.new_value;
596 if (change.property != Channel) {
597 if ((prop = xml_change->property("channel")) != 0) {
598 istringstream channel_str(prop->value());
599 channel_str >> channel;
601 warning << "note information missing channel" << endmsg;
605 channel = change.new_value;
608 if (change.property != StartTime) {
609 if ((prop = xml_change->property("time")) != 0) {
610 istringstream time_str(prop->value());
613 warning << "note information missing time" << endmsg;
617 time = change.new_time;
620 if (change.property != Length) {
621 if ((prop = xml_change->property("length")) != 0) {
622 istringstream length_str(prop->value());
623 length_str >> length;
625 warning << "note information missing length" << endmsg;
629 length = change.new_time;
632 if (change.property != Velocity) {
633 if ((prop = xml_change->property("velocity")) != 0) {
634 istringstream velocity_str(prop->value());
635 velocity_str >> velocity;
637 warning << "note information missing velocity" << endmsg;
641 velocity = change.new_value;
644 /* we must point at the instance of the note that is actually in the model.
645 so go look for it ...
648 NotePtr new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
650 change.note = _model->find_note (new_note);
653 warning << "MIDI note " << *new_note << " not found in model - programmers should investigate this" << endmsg;
654 /* use the actual new note */
655 change.note = new_note;
662 MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
664 if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
670 _added_notes.clear();
671 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
673 XMLNodeList notes = added_notes->children();
674 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
675 boost::bind (&DiffCommand::unmarshal_note, this, _1));
681 _removed_notes.clear();
682 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
684 XMLNodeList notes = removed_notes->children();
685 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
686 boost::bind (&DiffCommand::unmarshal_note, this, _1));
694 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
697 XMLNodeList notes = changed_notes->children();
698 transform (notes.begin(), notes.end(), back_inserter(_changes),
699 boost::bind (&DiffCommand::unmarshal_change, this, _1));
703 /* side effect removals caused by changes */
705 side_effect_removals.clear();
707 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
709 if (side_effect_notes) {
710 XMLNodeList notes = side_effect_notes->children();
711 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
712 side_effect_removals.insert (unmarshal_note (*n));
720 MidiModel::DiffCommand::get_state ()
722 XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
723 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
725 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
726 for_each(_changes.begin(), _changes.end(),
728 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
729 boost::bind (&DiffCommand::marshal_change, this, _1)));
731 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
732 for_each(_added_notes.begin(), _added_notes.end(),
734 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
735 boost::bind (&DiffCommand::marshal_note, this, _1)));
737 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
738 for_each(_removed_notes.begin(), _removed_notes.end(),
740 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
741 boost::bind (&DiffCommand::marshal_note, this, _1)));
743 /* if this command had side-effects, store that state too
746 if (!side_effect_removals.empty()) {
747 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
748 for_each(side_effect_removals.begin(), side_effect_removals.end(),
750 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
751 boost::bind (&DiffCommand::marshal_note, this, _1)));
754 return *diff_command;
757 /** Write all of the model to a MidiSource (i.e. save the model).
758 * This is different from manually using read to write to a source in that
759 * note off events are written regardless of the track mode. This is so the
760 * user can switch a recorded track (with note durations from some instrument)
761 * to percussive, save, reload, then switch it back to sustained without
762 * destroying the original note durations.
765 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
767 ReadLock lock(read_lock());
769 const bool old_percussive = percussive();
770 set_percussive(false);
772 source->drop_model();
773 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
775 for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
776 source->append_event_unlocked_beats(*i);
779 set_percussive(old_percussive);
780 source->mark_streaming_write_completed();
787 /** Write part or all of the model to a MidiSource (i.e. save the model).
788 * This is different from manually using read to write to a source in that
789 * note off events are written regardless of the track mode. This is so the
790 * user can switch a recorded track (with note durations from some instrument)
791 * to percussive, save, reload, then switch it back to sustained without
792 * destroying the original note durations.
795 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
797 ReadLock lock(read_lock());
798 MidiStateTracker mst;
799 Evoral::MusicalTime extra_note_on_time = end_time;
801 const bool old_percussive = percussive();
802 set_percussive(false);
804 source->drop_model();
805 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
807 for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
808 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
810 if (ev.time() >= begin_time && ev.time() < end_time) {
812 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
813 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
820 if (mev->is_note_off()) {
822 if (!mst.active (mev->note(), mev->channel())) {
824 /* add a note-on at the start of the range we're writing
825 to the file. velocity is just an arbitary reasonable value.
828 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
829 on.set_type (mev->type());
830 on.set_note (mev->note());
831 on.set_channel (mev->channel());
832 on.set_velocity (mev->velocity());
834 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
835 source->append_event_unlocked_beats (on);
836 mst.add (on.note(), on.channel());
838 extra_note_on_time += 1.0/128.0;
841 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
842 source->append_event_unlocked_beats (*i);
843 mst.remove (mev->note(), mev->channel());
846 } else if (mev->is_note_on()) {
847 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
848 mst.add (mev->note(), mev->channel());
849 source->append_event_unlocked_beats(*i);
852 cerr << "MIDI other event type\n";
853 source->append_event_unlocked_beats(*i);
858 mst.resolve_notes (*source, end_time);
860 set_percussive(old_percussive);
861 source->mark_streaming_write_completed();
869 MidiModel::get_state()
871 XMLNode *node = new XMLNode("MidiModel");
875 Evoral::Sequence<MidiModel::TimeType>::NotePtr
876 MidiModel::find_note (NotePtr other)
878 Notes::iterator l = notes().lower_bound(other);
880 if (l != notes().end()) {
881 for (; (*l)->time() == other->time(); ++l) {
882 /* NB: compare note contents, not note pointers.
883 If "other" was a ptr to a note already in
884 the model, we wouldn't be looking for it,
896 /** Lock and invalidate the source.
897 * This should be used by commands and editing things
900 MidiModel::edit_lock()
902 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
903 _midi_source->invalidate(); // Release cached iterator's read lock on model
904 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
907 /** Lock just the model, the source lock must already be held.
908 * This should only be called from libardour/evoral places
911 MidiModel::write_lock()
913 assert(!_midi_source->mutex().trylock());
914 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
918 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
920 using namespace Evoral;
922 if (_writing || insert_merge_policy() == InsertMergeRelax) {
926 DiffCommand* cmd = static_cast<DiffCommand*>(arg);
928 TimeType sa = note->time();
929 TimeType ea = note->end_time();
931 const Pitches& p (pitches (note->channel()));
932 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
933 set<NotePtr> to_be_deleted;
934 bool set_note_length = false;
935 bool set_note_time = false;
936 TimeType note_time = note->time();
937 TimeType note_length = note->length();
939 for (Pitches::const_iterator i = p.lower_bound (search_note);
940 i != p.end() && (*i)->note() == note->note(); ++i) {
942 TimeType sb = (*i)->time();
943 TimeType eb = (*i)->end_time();
944 OverlapType overlap = OverlapNone;
946 if ((sb > sa) && (eb <= ea)) {
947 overlap = OverlapInternal;
948 } else if ((eb >= sa) && (eb <= ea)) {
949 overlap = OverlapStart;
950 } else if ((sb > sa) && (sb <= ea)) {
951 overlap = OverlapEnd;
952 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
953 overlap = OverlapExternal;
959 if (insert_merge_policy() == InsertMergeReject) {
965 cerr << "OverlapStart\n";
966 /* existing note covers start of new note */
967 switch (insert_merge_policy()) {
968 case InsertMergeReplace:
969 to_be_deleted.insert (*i);
971 case InsertMergeTruncateExisting:
973 cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
975 (*i)->set_length (note->time() - (*i)->time());
977 case InsertMergeTruncateAddition:
978 set_note_time = true;
979 set_note_length = true;
980 note_time = (*i)->time() + (*i)->length();
981 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
983 case InsertMergeExtend:
985 cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
987 (*i)->set_length (note->end_time() - (*i)->time());
988 return -1; /* do not add the new note */
998 cerr << "OverlapEnd\n";
999 /* existing note covers end of new note */
1000 switch (insert_merge_policy()) {
1001 case InsertMergeReplace:
1002 to_be_deleted.insert (*i);
1005 case InsertMergeTruncateExisting:
1006 /* resetting the start time of the existing note
1007 is a problem because of time ordering.
1011 case InsertMergeTruncateAddition:
1012 set_note_length = true;
1013 note_length = min (note_length, ((*i)->time() - note->time()));
1016 case InsertMergeExtend:
1017 /* we can't reset the time of the existing note because
1018 that will corrupt time ordering. So remove the
1019 existing note and change the position/length
1020 of the new note (which has not been added yet)
1022 to_be_deleted.insert (*i);
1023 set_note_length = true;
1024 note_length = min (note_length, (*i)->end_time() - note->time());
1033 case OverlapExternal:
1034 cerr << "OverlapExt\n";
1035 /* existing note overlaps all the new note */
1036 switch (insert_merge_policy()) {
1037 case InsertMergeReplace:
1038 to_be_deleted.insert (*i);
1040 case InsertMergeTruncateExisting:
1041 case InsertMergeTruncateAddition:
1042 case InsertMergeExtend:
1043 /* cannot add in this case */
1052 case OverlapInternal:
1053 cerr << "OverlapInt\n";
1054 /* new note fully overlaps an existing note */
1055 switch (insert_merge_policy()) {
1056 case InsertMergeReplace:
1057 case InsertMergeTruncateExisting:
1058 case InsertMergeTruncateAddition:
1059 case InsertMergeExtend:
1060 /* delete the existing note, the new one will cover it */
1061 to_be_deleted.insert (*i);
1077 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1078 remove_note_unlocked (*i);
1081 cmd->side_effect_remove (*i);
1085 if (set_note_time) {
1087 cmd->change (note, DiffCommand::StartTime, note_time);
1089 note->set_time (note_time);
1092 if (set_note_length) {
1094 cmd->change (note, DiffCommand::Length, note_length);
1096 note->set_length (note_length);
1103 MidiModel::insert_merge_policy () const
1105 /* XXX ultimately this should be a per-track or even per-model policy */
1107 return _midi_source->session().config.get_insert_merge_policy();
1111 MidiModel::set_midi_source (MidiSource* s)
1113 _midi_source->invalidate ();