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;
758 /** Write all of the model to a MidiSource (i.e. save the model).
759 * This is different from manually using read to write to a source in that
760 * note off events are written regardless of the track mode. This is so the
761 * user can switch a recorded track (with note durations from some instrument)
762 * to percussive, save, reload, then switch it back to sustained without
763 * destroying the original note durations.
766 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
768 ReadLock lock(read_lock());
770 const bool old_percussive = percussive();
771 set_percussive(false);
773 source->drop_model();
774 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
776 for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
777 source->append_event_unlocked_beats(*i);
780 set_percussive(old_percussive);
781 source->mark_streaming_write_completed();
788 /** very similar to ::write_to() but writes to the model's own
789 existing midi_source, without making it call MidiSource::drop_model().
790 the caller is a MidiSource that needs to catch up with the state
794 MidiModel::sync_to_source ()
796 ReadLock lock(read_lock());
798 const bool old_percussive = percussive();
799 set_percussive(false);
801 _midi_source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
803 for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
804 _midi_source->append_event_unlocked_beats(*i);
807 set_percussive (old_percussive);
808 _midi_source->mark_streaming_write_completed ();
815 /** Write part or all of the model to a MidiSource (i.e. save the model).
816 * This is different from manually using read to write to a source in that
817 * note off events are written regardless of the track mode. This is so the
818 * user can switch a recorded track (with note durations from some instrument)
819 * to percussive, save, reload, then switch it back to sustained without
820 * destroying the original note durations.
823 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
825 ReadLock lock(read_lock());
826 MidiStateTracker mst;
827 Evoral::MusicalTime extra_note_on_time = end_time;
829 const bool old_percussive = percussive();
830 set_percussive(false);
832 source->drop_model();
833 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
835 for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
836 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
838 if (ev.time() >= begin_time && ev.time() < end_time) {
840 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
841 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
848 if (mev->is_note_off()) {
850 if (!mst.active (mev->note(), mev->channel())) {
852 /* add a note-on at the start of the range we're writing
853 to the file. velocity is just an arbitary reasonable value.
856 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
857 on.set_type (mev->type());
858 on.set_note (mev->note());
859 on.set_channel (mev->channel());
860 on.set_velocity (mev->velocity());
862 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
863 source->append_event_unlocked_beats (on);
864 mst.add (on.note(), on.channel());
866 extra_note_on_time += 1.0/128.0;
869 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
870 source->append_event_unlocked_beats (*i);
871 mst.remove (mev->note(), mev->channel());
874 } else if (mev->is_note_on()) {
875 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
876 mst.add (mev->note(), mev->channel());
877 source->append_event_unlocked_beats(*i);
880 cerr << "MIDI other event type\n";
881 source->append_event_unlocked_beats(*i);
886 mst.resolve_notes (*source, end_time);
888 set_percussive(old_percussive);
889 source->mark_streaming_write_completed();
897 MidiModel::get_state()
899 XMLNode *node = new XMLNode("MidiModel");
903 Evoral::Sequence<MidiModel::TimeType>::NotePtr
904 MidiModel::find_note (NotePtr other)
906 Notes::iterator l = notes().lower_bound(other);
908 if (l != notes().end()) {
909 for (; (*l)->time() == other->time(); ++l) {
910 /* NB: compare note contents, not note pointers.
911 If "other" was a ptr to a note already in
912 the model, we wouldn't be looking for it,
924 /** Lock and invalidate the source.
925 * This should be used by commands and editing things
928 MidiModel::edit_lock()
930 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
931 _midi_source->invalidate(); // Release cached iterator's read lock on model
932 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
935 /** Lock just the model, the source lock must already be held.
936 * This should only be called from libardour/evoral places
939 MidiModel::write_lock()
941 assert(!_midi_source->mutex().trylock());
942 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
946 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
948 using namespace Evoral;
950 if (_writing || insert_merge_policy() == InsertMergeRelax) {
954 DiffCommand* cmd = static_cast<DiffCommand*>(arg);
956 TimeType sa = note->time();
957 TimeType ea = note->end_time();
959 const Pitches& p (pitches (note->channel()));
960 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
961 set<NotePtr> to_be_deleted;
962 bool set_note_length = false;
963 bool set_note_time = false;
964 TimeType note_time = note->time();
965 TimeType note_length = note->length();
967 for (Pitches::const_iterator i = p.lower_bound (search_note);
968 i != p.end() && (*i)->note() == note->note(); ++i) {
970 TimeType sb = (*i)->time();
971 TimeType eb = (*i)->end_time();
972 OverlapType overlap = OverlapNone;
974 if ((sb > sa) && (eb <= ea)) {
975 overlap = OverlapInternal;
976 } else if ((eb >= sa) && (eb <= ea)) {
977 overlap = OverlapStart;
978 } else if ((sb > sa) && (sb <= ea)) {
979 overlap = OverlapEnd;
980 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
981 overlap = OverlapExternal;
987 if (insert_merge_policy() == InsertMergeReject) {
993 cerr << "OverlapStart\n";
994 /* existing note covers start of new note */
995 switch (insert_merge_policy()) {
996 case InsertMergeReplace:
997 to_be_deleted.insert (*i);
999 case InsertMergeTruncateExisting:
1001 cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
1003 (*i)->set_length (note->time() - (*i)->time());
1005 case InsertMergeTruncateAddition:
1006 set_note_time = true;
1007 set_note_length = true;
1008 note_time = (*i)->time() + (*i)->length();
1009 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1011 case InsertMergeExtend:
1013 cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
1015 (*i)->set_length (note->end_time() - (*i)->time());
1016 return -1; /* do not add the new note */
1026 cerr << "OverlapEnd\n";
1027 /* existing note covers end of new note */
1028 switch (insert_merge_policy()) {
1029 case InsertMergeReplace:
1030 to_be_deleted.insert (*i);
1033 case InsertMergeTruncateExisting:
1034 /* resetting the start time of the existing note
1035 is a problem because of time ordering.
1039 case InsertMergeTruncateAddition:
1040 set_note_length = true;
1041 note_length = min (note_length, ((*i)->time() - note->time()));
1044 case InsertMergeExtend:
1045 /* we can't reset the time of the existing note because
1046 that will corrupt time ordering. So remove the
1047 existing note and change the position/length
1048 of the new note (which has not been added yet)
1050 to_be_deleted.insert (*i);
1051 set_note_length = true;
1052 note_length = min (note_length, (*i)->end_time() - note->time());
1061 case OverlapExternal:
1062 cerr << "OverlapExt\n";
1063 /* existing note overlaps all the new note */
1064 switch (insert_merge_policy()) {
1065 case InsertMergeReplace:
1066 to_be_deleted.insert (*i);
1068 case InsertMergeTruncateExisting:
1069 case InsertMergeTruncateAddition:
1070 case InsertMergeExtend:
1071 /* cannot add in this case */
1080 case OverlapInternal:
1081 cerr << "OverlapInt\n";
1082 /* new note fully overlaps an existing note */
1083 switch (insert_merge_policy()) {
1084 case InsertMergeReplace:
1085 case InsertMergeTruncateExisting:
1086 case InsertMergeTruncateAddition:
1087 case InsertMergeExtend:
1088 /* delete the existing note, the new one will cover it */
1089 to_be_deleted.insert (*i);
1105 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1106 remove_note_unlocked (*i);
1109 cmd->side_effect_remove (*i);
1113 if (set_note_time) {
1115 cmd->change (note, DiffCommand::StartTime, note_time);
1117 note->set_time (note_time);
1120 if (set_note_length) {
1122 cmd->change (note, DiffCommand::Length, note_length);
1124 note->set_length (note_length);
1131 MidiModel::insert_merge_policy () const
1133 /* XXX ultimately this should be a per-track or even per-model policy */
1135 return _midi_source->session().config.get_insert_merge_policy();
1139 MidiModel::set_midi_source (MidiSource* s)
1141 _midi_source->invalidate ();