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)
494 XMLProperty const * prop;
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)
602 XMLProperty const * prop;
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)
879 XMLProperty const * prop;
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)
1238 XMLProperty const * prop;
1239 XMLProperty const * 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)
1280 XMLProperty const * prop;
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 /* Invalidate and store active notes, which will be picked up by the iterator
1457 on the next roll if time progresses linearly. */
1458 ms->invalidate(source_lock);
1460 ms->mark_streaming_midi_write_started (source_lock, note_mode());
1462 for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
1463 ms->append_event_beats(source_lock, *i);
1466 set_percussive (old_percussive);
1467 ms->mark_streaming_write_completed (source_lock);
1474 /** Write part or all of the model to a MidiSource (i.e. save the model).
1475 * This is different from manually using read to write to a source in that
1476 * note off events are written regardless of the track mode. This is so the
1477 * user can switch a recorded track (with note durations from some instrument)
1478 * to percussive, save, reload, then switch it back to sustained without
1479 * destroying the original note durations.
1482 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source,
1483 const Glib::Threads::Mutex::Lock& source_lock,
1484 TimeType begin_time,
1488 ReadLock lock(read_lock());
1489 MidiStateTracker mst;
1491 const bool old_percussive = percussive();
1492 set_percussive(false);
1494 source->drop_model(source_lock);
1495 source->mark_streaming_midi_write_started (source_lock, note_mode());
1497 for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
1498 if (i->time() >= begin_time && i->time() < end_time) {
1500 Evoral::Event<TimeType> mev (*i, true); /* copy the event */
1502 if (offset_events) {
1503 mev.set_time(mev.time() - begin_time);
1506 if (mev.is_note_off()) {
1508 if (!mst.active (mev.note(), mev.channel())) {
1509 /* the matching note-on was outside the
1510 time range we were given, so just
1511 ignore this note-off.
1516 source->append_event_beats (source_lock, mev);
1517 mst.remove (mev.note(), mev.channel());
1519 } else if (mev.is_note_on()) {
1520 mst.add (mev.note(), mev.channel());
1521 source->append_event_beats(source_lock, mev);
1523 source->append_event_beats(source_lock, mev);
1528 if (offset_events) {
1529 end_time -= begin_time;
1531 mst.resolve_notes (*source, source_lock, end_time);
1533 set_percussive(old_percussive);
1534 source->mark_streaming_write_completed(source_lock);
1542 MidiModel::get_state()
1544 XMLNode *node = new XMLNode("MidiModel");
1548 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1549 MidiModel::find_note (NotePtr other)
1551 Notes::iterator l = notes().lower_bound(other);
1553 if (l != notes().end()) {
1554 for (; (*l)->time() == other->time(); ++l) {
1555 /* NB: compare note contents, not note pointers.
1556 If "other" was a ptr to a note already in
1557 the model, we wouldn't be looking for it,
1560 if (**l == *other) {
1569 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1570 MidiModel::find_note (gint note_id)
1572 /* used only for looking up notes when reloading history from disk,
1573 so we don't care about performance *too* much.
1576 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1577 if ((*l)->id() == note_id) {
1585 MidiModel::PatchChangePtr
1586 MidiModel::find_patch_change (Evoral::event_id_t id)
1588 for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1589 if ((*i)->id() == id) {
1594 return PatchChangePtr ();
1597 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1598 MidiModel::find_sysex (gint sysex_id)
1600 /* used only for looking up notes when reloading history from disk,
1601 so we don't care about performance *too* much.
1604 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1605 if ((*l)->id() == sysex_id) {
1610 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1613 /** Lock and invalidate the source.
1614 * This should be used by commands and editing things
1616 MidiModel::WriteLock
1617 MidiModel::edit_lock()
1619 boost::shared_ptr<MidiSource> ms = _midi_source.lock();
1620 Glib::Threads::Mutex::Lock* source_lock = 0;
1623 /* Take source lock and invalidate iterator to release its lock on model.
1624 Add currently active notes to _active_notes so we can restore them
1625 if playback resumes at the same point after the edit. */
1626 source_lock = new Glib::Threads::Mutex::Lock(ms->mutex());
1627 ms->invalidate(*source_lock);
1630 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1634 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1636 using namespace Evoral;
1638 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1642 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1644 TimeType sa = note->time();
1645 TimeType ea = note->end_time();
1647 const Pitches& p (pitches (note->channel()));
1648 NotePtr search_note(new Note<TimeType>(0, TimeType(), TimeType(), note->note()));
1649 set<NotePtr> to_be_deleted;
1650 bool set_note_length = false;
1651 bool set_note_time = false;
1652 TimeType note_time = note->time();
1653 TimeType note_length = note->length();
1655 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1657 for (Pitches::const_iterator i = p.lower_bound (search_note);
1658 i != p.end() && (*i)->note() == note->note(); ++i) {
1660 TimeType sb = (*i)->time();
1661 TimeType eb = (*i)->end_time();
1662 OverlapType overlap = OverlapNone;
1664 if ((sb > sa) && (eb <= ea)) {
1665 overlap = OverlapInternal;
1666 } else if ((eb > sa) && (eb <= ea)) {
1667 overlap = OverlapStart;
1668 } else if ((sb > sa) && (sb < ea)) {
1669 overlap = OverlapEnd;
1670 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1671 overlap = OverlapExternal;
1677 DEBUG_TRACE (DEBUG::Sequence, string_compose (
1678 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1679 enum_2_string(overlap), sa, ea, sb, eb));
1681 if (insert_merge_policy() == InsertMergeReject) {
1682 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1688 cerr << "OverlapStart\n";
1689 /* existing note covers start of new note */
1690 switch (insert_merge_policy()) {
1691 case InsertMergeReplace:
1692 to_be_deleted.insert (*i);
1694 case InsertMergeTruncateExisting:
1696 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1698 (*i)->set_length (note->time() - (*i)->time());
1700 case InsertMergeTruncateAddition:
1701 set_note_time = true;
1702 set_note_length = true;
1703 note_time = (*i)->time() + (*i)->length();
1704 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1706 case InsertMergeExtend:
1708 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1710 (*i)->set_length (note->end_time() - (*i)->time());
1711 return -1; /* do not add the new note */
1714 abort(); /*NOTREACHED*/
1721 cerr << "OverlapEnd\n";
1722 /* existing note covers end of new note */
1723 switch (insert_merge_policy()) {
1724 case InsertMergeReplace:
1725 to_be_deleted.insert (*i);
1728 case InsertMergeTruncateExisting:
1729 /* resetting the start time of the existing note
1730 is a problem because of time ordering.
1734 case InsertMergeTruncateAddition:
1735 set_note_length = true;
1736 note_length = min (note_length, ((*i)->time() - note->time()));
1739 case InsertMergeExtend:
1740 /* we can't reset the time of the existing note because
1741 that will corrupt time ordering. So remove the
1742 existing note and change the position/length
1743 of the new note (which has not been added yet)
1745 to_be_deleted.insert (*i);
1746 set_note_length = true;
1747 note_length = min (note_length, (*i)->end_time() - note->time());
1750 abort(); /*NOTREACHED*/
1756 case OverlapExternal:
1757 cerr << "OverlapExt\n";
1758 /* existing note overlaps all the new note */
1759 switch (insert_merge_policy()) {
1760 case InsertMergeReplace:
1761 to_be_deleted.insert (*i);
1763 case InsertMergeTruncateExisting:
1764 case InsertMergeTruncateAddition:
1765 case InsertMergeExtend:
1766 /* cannot add in this case */
1769 abort(); /*NOTREACHED*/
1775 case OverlapInternal:
1776 cerr << "OverlapInt\n";
1777 /* new note fully overlaps an existing note */
1778 switch (insert_merge_policy()) {
1779 case InsertMergeReplace:
1780 case InsertMergeTruncateExisting:
1781 case InsertMergeTruncateAddition:
1782 case InsertMergeExtend:
1783 /* delete the existing note, the new one will cover it */
1784 to_be_deleted.insert (*i);
1787 abort(); /*NOTREACHED*/
1794 abort(); /*NOTREACHED*/
1800 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1801 remove_note_unlocked (*i);
1804 cmd->side_effect_remove (*i);
1808 if (set_note_time) {
1810 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1812 note->set_time (note_time);
1815 if (set_note_length) {
1817 cmd->change (note, NoteDiffCommand::Length, note_length);
1819 note->set_length (note_length);
1826 MidiModel::insert_merge_policy () const
1828 /* XXX ultimately this should be a per-track or even per-model policy */
1829 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1832 return ms->session().config.get_insert_merge_policy ();
1836 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1838 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1841 Source::Lock lm(old->mutex());
1842 old->invalidate (lm);
1845 _midi_source_connections.drop_connections ();
1849 s->InterpolationChanged.connect_same_thread (
1850 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1853 s->AutomationStateChanged.connect_same_thread (
1854 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1858 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1859 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1860 * appropriate ControlList.
1862 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1863 * or the other is listened to by the GUI.
1866 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1868 Glib::Threads::Mutex::Lock lm (_control_lock);
1869 control(p)->list()->set_interpolation (s);
1872 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1873 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1876 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1878 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1881 ms->set_interpolation_of (p, s);
1885 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1887 Glib::Threads::Mutex::Lock lm (_control_lock);
1888 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1889 al->set_automation_state (s);
1893 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1895 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1897 ms->set_automation_state_of (p, s);
1900 boost::shared_ptr<Evoral::Control>
1901 MidiModel::control_factory (Evoral::Parameter const & p)
1903 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1905 /* Set up newly created control's lists to the appropriate interpolation and
1906 automation state from our source.
1909 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1912 c->list()->set_interpolation (ms->interpolation_of (p));
1914 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1917 al->set_automation_state (ms->automation_state_of (p));
1922 boost::shared_ptr<const MidiSource>
1923 MidiModel::midi_source ()
1925 return _midi_source.lock ();
1928 /** Moves notes, patch changes, controllers and sys-ex to insert silence at the start of the model.
1929 * Adds commands to the session's current undo stack to reflect the movements.
1932 MidiModel::insert_silence_at_start (TimeType t)
1934 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1939 if (!notes().empty ()) {
1940 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1942 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1943 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1946 apply_command_as_subcommand (s->session(), c);
1951 if (!patch_changes().empty ()) {
1952 PatchChangeDiffCommand* c = new_patch_change_diff_command ("insert silence");
1954 for (PatchChanges::const_iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1955 c->change_time (*i, (*i)->time() + t);
1958 apply_command_as_subcommand (s->session(), c);
1963 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1964 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1965 XMLNode& before = ac->alist()->get_state ();
1966 i->second->list()->shift (0, t.to_double());
1967 XMLNode& after = ac->alist()->get_state ();
1968 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1973 if (!sysexes().empty()) {
1974 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1976 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1977 c->change (*i, (*i)->time() + t);
1980 apply_command_as_subcommand (s->session(), c);
1985 MidiModel::transpose (NoteDiffCommand* c, const NotePtr note_ptr, int semitones)
1987 int new_note = note_ptr->note() + semitones;
1991 } else if (new_note > 127) {
1995 c->change (note_ptr, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
1999 MidiModel::control_list_marked_dirty ()
2001 AutomatableSequence<Evoral::Beats>::control_list_marked_dirty ();
2003 ContentsChanged (); /* EMIT SIGNAL */