2 Copyright (C) 2007 Paul Davis
3 Author: David Robillard
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.
27 #include "pbd/compose.h"
28 #include "pbd/enumwriter.h"
29 #include "pbd/error.h"
31 #include "evoral/Control.hpp"
33 #include "midi++/events.h"
35 #include "ardour/automation_control.h"
36 #include "ardour/midi_automation_list_binder.h"
37 #include "ardour/midi_model.h"
38 #include "ardour/midi_source.h"
39 #include "ardour/midi_state_tracker.h"
40 #include "ardour/session.h"
41 #include "ardour/types.h"
46 using namespace ARDOUR;
49 MidiModel::MidiModel (boost::shared_ptr<MidiSource> s)
50 : AutomatableSequence<TimeType>(s->session())
55 /** Start a new NoteDiff command.
57 * This has no side-effects on the model or Session, the returned command
58 * can be held on to for as long as the caller wishes, or discarded without
59 * formality, until apply_command is called and ownership is taken.
61 MidiModel::NoteDiffCommand*
62 MidiModel::new_note_diff_command (const string name)
64 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
67 return new NoteDiffCommand (ms->model(), name);
70 /** Start a new SysExDiff command */
71 MidiModel::SysExDiffCommand*
72 MidiModel::new_sysex_diff_command (const string name)
74 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
77 return new SysExDiffCommand (ms->model(), name);
80 /** Start a new PatchChangeDiff command */
81 MidiModel::PatchChangeDiffCommand*
82 MidiModel::new_patch_change_diff_command (const string name)
84 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
87 return new PatchChangeDiffCommand (ms->model(), name);
93 * Ownership of cmd is taken, it must not be deleted by the caller.
94 * The command will constitute one item on the undo stack.
97 MidiModel::apply_command(Session& session, Command* cmd)
99 session.begin_reversible_command(cmd->name());
101 session.commit_reversible_command(cmd);
105 /** Apply a command as part of a larger reversible transaction
107 * Ownership of cmd is taken, it must not be deleted by the caller.
108 * The command will constitute one item on the undo stack.
111 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
114 session.add_command(cmd);
118 /************** DIFF COMMAND ********************/
120 #define NOTE_DIFF_COMMAND_ELEMENT "NoteDiffCommand"
121 #define DIFF_NOTES_ELEMENT "ChangedNotes"
122 #define ADDED_NOTES_ELEMENT "AddedNotes"
123 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
124 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
125 #define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand"
126 #define DIFF_SYSEXES_ELEMENT "ChangedSysExes"
127 #define PATCH_CHANGE_DIFF_COMMAND_ELEMENT "PatchChangeDiffCommand"
128 #define ADDED_PATCH_CHANGES_ELEMENT "AddedPatchChanges"
129 #define REMOVED_PATCH_CHANGES_ELEMENT "RemovedPatchChanges"
130 #define DIFF_PATCH_CHANGES_ELEMENT "ChangedPatchChanges"
132 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
140 MidiModel::NoteDiffCommand::NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
141 : DiffCommand (m, "")
144 set_state (node, Stateful::loading_state_version);
148 MidiModel::NoteDiffCommand::add (const NotePtr note)
150 _removed_notes.remove(note);
151 _added_notes.push_back(note);
155 MidiModel::NoteDiffCommand::remove (const NotePtr note)
157 _added_notes.remove(note);
158 _removed_notes.push_back(note);
162 MidiModel::NoteDiffCommand::side_effect_remove (const NotePtr note)
164 side_effect_removals.insert (note);
168 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
177 if (new_value == note->note()) {
180 change.old_value = note->note();
183 if (new_value == note->velocity()) {
186 change.old_value = note->velocity();
189 if (new_value == note->channel()) {
192 change.old_value = note->channel();
197 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
198 abort(); /*NOTREACHED*/
201 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
202 abort(); /*NOTREACHED*/
207 change.property = prop;
208 change.new_value = new_value;
210 _changes.push_back (change);
214 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
225 fatal << "MidiModel::NoteDiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
229 if (Evoral::musical_time_equal (note->time(), new_time)) {
232 change.old_time = note->time();
235 if (Evoral::musical_time_equal (note->length(), new_time)) {
238 change.old_time = note->length();
243 change.property = prop;
244 change.new_time = new_time;
246 _changes.push_back (change);
249 MidiModel::NoteDiffCommand &
250 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand& other)
252 if (this == &other) {
256 if (_model != other._model) {
260 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
261 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
262 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
263 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
269 MidiModel::NoteDiffCommand::operator() ()
272 MidiModel::WriteLock lock(_model->edit_lock());
274 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
275 if (!_model->add_note_unlocked(*i)) {
276 /* failed to add it, so don't leave it in the removed list, to
277 avoid apparent errors on undo.
279 _removed_notes.remove (*i);
283 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
284 _model->remove_note_unlocked(*i);
287 /* notes we modify in a way that requires remove-then-add to maintain ordering */
288 set<NotePtr> temporary_removals;
290 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
291 Property prop = i->property;
294 /* note found during deserialization, so try
295 again now that the model state is different.
297 i->note = _model->find_note (i->note_id);
303 if (temporary_removals.find (i->note) == temporary_removals.end()) {
304 _model->remove_note_unlocked (i->note);
305 temporary_removals.insert (i->note);
307 i->note->set_note (i->new_value);
311 if (temporary_removals.find (i->note) == temporary_removals.end()) {
312 _model->remove_note_unlocked (i->note);
313 temporary_removals.insert (i->note);
315 i->note->set_time (i->new_time);
319 if (temporary_removals.find (i->note) == temporary_removals.end()) {
320 _model->remove_note_unlocked (i->note);
321 temporary_removals.insert (i->note);
323 i->note->set_channel (i->new_value);
326 /* no remove-then-add required for these properties, since we do not index them
330 i->note->set_velocity (i->new_value);
334 i->note->set_length (i->new_time);
340 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
341 NoteDiffCommand side_effects (model(), "side effects");
342 if (_model->add_note_unlocked (*i, &side_effects)) {
343 /* The note was re-added ok */
344 *this += side_effects;
346 /* The note that we removed earlier could not be re-added. This change record
347 must say that the note was removed. We'll keep the changes we made, though,
348 as if the note is re-added by the undo the changes must also be undone.
350 _removed_notes.push_back (*i);
354 if (!side_effect_removals.empty()) {
356 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
357 cerr << "\t" << *i << ' ' << **i << endl;
362 _model->ContentsChanged(); /* EMIT SIGNAL */
366 MidiModel::NoteDiffCommand::undo ()
369 MidiModel::WriteLock lock(_model->edit_lock());
371 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
372 _model->remove_note_unlocked(*i);
375 /* Apply changes first; this is important in the case of a note change which
376 resulted in the note being removed by the overlap checker. If the overlap
377 checker removes a note, it will be in _removed_notes. We are going to re-add
378 it below, but first we must undo the changes we made so that the overlap
379 checker doesn't refuse the re-add.
382 /* notes we modify in a way that requires remove-then-add to maintain ordering */
383 set<NotePtr> temporary_removals;
386 /* lazily discover any affected notes that were not discovered when
387 * loading the history because of deletions, etc.
390 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
392 i->note = _model->find_note (i->note_id);
397 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
398 Property prop = i->property;
402 if (temporary_removals.find (i->note) == temporary_removals.end() &&
403 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
405 /* We only need to mark this note for re-add if (a) we haven't
406 already marked it and (b) it isn't on the _removed_notes
407 list (which means that it has already been removed and it
408 will be re-added anyway)
411 _model->remove_note_unlocked (i->note);
412 temporary_removals.insert (i->note);
414 i->note->set_note (i->old_value);
418 if (temporary_removals.find (i->note) == temporary_removals.end() &&
419 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
423 _model->remove_note_unlocked (i->note);
424 temporary_removals.insert (i->note);
426 i->note->set_time (i->old_time);
430 if (temporary_removals.find (i->note) == temporary_removals.end() &&
431 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
435 _model->remove_note_unlocked (i->note);
436 temporary_removals.insert (i->note);
438 i->note->set_channel (i->old_value);
441 /* no remove-then-add required for these properties, since we do not index them
445 i->note->set_velocity (i->old_value);
449 i->note->set_length (i->old_time);
454 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
455 _model->add_note_unlocked(*i);
458 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
459 _model->add_note_unlocked (*i);
462 /* finally add back notes that were removed by the "do". we don't care
463 about side effects here since the model should be back to its original
464 state once this is done.
467 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
468 _model->add_note_unlocked (*i);
472 _model->ContentsChanged(); /* EMIT SIGNAL */
476 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
478 XMLNode* xml_note = new XMLNode("note");
481 ostringstream id_str(ios::ate);
482 id_str << int(note->id());
483 xml_note->add_property("id", id_str.str());
487 ostringstream note_str(ios::ate);
488 note_str << int(note->note());
489 xml_note->add_property("note", note_str.str());
493 ostringstream channel_str(ios::ate);
494 channel_str << int(note->channel());
495 xml_note->add_property("channel", channel_str.str());
499 ostringstream time_str(ios::ate);
500 time_str << note->time();
501 xml_note->add_property("time", time_str.str());
505 ostringstream length_str(ios::ate);
506 length_str << note->length();
507 xml_note->add_property("length", length_str.str());
511 ostringstream velocity_str(ios::ate);
512 velocity_str << (unsigned int) note->velocity();
513 xml_note->add_property("velocity", velocity_str.str());
519 Evoral::Sequence<MidiModel::TimeType>::NotePtr
520 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
524 unsigned int channel;
525 MidiModel::TimeType time;
526 MidiModel::TimeType length;
527 unsigned int velocity;
530 if ((prop = xml_note->property("id")) != 0) {
531 istringstream id_str(prop->value());
534 error << "note information missing ID value" << endmsg;
538 if ((prop = xml_note->property("note")) != 0) {
539 istringstream note_str(prop->value());
542 warning << "note information missing note value" << endmsg;
546 if ((prop = xml_note->property("channel")) != 0) {
547 istringstream channel_str(prop->value());
548 channel_str >> channel;
550 warning << "note information missing channel" << endmsg;
554 if ((prop = xml_note->property("time")) != 0) {
555 istringstream time_str(prop->value());
558 warning << "note information missing time" << endmsg;
562 if ((prop = xml_note->property("length")) != 0) {
563 istringstream length_str(prop->value());
564 length_str >> length;
566 warning << "note information missing length" << endmsg;
570 if ((prop = xml_note->property("velocity")) != 0) {
571 istringstream velocity_str(prop->value());
572 velocity_str >> velocity;
574 warning << "note information missing velocity" << endmsg;
578 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
579 note_ptr->set_id (id);
585 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
587 XMLNode* xml_change = new XMLNode("Change");
589 /* first, the change itself */
591 xml_change->add_property ("property", enum_2_string (change.property));
594 ostringstream old_value_str (ios::ate);
595 if (change.property == StartTime || change.property == Length) {
596 old_value_str << change.old_time;
598 old_value_str << (unsigned int) change.old_value;
600 xml_change->add_property ("old", old_value_str.str());
604 ostringstream new_value_str (ios::ate);
605 if (change.property == StartTime || change.property == Length) {
606 new_value_str << change.new_time;
608 new_value_str << (unsigned int) change.new_value;
610 xml_change->add_property ("new", new_value_str.str());
613 ostringstream id_str;
614 id_str << change.note->id();
615 xml_change->add_property ("id", id_str.str());
620 MidiModel::NoteDiffCommand::NoteChange
621 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
626 if ((prop = xml_change->property("property")) != 0) {
627 change.property = (Property) string_2_enum (prop->value(), change.property);
629 fatal << "!!!" << endmsg;
630 abort(); /*NOTREACHED*/
633 if ((prop = xml_change->property ("id")) == 0) {
634 error << _("No NoteID found for note property change - ignored") << endmsg;
638 gint note_id = atoi (prop->value().c_str());
640 if ((prop = xml_change->property ("old")) != 0) {
641 istringstream old_str (prop->value());
642 if (change.property == StartTime || change.property == Length) {
643 old_str >> change.old_time;
645 int integer_value_so_that_istream_does_the_right_thing;
646 old_str >> integer_value_so_that_istream_does_the_right_thing;
647 change.old_value = integer_value_so_that_istream_does_the_right_thing;
650 fatal << "!!!" << endmsg;
651 abort(); /*NOTREACHED*/
654 if ((prop = xml_change->property ("new")) != 0) {
655 istringstream new_str (prop->value());
656 if (change.property == StartTime || change.property == Length) {
657 new_str >> change.new_time;
659 int integer_value_so_that_istream_does_the_right_thing;
660 new_str >> integer_value_so_that_istream_does_the_right_thing;
661 change.new_value = integer_value_so_that_istream_does_the_right_thing;
664 fatal << "!!!" << endmsg;
665 abort(); /*NOTREACHED*/
668 /* we must point at the instance of the note that is actually in the model.
669 so go look for it ... it may not be there (it could have been
670 deleted in a later operation, so store the note id so that we can
671 look it up again later).
674 change.note = _model->find_note (note_id);
675 change.note_id = note_id;
681 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
683 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
689 _added_notes.clear();
690 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
692 XMLNodeList notes = added_notes->children();
693 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
694 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
700 _removed_notes.clear();
701 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
703 XMLNodeList notes = removed_notes->children();
704 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
705 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
713 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
716 XMLNodeList notes = changed_notes->children();
717 transform (notes.begin(), notes.end(), back_inserter(_changes),
718 boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
722 /* side effect removals caused by changes */
724 side_effect_removals.clear();
726 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
728 if (side_effect_notes) {
729 XMLNodeList notes = side_effect_notes->children();
730 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
731 side_effect_removals.insert (unmarshal_note (*n));
739 MidiModel::NoteDiffCommand::get_state ()
741 XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
742 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
744 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
745 for_each(_changes.begin(), _changes.end(),
747 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
748 boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
750 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
751 for_each(_added_notes.begin(), _added_notes.end(),
753 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
754 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
756 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
757 for_each(_removed_notes.begin(), _removed_notes.end(),
759 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
760 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
762 /* if this command had side-effects, store that state too
765 if (!side_effect_removals.empty()) {
766 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
767 for_each(side_effect_removals.begin(), side_effect_removals.end(),
769 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
770 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
773 return *diff_command;
776 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
777 : DiffCommand (m, "")
780 set_state (node, Stateful::loading_state_version);
784 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
789 change.property = Time;
790 change.old_time = s->time ();
791 change.new_time = new_time;
793 _changes.push_back (change);
797 MidiModel::SysExDiffCommand::operator() ()
800 MidiModel::WriteLock lock (_model->edit_lock ());
802 for (list<SysExPtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
803 _model->remove_sysex_unlocked (*i);
806 /* find any sysex events that were missing when unmarshalling */
808 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
810 i->sysex = _model->find_sysex (i->sysex_id);
815 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
816 switch (i->property) {
818 i->sysex->set_time (i->new_time);
823 _model->ContentsChanged (); /* EMIT SIGNAL */
827 MidiModel::SysExDiffCommand::undo ()
830 MidiModel::WriteLock lock (_model->edit_lock ());
832 for (list<SysExPtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
833 _model->add_sysex_unlocked (*i);
836 /* find any sysex events that were missing when unmarshalling */
838 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
840 i->sysex = _model->find_sysex (i->sysex_id);
845 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
846 switch (i->property) {
848 i->sysex->set_time (i->old_time);
855 _model->ContentsChanged(); /* EMIT SIGNAL */
859 MidiModel::SysExDiffCommand::remove (SysExPtr sysex)
861 _removed.push_back(sysex);
865 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
867 XMLNode* xml_change = new XMLNode ("Change");
869 /* first, the change itself */
871 xml_change->add_property ("property", enum_2_string (change.property));
874 ostringstream old_value_str (ios::ate);
875 old_value_str << change.old_time;
876 xml_change->add_property ("old", old_value_str.str());
880 ostringstream new_value_str (ios::ate);
881 new_value_str << change.new_time;
882 xml_change->add_property ("new", new_value_str.str());
885 ostringstream id_str;
886 id_str << change.sysex->id();
887 xml_change->add_property ("id", id_str.str());
892 MidiModel::SysExDiffCommand::Change
893 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
898 if ((prop = xml_change->property ("property")) != 0) {
899 change.property = (Property) string_2_enum (prop->value(), change.property);
901 fatal << "!!!" << endmsg;
902 abort(); /*NOTREACHED*/
905 if ((prop = xml_change->property ("id")) == 0) {
906 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
910 gint sysex_id = atoi (prop->value().c_str());
912 if ((prop = xml_change->property ("old")) != 0) {
913 istringstream old_str (prop->value());
914 old_str >> change.old_time;
916 fatal << "!!!" << endmsg;
917 abort(); /*NOTREACHED*/
920 if ((prop = xml_change->property ("new")) != 0) {
921 istringstream new_str (prop->value());
922 new_str >> change.new_time;
924 fatal << "!!!" << endmsg;
925 abort(); /*NOTREACHED*/
928 /* we must point at the instance of the sysex that is actually in the model.
929 so go look for it ...
932 change.sysex = _model->find_sysex (sysex_id);
933 change.sysex_id = sysex_id;
939 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
941 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
949 XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
951 if (changed_sysexes) {
952 XMLNodeList sysexes = changed_sysexes->children();
953 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
954 boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
962 MidiModel::SysExDiffCommand::get_state ()
964 XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
965 diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
967 XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
968 for_each (_changes.begin(), _changes.end(),
970 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
971 boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
973 return *diff_command;
976 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
977 : DiffCommand (m, name)
982 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
983 : DiffCommand (m, "")
986 set_state (node, Stateful::loading_state_version);
990 MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
992 _added.push_back (p);
996 MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
998 _removed.push_back (p);
1002 MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
1007 c.old_time = patch->time ();
1010 _changes.push_back (c);
1014 MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
1017 c.property = Channel;
1019 c.old_channel = patch->channel ();
1020 c.new_channel = channel;
1021 c.patch_id = patch->id();
1023 _changes.push_back (c);
1027 MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
1030 c.property = Program;
1032 c.old_program = patch->program ();
1033 c.new_program = program;
1034 c.patch_id = patch->id();
1036 _changes.push_back (c);
1040 MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
1045 c.old_bank = patch->bank ();
1048 _changes.push_back (c);
1052 MidiModel::PatchChangeDiffCommand::operator() ()
1055 MidiModel::WriteLock lock (_model->edit_lock ());
1057 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1058 _model->add_patch_change_unlocked (*i);
1061 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1062 _model->remove_patch_change_unlocked (*i);
1065 /* find any patch change events that were missing when unmarshalling */
1067 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1069 i->patch = _model->find_patch_change (i->patch_id);
1074 set<PatchChangePtr> temporary_removals;
1076 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1077 switch (i->property) {
1079 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1080 _model->remove_patch_change_unlocked (i->patch);
1081 temporary_removals.insert (i->patch);
1083 i->patch->set_time (i->new_time);
1087 i->patch->set_channel (i->new_channel);
1091 i->patch->set_program (i->new_program);
1095 i->patch->set_bank (i->new_bank);
1100 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1101 _model->add_patch_change_unlocked (*i);
1105 _model->ContentsChanged (); /* EMIT SIGNAL */
1109 MidiModel::PatchChangeDiffCommand::undo ()
1112 MidiModel::WriteLock lock (_model->edit_lock());
1114 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1115 _model->remove_patch_change_unlocked (*i);
1118 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1119 _model->add_patch_change_unlocked (*i);
1122 /* find any patch change events that were missing when unmarshalling */
1124 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1126 i->patch = _model->find_patch_change (i->patch_id);
1131 set<PatchChangePtr> temporary_removals;
1133 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1134 switch (i->property) {
1136 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1137 _model->remove_patch_change_unlocked (i->patch);
1138 temporary_removals.insert (i->patch);
1140 i->patch->set_time (i->old_time);
1144 i->patch->set_channel (i->old_channel);
1148 i->patch->set_program (i->old_program);
1152 i->patch->set_bank (i->old_bank);
1157 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1158 _model->add_patch_change_unlocked (*i);
1163 _model->ContentsChanged (); /* EMIT SIGNAL */
1167 MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
1169 XMLNode* n = new XMLNode ("patch-change");
1172 ostringstream s (ios::ate);
1173 s << int (p->id ());
1174 n->add_property ("id", s.str());
1178 ostringstream s (ios::ate);
1180 n->add_property ("time", s.str ());
1184 ostringstream s (ios::ate);
1185 s << int (p->channel ());
1186 n->add_property ("channel", s.str ());
1190 ostringstream s (ios::ate);
1191 s << int (p->program ());
1192 n->add_property ("program", s.str ());
1196 ostringstream s (ios::ate);
1197 s << int (p->bank ());
1198 n->add_property ("bank", s.str ());
1205 MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
1207 XMLNode* n = new XMLNode (X_("Change"));
1209 n->add_property (X_("property"), enum_2_string (c.property));
1212 ostringstream s (ios::ate);
1213 if (c.property == Time) {
1215 } else if (c.property == Channel) {
1217 } else if (c.property == Program) {
1218 s << int (c.old_program);
1219 } else if (c.property == Bank) {
1223 n->add_property (X_("old"), s.str ());
1227 ostringstream s (ios::ate);
1229 if (c.property == Time) {
1231 } else if (c.property == Channel) {
1233 } else if (c.property == Program) {
1234 s << int (c.new_program);
1235 } else if (c.property == Bank) {
1239 n->add_property (X_("new"), s.str ());
1244 s << c.patch->id ();
1245 n->add_property ("id", s.str ());
1251 MidiModel::PatchChangePtr
1252 MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
1255 Evoral::event_id_t id = 0;
1256 Evoral::MusicalTime time = 0;
1261 if ((prop = n->property ("id")) != 0) {
1262 istringstream s (prop->value());
1266 if ((prop = n->property ("time")) != 0) {
1267 istringstream s (prop->value ());
1271 if ((prop = n->property ("channel")) != 0) {
1272 istringstream s (prop->value ());
1276 if ((prop = n->property ("program")) != 0) {
1277 istringstream s (prop->value ());
1281 if ((prop = n->property ("bank")) != 0) {
1282 istringstream s (prop->value ());
1286 PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
1292 MidiModel::PatchChangeDiffCommand::Change
1293 MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
1299 prop = n->property ("property");
1301 c.property = (Property) string_2_enum (prop->value(), c.property);
1303 prop = n->property ("id");
1305 Evoral::event_id_t const id = atoi (prop->value().c_str());
1307 /* we need to load via an int intermediate for all properties that are
1308 actually uint8_t (char/byte).
1311 prop = n->property ("old");
1314 istringstream s (prop->value ());
1315 if (c.property == Time) {
1317 } else if (c.property == Channel) {
1319 c.old_channel = an_int;
1320 } else if (c.property == Program) {
1322 c.old_program = an_int;
1323 } else if (c.property == Bank) {
1325 c.old_bank = an_int;
1329 prop = n->property ("new");
1332 istringstream s (prop->value ());
1334 if (c.property == Time) {
1336 } else if (c.property == Channel) {
1338 c.new_channel = an_int;
1339 } else if (c.property == Program) {
1341 c.new_program = an_int;
1342 } else if (c.property == Bank) {
1344 c.new_bank = an_int;
1348 c.patch = _model->find_patch_change (id);
1355 MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
1357 if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
1362 XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
1364 XMLNodeList p = added->children ();
1365 transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1369 XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
1371 XMLNodeList p = removed->children ();
1372 transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1376 XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
1378 XMLNodeList p = changed->children ();
1379 transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
1386 MidiModel::PatchChangeDiffCommand::get_state ()
1388 XMLNode* diff_command = new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT);
1389 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
1391 XMLNode* added = diff_command->add_child (ADDED_PATCH_CHANGES_ELEMENT);
1392 for_each (_added.begin(), _added.end(),
1394 boost::bind (&XMLNode::add_child_nocopy, added, _1),
1395 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1399 XMLNode* removed = diff_command->add_child (REMOVED_PATCH_CHANGES_ELEMENT);
1400 for_each (_removed.begin(), _removed.end(),
1402 boost::bind (&XMLNode::add_child_nocopy, removed, _1),
1403 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1407 XMLNode* changes = diff_command->add_child (DIFF_PATCH_CHANGES_ELEMENT);
1408 for_each (_changes.begin(), _changes.end(),
1410 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
1411 boost::bind (&PatchChangeDiffCommand::marshal_change, this, _1)
1415 return *diff_command;
1418 /** Write all of the model to a MidiSource (i.e. save the model).
1419 * This is different from manually using read to write to a source in that
1420 * note off events are written regardless of the track mode. This is so the
1421 * user can switch a recorded track (with note durations from some instrument)
1422 * to percussive, save, reload, then switch it back to sustained without
1423 * destroying the original note durations.
1425 * Similarly, control events are written without interpolation (as with the
1429 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
1431 ReadLock lock(read_lock());
1433 const bool old_percussive = percussive();
1434 set_percussive(false);
1436 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1439 source->drop_model();
1440 source->mark_streaming_midi_write_started (note_mode());
1442 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1443 source->append_event_unlocked_beats(*i);
1446 set_percussive(old_percussive);
1447 source->mark_streaming_write_completed();
1454 /** very similar to ::write_to() but writes to the model's own
1455 existing midi_source, without making it call MidiSource::drop_model().
1456 the caller is a MidiSource that needs to catch up with the state
1460 MidiModel::sync_to_source ()
1462 ReadLock lock(read_lock());
1464 const bool old_percussive = percussive();
1465 set_percussive(false);
1467 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1470 ms->mark_streaming_midi_write_started (note_mode());
1472 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1473 ms->append_event_unlocked_beats(*i);
1476 set_percussive (old_percussive);
1477 ms->mark_streaming_write_completed ();
1484 /** Write part or all of the model to a MidiSource (i.e. save the model).
1485 * This is different from manually using read to write to a source in that
1486 * note off events are written regardless of the track mode. This is so the
1487 * user can switch a recorded track (with note durations from some instrument)
1488 * to percussive, save, reload, then switch it back to sustained without
1489 * destroying the original note durations.
1492 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
1494 ReadLock lock(read_lock());
1495 MidiStateTracker mst;
1497 const bool old_percussive = percussive();
1498 set_percussive(false);
1500 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1503 source->drop_model();
1504 source->mark_streaming_midi_write_started (note_mode());
1506 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1507 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
1509 if (ev.time() >= begin_time && ev.time() < end_time) {
1511 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
1512 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
1519 if (mev->is_note_off()) {
1521 if (!mst.active (mev->note(), mev->channel())) {
1522 /* the matching note-on was outside the
1523 time range we were given, so just
1524 ignore this note-off.
1529 source->append_event_unlocked_beats (*i);
1530 mst.remove (mev->note(), mev->channel());
1532 } else if (mev->is_note_on()) {
1533 mst.add (mev->note(), mev->channel());
1534 source->append_event_unlocked_beats(*i);
1536 source->append_event_unlocked_beats(*i);
1541 mst.resolve_notes (*source, end_time);
1543 set_percussive(old_percussive);
1544 source->mark_streaming_write_completed();
1552 MidiModel::get_state()
1554 XMLNode *node = new XMLNode("MidiModel");
1558 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1559 MidiModel::find_note (NotePtr other)
1561 Notes::iterator l = notes().lower_bound(other);
1563 if (l != notes().end()) {
1564 for (; (*l)->time() == other->time(); ++l) {
1565 /* NB: compare note contents, not note pointers.
1566 If "other" was a ptr to a note already in
1567 the model, we wouldn't be looking for it,
1570 if (**l == *other) {
1579 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1580 MidiModel::find_note (gint note_id)
1582 /* used only for looking up notes when reloading history from disk,
1583 so we don't care about performance *too* much.
1586 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1587 if ((*l)->id() == note_id) {
1595 MidiModel::PatchChangePtr
1596 MidiModel::find_patch_change (Evoral::event_id_t id)
1598 for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1599 if ((*i)->id() == id) {
1604 return PatchChangePtr ();
1607 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1608 MidiModel::find_sysex (gint sysex_id)
1610 /* used only for looking up notes when reloading history from disk,
1611 so we don't care about performance *too* much.
1614 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1615 if ((*l)->id() == sysex_id) {
1620 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1623 /** Lock and invalidate the source.
1624 * This should be used by commands and editing things
1626 MidiModel::WriteLock
1627 MidiModel::edit_lock()
1629 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1632 Glib::Threads::Mutex::Lock* source_lock = new Glib::Threads::Mutex::Lock (ms->mutex());
1633 ms->invalidate(); // Release cached iterator's read lock on model
1634 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1637 /** Lock just the model, the source lock must already be held.
1638 * This should only be called from libardour/evoral places
1640 MidiModel::WriteLock
1641 MidiModel::write_lock()
1643 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1646 assert (!ms->mutex().trylock ());
1647 return WriteLock(new WriteLockImpl(0, _lock, _control_lock));
1651 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1653 using namespace Evoral;
1655 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1659 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1661 TimeType sa = note->time();
1662 TimeType ea = note->end_time();
1664 const Pitches& p (pitches (note->channel()));
1665 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
1666 set<NotePtr> to_be_deleted;
1667 bool set_note_length = false;
1668 bool set_note_time = false;
1669 TimeType note_time = note->time();
1670 TimeType note_length = note->length();
1672 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1674 for (Pitches::const_iterator i = p.lower_bound (search_note);
1675 i != p.end() && (*i)->note() == note->note(); ++i) {
1677 TimeType sb = (*i)->time();
1678 TimeType eb = (*i)->end_time();
1679 OverlapType overlap = OverlapNone;
1681 if ((sb > sa) && (eb <= ea)) {
1682 overlap = OverlapInternal;
1683 } else if ((eb >= sa) && (eb <= ea)) {
1684 overlap = OverlapStart;
1685 } else if ((sb > sa) && (sb <= ea)) {
1686 overlap = OverlapEnd;
1687 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1688 overlap = OverlapExternal;
1694 DEBUG_TRACE (DEBUG::Sequence, string_compose (
1695 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1696 enum_2_string(overlap), sa, ea, sb, eb));
1698 if (insert_merge_policy() == InsertMergeReject) {
1699 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1705 cerr << "OverlapStart\n";
1706 /* existing note covers start of new note */
1707 switch (insert_merge_policy()) {
1708 case InsertMergeReplace:
1709 to_be_deleted.insert (*i);
1711 case InsertMergeTruncateExisting:
1713 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1715 (*i)->set_length (note->time() - (*i)->time());
1717 case InsertMergeTruncateAddition:
1718 set_note_time = true;
1719 set_note_length = true;
1720 note_time = (*i)->time() + (*i)->length();
1721 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1723 case InsertMergeExtend:
1725 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1727 (*i)->set_length (note->end_time() - (*i)->time());
1728 return -1; /* do not add the new note */
1731 abort(); /*NOTREACHED*/
1738 cerr << "OverlapEnd\n";
1739 /* existing note covers end of new note */
1740 switch (insert_merge_policy()) {
1741 case InsertMergeReplace:
1742 to_be_deleted.insert (*i);
1745 case InsertMergeTruncateExisting:
1746 /* resetting the start time of the existing note
1747 is a problem because of time ordering.
1751 case InsertMergeTruncateAddition:
1752 set_note_length = true;
1753 note_length = min (note_length, ((*i)->time() - note->time()));
1756 case InsertMergeExtend:
1757 /* we can't reset the time of the existing note because
1758 that will corrupt time ordering. So remove the
1759 existing note and change the position/length
1760 of the new note (which has not been added yet)
1762 to_be_deleted.insert (*i);
1763 set_note_length = true;
1764 note_length = min (note_length, (*i)->end_time() - note->time());
1767 abort(); /*NOTREACHED*/
1773 case OverlapExternal:
1774 cerr << "OverlapExt\n";
1775 /* existing note overlaps all the new note */
1776 switch (insert_merge_policy()) {
1777 case InsertMergeReplace:
1778 to_be_deleted.insert (*i);
1780 case InsertMergeTruncateExisting:
1781 case InsertMergeTruncateAddition:
1782 case InsertMergeExtend:
1783 /* cannot add in this case */
1786 abort(); /*NOTREACHED*/
1792 case OverlapInternal:
1793 cerr << "OverlapInt\n";
1794 /* new note fully overlaps an existing note */
1795 switch (insert_merge_policy()) {
1796 case InsertMergeReplace:
1797 case InsertMergeTruncateExisting:
1798 case InsertMergeTruncateAddition:
1799 case InsertMergeExtend:
1800 /* delete the existing note, the new one will cover it */
1801 to_be_deleted.insert (*i);
1804 abort(); /*NOTREACHED*/
1811 abort(); /*NOTREACHED*/
1817 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1818 remove_note_unlocked (*i);
1821 cmd->side_effect_remove (*i);
1825 if (set_note_time) {
1827 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1829 note->set_time (note_time);
1832 if (set_note_length) {
1834 cmd->change (note, NoteDiffCommand::Length, note_length);
1836 note->set_length (note_length);
1843 MidiModel::insert_merge_policy () const
1845 /* XXX ultimately this should be a per-track or even per-model policy */
1846 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1849 return ms->session().config.get_insert_merge_policy ();
1853 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1855 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1861 _midi_source_connections.drop_connections ();
1865 s->InterpolationChanged.connect_same_thread (
1866 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1869 s->AutomationStateChanged.connect_same_thread (
1870 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1874 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1875 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1876 * appropriate ControlList.
1878 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1879 * or the other is listened to by the GUI.
1882 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1884 Glib::Threads::Mutex::Lock lm (_control_lock);
1885 control(p)->list()->set_interpolation (s);
1888 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1889 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1892 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1894 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1897 ms->set_interpolation_of (p, s);
1901 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1903 Glib::Threads::Mutex::Lock lm (_control_lock);
1904 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1905 al->set_automation_state (s);
1909 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1911 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1913 ms->set_automation_state_of (p, s);
1916 boost::shared_ptr<Evoral::Control>
1917 MidiModel::control_factory (Evoral::Parameter const & p)
1919 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1921 /* Set up newly created control's lists to the appropriate interpolation and
1922 automation state from our source.
1925 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1928 c->list()->set_interpolation (ms->interpolation_of (p));
1930 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1933 al->set_automation_state (ms->automation_state_of (p));
1938 boost::shared_ptr<const MidiSource>
1939 MidiModel::midi_source ()
1941 return _midi_source.lock ();
1944 /** Moves notes, patch changes, controllers and sys-ex to insert silence at the start of the model.
1945 * Adds commands to the session's current undo stack to reflect the movements.
1948 MidiModel::insert_silence_at_start (TimeType t)
1950 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1955 if (!notes().empty ()) {
1956 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1958 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1959 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1962 apply_command_as_subcommand (s->session(), c);
1967 if (!patch_changes().empty ()) {
1968 PatchChangeDiffCommand* c = new_patch_change_diff_command ("insert silence");
1970 for (PatchChanges::const_iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1971 c->change_time (*i, (*i)->time() + t);
1974 apply_command_as_subcommand (s->session(), c);
1979 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1980 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1981 XMLNode& before = ac->alist()->get_state ();
1982 i->second->list()->shift (0, t);
1983 XMLNode& after = ac->alist()->get_state ();
1984 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1989 if (!sysexes().empty()) {
1990 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1992 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1993 c->change (*i, (*i)->time() + t);
1996 apply_command_as_subcommand (s->session(), c);
2000 /** Transpose notes in a time range by a given number of semitones. Notes
2001 * will be clamped at 0 and 127 if the transposition would make them exceed
2004 * @param from Start time.
2005 * @param end End time.
2006 * @param semitones Number of semitones to transpose by (+ve is higher, -ve is lower).
2009 MidiModel::transpose (TimeType from, TimeType to, int semitones)
2011 boost::shared_ptr<const MidiSource> s = midi_source ();
2013 NoteDiffCommand* c = new_note_diff_command (_("transpose"));
2015 for (Notes::iterator i = notes().begin(); i != notes().end(); ++i) {
2017 if ((*i)->time() >= to) {
2022 } else if ((*i)->time() >= from) {
2024 int new_note = (*i)->note() + semitones;
2028 } else if (new_note > 127) {
2032 c->change (*i, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
2037 apply_command (s->session (), c);
2041 MidiModel::control_list_marked_dirty ()
2043 AutomatableSequence<Evoral::MusicalTime>::control_list_marked_dirty ();
2045 ContentsChanged (); /* EMIT SIGNAL */