2 Copyright (C) 2007 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "pbd/compose.h"
28 #include "pbd/enumwriter.h"
29 #include "pbd/error.h"
31 #include "evoral/Control.hpp"
33 #include "midi++/events.h"
35 #include "ardour/automation_control.h"
36 #include "ardour/midi_automation_list_binder.h"
37 #include "ardour/midi_model.h"
38 #include "ardour/midi_source.h"
39 #include "ardour/midi_state_tracker.h"
40 #include "ardour/session.h"
41 #include "ardour/types.h"
46 using namespace ARDOUR;
49 MidiModel::MidiModel (boost::shared_ptr<MidiSource> s)
50 : AutomatableSequence<TimeType>(s->session())
55 /** Start a new NoteDiff command.
57 * This has no side-effects on the model or Session, the returned command
58 * can be held on to for as long as the caller wishes, or discarded without
59 * formality, until apply_command is called and ownership is taken.
61 MidiModel::NoteDiffCommand*
62 MidiModel::new_note_diff_command (const string name)
64 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
67 return new NoteDiffCommand (ms->model(), name);
70 /** Start a new SysExDiff command */
71 MidiModel::SysExDiffCommand*
72 MidiModel::new_sysex_diff_command (const string name)
74 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
77 return new SysExDiffCommand (ms->model(), name);
80 /** Start a new PatchChangeDiff command */
81 MidiModel::PatchChangeDiffCommand*
82 MidiModel::new_patch_change_diff_command (const string name)
84 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
87 return new PatchChangeDiffCommand (ms->model(), name);
93 * Ownership of cmd is taken, it must not be deleted by the caller.
94 * The command will constitute one item on the undo stack.
97 MidiModel::apply_command(Session& session, Command* cmd)
99 session.begin_reversible_command(cmd->name());
101 session.commit_reversible_command(cmd);
105 /** Apply a command as part of a larger reversible transaction
107 * Ownership of cmd is taken, it must not be deleted by the caller.
108 * The command will constitute one item on the undo stack.
111 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
114 session.add_command(cmd);
118 /************** DIFF COMMAND ********************/
120 #define NOTE_DIFF_COMMAND_ELEMENT "NoteDiffCommand"
121 #define DIFF_NOTES_ELEMENT "ChangedNotes"
122 #define ADDED_NOTES_ELEMENT "AddedNotes"
123 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
124 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
125 #define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand"
126 #define DIFF_SYSEXES_ELEMENT "ChangedSysExes"
127 #define PATCH_CHANGE_DIFF_COMMAND_ELEMENT "PatchChangeDiffCommand"
128 #define ADDED_PATCH_CHANGES_ELEMENT "AddedPatchChanges"
129 #define REMOVED_PATCH_CHANGES_ELEMENT "RemovedPatchChanges"
130 #define DIFF_PATCH_CHANGES_ELEMENT "ChangedPatchChanges"
132 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
140 MidiModel::NoteDiffCommand::NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
141 : DiffCommand (m, "")
144 set_state (node, Stateful::loading_state_version);
148 MidiModel::NoteDiffCommand::add (const NotePtr note)
150 _removed_notes.remove(note);
151 _added_notes.push_back(note);
155 MidiModel::NoteDiffCommand::remove (const NotePtr note)
157 _added_notes.remove(note);
158 _removed_notes.push_back(note);
162 MidiModel::NoteDiffCommand::side_effect_remove (const NotePtr note)
164 side_effect_removals.insert (note);
168 MidiModel::NoteDiffCommand::get_value (const NotePtr note, Property prop)
172 return Variant(note->note());
174 return Variant(note->velocity());
176 return Variant(note->channel());
178 return Variant(note->time());
180 return Variant(note->length());
187 MidiModel::NoteDiffCommand::value_type(Property prop)
196 return Variant::BEATS;
199 return Variant::NOTHING;
203 MidiModel::NoteDiffCommand::change (const NotePtr note,
205 const Variant& new_value)
209 const NoteChange change = {
210 prop, note, 0, get_value(note, prop), new_value
213 if (change.old_value == new_value) {
217 _changes.push_back (change);
220 MidiModel::NoteDiffCommand &
221 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand& other)
223 if (this == &other) {
227 if (_model != other._model) {
231 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
232 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
233 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
234 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
240 MidiModel::NoteDiffCommand::operator() ()
243 MidiModel::WriteLock lock(_model->edit_lock());
245 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
246 if (!_model->add_note_unlocked(*i)) {
247 /* failed to add it, so don't leave it in the removed list, to
248 avoid apparent errors on undo.
250 _removed_notes.remove (*i);
254 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
255 _model->remove_note_unlocked(*i);
258 /* notes we modify in a way that requires remove-then-add to maintain ordering */
259 set<NotePtr> temporary_removals;
261 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
262 Property prop = i->property;
265 /* note found during deserialization, so try
266 again now that the model state is different.
268 i->note = _model->find_note (i->note_id);
274 if (temporary_removals.find (i->note) == temporary_removals.end()) {
275 _model->remove_note_unlocked (i->note);
276 temporary_removals.insert (i->note);
278 i->note->set_note (i->new_value.get_int());
282 if (temporary_removals.find (i->note) == temporary_removals.end()) {
283 _model->remove_note_unlocked (i->note);
284 temporary_removals.insert (i->note);
286 i->note->set_time (i->new_value.get_beats());
290 if (temporary_removals.find (i->note) == temporary_removals.end()) {
291 _model->remove_note_unlocked (i->note);
292 temporary_removals.insert (i->note);
294 i->note->set_channel (i->new_value.get_int());
297 /* no remove-then-add required for these properties, since we do not index them
301 i->note->set_velocity (i->new_value.get_int());
305 i->note->set_length (i->new_value.get_beats());
311 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
312 NoteDiffCommand side_effects (model(), "side effects");
313 if (_model->add_note_unlocked (*i, &side_effects)) {
314 /* The note was re-added ok */
315 *this += side_effects;
317 /* The note that we removed earlier could not be re-added. This change record
318 must say that the note was removed. We'll keep the changes we made, though,
319 as if the note is re-added by the undo the changes must also be undone.
321 _removed_notes.push_back (*i);
325 if (!side_effect_removals.empty()) {
327 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
328 cerr << "\t" << *i << ' ' << **i << endl;
333 _model->ContentsChanged(); /* EMIT SIGNAL */
337 MidiModel::NoteDiffCommand::undo ()
340 MidiModel::WriteLock lock(_model->edit_lock());
342 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
343 _model->remove_note_unlocked(*i);
346 /* Apply changes first; this is important in the case of a note change which
347 resulted in the note being removed by the overlap checker. If the overlap
348 checker removes a note, it will be in _removed_notes. We are going to re-add
349 it below, but first we must undo the changes we made so that the overlap
350 checker doesn't refuse the re-add.
353 /* notes we modify in a way that requires remove-then-add to maintain ordering */
354 set<NotePtr> temporary_removals;
357 /* lazily discover any affected notes that were not discovered when
358 * loading the history because of deletions, etc.
361 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
363 i->note = _model->find_note (i->note_id);
368 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
369 Property prop = i->property;
373 if (temporary_removals.find (i->note) == temporary_removals.end() &&
374 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
376 /* We only need to mark this note for re-add if (a) we haven't
377 already marked it and (b) it isn't on the _removed_notes
378 list (which means that it has already been removed and it
379 will be re-added anyway)
382 _model->remove_note_unlocked (i->note);
383 temporary_removals.insert (i->note);
385 i->note->set_note (i->old_value.get_int());
389 if (temporary_removals.find (i->note) == temporary_removals.end() &&
390 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
394 _model->remove_note_unlocked (i->note);
395 temporary_removals.insert (i->note);
397 i->note->set_time (i->old_value.get_beats());
401 if (temporary_removals.find (i->note) == temporary_removals.end() &&
402 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
406 _model->remove_note_unlocked (i->note);
407 temporary_removals.insert (i->note);
409 i->note->set_channel (i->old_value.get_int());
412 /* no remove-then-add required for these properties, since we do not index them
416 i->note->set_velocity (i->old_value.get_int());
420 i->note->set_length (i->old_value.get_beats());
425 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
426 _model->add_note_unlocked(*i);
429 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
430 _model->add_note_unlocked (*i);
433 /* finally add back notes that were removed by the "do". we don't care
434 about side effects here since the model should be back to its original
435 state once this is done.
438 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
439 _model->add_note_unlocked (*i);
443 _model->ContentsChanged(); /* EMIT SIGNAL */
447 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
449 XMLNode* xml_note = new XMLNode("note");
452 ostringstream id_str(ios::ate);
453 id_str << int(note->id());
454 xml_note->add_property("id", id_str.str());
458 ostringstream note_str(ios::ate);
459 note_str << int(note->note());
460 xml_note->add_property("note", note_str.str());
464 ostringstream channel_str(ios::ate);
465 channel_str << int(note->channel());
466 xml_note->add_property("channel", channel_str.str());
470 ostringstream time_str(ios::ate);
471 time_str << note->time();
472 xml_note->add_property("time", time_str.str());
476 ostringstream length_str(ios::ate);
477 length_str << note->length();
478 xml_note->add_property("length", length_str.str());
482 ostringstream velocity_str(ios::ate);
483 velocity_str << (unsigned int) note->velocity();
484 xml_note->add_property("velocity", velocity_str.str());
490 Evoral::Sequence<MidiModel::TimeType>::NotePtr
491 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
495 unsigned int channel;
496 MidiModel::TimeType time;
497 MidiModel::TimeType length;
498 unsigned int velocity;
501 if ((prop = xml_note->property("id")) != 0) {
502 istringstream id_str(prop->value());
505 error << "note information missing ID value" << endmsg;
509 if ((prop = xml_note->property("note")) != 0) {
510 istringstream note_str(prop->value());
513 warning << "note information missing note value" << endmsg;
517 if ((prop = xml_note->property("channel")) != 0) {
518 istringstream channel_str(prop->value());
519 channel_str >> channel;
521 warning << "note information missing channel" << endmsg;
525 if ((prop = xml_note->property("time")) != 0) {
526 istringstream time_str(prop->value());
529 warning << "note information missing time" << endmsg;
530 time = MidiModel::TimeType();
533 if ((prop = xml_note->property("length")) != 0) {
534 istringstream length_str(prop->value());
535 length_str >> length;
537 warning << "note information missing length" << endmsg;
538 length = MidiModel::TimeType(1);
541 if ((prop = xml_note->property("velocity")) != 0) {
542 istringstream velocity_str(prop->value());
543 velocity_str >> velocity;
545 warning << "note information missing velocity" << endmsg;
549 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
550 note_ptr->set_id (id);
556 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
558 XMLNode* xml_change = new XMLNode("Change");
560 /* first, the change itself */
562 xml_change->add_property ("property", enum_2_string (change.property));
565 ostringstream old_value_str (ios::ate);
566 if (change.property == StartTime || change.property == Length) {
567 old_value_str << change.old_value.get_beats();
569 old_value_str << change.old_value.get_int();
571 xml_change->add_property ("old", old_value_str.str());
575 ostringstream new_value_str (ios::ate);
576 if (change.property == StartTime || change.property == Length) {
577 new_value_str << change.new_value.get_beats();
579 new_value_str << change.new_value.get_int();
581 xml_change->add_property ("new", new_value_str.str());
584 ostringstream id_str;
585 id_str << change.note->id();
586 xml_change->add_property ("id", id_str.str());
591 MidiModel::NoteDiffCommand::NoteChange
592 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
597 if ((prop = xml_change->property("property")) != 0) {
598 change.property = (Property) string_2_enum (prop->value(), change.property);
600 fatal << "!!!" << endmsg;
601 abort(); /*NOTREACHED*/
604 if ((prop = xml_change->property ("id")) == 0) {
605 error << _("No NoteID found for note property change - ignored") << endmsg;
609 gint note_id = atoi (prop->value().c_str());
611 if ((prop = xml_change->property ("old")) != 0) {
612 istringstream old_str (prop->value());
613 if (change.property == StartTime || change.property == Length) {
614 Evoral::MusicalTime old_time;
616 change.old_value = old_time;
618 int integer_value_so_that_istream_does_the_right_thing;
619 old_str >> integer_value_so_that_istream_does_the_right_thing;
620 change.old_value = integer_value_so_that_istream_does_the_right_thing;
623 fatal << "!!!" << endmsg;
624 abort(); /*NOTREACHED*/
627 if ((prop = xml_change->property ("new")) != 0) {
628 istringstream new_str (prop->value());
629 if (change.property == StartTime || change.property == Length) {
630 Evoral::MusicalTime new_time;
632 change.new_value = Variant(new_time);
634 int integer_value_so_that_istream_does_the_right_thing;
635 new_str >> integer_value_so_that_istream_does_the_right_thing;
636 change.new_value = integer_value_so_that_istream_does_the_right_thing;
639 fatal << "!!!" << endmsg;
640 abort(); /*NOTREACHED*/
643 /* we must point at the instance of the note that is actually in the model.
644 so go look for it ... it may not be there (it could have been
645 deleted in a later operation, so store the note id so that we can
646 look it up again later).
649 change.note = _model->find_note (note_id);
650 change.note_id = note_id;
656 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
658 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
664 _added_notes.clear();
665 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
667 XMLNodeList notes = added_notes->children();
668 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
669 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
675 _removed_notes.clear();
676 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
678 XMLNodeList notes = removed_notes->children();
679 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
680 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
688 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
691 XMLNodeList notes = changed_notes->children();
692 transform (notes.begin(), notes.end(), back_inserter(_changes),
693 boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
697 /* side effect removals caused by changes */
699 side_effect_removals.clear();
701 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
703 if (side_effect_notes) {
704 XMLNodeList notes = side_effect_notes->children();
705 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
706 side_effect_removals.insert (unmarshal_note (*n));
714 MidiModel::NoteDiffCommand::get_state ()
716 XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
717 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
719 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
720 for_each(_changes.begin(), _changes.end(),
722 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
723 boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
725 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
726 for_each(_added_notes.begin(), _added_notes.end(),
728 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
729 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
731 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
732 for_each(_removed_notes.begin(), _removed_notes.end(),
734 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
735 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
737 /* if this command had side-effects, store that state too
740 if (!side_effect_removals.empty()) {
741 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
742 for_each(side_effect_removals.begin(), side_effect_removals.end(),
744 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
745 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
748 return *diff_command;
751 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
752 : DiffCommand (m, "")
755 set_state (node, Stateful::loading_state_version);
759 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
764 change.property = Time;
765 change.old_time = s->time ();
766 change.new_time = new_time;
768 _changes.push_back (change);
772 MidiModel::SysExDiffCommand::operator() ()
775 MidiModel::WriteLock lock (_model->edit_lock ());
777 for (list<SysExPtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
778 _model->remove_sysex_unlocked (*i);
781 /* find any sysex events that were missing when unmarshalling */
783 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
785 i->sysex = _model->find_sysex (i->sysex_id);
790 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
791 switch (i->property) {
793 i->sysex->set_time (i->new_time);
798 _model->ContentsChanged (); /* EMIT SIGNAL */
802 MidiModel::SysExDiffCommand::undo ()
805 MidiModel::WriteLock lock (_model->edit_lock ());
807 for (list<SysExPtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
808 _model->add_sysex_unlocked (*i);
811 /* find any sysex events that were missing when unmarshalling */
813 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
815 i->sysex = _model->find_sysex (i->sysex_id);
820 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
821 switch (i->property) {
823 i->sysex->set_time (i->old_time);
830 _model->ContentsChanged(); /* EMIT SIGNAL */
834 MidiModel::SysExDiffCommand::remove (SysExPtr sysex)
836 _removed.push_back(sysex);
840 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
842 XMLNode* xml_change = new XMLNode ("Change");
844 /* first, the change itself */
846 xml_change->add_property ("property", enum_2_string (change.property));
849 ostringstream old_value_str (ios::ate);
850 old_value_str << change.old_time;
851 xml_change->add_property ("old", old_value_str.str());
855 ostringstream new_value_str (ios::ate);
856 new_value_str << change.new_time;
857 xml_change->add_property ("new", new_value_str.str());
860 ostringstream id_str;
861 id_str << change.sysex->id();
862 xml_change->add_property ("id", id_str.str());
867 MidiModel::SysExDiffCommand::Change
868 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
873 if ((prop = xml_change->property ("property")) != 0) {
874 change.property = (Property) string_2_enum (prop->value(), change.property);
876 fatal << "!!!" << endmsg;
877 abort(); /*NOTREACHED*/
880 if ((prop = xml_change->property ("id")) == 0) {
881 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
885 gint sysex_id = atoi (prop->value().c_str());
887 if ((prop = xml_change->property ("old")) != 0) {
888 istringstream old_str (prop->value());
889 old_str >> change.old_time;
891 fatal << "!!!" << endmsg;
892 abort(); /*NOTREACHED*/
895 if ((prop = xml_change->property ("new")) != 0) {
896 istringstream new_str (prop->value());
897 new_str >> change.new_time;
899 fatal << "!!!" << endmsg;
900 abort(); /*NOTREACHED*/
903 /* we must point at the instance of the sysex that is actually in the model.
904 so go look for it ...
907 change.sysex = _model->find_sysex (sysex_id);
908 change.sysex_id = sysex_id;
914 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
916 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
924 XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
926 if (changed_sysexes) {
927 XMLNodeList sysexes = changed_sysexes->children();
928 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
929 boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
937 MidiModel::SysExDiffCommand::get_state ()
939 XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
940 diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
942 XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
943 for_each (_changes.begin(), _changes.end(),
945 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
946 boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
948 return *diff_command;
951 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
952 : DiffCommand (m, name)
957 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
958 : DiffCommand (m, "")
961 set_state (node, Stateful::loading_state_version);
965 MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
967 _added.push_back (p);
971 MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
973 _removed.push_back (p);
977 MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
982 c.old_time = patch->time ();
985 _changes.push_back (c);
989 MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
992 c.property = Channel;
994 c.old_channel = patch->channel ();
995 c.new_channel = channel;
996 c.patch_id = patch->id();
998 _changes.push_back (c);
1002 MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
1005 c.property = Program;
1007 c.old_program = patch->program ();
1008 c.new_program = program;
1009 c.patch_id = patch->id();
1011 _changes.push_back (c);
1015 MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
1020 c.old_bank = patch->bank ();
1023 _changes.push_back (c);
1027 MidiModel::PatchChangeDiffCommand::operator() ()
1030 MidiModel::WriteLock lock (_model->edit_lock ());
1032 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1033 _model->add_patch_change_unlocked (*i);
1036 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1037 _model->remove_patch_change_unlocked (*i);
1040 /* find any patch change events that were missing when unmarshalling */
1042 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1044 i->patch = _model->find_patch_change (i->patch_id);
1049 set<PatchChangePtr> temporary_removals;
1051 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1052 switch (i->property) {
1054 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1055 _model->remove_patch_change_unlocked (i->patch);
1056 temporary_removals.insert (i->patch);
1058 i->patch->set_time (i->new_time);
1062 i->patch->set_channel (i->new_channel);
1066 i->patch->set_program (i->new_program);
1070 i->patch->set_bank (i->new_bank);
1075 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1076 _model->add_patch_change_unlocked (*i);
1080 _model->ContentsChanged (); /* EMIT SIGNAL */
1084 MidiModel::PatchChangeDiffCommand::undo ()
1087 MidiModel::WriteLock lock (_model->edit_lock());
1089 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1090 _model->remove_patch_change_unlocked (*i);
1093 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1094 _model->add_patch_change_unlocked (*i);
1097 /* find any patch change events that were missing when unmarshalling */
1099 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1101 i->patch = _model->find_patch_change (i->patch_id);
1106 set<PatchChangePtr> temporary_removals;
1108 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1109 switch (i->property) {
1111 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1112 _model->remove_patch_change_unlocked (i->patch);
1113 temporary_removals.insert (i->patch);
1115 i->patch->set_time (i->old_time);
1119 i->patch->set_channel (i->old_channel);
1123 i->patch->set_program (i->old_program);
1127 i->patch->set_bank (i->old_bank);
1132 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1133 _model->add_patch_change_unlocked (*i);
1138 _model->ContentsChanged (); /* EMIT SIGNAL */
1142 MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
1144 XMLNode* n = new XMLNode ("patch-change");
1147 ostringstream s (ios::ate);
1148 s << int (p->id ());
1149 n->add_property ("id", s.str());
1153 ostringstream s (ios::ate);
1155 n->add_property ("time", s.str ());
1159 ostringstream s (ios::ate);
1160 s << int (p->channel ());
1161 n->add_property ("channel", s.str ());
1165 ostringstream s (ios::ate);
1166 s << int (p->program ());
1167 n->add_property ("program", s.str ());
1171 ostringstream s (ios::ate);
1172 s << int (p->bank ());
1173 n->add_property ("bank", s.str ());
1180 MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
1182 XMLNode* n = new XMLNode (X_("Change"));
1184 n->add_property (X_("property"), enum_2_string (c.property));
1187 ostringstream s (ios::ate);
1188 if (c.property == Time) {
1190 } else if (c.property == Channel) {
1192 } else if (c.property == Program) {
1193 s << int (c.old_program);
1194 } else if (c.property == Bank) {
1198 n->add_property (X_("old"), s.str ());
1202 ostringstream s (ios::ate);
1204 if (c.property == Time) {
1206 } else if (c.property == Channel) {
1208 } else if (c.property == Program) {
1209 s << int (c.new_program);
1210 } else if (c.property == Bank) {
1214 n->add_property (X_("new"), s.str ());
1219 s << c.patch->id ();
1220 n->add_property ("id", s.str ());
1226 MidiModel::PatchChangePtr
1227 MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
1230 Evoral::event_id_t id = 0;
1231 Evoral::MusicalTime time = Evoral::MusicalTime();
1236 if ((prop = n->property ("id")) != 0) {
1237 istringstream s (prop->value());
1241 if ((prop = n->property ("time")) != 0) {
1242 istringstream s (prop->value ());
1246 if ((prop = n->property ("channel")) != 0) {
1247 istringstream s (prop->value ());
1251 if ((prop = n->property ("program")) != 0) {
1252 istringstream s (prop->value ());
1256 if ((prop = n->property ("bank")) != 0) {
1257 istringstream s (prop->value ());
1261 PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
1267 MidiModel::PatchChangeDiffCommand::Change
1268 MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
1274 prop = n->property ("property");
1276 c.property = (Property) string_2_enum (prop->value(), c.property);
1278 prop = n->property ("id");
1280 Evoral::event_id_t const id = atoi (prop->value().c_str());
1282 /* we need to load via an int intermediate for all properties that are
1283 actually uint8_t (char/byte).
1286 prop = n->property ("old");
1289 istringstream s (prop->value ());
1290 if (c.property == Time) {
1292 } else if (c.property == Channel) {
1294 c.old_channel = an_int;
1295 } else if (c.property == Program) {
1297 c.old_program = an_int;
1298 } else if (c.property == Bank) {
1300 c.old_bank = an_int;
1304 prop = n->property ("new");
1307 istringstream s (prop->value ());
1309 if (c.property == Time) {
1311 } else if (c.property == Channel) {
1313 c.new_channel = an_int;
1314 } else if (c.property == Program) {
1316 c.new_program = an_int;
1317 } else if (c.property == Bank) {
1319 c.new_bank = an_int;
1323 c.patch = _model->find_patch_change (id);
1330 MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
1332 if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
1337 XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
1339 XMLNodeList p = added->children ();
1340 transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1344 XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
1346 XMLNodeList p = removed->children ();
1347 transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1351 XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
1353 XMLNodeList p = changed->children ();
1354 transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
1361 MidiModel::PatchChangeDiffCommand::get_state ()
1363 XMLNode* diff_command = new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT);
1364 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
1366 XMLNode* added = diff_command->add_child (ADDED_PATCH_CHANGES_ELEMENT);
1367 for_each (_added.begin(), _added.end(),
1369 boost::bind (&XMLNode::add_child_nocopy, added, _1),
1370 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1374 XMLNode* removed = diff_command->add_child (REMOVED_PATCH_CHANGES_ELEMENT);
1375 for_each (_removed.begin(), _removed.end(),
1377 boost::bind (&XMLNode::add_child_nocopy, removed, _1),
1378 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1382 XMLNode* changes = diff_command->add_child (DIFF_PATCH_CHANGES_ELEMENT);
1383 for_each (_changes.begin(), _changes.end(),
1385 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
1386 boost::bind (&PatchChangeDiffCommand::marshal_change, this, _1)
1390 return *diff_command;
1393 /** Write all of the model to a MidiSource (i.e. save the model).
1394 * This is different from manually using read to write to a source in that
1395 * note off events are written regardless of the track mode. This is so the
1396 * user can switch a recorded track (with note durations from some instrument)
1397 * to percussive, save, reload, then switch it back to sustained without
1398 * destroying the original note durations.
1400 * Similarly, control events are written without interpolation (as with the
1404 MidiModel::write_to (boost::shared_ptr<MidiSource> source,
1405 const Glib::Threads::Mutex::Lock& source_lock)
1407 ReadLock lock(read_lock());
1409 const bool old_percussive = percussive();
1410 set_percussive(false);
1412 source->drop_model(source_lock);
1413 source->mark_streaming_midi_write_started (source_lock, note_mode());
1415 for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
1416 source->append_event_beats(source_lock, *i);
1419 set_percussive(old_percussive);
1420 source->mark_streaming_write_completed(source_lock);
1427 /** very similar to ::write_to() but writes to the model's own
1428 existing midi_source, without making it call MidiSource::drop_model().
1429 the caller is a MidiSource that needs to catch up with the state
1433 MidiModel::sync_to_source (const Glib::Threads::Mutex::Lock& source_lock)
1435 ReadLock lock(read_lock());
1437 const bool old_percussive = percussive();
1438 set_percussive(false);
1440 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1442 error << "MIDI model has no source to sync to" << endmsg;
1446 ms->mark_streaming_midi_write_started (source_lock, note_mode());
1448 for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
1449 ms->append_event_beats(source_lock, *i);
1452 set_percussive (old_percussive);
1453 ms->mark_streaming_write_completed (source_lock);
1460 /** Write part or all of the model to a MidiSource (i.e. save the model).
1461 * This is different from manually using read to write to a source in that
1462 * note off events are written regardless of the track mode. This is so the
1463 * user can switch a recorded track (with note durations from some instrument)
1464 * to percussive, save, reload, then switch it back to sustained without
1465 * destroying the original note durations.
1468 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source,
1469 const Glib::Threads::Mutex::Lock& source_lock,
1470 Evoral::MusicalTime begin_time,
1471 Evoral::MusicalTime end_time)
1473 ReadLock lock(read_lock());
1474 MidiStateTracker mst;
1476 const bool old_percussive = percussive();
1477 set_percussive(false);
1479 source->drop_model(source_lock);
1480 source->mark_streaming_midi_write_started (source_lock, note_mode());
1482 for (Evoral::Sequence<TimeType>::const_iterator i = begin(TimeType(), true); i != end(); ++i) {
1483 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
1485 if (ev.time() >= begin_time && ev.time() < end_time) {
1487 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
1488 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
1495 if (mev->is_note_off()) {
1497 if (!mst.active (mev->note(), mev->channel())) {
1498 /* the matching note-on was outside the
1499 time range we were given, so just
1500 ignore this note-off.
1505 source->append_event_beats (source_lock, *i);
1506 mst.remove (mev->note(), mev->channel());
1508 } else if (mev->is_note_on()) {
1509 mst.add (mev->note(), mev->channel());
1510 source->append_event_beats(source_lock, *i);
1512 source->append_event_beats(source_lock, *i);
1517 mst.resolve_notes (*source, source_lock, end_time);
1519 set_percussive(old_percussive);
1520 source->mark_streaming_write_completed(source_lock);
1528 MidiModel::get_state()
1530 XMLNode *node = new XMLNode("MidiModel");
1534 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1535 MidiModel::find_note (NotePtr other)
1537 Notes::iterator l = notes().lower_bound(other);
1539 if (l != notes().end()) {
1540 for (; (*l)->time() == other->time(); ++l) {
1541 /* NB: compare note contents, not note pointers.
1542 If "other" was a ptr to a note already in
1543 the model, we wouldn't be looking for it,
1546 if (**l == *other) {
1555 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1556 MidiModel::find_note (gint note_id)
1558 /* used only for looking up notes when reloading history from disk,
1559 so we don't care about performance *too* much.
1562 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1563 if ((*l)->id() == note_id) {
1571 MidiModel::PatchChangePtr
1572 MidiModel::find_patch_change (Evoral::event_id_t id)
1574 for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1575 if ((*i)->id() == id) {
1580 return PatchChangePtr ();
1583 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1584 MidiModel::find_sysex (gint sysex_id)
1586 /* used only for looking up notes when reloading history from disk,
1587 so we don't care about performance *too* much.
1590 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1591 if ((*l)->id() == sysex_id) {
1596 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1599 /** Lock and invalidate the source.
1600 * This should be used by commands and editing things
1602 MidiModel::WriteLock
1603 MidiModel::edit_lock()
1605 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1608 Glib::Threads::Mutex::Lock* source_lock = new Glib::Threads::Mutex::Lock (ms->mutex());
1609 ms->invalidate(*source_lock); // Release cached iterator's read lock on model
1610 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1613 /** Lock just the model, the source lock must already be held.
1614 * This should only be called from libardour/evoral places
1616 MidiModel::WriteLock
1617 MidiModel::write_lock()
1619 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1622 assert (!ms->mutex().trylock ());
1623 return WriteLock(new WriteLockImpl(0, _lock, _control_lock));
1627 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1629 using namespace Evoral;
1631 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1635 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1637 TimeType sa = note->time();
1638 TimeType ea = note->end_time();
1640 const Pitches& p (pitches (note->channel()));
1641 NotePtr search_note(new Note<TimeType>(0, TimeType(), TimeType(), note->note()));
1642 set<NotePtr> to_be_deleted;
1643 bool set_note_length = false;
1644 bool set_note_time = false;
1645 TimeType note_time = note->time();
1646 TimeType note_length = note->length();
1648 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1650 for (Pitches::const_iterator i = p.lower_bound (search_note);
1651 i != p.end() && (*i)->note() == note->note(); ++i) {
1653 TimeType sb = (*i)->time();
1654 TimeType eb = (*i)->end_time();
1655 OverlapType overlap = OverlapNone;
1657 if ((sb > sa) && (eb <= ea)) {
1658 overlap = OverlapInternal;
1659 } else if ((eb >= sa) && (eb <= ea)) {
1660 overlap = OverlapStart;
1661 } else if ((sb > sa) && (sb <= ea)) {
1662 overlap = OverlapEnd;
1663 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1664 overlap = OverlapExternal;
1670 DEBUG_TRACE (DEBUG::Sequence, string_compose (
1671 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1672 enum_2_string(overlap), sa, ea, sb, eb));
1674 if (insert_merge_policy() == InsertMergeReject) {
1675 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1681 cerr << "OverlapStart\n";
1682 /* existing note covers start of new note */
1683 switch (insert_merge_policy()) {
1684 case InsertMergeReplace:
1685 to_be_deleted.insert (*i);
1687 case InsertMergeTruncateExisting:
1689 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1691 (*i)->set_length (note->time() - (*i)->time());
1693 case InsertMergeTruncateAddition:
1694 set_note_time = true;
1695 set_note_length = true;
1696 note_time = (*i)->time() + (*i)->length();
1697 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1699 case InsertMergeExtend:
1701 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1703 (*i)->set_length (note->end_time() - (*i)->time());
1704 return -1; /* do not add the new note */
1707 abort(); /*NOTREACHED*/
1714 cerr << "OverlapEnd\n";
1715 /* existing note covers end of new note */
1716 switch (insert_merge_policy()) {
1717 case InsertMergeReplace:
1718 to_be_deleted.insert (*i);
1721 case InsertMergeTruncateExisting:
1722 /* resetting the start time of the existing note
1723 is a problem because of time ordering.
1727 case InsertMergeTruncateAddition:
1728 set_note_length = true;
1729 note_length = min (note_length, ((*i)->time() - note->time()));
1732 case InsertMergeExtend:
1733 /* we can't reset the time of the existing note because
1734 that will corrupt time ordering. So remove the
1735 existing note and change the position/length
1736 of the new note (which has not been added yet)
1738 to_be_deleted.insert (*i);
1739 set_note_length = true;
1740 note_length = min (note_length, (*i)->end_time() - note->time());
1743 abort(); /*NOTREACHED*/
1749 case OverlapExternal:
1750 cerr << "OverlapExt\n";
1751 /* existing note overlaps all the new note */
1752 switch (insert_merge_policy()) {
1753 case InsertMergeReplace:
1754 to_be_deleted.insert (*i);
1756 case InsertMergeTruncateExisting:
1757 case InsertMergeTruncateAddition:
1758 case InsertMergeExtend:
1759 /* cannot add in this case */
1762 abort(); /*NOTREACHED*/
1768 case OverlapInternal:
1769 cerr << "OverlapInt\n";
1770 /* new note fully overlaps an existing note */
1771 switch (insert_merge_policy()) {
1772 case InsertMergeReplace:
1773 case InsertMergeTruncateExisting:
1774 case InsertMergeTruncateAddition:
1775 case InsertMergeExtend:
1776 /* delete the existing note, the new one will cover it */
1777 to_be_deleted.insert (*i);
1780 abort(); /*NOTREACHED*/
1787 abort(); /*NOTREACHED*/
1793 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1794 remove_note_unlocked (*i);
1797 cmd->side_effect_remove (*i);
1801 if (set_note_time) {
1803 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1805 note->set_time (note_time);
1808 if (set_note_length) {
1810 cmd->change (note, NoteDiffCommand::Length, note_length);
1812 note->set_length (note_length);
1819 MidiModel::insert_merge_policy () const
1821 /* XXX ultimately this should be a per-track or even per-model policy */
1822 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1825 return ms->session().config.get_insert_merge_policy ();
1829 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1831 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1834 Source::Lock lm(old->mutex());
1835 old->invalidate (lm);
1838 _midi_source_connections.drop_connections ();
1842 s->InterpolationChanged.connect_same_thread (
1843 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1846 s->AutomationStateChanged.connect_same_thread (
1847 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1851 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1852 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1853 * appropriate ControlList.
1855 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1856 * or the other is listened to by the GUI.
1859 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1861 Glib::Threads::Mutex::Lock lm (_control_lock);
1862 control(p)->list()->set_interpolation (s);
1865 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1866 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1869 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1871 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1874 ms->set_interpolation_of (p, s);
1878 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1880 Glib::Threads::Mutex::Lock lm (_control_lock);
1881 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1882 al->set_automation_state (s);
1886 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1888 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1890 ms->set_automation_state_of (p, s);
1893 boost::shared_ptr<Evoral::Control>
1894 MidiModel::control_factory (Evoral::Parameter const & p)
1896 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1898 /* Set up newly created control's lists to the appropriate interpolation and
1899 automation state from our source.
1902 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1905 c->list()->set_interpolation (ms->interpolation_of (p));
1907 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1910 al->set_automation_state (ms->automation_state_of (p));
1915 boost::shared_ptr<const MidiSource>
1916 MidiModel::midi_source ()
1918 return _midi_source.lock ();
1921 /** Moves notes, patch changes, controllers and sys-ex to insert silence at the start of the model.
1922 * Adds commands to the session's current undo stack to reflect the movements.
1925 MidiModel::insert_silence_at_start (TimeType t)
1927 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1932 if (!notes().empty ()) {
1933 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1935 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1936 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1939 apply_command_as_subcommand (s->session(), c);
1944 if (!patch_changes().empty ()) {
1945 PatchChangeDiffCommand* c = new_patch_change_diff_command ("insert silence");
1947 for (PatchChanges::const_iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1948 c->change_time (*i, (*i)->time() + t);
1951 apply_command_as_subcommand (s->session(), c);
1956 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1957 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1958 XMLNode& before = ac->alist()->get_state ();
1959 i->second->list()->shift (0, t.to_double());
1960 XMLNode& after = ac->alist()->get_state ();
1961 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1966 if (!sysexes().empty()) {
1967 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1969 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1970 c->change (*i, (*i)->time() + t);
1973 apply_command_as_subcommand (s->session(), c);
1977 /** Transpose notes in a time range by a given number of semitones. Notes
1978 * will be clamped at 0 and 127 if the transposition would make them exceed
1981 * @param from Start time.
1982 * @param end End time.
1983 * @param semitones Number of semitones to transpose by (+ve is higher, -ve is lower).
1986 MidiModel::transpose (TimeType from, TimeType to, int semitones)
1988 boost::shared_ptr<const MidiSource> s = midi_source ();
1990 NoteDiffCommand* c = new_note_diff_command (_("transpose"));
1992 for (Notes::iterator i = notes().begin(); i != notes().end(); ++i) {
1994 if ((*i)->time() >= to) {
1999 } else if ((*i)->time() >= from) {
2001 int new_note = (*i)->note() + semitones;
2005 } else if (new_note > 127) {
2009 c->change (*i, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
2014 apply_command (s->session (), c);
2018 MidiModel::control_list_marked_dirty ()
2020 AutomatableSequence<Evoral::MusicalTime>::control_list_marked_dirty ();
2022 ContentsChanged (); /* EMIT SIGNAL */