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())
49 /** Start a new Diff command.
51 * This has no side-effects on the model or Session, the returned command
52 * can be held on to for as long as the caller wishes, or discarded without
53 * formality, until apply_command is called and ownership is taken.
55 MidiModel::DiffCommand
*
56 MidiModel::new_diff_command(const string name
)
58 DiffCommand
* cmd
= new DiffCommand(_midi_source
->model(), name
);
64 * Ownership of cmd is taken, it must not be deleted by the caller.
65 * The command will constitute one item on the undo stack.
68 MidiModel::apply_command(Session
& session
, Command
* cmd
)
70 session
.begin_reversible_command(cmd
->name());
72 session
.commit_reversible_command(cmd
);
76 /** Apply a command as part of a larger reversible transaction
78 * Ownership of cmd is taken, it must not be deleted by the caller.
79 * The command will constitute one item on the undo stack.
82 MidiModel::apply_command_as_subcommand(Session
& session
, Command
* cmd
)
85 session
.add_command(cmd
);
89 /************** DIFF COMMAND ********************/
91 #define DIFF_COMMAND_ELEMENT "DiffCommand"
92 #define DIFF_NOTES_ELEMENT "ChangedNotes"
93 #define ADDED_NOTES_ELEMENT "AddedNotes"
94 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
95 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
97 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr
<MidiModel
> m
, const std::string
& name
)
105 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr
<MidiModel
> m
, const XMLNode
& node
)
109 set_state(node
, Stateful::loading_state_version
);
113 MidiModel::DiffCommand::add(const NotePtr note
)
115 _removed_notes
.remove(note
);
116 _added_notes
.push_back(note
);
120 MidiModel::DiffCommand::remove(const NotePtr note
)
122 _added_notes
.remove(note
);
123 _removed_notes
.push_back(note
);
127 MidiModel::DiffCommand::side_effect_remove(const NotePtr note
)
129 side_effect_removals
.insert (note
);
133 MidiModel::DiffCommand::change(const NotePtr note
, Property prop
,
140 if (new_value
== note
->note()) {
143 change
.old_value
= note
->note();
146 if (new_value
== note
->velocity()) {
149 change
.old_value
= note
->velocity();
152 if (new_value
== note
->channel()) {
155 change
.old_value
= note
->channel();
160 fatal
<< "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg
;
164 fatal
<< "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg
;
170 change
.property
= prop
;
171 change
.new_value
= new_value
;
173 _changes
.push_back (change
);
177 MidiModel::DiffCommand::change(const NotePtr note
, Property prop
,
186 fatal
<< "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg
;
190 if (Evoral::musical_time_equal (note
->time(), new_time
)) {
193 change
.old_time
= note
->time();
196 if (Evoral::musical_time_equal (note
->length(), new_time
)) {
199 change
.old_time
= note
->length();
204 change
.property
= prop
;
205 change
.new_time
= new_time
;
207 _changes
.push_back (change
);
210 MidiModel::DiffCommand
&
211 MidiModel::DiffCommand::operator+= (const DiffCommand
& other
)
213 if (this == &other
) {
217 if (_model
!= other
._model
) {
221 _added_notes
.insert (_added_notes
.end(), other
._added_notes
.begin(), other
._added_notes
.end());
222 _removed_notes
.insert (_removed_notes
.end(), other
._removed_notes
.begin(), other
._removed_notes
.end());
223 side_effect_removals
.insert (other
.side_effect_removals
.begin(), other
.side_effect_removals
.end());
224 _changes
.insert (_changes
.end(), other
._changes
.begin(), other
._changes
.end());
230 MidiModel::DiffCommand::operator()()
233 MidiModel::WriteLock
lock(_model
->edit_lock());
235 for (NoteList::iterator i
= _added_notes
.begin(); i
!= _added_notes
.end(); ++i
) {
236 if (!_model
->add_note_unlocked(*i
)) {
237 /* failed to add it, so don't leave it in the removed list, to
238 avoid apparent errors on undo.
240 _removed_notes
.remove (*i
);
244 for (NoteList::iterator i
= _removed_notes
.begin(); i
!= _removed_notes
.end(); ++i
) {
245 _model
->remove_note_unlocked(*i
);
248 /* notes we modify in a way that requires remove-then-add to maintain ordering */
249 set
<NotePtr
> temporary_removals
;
251 for (ChangeList::iterator i
= _changes
.begin(); i
!= _changes
.end(); ++i
) {
252 Property prop
= i
->property
;
255 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
256 _model
->remove_note_unlocked (i
->note
);
257 temporary_removals
.insert (i
->note
);
259 i
->note
->set_note (i
->new_value
);
263 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
264 _model
->remove_note_unlocked (i
->note
);
265 temporary_removals
.insert (i
->note
);
268 i
->note
->set_time (i
->new_time
);
272 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
273 _model
->remove_note_unlocked (i
->note
);
274 temporary_removals
.insert (i
->note
);
276 i
->note
->set_channel (i
->new_value
);
279 /* no remove-then-add required for these properties, since we do not index them
283 i
->note
->set_velocity (i
->new_value
);
287 i
->note
->set_length (i
->new_time
);
294 for (set
<NotePtr
>::iterator i
= temporary_removals
.begin(); i
!= temporary_removals
.end(); ++i
) {
295 DiffCommand
side_effects (model(), "side effects");
296 _model
->add_note_unlocked (*i
, &side_effects
);
297 *this += side_effects
;
300 if (!side_effect_removals
.empty()) {
302 for (set
<NotePtr
>::iterator i
= side_effect_removals
.begin(); i
!= side_effect_removals
.end(); ++i
) {
303 cerr
<< "\t" << *i
<< ' ' << **i
<< endl
;
308 _model
->ContentsChanged(); /* EMIT SIGNAL */
312 MidiModel::DiffCommand::undo()
315 MidiModel::WriteLock
lock(_model
->edit_lock());
317 for (NoteList::iterator i
= _added_notes
.begin(); i
!= _added_notes
.end(); ++i
) {
318 _model
->remove_note_unlocked(*i
);
321 for (NoteList::iterator i
= _removed_notes
.begin(); i
!= _removed_notes
.end(); ++i
) {
322 _model
->add_note_unlocked(*i
);
325 /* notes we modify in a way that requires remove-then-add to maintain ordering */
326 set
<NotePtr
> temporary_removals
;
328 for (ChangeList::iterator i
= _changes
.begin(); i
!= _changes
.end(); ++i
) {
329 Property prop
= i
->property
;
332 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
333 _model
->remove_note_unlocked (i
->note
);
334 temporary_removals
.insert (i
->note
);
336 i
->note
->set_note (i
->old_value
);
339 i
->note
->set_velocity (i
->old_value
);
342 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
343 _model
->remove_note_unlocked (i
->note
);
344 temporary_removals
.insert (i
->note
);
346 i
->note
->set_time (i
->old_time
);
349 i
->note
->set_length (i
->old_time
);
352 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
353 _model
->remove_note_unlocked (i
->note
);
354 temporary_removals
.insert (i
->note
);
356 i
->note
->set_channel (i
->old_value
);
361 for (set
<NotePtr
>::iterator i
= temporary_removals
.begin(); i
!= temporary_removals
.end(); ++i
) {
362 _model
->add_note_unlocked (*i
);
365 /* finally add back notes that were removed by the "do". we don't care
366 about side effects here since the model should be back to its original
367 state once this is done.
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
;
386 ostringstream
id_str(ios::ate
);
387 id_str
<< int(note
->id());
388 xml_note
->add_property("id", id_str
.str());
392 ostringstream
note_str(ios::ate
);
393 note_str
<< int(note
->note());
394 xml_note
->add_property("note", note_str
.str());
398 ostringstream
channel_str(ios::ate
);
399 channel_str
<< int(note
->channel());
400 xml_note
->add_property("channel", channel_str
.str());
404 ostringstream
time_str(ios::ate
);
405 time_str
<< note
->time();
406 xml_note
->add_property("time", time_str
.str());
410 ostringstream
length_str(ios::ate
);
411 length_str
<< note
->length();
412 xml_note
->add_property("length", length_str
.str());
416 ostringstream
velocity_str(ios::ate
);
417 velocity_str
<< (unsigned int) note
->velocity();
418 xml_note
->add_property("velocity", velocity_str
.str());
424 Evoral::Sequence
<MidiModel::TimeType
>::NotePtr
425 MidiModel::DiffCommand::unmarshal_note(XMLNode
*xml_note
)
429 unsigned int channel
;
432 unsigned int velocity
;
435 if ((prop
= xml_note
->property("id")) != 0) {
436 istringstream
id_str(prop
->value());
439 error
<< "note information missing ID value" << endmsg
;
443 if ((prop
= xml_note
->property("note")) != 0) {
444 istringstream
note_str(prop
->value());
447 warning
<< "note information missing note value" << endmsg
;
451 if ((prop
= xml_note
->property("channel")) != 0) {
452 istringstream
channel_str(prop
->value());
453 channel_str
>> channel
;
455 warning
<< "note information missing channel" << endmsg
;
459 if ((prop
= xml_note
->property("time")) != 0) {
460 istringstream
time_str(prop
->value());
463 warning
<< "note information missing time" << endmsg
;
467 if ((prop
= xml_note
->property("length")) != 0) {
468 istringstream
length_str(prop
->value());
469 length_str
>> length
;
471 warning
<< "note information missing length" << endmsg
;
475 if ((prop
= xml_note
->property("velocity")) != 0) {
476 istringstream
velocity_str(prop
->value());
477 velocity_str
>> velocity
;
479 warning
<< "note information missing velocity" << endmsg
;
483 NotePtr
note_ptr(new Evoral::Note
<TimeType
>(channel
, time
, length
, note
, velocity
));
484 note_ptr
->set_id (id
);
490 MidiModel::DiffCommand::marshal_change(const NoteChange
& change
)
492 XMLNode
* xml_change
= new XMLNode("Change");
494 /* first, the change itself */
496 xml_change
->add_property ("property", enum_2_string (change
.property
));
499 ostringstream
old_value_str (ios::ate
);
500 if (change
.property
== StartTime
|| change
.property
== Length
) {
501 old_value_str
<< change
.old_time
;
503 old_value_str
<< (unsigned int) change
.old_value
;
505 xml_change
->add_property ("old", old_value_str
.str());
509 ostringstream
new_value_str (ios::ate
);
510 if (change
.property
== StartTime
|| change
.property
== Length
) {
511 new_value_str
<< change
.new_time
;
513 new_value_str
<< (unsigned int) change
.new_value
;
515 xml_change
->add_property ("new", new_value_str
.str());
518 ostringstream id_str
;
519 id_str
<< change
.note
->id();
520 xml_change
->add_property ("id", id_str
.str());
525 MidiModel::DiffCommand::NoteChange
526 MidiModel::DiffCommand::unmarshal_change(XMLNode
*xml_change
)
531 if ((prop
= xml_change
->property("property")) != 0) {
532 change
.property
= (Property
) string_2_enum (prop
->value(), change
.property
);
534 fatal
<< "!!!" << endmsg
;
538 if ((prop
= xml_change
->property ("id")) == 0) {
539 error
<< _("No NoteID found for note property change - ignored") << endmsg
;
543 gint note_id
= atoi (prop
->value().c_str());
545 if ((prop
= xml_change
->property ("old")) != 0) {
546 istringstream
old_str (prop
->value());
547 if (change
.property
== StartTime
|| change
.property
== Length
) {
548 old_str
>> change
.old_time
;
550 int integer_value_so_that_istream_does_the_right_thing
;
551 old_str
>> integer_value_so_that_istream_does_the_right_thing
;
552 change
.old_value
= integer_value_so_that_istream_does_the_right_thing
;
555 fatal
<< "!!!" << endmsg
;
559 if ((prop
= xml_change
->property ("new")) != 0) {
560 istringstream
new_str (prop
->value());
561 if (change
.property
== StartTime
|| change
.property
== Length
) {
562 new_str
>> change
.new_time
;
564 int integer_value_so_that_istream_does_the_right_thing
;
565 new_str
>> integer_value_so_that_istream_does_the_right_thing
;
566 change
.new_value
= integer_value_so_that_istream_does_the_right_thing
;
569 fatal
<< "!!!" << endmsg
;
573 /* we must point at the instance of the note that is actually in the model.
574 so go look for it ...
577 change
.note
= _model
->find_note (note_id
);
580 warning
<< "MIDI note #" << note_id
<< " not found in model - programmers should investigate this" << endmsg
;
588 MidiModel::DiffCommand::set_state(const XMLNode
& diff_command
, int /*version*/)
590 if (diff_command
.name() != string(DIFF_COMMAND_ELEMENT
)) {
596 _added_notes
.clear();
597 XMLNode
* added_notes
= diff_command
.child(ADDED_NOTES_ELEMENT
);
599 XMLNodeList notes
= added_notes
->children();
600 transform(notes
.begin(), notes
.end(), back_inserter(_added_notes
),
601 boost::bind (&DiffCommand::unmarshal_note
, this, _1
));
607 _removed_notes
.clear();
608 XMLNode
* removed_notes
= diff_command
.child(REMOVED_NOTES_ELEMENT
);
610 XMLNodeList notes
= removed_notes
->children();
611 transform(notes
.begin(), notes
.end(), back_inserter(_removed_notes
),
612 boost::bind (&DiffCommand::unmarshal_note
, this, _1
));
620 XMLNode
* changed_notes
= diff_command
.child(DIFF_NOTES_ELEMENT
);
623 XMLNodeList notes
= changed_notes
->children();
624 transform (notes
.begin(), notes
.end(), back_inserter(_changes
),
625 boost::bind (&DiffCommand::unmarshal_change
, this, _1
));
629 /* side effect removals caused by changes */
631 side_effect_removals
.clear();
633 XMLNode
* side_effect_notes
= diff_command
.child(SIDE_EFFECT_REMOVALS_ELEMENT
);
635 if (side_effect_notes
) {
636 XMLNodeList notes
= side_effect_notes
->children();
637 for (XMLNodeList::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
638 side_effect_removals
.insert (unmarshal_note (*n
));
646 MidiModel::DiffCommand::get_state ()
648 XMLNode
* diff_command
= new XMLNode(DIFF_COMMAND_ELEMENT
);
649 diff_command
->add_property("midi-source", _model
->midi_source()->id().to_s());
651 XMLNode
* changes
= diff_command
->add_child(DIFF_NOTES_ELEMENT
);
652 for_each(_changes
.begin(), _changes
.end(),
654 boost::bind (&XMLNode::add_child_nocopy
, changes
, _1
),
655 boost::bind (&DiffCommand::marshal_change
, this, _1
)));
657 XMLNode
* added_notes
= diff_command
->add_child(ADDED_NOTES_ELEMENT
);
658 for_each(_added_notes
.begin(), _added_notes
.end(),
660 boost::bind (&XMLNode::add_child_nocopy
, added_notes
, _1
),
661 boost::bind (&DiffCommand::marshal_note
, this, _1
)));
663 XMLNode
* removed_notes
= diff_command
->add_child(REMOVED_NOTES_ELEMENT
);
664 for_each(_removed_notes
.begin(), _removed_notes
.end(),
666 boost::bind (&XMLNode::add_child_nocopy
, removed_notes
, _1
),
667 boost::bind (&DiffCommand::marshal_note
, this, _1
)));
669 /* if this command had side-effects, store that state too
672 if (!side_effect_removals
.empty()) {
673 XMLNode
* side_effect_notes
= diff_command
->add_child(SIDE_EFFECT_REMOVALS_ELEMENT
);
674 for_each(side_effect_removals
.begin(), side_effect_removals
.end(),
676 boost::bind (&XMLNode::add_child_nocopy
, side_effect_notes
, _1
),
677 boost::bind (&DiffCommand::marshal_note
, this, _1
)));
680 return *diff_command
;
684 /** Write all of the model to a MidiSource (i.e. save the model).
685 * This is different from manually using read to write to a source in that
686 * note off events are written regardless of the track mode. This is so the
687 * user can switch a recorded track (with note durations from some instrument)
688 * to percussive, save, reload, then switch it back to sustained without
689 * destroying the original note durations.
691 * Similarly, control events are written without interpolation (as with the
695 MidiModel::write_to (boost::shared_ptr
<MidiSource
> source
)
697 ReadLock
lock(read_lock());
699 const bool old_percussive
= percussive();
700 set_percussive(false);
702 source
->drop_model();
703 source
->mark_streaming_midi_write_started(note_mode(), _midi_source
->timeline_position());
705 for (Evoral::Sequence
<TimeType
>::const_iterator i
= begin(0, true); i
!= end(); ++i
) {
706 source
->append_event_unlocked_beats(*i
);
709 set_percussive(old_percussive
);
710 source
->mark_streaming_write_completed();
717 /** very similar to ::write_to() but writes to the model's own
718 existing midi_source, without making it call MidiSource::drop_model().
719 the caller is a MidiSource that needs to catch up with the state
723 MidiModel::sync_to_source ()
725 ReadLock
lock(read_lock());
727 const bool old_percussive
= percussive();
728 set_percussive(false);
730 _midi_source
->mark_streaming_midi_write_started(note_mode(), _midi_source
->timeline_position());
732 for (Evoral::Sequence
<TimeType
>::const_iterator i
= begin(0, true); i
!= end(); ++i
) {
733 _midi_source
->append_event_unlocked_beats(*i
);
736 set_percussive (old_percussive
);
737 _midi_source
->mark_streaming_write_completed ();
744 /** Write part or all of the model to a MidiSource (i.e. save the model).
745 * This is different from manually using read to write to a source in that
746 * note off events are written regardless of the track mode. This is so the
747 * user can switch a recorded track (with note durations from some instrument)
748 * to percussive, save, reload, then switch it back to sustained without
749 * destroying the original note durations.
752 MidiModel::write_section_to (boost::shared_ptr
<MidiSource
> source
, Evoral::MusicalTime begin_time
, Evoral::MusicalTime end_time
)
754 ReadLock
lock(read_lock());
755 MidiStateTracker mst
;
756 Evoral::MusicalTime extra_note_on_time
= end_time
;
758 const bool old_percussive
= percussive();
759 set_percussive(false);
761 source
->drop_model();
762 source
->mark_streaming_midi_write_started(note_mode(), _midi_source
->timeline_position());
764 for (Evoral::Sequence
<TimeType
>::const_iterator i
= begin(0, true); i
!= end(); ++i
) {
765 const Evoral::Event
<Evoral::MusicalTime
>& ev (*i
);
767 if (ev
.time() >= begin_time
&& ev
.time() < end_time
) {
769 const Evoral::MIDIEvent
<Evoral::MusicalTime
>* mev
=
770 static_cast<const Evoral::MIDIEvent
<Evoral::MusicalTime
>* > (&ev
);
777 if (mev
->is_note_off()) {
779 if (!mst
.active (mev
->note(), mev
->channel())) {
781 /* add a note-on at the start of the range we're writing
782 to the file. velocity is just an arbitary reasonable value.
785 Evoral::MIDIEvent
<Evoral::MusicalTime
> on (mev
->event_type(), extra_note_on_time
, 3, 0, true);
786 on
.set_type (mev
->type());
787 on
.set_note (mev
->note());
788 on
.set_channel (mev
->channel());
789 on
.set_velocity (mev
->velocity());
791 cerr
<< "Add note on for odd note off, note = " << (int) on
.note() << endl
;
792 source
->append_event_unlocked_beats (on
);
793 mst
.add (on
.note(), on
.channel());
795 extra_note_on_time
+= 1.0/128.0;
798 cerr
<< "MIDI Note off (note = " << (int) mev
->note() << endl
;
799 source
->append_event_unlocked_beats (*i
);
800 mst
.remove (mev
->note(), mev
->channel());
803 } else if (mev
->is_note_on()) {
804 cerr
<< "MIDI Note on (note = " << (int) mev
->note() << endl
;
805 mst
.add (mev
->note(), mev
->channel());
806 source
->append_event_unlocked_beats(*i
);
809 cerr
<< "MIDI other event type\n";
810 source
->append_event_unlocked_beats(*i
);
815 mst
.resolve_notes (*source
, end_time
);
817 set_percussive(old_percussive
);
818 source
->mark_streaming_write_completed();
826 MidiModel::get_state()
828 XMLNode
*node
= new XMLNode("MidiModel");
832 Evoral::Sequence
<MidiModel::TimeType
>::NotePtr
833 MidiModel::find_note (NotePtr other
)
835 Notes::iterator l
= notes().lower_bound(other
);
837 if (l
!= notes().end()) {
838 for (; (*l
)->time() == other
->time(); ++l
) {
839 /* NB: compare note contents, not note pointers.
840 If "other" was a ptr to a note already in
841 the model, we wouldn't be looking for it,
853 Evoral::Sequence
<MidiModel::TimeType
>::NotePtr
854 MidiModel::find_note (gint note_id
)
856 /* used only for looking up notes when reloading history from disk,
857 so we don't care about performance *too* much.
860 for (Notes::iterator l
= notes().begin(); l
!= notes().end(); ++l
) {
861 if ((*l
)->id() == note_id
) {
869 /** Lock and invalidate the source.
870 * This should be used by commands and editing things
873 MidiModel::edit_lock()
875 Glib::Mutex::Lock
* source_lock
= new Glib::Mutex::Lock(_midi_source
->mutex());
876 _midi_source
->invalidate(); // Release cached iterator's read lock on model
877 return WriteLock(new WriteLockImpl(source_lock
, _lock
, _control_lock
));
880 /** Lock just the model, the source lock must already be held.
881 * This should only be called from libardour/evoral places
884 MidiModel::write_lock()
886 assert(!_midi_source
->mutex().trylock());
887 return WriteLock(new WriteLockImpl(NULL
, _lock
, _control_lock
));
891 MidiModel::resolve_overlaps_unlocked (const NotePtr note
, void* arg
)
893 using namespace Evoral
;
895 if (_writing
|| insert_merge_policy() == InsertMergeRelax
) {
899 DiffCommand
* cmd
= static_cast<DiffCommand
*>(arg
);
901 TimeType sa
= note
->time();
902 TimeType ea
= note
->end_time();
904 const Pitches
& p (pitches (note
->channel()));
905 NotePtr
search_note(new Note
<TimeType
>(0, 0, 0, note
->note()));
906 set
<NotePtr
> to_be_deleted
;
907 bool set_note_length
= false;
908 bool set_note_time
= false;
909 TimeType note_time
= note
->time();
910 TimeType note_length
= note
->length();
912 for (Pitches::const_iterator i
= p
.lower_bound (search_note
);
913 i
!= p
.end() && (*i
)->note() == note
->note(); ++i
) {
915 TimeType sb
= (*i
)->time();
916 TimeType eb
= (*i
)->end_time();
917 OverlapType overlap
= OverlapNone
;
919 if ((sb
> sa
) && (eb
<= ea
)) {
920 overlap
= OverlapInternal
;
921 } else if ((eb
>= sa
) && (eb
<= ea
)) {
922 overlap
= OverlapStart
;
923 } else if ((sb
> sa
) && (sb
<= ea
)) {
924 overlap
= OverlapEnd
;
925 } else if ((sa
>= sb
) && (sa
<= eb
) && (ea
<= eb
)) {
926 overlap
= OverlapExternal
;
932 if (insert_merge_policy() == InsertMergeReject
) {
938 cerr
<< "OverlapStart\n";
939 /* existing note covers start of new note */
940 switch (insert_merge_policy()) {
941 case InsertMergeReplace
:
942 to_be_deleted
.insert (*i
);
944 case InsertMergeTruncateExisting
:
946 cmd
->change (*i
, DiffCommand::Length
, (note
->time() - (*i
)->time()));
948 (*i
)->set_length (note
->time() - (*i
)->time());
950 case InsertMergeTruncateAddition
:
951 set_note_time
= true;
952 set_note_length
= true;
953 note_time
= (*i
)->time() + (*i
)->length();
954 note_length
= min (note_length
, (*i
)->length() - ((*i
)->end_time() - note
->time()));
956 case InsertMergeExtend
:
958 cmd
->change ((*i
), DiffCommand::Length
, note
->end_time() - (*i
)->time());
960 (*i
)->set_length (note
->end_time() - (*i
)->time());
961 return -1; /* do not add the new note */
971 cerr
<< "OverlapEnd\n";
972 /* existing note covers end of new note */
973 switch (insert_merge_policy()) {
974 case InsertMergeReplace
:
975 to_be_deleted
.insert (*i
);
978 case InsertMergeTruncateExisting
:
979 /* resetting the start time of the existing note
980 is a problem because of time ordering.
984 case InsertMergeTruncateAddition
:
985 set_note_length
= true;
986 note_length
= min (note_length
, ((*i
)->time() - note
->time()));
989 case InsertMergeExtend
:
990 /* we can't reset the time of the existing note because
991 that will corrupt time ordering. So remove the
992 existing note and change the position/length
993 of the new note (which has not been added yet)
995 to_be_deleted
.insert (*i
);
996 set_note_length
= true;
997 note_length
= min (note_length
, (*i
)->end_time() - note
->time());
1006 case OverlapExternal
:
1007 cerr
<< "OverlapExt\n";
1008 /* existing note overlaps all the new note */
1009 switch (insert_merge_policy()) {
1010 case InsertMergeReplace
:
1011 to_be_deleted
.insert (*i
);
1013 case InsertMergeTruncateExisting
:
1014 case InsertMergeTruncateAddition
:
1015 case InsertMergeExtend
:
1016 /* cannot add in this case */
1025 case OverlapInternal
:
1026 cerr
<< "OverlapInt\n";
1027 /* new note fully overlaps an existing note */
1028 switch (insert_merge_policy()) {
1029 case InsertMergeReplace
:
1030 case InsertMergeTruncateExisting
:
1031 case InsertMergeTruncateAddition
:
1032 case InsertMergeExtend
:
1033 /* delete the existing note, the new one will cover it */
1034 to_be_deleted
.insert (*i
);
1050 for (set
<NotePtr
>::iterator i
= to_be_deleted
.begin(); i
!= to_be_deleted
.end(); ++i
) {
1051 remove_note_unlocked (*i
);
1054 cmd
->side_effect_remove (*i
);
1058 if (set_note_time
) {
1060 cmd
->change (note
, DiffCommand::StartTime
, note_time
);
1062 note
->set_time (note_time
);
1065 if (set_note_length
) {
1067 cmd
->change (note
, DiffCommand::Length
, note_length
);
1069 note
->set_length (note_length
);
1076 MidiModel::insert_merge_policy () const
1078 /* XXX ultimately this should be a per-track or even per-model policy */
1080 return _midi_source
->session().config
.get_insert_merge_policy();
1084 MidiModel::set_midi_source (MidiSource
* s
)
1087 _midi_source
->invalidate ();
1090 _midi_source_connections
.drop_connections ();
1094 _midi_source
->InterpolationChanged
.connect_same_thread (
1095 _midi_source_connections
, boost::bind (&MidiModel::source_interpolation_changed
, this, _1
, _2
)
1099 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1100 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1101 * appropriate ControlList.
1103 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and the
1104 * MidiSource's InterpolationChanged signal is listened to by the GUI.
1107 MidiModel::source_interpolation_changed (Evoral::Parameter p
, Evoral::ControlList::InterpolationStyle s
)
1109 Glib::Mutex::Lock
lm (_control_lock
);
1110 control(p
)->list()->set_interpolation (s
);
1113 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1114 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1117 MidiModel::control_list_interpolation_changed (Evoral::Parameter p
, Evoral::ControlList::InterpolationStyle s
)
1119 _midi_source
->set_interpolation_of (p
, s
);
1122 boost::shared_ptr
<Evoral::Control
>
1123 MidiModel::control_factory (Evoral::Parameter
const & p
)
1125 boost::shared_ptr
<Evoral::Control
> c
= Automatable::control_factory (p
);
1127 /* Set up newly created control's lists to the appropriate interpolation state
1131 assert (_midi_source
);
1133 c
->list()->set_interpolation (_midi_source
->interpolation_of (p
));