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 _model->add_note_unlocked(*i);
238 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
239 _model->remove_note_unlocked(*i);
242 /* notes we modify in a way that requires remove-then-add to maintain ordering */
243 set<NotePtr> temporary_removals;
245 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
246 Property prop = i->property;
249 if (temporary_removals.find (i->note) == temporary_removals.end()) {
250 _model->remove_note_unlocked (i->note);
251 temporary_removals.insert (i->note);
253 i->note->set_note (i->new_value);
257 if (temporary_removals.find (i->note) == temporary_removals.end()) {
258 _model->remove_note_unlocked (i->note);
259 temporary_removals.insert (i->note);
262 i->note->set_time (i->new_time);
266 if (temporary_removals.find (i->note) == temporary_removals.end()) {
267 _model->remove_note_unlocked (i->note);
268 temporary_removals.insert (i->note);
270 i->note->set_channel (i->new_value);
273 /* no remove-then-add required for these properties, since we do not index them
277 i->note->set_velocity (i->new_value);
281 i->note->set_length (i->new_time);
288 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
289 DiffCommand side_effects (model(), "side effects");
290 _model->add_note_unlocked (*i, &side_effects);
291 *this += side_effects;
294 if (!side_effect_removals.empty()) {
296 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
297 cerr << "\t" << *i << ' ' << **i << endl;
302 _model->ContentsChanged(); /* EMIT SIGNAL */
306 MidiModel::DiffCommand::undo()
309 MidiModel::WriteLock lock(_model->edit_lock());
311 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
312 _model->remove_note_unlocked(*i);
315 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
316 _model->add_note_unlocked(*i);
319 /* notes we modify in a way that requires remove-then-add to maintain ordering */
320 set<NotePtr> temporary_removals;
322 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
323 Property prop = i->property;
326 if (temporary_removals.find (i->note) == temporary_removals.end()) {
327 _model->remove_note_unlocked (i->note);
328 temporary_removals.insert (i->note);
330 i->note->set_note (i->old_value);
333 i->note->set_velocity (i->old_value);
336 if (temporary_removals.find (i->note) == temporary_removals.end()) {
337 _model->remove_note_unlocked (i->note);
338 temporary_removals.insert (i->note);
340 i->note->set_time (i->old_time);
343 i->note->set_length (i->old_time);
346 if (temporary_removals.find (i->note) == temporary_removals.end()) {
347 _model->remove_note_unlocked (i->note);
348 temporary_removals.insert (i->note);
350 i->note->set_channel (i->old_value);
355 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
356 _model->add_note_unlocked (*i);
359 /* finally add back notes that were removed by the "do". we don't care
360 about side effects here since the model should be back to its original
361 state once this is done.
364 cerr << "This undo has " << side_effect_removals.size() << " SER's to be re-added\n";
365 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
366 _model->add_note_unlocked (*i);
370 _model->ContentsChanged(); /* EMIT SIGNAL */
374 MidiModel::DiffCommand::marshal_note(const NotePtr note)
376 XMLNode* xml_note = new XMLNode("note");
378 cerr << "Marshalling note: " << *note << endl;
380 ostringstream note_str(ios::ate);
381 note_str << int(note->note());
382 xml_note->add_property("note", note_str.str());
384 ostringstream channel_str(ios::ate);
385 channel_str << int(note->channel());
386 xml_note->add_property("channel", channel_str.str());
388 ostringstream time_str(ios::ate);
389 time_str << note->time();
390 xml_note->add_property("time", time_str.str());
392 ostringstream length_str(ios::ate);
393 length_str << note->length();
394 xml_note->add_property("length", length_str.str());
396 ostringstream velocity_str(ios::ate);
397 velocity_str << (unsigned int) note->velocity();
398 xml_note->add_property("velocity", velocity_str.str());
403 Evoral::Sequence<MidiModel::TimeType>::NotePtr
404 MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
408 unsigned int channel;
411 unsigned int velocity;
413 if ((prop = xml_note->property("note")) != 0) {
414 istringstream note_str(prop->value());
417 warning << "note information missing note value" << endmsg;
421 if ((prop = xml_note->property("channel")) != 0) {
422 istringstream channel_str(prop->value());
423 channel_str >> channel;
425 warning << "note information missing channel" << endmsg;
429 if ((prop = xml_note->property("time")) != 0) {
430 istringstream time_str(prop->value());
433 warning << "note information missing time" << endmsg;
437 if ((prop = xml_note->property("length")) != 0) {
438 istringstream length_str(prop->value());
439 length_str >> length;
441 warning << "note information missing length" << endmsg;
445 if ((prop = xml_note->property("velocity")) != 0) {
446 istringstream velocity_str(prop->value());
447 velocity_str >> velocity;
449 warning << "note information missing velocity" << endmsg;
453 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
459 MidiModel::DiffCommand::marshal_change(const NoteChange& change)
461 XMLNode* xml_change = new XMLNode("change");
463 /* first, the change itself */
465 xml_change->add_property ("property", enum_2_string (change.property));
468 ostringstream old_value_str (ios::ate);
469 if (change.property == StartTime || change.property == Length) {
470 old_value_str << change.old_time;
472 old_value_str << (unsigned int) change.old_value;
474 xml_change->add_property ("old", old_value_str.str());
478 ostringstream new_value_str (ios::ate);
479 if (change.property == StartTime || change.property == Length) {
480 new_value_str << change.new_time;
482 new_value_str << (unsigned int) change.new_value;
484 xml_change->add_property ("new", new_value_str.str());
487 /* now the rest of the note */
489 const SMFSource* smf = dynamic_cast<const SMFSource*> (_model->midi_source());
491 if (change.property != NoteNumber) {
492 ostringstream note_str;
493 note_str << int(change.note->note());
494 xml_change->add_property("note", note_str.str());
497 if (change.property != Channel) {
498 ostringstream channel_str;
499 channel_str << int(change.note->channel());
500 xml_change->add_property("channel", channel_str.str());
503 if (change.property != StartTime) {
504 ostringstream time_str;
506 time_str << smf->round_to_file_precision (change.note->time());
508 time_str << change.note->time();
510 xml_change->add_property("time", time_str.str());
513 if (change.property != Length) {
514 ostringstream length_str;
516 length_str << smf->round_to_file_precision (change.note->length());
518 length_str << change.note->length();
520 xml_change->add_property ("length", length_str.str());
523 if (change.property != Velocity) {
524 ostringstream velocity_str;
525 velocity_str << int (change.note->velocity());
526 xml_change->add_property("velocity", velocity_str.str());
529 /* and now notes that were remove as a side-effect */
534 MidiModel::DiffCommand::NoteChange
535 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
540 unsigned int channel;
541 unsigned int velocity;
542 Evoral::MusicalTime time;
543 Evoral::MusicalTime length;
545 if ((prop = xml_change->property("property")) != 0) {
546 change.property = (Property) string_2_enum (prop->value(), change.property);
548 fatal << "!!!" << endmsg;
552 if ((prop = xml_change->property ("old")) != 0) {
553 istringstream old_str (prop->value());
554 if (change.property == StartTime || change.property == Length) {
555 old_str >> change.old_time;
557 int integer_value_so_that_istream_does_the_right_thing;
558 old_str >> integer_value_so_that_istream_does_the_right_thing;
559 change.old_value = integer_value_so_that_istream_does_the_right_thing;
562 fatal << "!!!" << endmsg;
566 if ((prop = xml_change->property ("new")) != 0) {
567 istringstream new_str (prop->value());
568 if (change.property == StartTime || change.property == Length) {
569 new_str >> change.new_time;
571 int integer_value_so_that_istream_does_the_right_thing;
572 new_str >> integer_value_so_that_istream_does_the_right_thing;
573 change.new_value = integer_value_so_that_istream_does_the_right_thing;
576 fatal << "!!!" << endmsg;
580 if (change.property != NoteNumber) {
581 if ((prop = xml_change->property("note")) != 0) {
582 istringstream note_str(prop->value());
585 warning << "note information missing note value" << endmsg;
589 note = change.new_value;
592 if (change.property != Channel) {
593 if ((prop = xml_change->property("channel")) != 0) {
594 istringstream channel_str(prop->value());
595 channel_str >> channel;
597 warning << "note information missing channel" << endmsg;
601 channel = change.new_value;
604 if (change.property != StartTime) {
605 if ((prop = xml_change->property("time")) != 0) {
606 istringstream time_str(prop->value());
609 warning << "note information missing time" << endmsg;
613 time = change.new_time;
616 if (change.property != Length) {
617 if ((prop = xml_change->property("length")) != 0) {
618 istringstream length_str(prop->value());
619 length_str >> length;
621 warning << "note information missing length" << endmsg;
625 length = change.new_time;
628 if (change.property != Velocity) {
629 if ((prop = xml_change->property("velocity")) != 0) {
630 istringstream velocity_str(prop->value());
631 velocity_str >> velocity;
633 warning << "note information missing velocity" << endmsg;
637 velocity = change.new_value;
640 /* we must point at the instance of the note that is actually in the model.
641 so go look for it ...
644 NotePtr new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
646 change.note = _model->find_note (new_note);
649 warning << "MIDI note " << *new_note << " not found in model - programmers should investigate this" << endmsg;
650 /* use the actual new note */
651 change.note = new_note;
658 MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
660 if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
666 _added_notes.clear();
667 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
669 XMLNodeList notes = added_notes->children();
670 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
671 boost::bind (&DiffCommand::unmarshal_note, this, _1));
677 _removed_notes.clear();
678 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
680 XMLNodeList notes = removed_notes->children();
681 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
682 boost::bind (&DiffCommand::unmarshal_note, this, _1));
690 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
693 XMLNodeList notes = changed_notes->children();
694 transform (notes.begin(), notes.end(), back_inserter(_changes),
695 boost::bind (&DiffCommand::unmarshal_change, this, _1));
699 /* side effect removals caused by changes */
701 side_effect_removals.clear();
703 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
705 if (side_effect_notes) {
706 XMLNodeList notes = side_effect_notes->children();
707 cerr << "Reconstruct DiffCommand with " << notes.size() << " SER's\n";
708 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
709 side_effect_removals.insert (unmarshal_note (*n));
717 MidiModel::DiffCommand::get_state ()
719 XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
720 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
722 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
723 for_each(_changes.begin(), _changes.end(),
725 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
726 boost::bind (&DiffCommand::marshal_change, this, _1)));
728 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
729 for_each(_added_notes.begin(), _added_notes.end(),
731 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
732 boost::bind (&DiffCommand::marshal_note, this, _1)));
734 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
735 for_each(_removed_notes.begin(), _removed_notes.end(),
737 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
738 boost::bind (&DiffCommand::marshal_note, this, _1)));
740 /* if this command had side-effects, store that state too
743 if (!side_effect_removals.empty()) {
744 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
745 for_each(side_effect_removals.begin(), side_effect_removals.end(),
747 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
748 boost::bind (&DiffCommand::marshal_note, this, _1)));
751 return *diff_command;
754 /** Write all of the model to a MidiSource (i.e. save the model).
755 * This is different from manually using read to write to a source in that
756 * note off events are written regardless of the track mode. This is so the
757 * user can switch a recorded track (with note durations from some instrument)
758 * to percussive, save, reload, then switch it back to sustained without
759 * destroying the original note durations.
762 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
764 ReadLock lock(read_lock());
766 const bool old_percussive = percussive();
767 set_percussive(false);
769 source->drop_model();
770 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
772 for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
773 source->append_event_unlocked_beats(*i);
776 set_percussive(old_percussive);
777 source->mark_streaming_write_completed();
784 /** Write part or all of the model to a MidiSource (i.e. save the model).
785 * This is different from manually using read to write to a source in that
786 * note off events are written regardless of the track mode. This is so the
787 * user can switch a recorded track (with note durations from some instrument)
788 * to percussive, save, reload, then switch it back to sustained without
789 * destroying the original note durations.
792 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
794 ReadLock lock(read_lock());
795 MidiStateTracker mst;
796 Evoral::MusicalTime extra_note_on_time = end_time;
798 const bool old_percussive = percussive();
799 set_percussive(false);
801 source->drop_model();
802 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
804 for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
805 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
807 if (ev.time() >= begin_time && ev.time() < end_time) {
809 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
810 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
817 if (mev->is_note_off()) {
819 if (!mst.active (mev->note(), mev->channel())) {
821 /* add a note-on at the start of the range we're writing
822 to the file. velocity is just an arbitary reasonable value.
825 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
826 on.set_type (mev->type());
827 on.set_note (mev->note());
828 on.set_channel (mev->channel());
829 on.set_velocity (mev->velocity());
831 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
832 source->append_event_unlocked_beats (on);
833 mst.add (on.note(), on.channel());
835 extra_note_on_time += 1.0/128.0;
838 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
839 source->append_event_unlocked_beats (*i);
840 mst.remove (mev->note(), mev->channel());
843 } else if (mev->is_note_on()) {
844 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
845 mst.add (mev->note(), mev->channel());
846 source->append_event_unlocked_beats(*i);
849 cerr << "MIDI other event type\n";
850 source->append_event_unlocked_beats(*i);
855 mst.resolve_notes (*source, end_time);
857 set_percussive(old_percussive);
858 source->mark_streaming_write_completed();
866 MidiModel::get_state()
868 XMLNode *node = new XMLNode("MidiModel");
872 Evoral::Sequence<MidiModel::TimeType>::NotePtr
873 MidiModel::find_note (NotePtr other)
875 Notes::iterator l = notes().lower_bound(other);
877 if (l != notes().end()) {
878 for (; (*l)->time() == other->time(); ++l) {
879 /* NB: compare note contents, not note pointers.
880 If "other" was a ptr to a note already in
881 the model, we wouldn't be looking for it,
893 /** Lock and invalidate the source.
894 * This should be used by commands and editing things
897 MidiModel::edit_lock()
899 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
900 _midi_source->invalidate(); // Release cached iterator's read lock on model
901 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
904 /** Lock just the model, the source lock must already be held.
905 * This should only be called from libardour/evoral places
908 MidiModel::write_lock()
910 assert(!_midi_source->mutex().trylock());
911 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
915 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
917 using namespace Evoral;
919 if (_writing || insert_merge_policy() == InsertMergeRelax) {
923 DiffCommand* cmd = static_cast<DiffCommand*>(arg);
925 TimeType sa = note->time();
926 TimeType ea = note->end_time();
928 const Pitches& p (pitches (note->channel()));
929 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
930 set<NotePtr> to_be_deleted;
931 bool set_note_length = false;
932 bool set_note_time = false;
933 TimeType note_time = note->time();
934 TimeType note_length = note->length();
936 for (Pitches::const_iterator i = p.lower_bound (search_note);
937 i != p.end() && (*i)->note() == note->note(); ++i) {
939 TimeType sb = (*i)->time();
940 TimeType eb = (*i)->end_time();
941 OverlapType overlap = OverlapNone;
943 if ((sb > sa) && (eb <= ea)) {
944 overlap = OverlapInternal;
945 } else if ((eb >= sa) && (eb <= ea)) {
946 overlap = OverlapStart;
947 } else if ((sb > sa) && (sb <= ea)) {
948 overlap = OverlapEnd;
949 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
950 overlap = OverlapExternal;
956 if (insert_merge_policy() == InsertMergeReject) {
962 cerr << "OverlapStart\n";
963 /* existing note covers start of new note */
964 switch (insert_merge_policy()) {
965 case InsertMergeReplace:
966 to_be_deleted.insert (*i);
968 case InsertMergeTruncateExisting:
970 cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
972 (*i)->set_length (note->time() - (*i)->time());
974 case InsertMergeTruncateAddition:
975 set_note_time = true;
976 set_note_length = true;
977 note_time = (*i)->time() + (*i)->length();
978 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
980 case InsertMergeExtend:
982 cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
984 (*i)->set_length (note->end_time() - (*i)->time());
985 return -1; /* do not add the new note */
995 cerr << "OverlapEnd\n";
996 /* existing note covers end of new note */
997 switch (insert_merge_policy()) {
998 case InsertMergeReplace:
999 to_be_deleted.insert (*i);
1002 case InsertMergeTruncateExisting:
1003 /* resetting the start time of the existing note
1004 is a problem because of time ordering.
1008 case InsertMergeTruncateAddition:
1009 set_note_length = true;
1010 note_length = min (note_length, ((*i)->time() - note->time()));
1013 case InsertMergeExtend:
1014 /* we can't reset the time of the existing note because
1015 that will corrupt time ordering. So remove the
1016 existing note and change the position/length
1017 of the new note (which has not been added yet)
1019 to_be_deleted.insert (*i);
1020 set_note_length = true;
1021 note_length = min (note_length, (*i)->end_time() - note->time());
1030 case OverlapExternal:
1031 cerr << "OverlapExt\n";
1032 /* existing note overlaps all the new note */
1033 switch (insert_merge_policy()) {
1034 case InsertMergeReplace:
1035 to_be_deleted.insert (*i);
1037 case InsertMergeTruncateExisting:
1038 case InsertMergeTruncateAddition:
1039 case InsertMergeExtend:
1040 /* cannot add in this case */
1049 case OverlapInternal:
1050 cerr << "OverlapInt\n";
1051 /* new note fully overlaps an existing note */
1052 switch (insert_merge_policy()) {
1053 case InsertMergeReplace:
1054 case InsertMergeTruncateExisting:
1055 case InsertMergeTruncateAddition:
1056 case InsertMergeExtend:
1057 /* delete the existing note, the new one will cover it */
1058 to_be_deleted.insert (*i);
1074 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1075 remove_note_unlocked (*i);
1078 cmd->side_effect_remove (*i);
1082 if (set_note_time) {
1084 cmd->change (note, DiffCommand::StartTime, note_time);
1086 note->set_time (note_time);
1089 if (set_note_length) {
1091 cmd->change (note, DiffCommand::Length, note_length);
1093 note->set_length (note_length);
1100 MidiModel::insert_merge_policy () const
1102 char* c = getenv ("AMP");
1104 if (!c || c[0] == 0) {
1105 return InsertMergeReject;
1110 return InsertMergeRelax;
1112 return InsertMergeReplace;
1114 return InsertMergeTruncateExisting;
1116 return InsertMergeTruncateAddition;
1119 return InsertMergeExtend;