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,
1459 ms->session().transport_rolling() ? &_active_notes : NULL);
1461 ms->mark_streaming_midi_write_started (source_lock, note_mode());
1463 for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
1464 ms->append_event_beats(source_lock, *i);
1467 set_percussive (old_percussive);
1468 ms->mark_streaming_write_completed (source_lock);
1475 /** Write part or all of the model to a MidiSource (i.e. save the model).
1476 * This is different from manually using read to write to a source in that
1477 * note off events are written regardless of the track mode. This is so the
1478 * user can switch a recorded track (with note durations from some instrument)
1479 * to percussive, save, reload, then switch it back to sustained without
1480 * destroying the original note durations.
1483 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source,
1484 const Glib::Threads::Mutex::Lock& source_lock,
1485 Evoral::Beats begin_time,
1486 Evoral::Beats end_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 const Evoral::Event<Evoral::Beats>& ev (*i);
1500 if (ev.time() >= begin_time && ev.time() < end_time) {
1502 const Evoral::MIDIEvent<Evoral::Beats>* mev =
1503 static_cast<const Evoral::MIDIEvent<Evoral::Beats>* > (&ev);
1510 if (mev->is_note_off()) {
1512 if (!mst.active (mev->note(), mev->channel())) {
1513 /* the matching note-on was outside the
1514 time range we were given, so just
1515 ignore this note-off.
1520 source->append_event_beats (source_lock, *i);
1521 mst.remove (mev->note(), mev->channel());
1523 } else if (mev->is_note_on()) {
1524 mst.add (mev->note(), mev->channel());
1525 source->append_event_beats(source_lock, *i);
1527 source->append_event_beats(source_lock, *i);
1532 mst.resolve_notes (*source, source_lock, end_time);
1534 set_percussive(old_percussive);
1535 source->mark_streaming_write_completed(source_lock);
1543 MidiModel::get_state()
1545 XMLNode *node = new XMLNode("MidiModel");
1549 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1550 MidiModel::find_note (NotePtr other)
1552 Notes::iterator l = notes().lower_bound(other);
1554 if (l != notes().end()) {
1555 for (; (*l)->time() == other->time(); ++l) {
1556 /* NB: compare note contents, not note pointers.
1557 If "other" was a ptr to a note already in
1558 the model, we wouldn't be looking for it,
1561 if (**l == *other) {
1570 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1571 MidiModel::find_note (gint note_id)
1573 /* used only for looking up notes when reloading history from disk,
1574 so we don't care about performance *too* much.
1577 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1578 if ((*l)->id() == note_id) {
1586 MidiModel::PatchChangePtr
1587 MidiModel::find_patch_change (Evoral::event_id_t id)
1589 for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1590 if ((*i)->id() == id) {
1595 return PatchChangePtr ();
1598 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1599 MidiModel::find_sysex (gint sysex_id)
1601 /* used only for looking up notes when reloading history from disk,
1602 so we don't care about performance *too* much.
1605 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1606 if ((*l)->id() == sysex_id) {
1611 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1614 /** Lock and invalidate the source.
1615 * This should be used by commands and editing things
1617 MidiModel::WriteLock
1618 MidiModel::edit_lock()
1620 boost::shared_ptr<MidiSource> ms = _midi_source.lock();
1621 Glib::Threads::Mutex::Lock* source_lock = 0;
1624 /* Take source lock and invalidate iterator to release its lock on model.
1625 Add currently active notes to _active_notes so we can restore them
1626 if playback resumes at the same point after the edit. */
1627 source_lock = new Glib::Threads::Mutex::Lock(ms->mutex());
1628 ms->invalidate(*source_lock,
1629 ms->session().transport_rolling() ? &_active_notes : NULL);
1632 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1636 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1638 using namespace Evoral;
1640 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1644 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1646 TimeType sa = note->time();
1647 TimeType ea = note->end_time();
1649 const Pitches& p (pitches (note->channel()));
1650 NotePtr search_note(new Note<TimeType>(0, TimeType(), TimeType(), note->note()));
1651 set<NotePtr> to_be_deleted;
1652 bool set_note_length = false;
1653 bool set_note_time = false;
1654 TimeType note_time = note->time();
1655 TimeType note_length = note->length();
1657 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1659 for (Pitches::const_iterator i = p.lower_bound (search_note);
1660 i != p.end() && (*i)->note() == note->note(); ++i) {
1662 TimeType sb = (*i)->time();
1663 TimeType eb = (*i)->end_time();
1664 OverlapType overlap = OverlapNone;
1666 if ((sb > sa) && (eb <= ea)) {
1667 overlap = OverlapInternal;
1668 } else if ((eb > sa) && (eb <= ea)) {
1669 overlap = OverlapStart;
1670 } else if ((sb > sa) && (sb < ea)) {
1671 overlap = OverlapEnd;
1672 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1673 overlap = OverlapExternal;
1679 DEBUG_TRACE (DEBUG::Sequence, string_compose (
1680 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1681 enum_2_string(overlap), sa, ea, sb, eb));
1683 if (insert_merge_policy() == InsertMergeReject) {
1684 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1690 cerr << "OverlapStart\n";
1691 /* existing note covers start of new note */
1692 switch (insert_merge_policy()) {
1693 case InsertMergeReplace:
1694 to_be_deleted.insert (*i);
1696 case InsertMergeTruncateExisting:
1698 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1700 (*i)->set_length (note->time() - (*i)->time());
1702 case InsertMergeTruncateAddition:
1703 set_note_time = true;
1704 set_note_length = true;
1705 note_time = (*i)->time() + (*i)->length();
1706 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1708 case InsertMergeExtend:
1710 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1712 (*i)->set_length (note->end_time() - (*i)->time());
1713 return -1; /* do not add the new note */
1716 abort(); /*NOTREACHED*/
1723 cerr << "OverlapEnd\n";
1724 /* existing note covers end of new note */
1725 switch (insert_merge_policy()) {
1726 case InsertMergeReplace:
1727 to_be_deleted.insert (*i);
1730 case InsertMergeTruncateExisting:
1731 /* resetting the start time of the existing note
1732 is a problem because of time ordering.
1736 case InsertMergeTruncateAddition:
1737 set_note_length = true;
1738 note_length = min (note_length, ((*i)->time() - note->time()));
1741 case InsertMergeExtend:
1742 /* we can't reset the time of the existing note because
1743 that will corrupt time ordering. So remove the
1744 existing note and change the position/length
1745 of the new note (which has not been added yet)
1747 to_be_deleted.insert (*i);
1748 set_note_length = true;
1749 note_length = min (note_length, (*i)->end_time() - note->time());
1752 abort(); /*NOTREACHED*/
1758 case OverlapExternal:
1759 cerr << "OverlapExt\n";
1760 /* existing note overlaps all the new note */
1761 switch (insert_merge_policy()) {
1762 case InsertMergeReplace:
1763 to_be_deleted.insert (*i);
1765 case InsertMergeTruncateExisting:
1766 case InsertMergeTruncateAddition:
1767 case InsertMergeExtend:
1768 /* cannot add in this case */
1771 abort(); /*NOTREACHED*/
1777 case OverlapInternal:
1778 cerr << "OverlapInt\n";
1779 /* new note fully overlaps an existing note */
1780 switch (insert_merge_policy()) {
1781 case InsertMergeReplace:
1782 case InsertMergeTruncateExisting:
1783 case InsertMergeTruncateAddition:
1784 case InsertMergeExtend:
1785 /* delete the existing note, the new one will cover it */
1786 to_be_deleted.insert (*i);
1789 abort(); /*NOTREACHED*/
1796 abort(); /*NOTREACHED*/
1802 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1803 remove_note_unlocked (*i);
1806 cmd->side_effect_remove (*i);
1810 if (set_note_time) {
1812 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1814 note->set_time (note_time);
1817 if (set_note_length) {
1819 cmd->change (note, NoteDiffCommand::Length, note_length);
1821 note->set_length (note_length);
1828 MidiModel::insert_merge_policy () const
1830 /* XXX ultimately this should be a per-track or even per-model policy */
1831 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1834 return ms->session().config.get_insert_merge_policy ();
1838 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1840 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1843 Source::Lock lm(old->mutex());
1844 old->invalidate (lm);
1847 _midi_source_connections.drop_connections ();
1851 s->InterpolationChanged.connect_same_thread (
1852 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1855 s->AutomationStateChanged.connect_same_thread (
1856 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1860 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1861 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1862 * appropriate ControlList.
1864 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1865 * or the other is listened to by the GUI.
1868 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1870 Glib::Threads::Mutex::Lock lm (_control_lock);
1871 control(p)->list()->set_interpolation (s);
1874 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1875 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1878 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1880 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1883 ms->set_interpolation_of (p, s);
1887 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1889 Glib::Threads::Mutex::Lock lm (_control_lock);
1890 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1891 al->set_automation_state (s);
1895 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1897 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1899 ms->set_automation_state_of (p, s);
1902 boost::shared_ptr<Evoral::Control>
1903 MidiModel::control_factory (Evoral::Parameter const & p)
1905 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1907 /* Set up newly created control's lists to the appropriate interpolation and
1908 automation state from our source.
1911 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1914 c->list()->set_interpolation (ms->interpolation_of (p));
1916 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1919 al->set_automation_state (ms->automation_state_of (p));
1924 boost::shared_ptr<const MidiSource>
1925 MidiModel::midi_source ()
1927 return _midi_source.lock ();
1930 /** Moves notes, patch changes, controllers and sys-ex to insert silence at the start of the model.
1931 * Adds commands to the session's current undo stack to reflect the movements.
1934 MidiModel::insert_silence_at_start (TimeType t)
1936 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1941 if (!notes().empty ()) {
1942 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1944 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1945 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1948 apply_command_as_subcommand (s->session(), c);
1953 if (!patch_changes().empty ()) {
1954 PatchChangeDiffCommand* c = new_patch_change_diff_command ("insert silence");
1956 for (PatchChanges::const_iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1957 c->change_time (*i, (*i)->time() + t);
1960 apply_command_as_subcommand (s->session(), c);
1965 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1966 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1967 XMLNode& before = ac->alist()->get_state ();
1968 i->second->list()->shift (0, t.to_double());
1969 XMLNode& after = ac->alist()->get_state ();
1970 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1975 if (!sysexes().empty()) {
1976 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1978 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1979 c->change (*i, (*i)->time() + t);
1982 apply_command_as_subcommand (s->session(), c);
1987 MidiModel::transpose (NoteDiffCommand* c, const NotePtr note_ptr, int semitones)
1989 int new_note = note_ptr->note() + semitones;
1993 } else if (new_note > 127) {
1997 c->change (note_ptr, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
2001 MidiModel::control_list_marked_dirty ()
2003 AutomatableSequence<Evoral::Beats>::control_list_marked_dirty ();
2005 ContentsChanged (); /* EMIT SIGNAL */