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::get_value (const NotePtr note, Property prop)
172 return Variant(note->note());
174 return Variant(note->velocity());
176 return Variant(note->channel());
178 return Variant(note->time());
180 return Variant(note->length());
187 MidiModel::NoteDiffCommand::value_type(Property prop)
196 return Variant::BEATS;
199 return Variant::NOTHING;
203 MidiModel::NoteDiffCommand::change (const NotePtr note,
205 const Variant& new_value)
209 const NoteChange change = {
210 prop, note, 0, get_value(note, prop), new_value
213 if (change.old_value == new_value) {
217 _changes.push_back (change);
220 MidiModel::NoteDiffCommand &
221 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand& other)
223 if (this == &other) {
227 if (_model != other._model) {
231 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
232 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
233 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
234 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
240 MidiModel::NoteDiffCommand::operator() ()
243 MidiModel::WriteLock lock(_model->edit_lock());
245 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
246 if (!_model->add_note_unlocked(*i)) {
247 /* failed to add it, so don't leave it in the removed list, to
248 avoid apparent errors on undo.
250 _removed_notes.remove (*i);
254 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
255 _model->remove_note_unlocked(*i);
258 /* notes we modify in a way that requires remove-then-add to maintain ordering */
259 set<NotePtr> temporary_removals;
261 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
262 Property prop = i->property;
265 /* note found during deserialization, so try
266 again now that the model state is different.
268 i->note = _model->find_note (i->note_id);
274 if (temporary_removals.find (i->note) == temporary_removals.end()) {
275 _model->remove_note_unlocked (i->note);
276 temporary_removals.insert (i->note);
278 i->note->set_note (i->new_value.get_int());
282 if (temporary_removals.find (i->note) == temporary_removals.end()) {
283 _model->remove_note_unlocked (i->note);
284 temporary_removals.insert (i->note);
286 i->note->set_time (i->new_value.get_beats());
290 if (temporary_removals.find (i->note) == temporary_removals.end()) {
291 _model->remove_note_unlocked (i->note);
292 temporary_removals.insert (i->note);
294 i->note->set_channel (i->new_value.get_int());
297 /* no remove-then-add required for these properties, since we do not index them
301 i->note->set_velocity (i->new_value.get_int());
305 i->note->set_length (i->new_value.get_beats());
311 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
312 NoteDiffCommand side_effects (model(), "side effects");
313 if (_model->add_note_unlocked (*i, &side_effects)) {
314 /* The note was re-added ok */
315 *this += side_effects;
317 /* The note that we removed earlier could not be re-added. This change record
318 must say that the note was removed. We'll keep the changes we made, though,
319 as if the note is re-added by the undo the changes must also be undone.
321 _removed_notes.push_back (*i);
325 if (!side_effect_removals.empty()) {
327 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
328 cerr << "\t" << *i << ' ' << **i << endl;
333 _model->ContentsChanged(); /* EMIT SIGNAL */
337 MidiModel::NoteDiffCommand::undo ()
340 MidiModel::WriteLock lock(_model->edit_lock());
342 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
343 _model->remove_note_unlocked(*i);
346 /* Apply changes first; this is important in the case of a note change which
347 resulted in the note being removed by the overlap checker. If the overlap
348 checker removes a note, it will be in _removed_notes. We are going to re-add
349 it below, but first we must undo the changes we made so that the overlap
350 checker doesn't refuse the re-add.
353 /* notes we modify in a way that requires remove-then-add to maintain ordering */
354 set<NotePtr> temporary_removals;
357 /* lazily discover any affected notes that were not discovered when
358 * loading the history because of deletions, etc.
361 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
363 i->note = _model->find_note (i->note_id);
368 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
369 Property prop = i->property;
373 if (temporary_removals.find (i->note) == temporary_removals.end() &&
374 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
376 /* We only need to mark this note for re-add if (a) we haven't
377 already marked it and (b) it isn't on the _removed_notes
378 list (which means that it has already been removed and it
379 will be re-added anyway)
382 _model->remove_note_unlocked (i->note);
383 temporary_removals.insert (i->note);
385 i->note->set_note (i->old_value.get_int());
389 if (temporary_removals.find (i->note) == temporary_removals.end() &&
390 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
394 _model->remove_note_unlocked (i->note);
395 temporary_removals.insert (i->note);
397 i->note->set_time (i->old_value.get_beats());
401 if (temporary_removals.find (i->note) == temporary_removals.end() &&
402 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
406 _model->remove_note_unlocked (i->note);
407 temporary_removals.insert (i->note);
409 i->note->set_channel (i->old_value.get_int());
412 /* no remove-then-add required for these properties, since we do not index them
416 i->note->set_velocity (i->old_value.get_int());
420 i->note->set_length (i->old_value.get_beats());
425 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
426 _model->add_note_unlocked(*i);
429 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
430 _model->add_note_unlocked (*i);
433 /* finally add back notes that were removed by the "do". we don't care
434 about side effects here since the model should be back to its original
435 state once this is done.
438 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
439 _model->add_note_unlocked (*i);
443 _model->ContentsChanged(); /* EMIT SIGNAL */
447 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
449 XMLNode* xml_note = new XMLNode("note");
452 ostringstream id_str(ios::ate);
453 id_str << int(note->id());
454 xml_note->add_property("id", id_str.str());
458 ostringstream note_str(ios::ate);
459 note_str << int(note->note());
460 xml_note->add_property("note", note_str.str());
464 ostringstream channel_str(ios::ate);
465 channel_str << int(note->channel());
466 xml_note->add_property("channel", channel_str.str());
470 ostringstream time_str(ios::ate);
471 time_str << note->time();
472 xml_note->add_property("time", time_str.str());
476 ostringstream length_str(ios::ate);
477 length_str << note->length();
478 xml_note->add_property("length", length_str.str());
482 ostringstream velocity_str(ios::ate);
483 velocity_str << (unsigned int) note->velocity();
484 xml_note->add_property("velocity", velocity_str.str());
490 Evoral::Sequence<MidiModel::TimeType>::NotePtr
491 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
495 unsigned int channel;
496 MidiModel::TimeType time;
497 MidiModel::TimeType length;
498 unsigned int velocity;
501 if ((prop = xml_note->property("id")) != 0) {
502 istringstream id_str(prop->value());
505 error << "note information missing ID value" << endmsg;
509 if ((prop = xml_note->property("note")) != 0) {
510 istringstream note_str(prop->value());
513 warning << "note information missing note value" << endmsg;
517 if ((prop = xml_note->property("channel")) != 0) {
518 istringstream channel_str(prop->value());
519 channel_str >> channel;
521 warning << "note information missing channel" << endmsg;
525 if ((prop = xml_note->property("time")) != 0) {
526 istringstream time_str(prop->value());
529 warning << "note information missing time" << endmsg;
530 time = MidiModel::TimeType();
533 if ((prop = xml_note->property("length")) != 0) {
534 istringstream length_str(prop->value());
535 length_str >> length;
537 warning << "note information missing length" << endmsg;
538 length = MidiModel::TimeType(1);
541 if ((prop = xml_note->property("velocity")) != 0) {
542 istringstream velocity_str(prop->value());
543 velocity_str >> velocity;
545 warning << "note information missing velocity" << endmsg;
549 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
550 note_ptr->set_id (id);
556 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
558 XMLNode* xml_change = new XMLNode("Change");
560 /* first, the change itself */
562 xml_change->add_property ("property", enum_2_string (change.property));
565 ostringstream old_value_str (ios::ate);
566 if (change.property == StartTime || change.property == Length) {
567 old_value_str << change.old_value.get_beats();
569 old_value_str << change.old_value.get_int();
571 xml_change->add_property ("old", old_value_str.str());
575 ostringstream new_value_str (ios::ate);
576 if (change.property == StartTime || change.property == Length) {
577 new_value_str << change.new_value.get_beats();
579 new_value_str << change.new_value.get_int();
581 xml_change->add_property ("new", new_value_str.str());
584 ostringstream id_str;
586 id_str << change.note->id();
587 xml_change->add_property ("id", id_str.str());
588 } else if (change.note_id) {
589 warning << _("Change has no note, using note ID") << endmsg;
590 id_str << change.note_id;
591 xml_change->add_property ("id", id_str.str());
593 error << _("Change has no note or note ID") << endmsg;
599 MidiModel::NoteDiffCommand::NoteChange
600 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
606 if ((prop = xml_change->property("property")) != 0) {
607 change.property = (Property) string_2_enum (prop->value(), change.property);
609 fatal << "!!!" << endmsg;
610 abort(); /*NOTREACHED*/
613 if ((prop = xml_change->property ("id")) == 0) {
614 error << _("No NoteID found for note property change - ignored") << endmsg;
618 gint note_id = atoi (prop->value().c_str());
620 if ((prop = xml_change->property ("old")) != 0) {
621 istringstream old_str (prop->value());
622 if (change.property == StartTime || change.property == Length) {
623 Evoral::Beats old_time;
625 change.old_value = old_time;
627 int integer_value_so_that_istream_does_the_right_thing;
628 old_str >> integer_value_so_that_istream_does_the_right_thing;
629 change.old_value = integer_value_so_that_istream_does_the_right_thing;
632 fatal << "!!!" << endmsg;
633 abort(); /*NOTREACHED*/
636 if ((prop = xml_change->property ("new")) != 0) {
637 istringstream new_str (prop->value());
638 if (change.property == StartTime || change.property == Length) {
639 Evoral::Beats new_time;
641 change.new_value = Variant(new_time);
643 int integer_value_so_that_istream_does_the_right_thing;
644 new_str >> integer_value_so_that_istream_does_the_right_thing;
645 change.new_value = integer_value_so_that_istream_does_the_right_thing;
648 fatal << "!!!" << endmsg;
649 abort(); /*NOTREACHED*/
652 /* we must point at the instance of the note that is actually in the model.
653 so go look for it ... it may not be there (it could have been
654 deleted in a later operation, so store the note id so that we can
655 look it up again later).
658 change.note = _model->find_note (note_id);
659 change.note_id = note_id;
665 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
667 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
673 _added_notes.clear();
674 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
676 XMLNodeList notes = added_notes->children();
677 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
678 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
684 _removed_notes.clear();
685 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
687 XMLNodeList notes = removed_notes->children();
688 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
689 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
697 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
700 XMLNodeList notes = changed_notes->children();
701 transform (notes.begin(), notes.end(), back_inserter(_changes),
702 boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
706 /* side effect removals caused by changes */
708 side_effect_removals.clear();
710 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
712 if (side_effect_notes) {
713 XMLNodeList notes = side_effect_notes->children();
714 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
715 side_effect_removals.insert (unmarshal_note (*n));
723 MidiModel::NoteDiffCommand::get_state ()
725 XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
726 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
728 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
729 for_each(_changes.begin(), _changes.end(),
731 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
732 boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
734 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
735 for_each(_added_notes.begin(), _added_notes.end(),
737 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
738 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
740 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
741 for_each(_removed_notes.begin(), _removed_notes.end(),
743 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
744 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
746 /* if this command had side-effects, store that state too
749 if (!side_effect_removals.empty()) {
750 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
751 for_each(side_effect_removals.begin(), side_effect_removals.end(),
753 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
754 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
757 return *diff_command;
760 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
761 : DiffCommand (m, "")
764 set_state (node, Stateful::loading_state_version);
768 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
773 change.property = Time;
774 change.old_time = s->time ();
775 change.new_time = new_time;
777 _changes.push_back (change);
781 MidiModel::SysExDiffCommand::operator() ()
784 MidiModel::WriteLock lock (_model->edit_lock ());
786 for (list<SysExPtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
787 _model->remove_sysex_unlocked (*i);
790 /* find any sysex events that were missing when unmarshalling */
792 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
794 i->sysex = _model->find_sysex (i->sysex_id);
799 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
800 switch (i->property) {
802 i->sysex->set_time (i->new_time);
807 _model->ContentsChanged (); /* EMIT SIGNAL */
811 MidiModel::SysExDiffCommand::undo ()
814 MidiModel::WriteLock lock (_model->edit_lock ());
816 for (list<SysExPtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
817 _model->add_sysex_unlocked (*i);
820 /* find any sysex events that were missing when unmarshalling */
822 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
824 i->sysex = _model->find_sysex (i->sysex_id);
829 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
830 switch (i->property) {
832 i->sysex->set_time (i->old_time);
839 _model->ContentsChanged(); /* EMIT SIGNAL */
843 MidiModel::SysExDiffCommand::remove (SysExPtr sysex)
845 _removed.push_back(sysex);
849 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
851 XMLNode* xml_change = new XMLNode ("Change");
853 /* first, the change itself */
855 xml_change->add_property ("property", enum_2_string (change.property));
858 ostringstream old_value_str (ios::ate);
859 old_value_str << change.old_time;
860 xml_change->add_property ("old", old_value_str.str());
864 ostringstream new_value_str (ios::ate);
865 new_value_str << change.new_time;
866 xml_change->add_property ("new", new_value_str.str());
869 ostringstream id_str;
870 id_str << change.sysex->id();
871 xml_change->add_property ("id", id_str.str());
876 MidiModel::SysExDiffCommand::Change
877 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
882 if ((prop = xml_change->property ("property")) != 0) {
883 change.property = (Property) string_2_enum (prop->value(), change.property);
885 fatal << "!!!" << endmsg;
886 abort(); /*NOTREACHED*/
889 if ((prop = xml_change->property ("id")) == 0) {
890 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
894 gint sysex_id = atoi (prop->value().c_str());
896 if ((prop = xml_change->property ("old")) != 0) {
897 istringstream old_str (prop->value());
898 old_str >> change.old_time;
900 fatal << "!!!" << endmsg;
901 abort(); /*NOTREACHED*/
904 if ((prop = xml_change->property ("new")) != 0) {
905 istringstream new_str (prop->value());
906 new_str >> change.new_time;
908 fatal << "!!!" << endmsg;
909 abort(); /*NOTREACHED*/
912 /* we must point at the instance of the sysex that is actually in the model.
913 so go look for it ...
916 change.sysex = _model->find_sysex (sysex_id);
917 change.sysex_id = sysex_id;
923 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
925 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
933 XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
935 if (changed_sysexes) {
936 XMLNodeList sysexes = changed_sysexes->children();
937 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
938 boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
946 MidiModel::SysExDiffCommand::get_state ()
948 XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
949 diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
951 XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
952 for_each (_changes.begin(), _changes.end(),
954 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
955 boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
957 return *diff_command;
960 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
961 : DiffCommand (m, name)
966 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
967 : DiffCommand (m, "")
970 set_state (node, Stateful::loading_state_version);
974 MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
976 _added.push_back (p);
980 MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
982 _removed.push_back (p);
986 MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
991 c.old_time = patch->time ();
994 _changes.push_back (c);
998 MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
1001 c.property = Channel;
1003 c.old_channel = patch->channel ();
1004 c.new_channel = channel;
1005 c.patch_id = patch->id();
1007 _changes.push_back (c);
1011 MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
1014 c.property = Program;
1016 c.old_program = patch->program ();
1017 c.new_program = program;
1018 c.patch_id = patch->id();
1020 _changes.push_back (c);
1024 MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
1029 c.old_bank = patch->bank ();
1032 _changes.push_back (c);
1036 MidiModel::PatchChangeDiffCommand::operator() ()
1039 MidiModel::WriteLock lock (_model->edit_lock ());
1041 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1042 _model->add_patch_change_unlocked (*i);
1045 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1046 _model->remove_patch_change_unlocked (*i);
1049 /* find any patch change events that were missing when unmarshalling */
1051 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1053 i->patch = _model->find_patch_change (i->patch_id);
1058 set<PatchChangePtr> temporary_removals;
1060 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1061 switch (i->property) {
1063 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1064 _model->remove_patch_change_unlocked (i->patch);
1065 temporary_removals.insert (i->patch);
1067 i->patch->set_time (i->new_time);
1071 i->patch->set_channel (i->new_channel);
1075 i->patch->set_program (i->new_program);
1079 i->patch->set_bank (i->new_bank);
1084 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1085 _model->add_patch_change_unlocked (*i);
1089 _model->ContentsChanged (); /* EMIT SIGNAL */
1093 MidiModel::PatchChangeDiffCommand::undo ()
1096 MidiModel::WriteLock lock (_model->edit_lock());
1098 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1099 _model->remove_patch_change_unlocked (*i);
1102 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1103 _model->add_patch_change_unlocked (*i);
1106 /* find any patch change events that were missing when unmarshalling */
1108 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1110 i->patch = _model->find_patch_change (i->patch_id);
1115 set<PatchChangePtr> temporary_removals;
1117 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1118 switch (i->property) {
1120 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1121 _model->remove_patch_change_unlocked (i->patch);
1122 temporary_removals.insert (i->patch);
1124 i->patch->set_time (i->old_time);
1128 i->patch->set_channel (i->old_channel);
1132 i->patch->set_program (i->old_program);
1136 i->patch->set_bank (i->old_bank);
1141 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1142 _model->add_patch_change_unlocked (*i);
1147 _model->ContentsChanged (); /* EMIT SIGNAL */
1151 MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
1153 XMLNode* n = new XMLNode ("patch-change");
1156 ostringstream s (ios::ate);
1157 s << int (p->id ());
1158 n->add_property ("id", s.str());
1162 ostringstream s (ios::ate);
1164 n->add_property ("time", s.str ());
1168 ostringstream s (ios::ate);
1169 s << int (p->channel ());
1170 n->add_property ("channel", s.str ());
1174 ostringstream s (ios::ate);
1175 s << int (p->program ());
1176 n->add_property ("program", s.str ());
1180 ostringstream s (ios::ate);
1181 s << int (p->bank ());
1182 n->add_property ("bank", s.str ());
1189 MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
1191 XMLNode* n = new XMLNode (X_("Change"));
1193 n->add_property (X_("property"), enum_2_string (c.property));
1196 ostringstream s (ios::ate);
1197 if (c.property == Time) {
1199 } else if (c.property == Channel) {
1201 } else if (c.property == Program) {
1202 s << int (c.old_program);
1203 } else if (c.property == Bank) {
1207 n->add_property (X_("old"), s.str ());
1211 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.new_program);
1219 } else if (c.property == Bank) {
1223 n->add_property (X_("new"), s.str ());
1228 s << c.patch->id ();
1229 n->add_property ("id", s.str ());
1235 MidiModel::PatchChangePtr
1236 MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
1239 XMLProperty* prop_id;
1240 Evoral::event_id_t id = 0;
1241 Evoral::Beats time = Evoral::Beats();
1246 if ((prop_id = n->property ("id")) != 0) {
1247 istringstream s (prop_id->value());
1251 if ((prop = n->property ("time")) != 0) {
1252 istringstream s (prop->value ());
1256 if ((prop = n->property ("channel")) != 0) {
1257 istringstream s (prop->value ());
1261 if ((prop = n->property ("program")) != 0) {
1262 istringstream s (prop->value ());
1266 if ((prop = n->property ("bank")) != 0) {
1267 istringstream s (prop->value ());
1271 PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
1277 MidiModel::PatchChangeDiffCommand::Change
1278 MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
1284 prop = n->property ("property");
1286 c.property = (Property) string_2_enum (prop->value(), c.property);
1288 prop = n->property ("id");
1290 Evoral::event_id_t const id = atoi (prop->value().c_str());
1292 /* we need to load via an int intermediate for all properties that are
1293 actually uint8_t (char/byte).
1296 prop = n->property ("old");
1299 istringstream s (prop->value ());
1300 if (c.property == Time) {
1302 } else if (c.property == Channel) {
1304 c.old_channel = an_int;
1305 } else if (c.property == Program) {
1307 c.old_program = an_int;
1308 } else if (c.property == Bank) {
1310 c.old_bank = an_int;
1314 prop = n->property ("new");
1317 istringstream s (prop->value ());
1319 if (c.property == Time) {
1321 } else if (c.property == Channel) {
1323 c.new_channel = an_int;
1324 } else if (c.property == Program) {
1326 c.new_program = an_int;
1327 } else if (c.property == Bank) {
1329 c.new_bank = an_int;
1333 c.patch = _model->find_patch_change (id);
1340 MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
1342 if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
1347 XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
1349 XMLNodeList p = added->children ();
1350 transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1354 XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
1356 XMLNodeList p = removed->children ();
1357 transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1361 XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
1363 XMLNodeList p = changed->children ();
1364 transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
1371 MidiModel::PatchChangeDiffCommand::get_state ()
1373 XMLNode* diff_command = new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT);
1374 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
1376 XMLNode* added = diff_command->add_child (ADDED_PATCH_CHANGES_ELEMENT);
1377 for_each (_added.begin(), _added.end(),
1379 boost::bind (&XMLNode::add_child_nocopy, added, _1),
1380 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1384 XMLNode* removed = diff_command->add_child (REMOVED_PATCH_CHANGES_ELEMENT);
1385 for_each (_removed.begin(), _removed.end(),
1387 boost::bind (&XMLNode::add_child_nocopy, removed, _1),
1388 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1392 XMLNode* changes = diff_command->add_child (DIFF_PATCH_CHANGES_ELEMENT);
1393 for_each (_changes.begin(), _changes.end(),
1395 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
1396 boost::bind (&PatchChangeDiffCommand::marshal_change, this, _1)
1400 return *diff_command;
1403 /** Write all of the model to a MidiSource (i.e. save the model).
1404 * This is different from manually using read to write to a source in that
1405 * note off events are written regardless of the track mode. This is so the
1406 * user can switch a recorded track (with note durations from some instrument)
1407 * to percussive, save, reload, then switch it back to sustained without
1408 * destroying the original note durations.
1410 * Similarly, control events are written without interpolation (as with the
1414 MidiModel::write_to (boost::shared_ptr<MidiSource> source,
1415 const Glib::Threads::Mutex::Lock& source_lock)
1417 ReadLock lock(read_lock());
1419 const bool old_percussive = percussive();
1420 set_percussive(false);
1422 source->drop_model(source_lock);
1423 source->mark_streaming_midi_write_started (source_lock, note_mode());
1425 for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
1426 source->append_event_beats(source_lock, *i);
1429 set_percussive(old_percussive);
1430 source->mark_streaming_write_completed(source_lock);
1437 /** very similar to ::write_to() but writes to the model's own
1438 existing midi_source, without making it call MidiSource::drop_model().
1439 the caller is a MidiSource that needs to catch up with the state
1443 MidiModel::sync_to_source (const Glib::Threads::Mutex::Lock& source_lock)
1445 ReadLock lock(read_lock());
1447 const bool old_percussive = percussive();
1448 set_percussive(false);
1450 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1452 error << "MIDI model has no source to sync to" << endmsg;
1456 ms->mark_streaming_midi_write_started (source_lock, note_mode());
1458 for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
1459 ms->append_event_beats(source_lock, *i);
1462 set_percussive (old_percussive);
1463 ms->mark_streaming_write_completed (source_lock);
1470 /** Write part or all of the model to a MidiSource (i.e. save the model).
1471 * This is different from manually using read to write to a source in that
1472 * note off events are written regardless of the track mode. This is so the
1473 * user can switch a recorded track (with note durations from some instrument)
1474 * to percussive, save, reload, then switch it back to sustained without
1475 * destroying the original note durations.
1478 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source,
1479 const Glib::Threads::Mutex::Lock& source_lock,
1480 Evoral::Beats begin_time,
1481 Evoral::Beats end_time)
1483 ReadLock lock(read_lock());
1484 MidiStateTracker mst;
1486 const bool old_percussive = percussive();
1487 set_percussive(false);
1489 source->drop_model(source_lock);
1490 source->mark_streaming_midi_write_started (source_lock, note_mode());
1492 for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
1493 const Evoral::Event<Evoral::Beats>& ev (*i);
1495 if (ev.time() >= begin_time && ev.time() < end_time) {
1497 const Evoral::MIDIEvent<Evoral::Beats>* mev =
1498 static_cast<const Evoral::MIDIEvent<Evoral::Beats>* > (&ev);
1505 if (mev->is_note_off()) {
1507 if (!mst.active (mev->note(), mev->channel())) {
1508 /* the matching note-on was outside the
1509 time range we were given, so just
1510 ignore this note-off.
1515 source->append_event_beats (source_lock, *i);
1516 mst.remove (mev->note(), mev->channel());
1518 } else if (mev->is_note_on()) {
1519 mst.add (mev->note(), mev->channel());
1520 source->append_event_beats(source_lock, *i);
1522 source->append_event_beats(source_lock, *i);
1527 mst.resolve_notes (*source, source_lock, end_time);
1529 set_percussive(old_percussive);
1530 source->mark_streaming_write_completed(source_lock);
1538 MidiModel::get_state()
1540 XMLNode *node = new XMLNode("MidiModel");
1544 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1545 MidiModel::find_note (NotePtr other)
1547 Notes::iterator l = notes().lower_bound(other);
1549 if (l != notes().end()) {
1550 for (; (*l)->time() == other->time(); ++l) {
1551 /* NB: compare note contents, not note pointers.
1552 If "other" was a ptr to a note already in
1553 the model, we wouldn't be looking for it,
1556 if (**l == *other) {
1565 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1566 MidiModel::find_note (gint note_id)
1568 /* used only for looking up notes when reloading history from disk,
1569 so we don't care about performance *too* much.
1572 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1573 if ((*l)->id() == note_id) {
1581 MidiModel::PatchChangePtr
1582 MidiModel::find_patch_change (Evoral::event_id_t id)
1584 for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1585 if ((*i)->id() == id) {
1590 return PatchChangePtr ();
1593 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1594 MidiModel::find_sysex (gint sysex_id)
1596 /* used only for looking up notes when reloading history from disk,
1597 so we don't care about performance *too* much.
1600 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1601 if ((*l)->id() == sysex_id) {
1606 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1609 /** Lock and invalidate the source.
1610 * This should be used by commands and editing things
1612 MidiModel::WriteLock
1613 MidiModel::edit_lock()
1615 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1618 Glib::Threads::Mutex::Lock* source_lock = new Glib::Threads::Mutex::Lock (ms->mutex());
1619 ms->invalidate(*source_lock); // Release cached iterator's read lock on model
1620 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1623 /** Lock just the model, the source lock must already be held.
1624 * This should only be called from libardour/evoral places
1626 MidiModel::WriteLock
1627 MidiModel::write_lock()
1629 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1632 assert (!ms->mutex().trylock ());
1633 return WriteLock(new WriteLockImpl(0, _lock, _control_lock));
1637 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1639 using namespace Evoral;
1641 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1645 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1647 TimeType sa = note->time();
1648 TimeType ea = note->end_time();
1650 const Pitches& p (pitches (note->channel()));
1651 NotePtr search_note(new Note<TimeType>(0, TimeType(), TimeType(), note->note()));
1652 set<NotePtr> to_be_deleted;
1653 bool set_note_length = false;
1654 bool set_note_time = false;
1655 TimeType note_time = note->time();
1656 TimeType note_length = note->length();
1658 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1660 for (Pitches::const_iterator i = p.lower_bound (search_note);
1661 i != p.end() && (*i)->note() == note->note(); ++i) {
1663 TimeType sb = (*i)->time();
1664 TimeType eb = (*i)->end_time();
1665 OverlapType overlap = OverlapNone;
1667 if ((sb > sa) && (eb <= ea)) {
1668 overlap = OverlapInternal;
1669 } else if ((eb >= sa) && (eb <= ea)) {
1670 overlap = OverlapStart;
1671 } else if ((sb > sa) && (sb <= ea)) {
1672 overlap = OverlapEnd;
1673 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1674 overlap = OverlapExternal;
1680 DEBUG_TRACE (DEBUG::Sequence, string_compose (
1681 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1682 enum_2_string(overlap), sa, ea, sb, eb));
1684 if (insert_merge_policy() == InsertMergeReject) {
1685 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1691 cerr << "OverlapStart\n";
1692 /* existing note covers start of new note */
1693 switch (insert_merge_policy()) {
1694 case InsertMergeReplace:
1695 to_be_deleted.insert (*i);
1697 case InsertMergeTruncateExisting:
1699 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1701 (*i)->set_length (note->time() - (*i)->time());
1703 case InsertMergeTruncateAddition:
1704 set_note_time = true;
1705 set_note_length = true;
1706 note_time = (*i)->time() + (*i)->length();
1707 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1709 case InsertMergeExtend:
1711 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1713 (*i)->set_length (note->end_time() - (*i)->time());
1714 return -1; /* do not add the new note */
1717 abort(); /*NOTREACHED*/
1724 cerr << "OverlapEnd\n";
1725 /* existing note covers end of new note */
1726 switch (insert_merge_policy()) {
1727 case InsertMergeReplace:
1728 to_be_deleted.insert (*i);
1731 case InsertMergeTruncateExisting:
1732 /* resetting the start time of the existing note
1733 is a problem because of time ordering.
1737 case InsertMergeTruncateAddition:
1738 set_note_length = true;
1739 note_length = min (note_length, ((*i)->time() - note->time()));
1742 case InsertMergeExtend:
1743 /* we can't reset the time of the existing note because
1744 that will corrupt time ordering. So remove the
1745 existing note and change the position/length
1746 of the new note (which has not been added yet)
1748 to_be_deleted.insert (*i);
1749 set_note_length = true;
1750 note_length = min (note_length, (*i)->end_time() - note->time());
1753 abort(); /*NOTREACHED*/
1759 case OverlapExternal:
1760 cerr << "OverlapExt\n";
1761 /* existing note overlaps all the new note */
1762 switch (insert_merge_policy()) {
1763 case InsertMergeReplace:
1764 to_be_deleted.insert (*i);
1766 case InsertMergeTruncateExisting:
1767 case InsertMergeTruncateAddition:
1768 case InsertMergeExtend:
1769 /* cannot add in this case */
1772 abort(); /*NOTREACHED*/
1778 case OverlapInternal:
1779 cerr << "OverlapInt\n";
1780 /* new note fully overlaps an existing note */
1781 switch (insert_merge_policy()) {
1782 case InsertMergeReplace:
1783 case InsertMergeTruncateExisting:
1784 case InsertMergeTruncateAddition:
1785 case InsertMergeExtend:
1786 /* delete the existing note, the new one will cover it */
1787 to_be_deleted.insert (*i);
1790 abort(); /*NOTREACHED*/
1797 abort(); /*NOTREACHED*/
1803 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1804 remove_note_unlocked (*i);
1807 cmd->side_effect_remove (*i);
1811 if (set_note_time) {
1813 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1815 note->set_time (note_time);
1818 if (set_note_length) {
1820 cmd->change (note, NoteDiffCommand::Length, note_length);
1822 note->set_length (note_length);
1829 MidiModel::insert_merge_policy () const
1831 /* XXX ultimately this should be a per-track or even per-model policy */
1832 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1835 return ms->session().config.get_insert_merge_policy ();
1839 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1841 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1844 Source::Lock lm(old->mutex());
1845 old->invalidate (lm);
1848 _midi_source_connections.drop_connections ();
1852 s->InterpolationChanged.connect_same_thread (
1853 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1856 s->AutomationStateChanged.connect_same_thread (
1857 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1861 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1862 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1863 * appropriate ControlList.
1865 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1866 * or the other is listened to by the GUI.
1869 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1871 Glib::Threads::Mutex::Lock lm (_control_lock);
1872 control(p)->list()->set_interpolation (s);
1875 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1876 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1879 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1881 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1884 ms->set_interpolation_of (p, s);
1888 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1890 Glib::Threads::Mutex::Lock lm (_control_lock);
1891 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1892 al->set_automation_state (s);
1896 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1898 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1900 ms->set_automation_state_of (p, s);
1903 boost::shared_ptr<Evoral::Control>
1904 MidiModel::control_factory (Evoral::Parameter const & p)
1906 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1908 /* Set up newly created control's lists to the appropriate interpolation and
1909 automation state from our source.
1912 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1915 c->list()->set_interpolation (ms->interpolation_of (p));
1917 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1920 al->set_automation_state (ms->automation_state_of (p));
1925 boost::shared_ptr<const MidiSource>
1926 MidiModel::midi_source ()
1928 return _midi_source.lock ();
1931 /** Moves notes, patch changes, controllers and sys-ex to insert silence at the start of the model.
1932 * Adds commands to the session's current undo stack to reflect the movements.
1935 MidiModel::insert_silence_at_start (TimeType t)
1937 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1942 if (!notes().empty ()) {
1943 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1945 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1946 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1949 apply_command_as_subcommand (s->session(), c);
1954 if (!patch_changes().empty ()) {
1955 PatchChangeDiffCommand* c = new_patch_change_diff_command ("insert silence");
1957 for (PatchChanges::const_iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1958 c->change_time (*i, (*i)->time() + t);
1961 apply_command_as_subcommand (s->session(), c);
1966 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1967 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1968 XMLNode& before = ac->alist()->get_state ();
1969 i->second->list()->shift (0, t.to_double());
1970 XMLNode& after = ac->alist()->get_state ();
1971 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1976 if (!sysexes().empty()) {
1977 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1979 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1980 c->change (*i, (*i)->time() + t);
1983 apply_command_as_subcommand (s->session(), c);
1987 /** Transpose notes in a time range by a given number of semitones. Notes
1988 * will be clamped at 0 and 127 if the transposition would make them exceed
1991 * @param from Start time.
1992 * @param end End time.
1993 * @param semitones Number of semitones to transpose by (+ve is higher, -ve is lower).
1996 MidiModel::transpose (TimeType from, TimeType to, int semitones)
1998 boost::shared_ptr<const MidiSource> s = midi_source ();
2000 NoteDiffCommand* c = new_note_diff_command (_("transpose"));
2002 for (Notes::iterator i = notes().begin(); i != notes().end(); ++i) {
2004 if ((*i)->time() >= to) {
2009 } else if ((*i)->time() >= from) {
2011 int new_note = (*i)->note() + semitones;
2015 } else if (new_note > 127) {
2019 c->change (*i, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
2024 apply_command (s->session (), c);
2028 MidiModel::control_list_marked_dirty ()
2030 AutomatableSequence<Evoral::Beats>::control_list_marked_dirty ();
2032 ContentsChanged (); /* EMIT SIGNAL */