2 Copyright (C) 2007 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "pbd/compose.h"
28 #include "pbd/enumwriter.h"
29 #include "pbd/error.h"
31 #include "evoral/Control.hpp"
33 #include "midi++/events.h"
35 #include "ardour/automation_control.h"
36 #include "ardour/midi_automation_list_binder.h"
37 #include "ardour/midi_model.h"
38 #include "ardour/midi_source.h"
39 #include "ardour/midi_state_tracker.h"
40 #include "ardour/session.h"
41 #include "ardour/types.h"
46 using namespace ARDOUR;
49 MidiModel::MidiModel (boost::shared_ptr<MidiSource> s)
50 : AutomatableSequence<TimeType>(s->session())
55 /** Start a new NoteDiff command.
57 * This has no side-effects on the model or Session, the returned command
58 * can be held on to for as long as the caller wishes, or discarded without
59 * formality, until apply_command is called and ownership is taken.
61 MidiModel::NoteDiffCommand*
62 MidiModel::new_note_diff_command (const string name)
64 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
67 return new NoteDiffCommand (ms->model(), name);
70 /** Start a new SysExDiff command */
71 MidiModel::SysExDiffCommand*
72 MidiModel::new_sysex_diff_command (const string name)
74 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
77 return new SysExDiffCommand (ms->model(), name);
80 /** Start a new PatchChangeDiff command */
81 MidiModel::PatchChangeDiffCommand*
82 MidiModel::new_patch_change_diff_command (const string name)
84 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
87 return new PatchChangeDiffCommand (ms->model(), name);
93 * Ownership of cmd is taken, it must not be deleted by the caller.
94 * The command will constitute one item on the undo stack.
97 MidiModel::apply_command(Session& session, Command* cmd)
99 session.begin_reversible_command(cmd->name());
101 session.commit_reversible_command(cmd);
105 /** Apply a command as part of a larger reversible transaction
107 * Ownership of cmd is taken, it must not be deleted by the caller.
108 * The command will constitute one item on the undo stack.
111 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
114 session.add_command(cmd);
118 /************** DIFF COMMAND ********************/
120 #define NOTE_DIFF_COMMAND_ELEMENT "NoteDiffCommand"
121 #define DIFF_NOTES_ELEMENT "ChangedNotes"
122 #define ADDED_NOTES_ELEMENT "AddedNotes"
123 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
124 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
125 #define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand"
126 #define DIFF_SYSEXES_ELEMENT "ChangedSysExes"
127 #define PATCH_CHANGE_DIFF_COMMAND_ELEMENT "PatchChangeDiffCommand"
128 #define ADDED_PATCH_CHANGES_ELEMENT "AddedPatchChanges"
129 #define REMOVED_PATCH_CHANGES_ELEMENT "RemovedPatchChanges"
130 #define DIFF_PATCH_CHANGES_ELEMENT "ChangedPatchChanges"
132 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
140 MidiModel::NoteDiffCommand::NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
141 : DiffCommand (m, "")
144 set_state (node, Stateful::loading_state_version);
148 MidiModel::NoteDiffCommand::add (const NotePtr note)
150 _removed_notes.remove(note);
151 _added_notes.push_back(note);
155 MidiModel::NoteDiffCommand::remove (const NotePtr note)
157 _added_notes.remove(note);
158 _removed_notes.push_back(note);
162 MidiModel::NoteDiffCommand::side_effect_remove (const NotePtr note)
164 side_effect_removals.insert (note);
168 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
177 if (new_value == note->note()) {
180 change.old_value = note->note();
183 if (new_value == note->velocity()) {
186 change.old_value = note->velocity();
189 if (new_value == note->channel()) {
192 change.old_value = note->channel();
197 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
201 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
207 change.property = prop;
208 change.new_value = new_value;
210 _changes.push_back (change);
214 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
225 fatal << "MidiModel::NoteDiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
229 if (Evoral::musical_time_equal (note->time(), new_time)) {
232 change.old_time = note->time();
235 if (Evoral::musical_time_equal (note->length(), new_time)) {
238 change.old_time = note->length();
243 change.property = prop;
244 change.new_time = new_time;
246 _changes.push_back (change);
249 MidiModel::NoteDiffCommand &
250 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand& other)
252 if (this == &other) {
256 if (_model != other._model) {
260 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
261 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
262 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
263 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
269 MidiModel::NoteDiffCommand::operator() ()
272 MidiModel::WriteLock lock(_model->edit_lock());
274 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
275 if (!_model->add_note_unlocked(*i)) {
276 /* failed to add it, so don't leave it in the removed list, to
277 avoid apparent errors on undo.
279 _removed_notes.remove (*i);
283 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
284 _model->remove_note_unlocked(*i);
287 /* notes we modify in a way that requires remove-then-add to maintain ordering */
288 set<NotePtr> temporary_removals;
290 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
291 Property prop = i->property;
294 if (temporary_removals.find (i->note) == temporary_removals.end()) {
295 _model->remove_note_unlocked (i->note);
296 temporary_removals.insert (i->note);
298 i->note->set_note (i->new_value);
302 if (temporary_removals.find (i->note) == temporary_removals.end()) {
303 _model->remove_note_unlocked (i->note);
304 temporary_removals.insert (i->note);
306 i->note->set_time (i->new_time);
310 if (temporary_removals.find (i->note) == temporary_removals.end()) {
311 _model->remove_note_unlocked (i->note);
312 temporary_removals.insert (i->note);
314 i->note->set_channel (i->new_value);
317 /* no remove-then-add required for these properties, since we do not index them
321 i->note->set_velocity (i->new_value);
325 i->note->set_length (i->new_time);
331 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
332 NoteDiffCommand side_effects (model(), "side effects");
333 if (_model->add_note_unlocked (*i, &side_effects)) {
334 /* The note was re-added ok */
335 *this += side_effects;
337 /* The note that we removed earlier could not be re-added. This change record
338 must say that the note was removed. We'll keep the changes we made, though,
339 as if the note is re-added by the undo the changes must also be undone.
341 _removed_notes.push_back (*i);
345 if (!side_effect_removals.empty()) {
347 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
348 cerr << "\t" << *i << ' ' << **i << endl;
353 _model->ContentsChanged(); /* EMIT SIGNAL */
357 MidiModel::NoteDiffCommand::undo ()
360 MidiModel::WriteLock lock(_model->edit_lock());
362 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
363 _model->remove_note_unlocked(*i);
366 /* Apply changes first; this is important in the case of a note change which
367 resulted in the note being removed by the overlap checker. If the overlap
368 checker removes a note, it will be in _removed_notes. We are going to re-add
369 it below, but first we must undo the changes we made so that the overlap
370 checker doesn't refuse the re-add.
373 /* notes we modify in a way that requires remove-then-add to maintain ordering */
374 set<NotePtr> temporary_removals;
376 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
377 Property prop = i->property;
382 temporary_removals.find (i->note) == temporary_removals.end() &&
383 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()
386 /* We only need to mark this note for re-add if (a) we haven't
387 already marked it and (b) it isn't on the _removed_notes
388 list (which means that it has already been removed and it
389 will be re-added anyway)
392 _model->remove_note_unlocked (i->note);
393 temporary_removals.insert (i->note);
395 i->note->set_note (i->old_value);
400 temporary_removals.find (i->note) == temporary_removals.end() &&
401 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_time (i->old_time);
414 temporary_removals.find (i->note) == temporary_removals.end() &&
415 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()
420 _model->remove_note_unlocked (i->note);
421 temporary_removals.insert (i->note);
423 i->note->set_channel (i->old_value);
426 /* no remove-then-add required for these properties, since we do not index them
430 i->note->set_velocity (i->old_value);
434 i->note->set_length (i->old_time);
439 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
440 _model->add_note_unlocked(*i);
443 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
444 _model->add_note_unlocked (*i);
447 /* finally add back notes that were removed by the "do". we don't care
448 about side effects here since the model should be back to its original
449 state once this is done.
452 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
453 _model->add_note_unlocked (*i);
457 _model->ContentsChanged(); /* EMIT SIGNAL */
461 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
463 XMLNode* xml_note = new XMLNode("note");
466 ostringstream id_str(ios::ate);
467 id_str << int(note->id());
468 xml_note->add_property("id", id_str.str());
472 ostringstream note_str(ios::ate);
473 note_str << int(note->note());
474 xml_note->add_property("note", note_str.str());
478 ostringstream channel_str(ios::ate);
479 channel_str << int(note->channel());
480 xml_note->add_property("channel", channel_str.str());
484 ostringstream time_str(ios::ate);
485 time_str << note->time();
486 xml_note->add_property("time", time_str.str());
490 ostringstream length_str(ios::ate);
491 length_str << note->length();
492 xml_note->add_property("length", length_str.str());
496 ostringstream velocity_str(ios::ate);
497 velocity_str << (unsigned int) note->velocity();
498 xml_note->add_property("velocity", velocity_str.str());
504 Evoral::Sequence<MidiModel::TimeType>::NotePtr
505 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
509 unsigned int channel;
510 MidiModel::TimeType time;
511 MidiModel::TimeType length;
512 unsigned int velocity;
515 if ((prop = xml_note->property("id")) != 0) {
516 istringstream id_str(prop->value());
519 error << "note information missing ID value" << endmsg;
523 if ((prop = xml_note->property("note")) != 0) {
524 istringstream note_str(prop->value());
527 warning << "note information missing note value" << endmsg;
531 if ((prop = xml_note->property("channel")) != 0) {
532 istringstream channel_str(prop->value());
533 channel_str >> channel;
535 warning << "note information missing channel" << endmsg;
539 if ((prop = xml_note->property("time")) != 0) {
540 istringstream time_str(prop->value());
543 warning << "note information missing time" << endmsg;
547 if ((prop = xml_note->property("length")) != 0) {
548 istringstream length_str(prop->value());
549 length_str >> length;
551 warning << "note information missing length" << endmsg;
555 if ((prop = xml_note->property("velocity")) != 0) {
556 istringstream velocity_str(prop->value());
557 velocity_str >> velocity;
559 warning << "note information missing velocity" << endmsg;
563 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
564 note_ptr->set_id (id);
570 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
572 XMLNode* xml_change = new XMLNode("Change");
574 /* first, the change itself */
576 xml_change->add_property ("property", enum_2_string (change.property));
579 ostringstream old_value_str (ios::ate);
580 if (change.property == StartTime || change.property == Length) {
581 old_value_str << change.old_time;
583 old_value_str << (unsigned int) change.old_value;
585 xml_change->add_property ("old", old_value_str.str());
589 ostringstream new_value_str (ios::ate);
590 if (change.property == StartTime || change.property == Length) {
591 new_value_str << change.new_time;
593 new_value_str << (unsigned int) change.new_value;
595 xml_change->add_property ("new", new_value_str.str());
598 ostringstream id_str;
599 id_str << change.note->id();
600 xml_change->add_property ("id", id_str.str());
605 MidiModel::NoteDiffCommand::NoteChange
606 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
611 if ((prop = xml_change->property("property")) != 0) {
612 change.property = (Property) string_2_enum (prop->value(), change.property);
614 fatal << "!!!" << endmsg;
618 if ((prop = xml_change->property ("id")) == 0) {
619 error << _("No NoteID found for note property change - ignored") << endmsg;
623 gint note_id = atoi (prop->value().c_str());
625 if ((prop = xml_change->property ("old")) != 0) {
626 istringstream old_str (prop->value());
627 if (change.property == StartTime || change.property == Length) {
628 old_str >> change.old_time;
630 int integer_value_so_that_istream_does_the_right_thing;
631 old_str >> integer_value_so_that_istream_does_the_right_thing;
632 change.old_value = integer_value_so_that_istream_does_the_right_thing;
635 fatal << "!!!" << endmsg;
639 if ((prop = xml_change->property ("new")) != 0) {
640 istringstream new_str (prop->value());
641 if (change.property == StartTime || change.property == Length) {
642 new_str >> change.new_time;
644 int integer_value_so_that_istream_does_the_right_thing;
645 new_str >> integer_value_so_that_istream_does_the_right_thing;
646 change.new_value = integer_value_so_that_istream_does_the_right_thing;
649 fatal << "!!!" << endmsg;
653 /* we must point at the instance of the note that is actually in the model.
654 so go look for it ...
657 change.note = _model->find_note (note_id);
660 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
668 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
670 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
676 _added_notes.clear();
677 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
679 XMLNodeList notes = added_notes->children();
680 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
681 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
687 _removed_notes.clear();
688 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
690 XMLNodeList notes = removed_notes->children();
691 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
692 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
700 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
703 XMLNodeList notes = changed_notes->children();
704 transform (notes.begin(), notes.end(), back_inserter(_changes),
705 boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
709 /* side effect removals caused by changes */
711 side_effect_removals.clear();
713 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
715 if (side_effect_notes) {
716 XMLNodeList notes = side_effect_notes->children();
717 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
718 side_effect_removals.insert (unmarshal_note (*n));
726 MidiModel::NoteDiffCommand::get_state ()
728 XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
729 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
731 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
732 for_each(_changes.begin(), _changes.end(),
734 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
735 boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
737 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
738 for_each(_added_notes.begin(), _added_notes.end(),
740 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
741 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
743 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
744 for_each(_removed_notes.begin(), _removed_notes.end(),
746 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
747 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
749 /* if this command had side-effects, store that state too
752 if (!side_effect_removals.empty()) {
753 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
754 for_each(side_effect_removals.begin(), side_effect_removals.end(),
756 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
757 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
760 return *diff_command;
763 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
764 : DiffCommand (m, "")
767 set_state (node, Stateful::loading_state_version);
771 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
776 change.property = Time;
777 change.old_time = s->time ();
778 change.new_time = new_time;
780 _changes.push_back (change);
784 MidiModel::SysExDiffCommand::operator() ()
787 MidiModel::WriteLock lock (_model->edit_lock ());
789 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
790 switch (i->property) {
792 i->sysex->set_time (i->new_time);
797 _model->ContentsChanged (); /* EMIT SIGNAL */
801 MidiModel::SysExDiffCommand::undo ()
804 MidiModel::WriteLock lock (_model->edit_lock ());
806 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
807 switch (i->property) {
809 i->sysex->set_time (i->old_time);
816 _model->ContentsChanged(); /* EMIT SIGNAL */
820 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
822 XMLNode* xml_change = new XMLNode ("Change");
824 /* first, the change itself */
826 xml_change->add_property ("property", enum_2_string (change.property));
829 ostringstream old_value_str (ios::ate);
830 old_value_str << change.old_time;
831 xml_change->add_property ("old", old_value_str.str());
835 ostringstream new_value_str (ios::ate);
836 new_value_str << change.new_time;
837 xml_change->add_property ("new", new_value_str.str());
840 ostringstream id_str;
841 id_str << change.sysex->id();
842 xml_change->add_property ("id", id_str.str());
847 MidiModel::SysExDiffCommand::Change
848 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
853 if ((prop = xml_change->property ("property")) != 0) {
854 change.property = (Property) string_2_enum (prop->value(), change.property);
856 fatal << "!!!" << endmsg;
860 if ((prop = xml_change->property ("id")) == 0) {
861 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
865 gint sysex_id = atoi (prop->value().c_str());
867 if ((prop = xml_change->property ("old")) != 0) {
868 istringstream old_str (prop->value());
869 old_str >> change.old_time;
871 fatal << "!!!" << endmsg;
875 if ((prop = xml_change->property ("new")) != 0) {
876 istringstream new_str (prop->value());
877 new_str >> change.new_time;
879 fatal << "!!!" << endmsg;
883 /* we must point at the instance of the sysex that is actually in the model.
884 so go look for it ...
887 change.sysex = _model->find_sysex (sysex_id);
890 warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg;
898 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
900 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
908 XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
910 if (changed_sysexes) {
911 XMLNodeList sysexes = changed_sysexes->children();
912 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
913 boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
921 MidiModel::SysExDiffCommand::get_state ()
923 XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
924 diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
926 XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
927 for_each (_changes.begin(), _changes.end(),
929 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
930 boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
932 return *diff_command;
935 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
936 : DiffCommand (m, name)
941 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
942 : DiffCommand (m, "")
945 set_state (node, Stateful::loading_state_version);
949 MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
951 _added.push_back (p);
955 MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
957 _removed.push_back (p);
961 MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
966 c.old_time = patch->time ();
969 _changes.push_back (c);
973 MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
976 c.property = Channel;
978 c.old_channel = patch->channel ();
979 c.new_channel = channel;
981 _changes.push_back (c);
985 MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
988 c.property = Program;
990 c.old_program = patch->program ();
991 c.new_program = program;
993 _changes.push_back (c);
997 MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
1002 c.old_bank = patch->bank ();
1005 _changes.push_back (c);
1009 MidiModel::PatchChangeDiffCommand::operator() ()
1012 MidiModel::WriteLock lock (_model->edit_lock ());
1014 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1015 _model->add_patch_change_unlocked (*i);
1018 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1019 _model->remove_patch_change_unlocked (*i);
1022 set<PatchChangePtr> temporary_removals;
1024 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1025 switch (i->property) {
1027 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1028 _model->remove_patch_change_unlocked (i->patch);
1029 temporary_removals.insert (i->patch);
1031 i->patch->set_time (i->new_time);
1035 i->patch->set_channel (i->new_channel);
1039 i->patch->set_program (i->new_program);
1043 i->patch->set_bank (i->new_bank);
1048 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1049 _model->add_patch_change_unlocked (*i);
1053 _model->ContentsChanged (); /* EMIT SIGNAL */
1057 MidiModel::PatchChangeDiffCommand::undo ()
1060 MidiModel::WriteLock lock (_model->edit_lock());
1062 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1063 _model->remove_patch_change_unlocked (*i);
1066 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1067 _model->add_patch_change_unlocked (*i);
1070 set<PatchChangePtr> temporary_removals;
1072 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1073 switch (i->property) {
1075 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1076 _model->remove_patch_change_unlocked (i->patch);
1077 temporary_removals.insert (i->patch);
1079 i->patch->set_time (i->old_time);
1083 i->patch->set_channel (i->old_channel);
1087 i->patch->set_program (i->old_program);
1091 i->patch->set_bank (i->old_bank);
1096 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1097 _model->add_patch_change_unlocked (*i);
1102 _model->ContentsChanged (); /* EMIT SIGNAL */
1106 MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
1108 XMLNode* n = new XMLNode ("patch-change");
1111 ostringstream s (ios::ate);
1112 s << int (p->id ());
1113 n->add_property ("id", s.str());
1117 ostringstream s (ios::ate);
1119 n->add_property ("time", s.str ());
1123 ostringstream s (ios::ate);
1124 s << int (p->channel ());
1125 n->add_property ("channel", s.str ());
1129 ostringstream s (ios::ate);
1130 s << int (p->program ());
1131 n->add_property ("program", s.str ());
1135 ostringstream s (ios::ate);
1136 s << int (p->bank ());
1137 n->add_property ("bank", s.str ());
1144 MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
1146 XMLNode* n = new XMLNode (X_("Change"));
1148 n->add_property (X_("property"), enum_2_string (c.property));
1151 ostringstream s (ios::ate);
1152 if (c.property == Time) {
1154 } else if (c.property == Channel) {
1156 } else if (c.property == Program) {
1157 s << int (c.old_program);
1158 } else if (c.property == Bank) {
1162 n->add_property (X_("old"), s.str ());
1166 ostringstream s (ios::ate);
1168 if (c.property == Time) {
1170 } else if (c.property == Channel) {
1172 } else if (c.property == Program) {
1173 s << int (c.new_program);
1174 } else if (c.property == Bank) {
1178 n->add_property (X_("new"), s.str ());
1183 s << c.patch->id ();
1184 n->add_property ("id", s.str ());
1190 MidiModel::PatchChangePtr
1191 MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
1194 Evoral::event_id_t id;
1195 Evoral::MusicalTime time = 0;
1196 uint8_t channel = 0;
1197 uint8_t program = 0;
1200 if ((prop = n->property ("id")) != 0) {
1201 istringstream s (prop->value());
1205 if ((prop = n->property ("time")) != 0) {
1206 istringstream s (prop->value ());
1210 if ((prop = n->property ("channel")) != 0) {
1211 istringstream s (prop->value ());
1215 if ((prop = n->property ("program")) != 0) {
1216 istringstream s (prop->value ());
1220 if ((prop = n->property ("bank")) != 0) {
1221 istringstream s (prop->value ());
1225 PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
1230 MidiModel::PatchChangeDiffCommand::Change
1231 MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
1236 prop = n->property ("property");
1238 c.property = (Property) string_2_enum (prop->value(), c.property);
1240 prop = n->property ("id");
1242 Evoral::event_id_t const id = atoi (prop->value().c_str());
1244 prop = n->property ("old");
1247 istringstream s (prop->value ());
1248 if (c.property == Time) {
1250 } else if (c.property == Channel) {
1252 } else if (c.property == Program) {
1254 } else if (c.property == Bank) {
1259 prop = n->property ("new");
1262 istringstream s (prop->value ());
1263 if (c.property == Time) {
1265 } else if (c.property == Channel) {
1267 } else if (c.property == Program) {
1269 } else if (c.property == Bank) {
1274 c.patch = _model->find_patch_change (id);
1281 MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
1283 if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
1288 XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
1290 XMLNodeList p = added->children ();
1291 transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1295 XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
1297 XMLNodeList p = removed->children ();
1298 transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1302 XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
1304 XMLNodeList p = changed->children ();
1305 transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
1312 MidiModel::PatchChangeDiffCommand::get_state ()
1314 XMLNode* diff_command = new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT);
1315 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
1317 XMLNode* added = diff_command->add_child (ADDED_PATCH_CHANGES_ELEMENT);
1318 for_each (_added.begin(), _added.end(),
1320 boost::bind (&XMLNode::add_child_nocopy, added, _1),
1321 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1325 XMLNode* removed = diff_command->add_child (REMOVED_PATCH_CHANGES_ELEMENT);
1326 for_each (_removed.begin(), _removed.end(),
1328 boost::bind (&XMLNode::add_child_nocopy, removed, _1),
1329 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1333 XMLNode* changes = diff_command->add_child (DIFF_PATCH_CHANGES_ELEMENT);
1334 for_each (_changes.begin(), _changes.end(),
1336 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
1337 boost::bind (&PatchChangeDiffCommand::marshal_change, this, _1)
1341 return *diff_command;
1344 /** Write all of the model to a MidiSource (i.e. save the model).
1345 * This is different from manually using read to write to a source in that
1346 * note off events are written regardless of the track mode. This is so the
1347 * user can switch a recorded track (with note durations from some instrument)
1348 * to percussive, save, reload, then switch it back to sustained without
1349 * destroying the original note durations.
1351 * Similarly, control events are written without interpolation (as with the
1355 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
1357 ReadLock lock(read_lock());
1359 const bool old_percussive = percussive();
1360 set_percussive(false);
1362 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1365 source->drop_model();
1366 source->mark_streaming_midi_write_started (note_mode());
1368 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1369 source->append_event_unlocked_beats(*i);
1372 set_percussive(old_percussive);
1373 source->mark_streaming_write_completed();
1380 /** very similar to ::write_to() but writes to the model's own
1381 existing midi_source, without making it call MidiSource::drop_model().
1382 the caller is a MidiSource that needs to catch up with the state
1386 MidiModel::sync_to_source ()
1388 ReadLock lock(read_lock());
1390 const bool old_percussive = percussive();
1391 set_percussive(false);
1393 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1396 ms->mark_streaming_midi_write_started (note_mode());
1398 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1399 ms->append_event_unlocked_beats(*i);
1402 set_percussive (old_percussive);
1403 ms->mark_streaming_write_completed ();
1410 /** Write part or all of the model to a MidiSource (i.e. save the model).
1411 * This is different from manually using read to write to a source in that
1412 * note off events are written regardless of the track mode. This is so the
1413 * user can switch a recorded track (with note durations from some instrument)
1414 * to percussive, save, reload, then switch it back to sustained without
1415 * destroying the original note durations.
1418 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
1420 ReadLock lock(read_lock());
1421 MidiStateTracker mst;
1423 const bool old_percussive = percussive();
1424 set_percussive(false);
1426 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1429 source->drop_model();
1430 source->mark_streaming_midi_write_started (note_mode());
1432 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1433 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
1435 if (ev.time() >= begin_time && ev.time() < end_time) {
1437 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
1438 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
1445 if (mev->is_note_off()) {
1447 if (!mst.active (mev->note(), mev->channel())) {
1448 /* the matching note-on was outside the
1449 time range we were given, so just
1450 ignore this note-off.
1455 source->append_event_unlocked_beats (*i);
1456 mst.remove (mev->note(), mev->channel());
1458 } else if (mev->is_note_on()) {
1459 mst.add (mev->note(), mev->channel());
1460 source->append_event_unlocked_beats(*i);
1462 source->append_event_unlocked_beats(*i);
1467 mst.resolve_notes (*source, end_time);
1469 set_percussive(old_percussive);
1470 source->mark_streaming_write_completed();
1478 MidiModel::get_state()
1480 XMLNode *node = new XMLNode("MidiModel");
1484 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1485 MidiModel::find_note (NotePtr other)
1487 Notes::iterator l = notes().lower_bound(other);
1489 if (l != notes().end()) {
1490 for (; (*l)->time() == other->time(); ++l) {
1491 /* NB: compare note contents, not note pointers.
1492 If "other" was a ptr to a note already in
1493 the model, we wouldn't be looking for it,
1496 if (**l == *other) {
1505 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1506 MidiModel::find_note (gint note_id)
1508 /* used only for looking up notes when reloading history from disk,
1509 so we don't care about performance *too* much.
1512 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1513 if ((*l)->id() == note_id) {
1521 MidiModel::PatchChangePtr
1522 MidiModel::find_patch_change (Evoral::event_id_t id)
1524 for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1525 if ((*i)->id() == id) {
1530 return PatchChangePtr ();
1533 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1534 MidiModel::find_sysex (gint sysex_id)
1536 /* used only for looking up notes when reloading history from disk,
1537 so we don't care about performance *too* much.
1540 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1541 if ((*l)->id() == sysex_id) {
1546 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1549 /** Lock and invalidate the source.
1550 * This should be used by commands and editing things
1552 MidiModel::WriteLock
1553 MidiModel::edit_lock()
1555 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1558 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock (ms->mutex());
1559 ms->invalidate(); // Release cached iterator's read lock on model
1560 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1563 /** Lock just the model, the source lock must already be held.
1564 * This should only be called from libardour/evoral places
1566 MidiModel::WriteLock
1567 MidiModel::write_lock()
1569 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1572 assert (!ms->mutex().trylock ());
1573 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
1577 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1579 using namespace Evoral;
1581 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1585 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1587 TimeType sa = note->time();
1588 TimeType ea = note->end_time();
1590 const Pitches& p (pitches (note->channel()));
1591 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
1592 set<NotePtr> to_be_deleted;
1593 bool set_note_length = false;
1594 bool set_note_time = false;
1595 TimeType note_time = note->time();
1596 TimeType note_length = note->length();
1598 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1600 for (Pitches::const_iterator i = p.lower_bound (search_note);
1601 i != p.end() && (*i)->note() == note->note(); ++i) {
1603 TimeType sb = (*i)->time();
1604 TimeType eb = (*i)->end_time();
1605 OverlapType overlap = OverlapNone;
1607 if ((sb > sa) && (eb <= ea)) {
1608 overlap = OverlapInternal;
1609 } else if ((eb >= sa) && (eb <= ea)) {
1610 overlap = OverlapStart;
1611 } else if ((sb > sa) && (sb <= ea)) {
1612 overlap = OverlapEnd;
1613 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1614 overlap = OverlapExternal;
1620 DEBUG_TRACE (DEBUG::Sequence, string_compose (
1621 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1622 enum_2_string(overlap), sa, ea, sb, eb));
1624 if (insert_merge_policy() == InsertMergeReject) {
1625 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1631 cerr << "OverlapStart\n";
1632 /* existing note covers start of new note */
1633 switch (insert_merge_policy()) {
1634 case InsertMergeReplace:
1635 to_be_deleted.insert (*i);
1637 case InsertMergeTruncateExisting:
1639 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1641 (*i)->set_length (note->time() - (*i)->time());
1643 case InsertMergeTruncateAddition:
1644 set_note_time = true;
1645 set_note_length = true;
1646 note_time = (*i)->time() + (*i)->length();
1647 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1649 case InsertMergeExtend:
1651 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1653 (*i)->set_length (note->end_time() - (*i)->time());
1654 return -1; /* do not add the new note */
1664 cerr << "OverlapEnd\n";
1665 /* existing note covers end of new note */
1666 switch (insert_merge_policy()) {
1667 case InsertMergeReplace:
1668 to_be_deleted.insert (*i);
1671 case InsertMergeTruncateExisting:
1672 /* resetting the start time of the existing note
1673 is a problem because of time ordering.
1677 case InsertMergeTruncateAddition:
1678 set_note_length = true;
1679 note_length = min (note_length, ((*i)->time() - note->time()));
1682 case InsertMergeExtend:
1683 /* we can't reset the time of the existing note because
1684 that will corrupt time ordering. So remove the
1685 existing note and change the position/length
1686 of the new note (which has not been added yet)
1688 to_be_deleted.insert (*i);
1689 set_note_length = true;
1690 note_length = min (note_length, (*i)->end_time() - note->time());
1699 case OverlapExternal:
1700 cerr << "OverlapExt\n";
1701 /* existing note overlaps all the new note */
1702 switch (insert_merge_policy()) {
1703 case InsertMergeReplace:
1704 to_be_deleted.insert (*i);
1706 case InsertMergeTruncateExisting:
1707 case InsertMergeTruncateAddition:
1708 case InsertMergeExtend:
1709 /* cannot add in this case */
1718 case OverlapInternal:
1719 cerr << "OverlapInt\n";
1720 /* new note fully overlaps an existing note */
1721 switch (insert_merge_policy()) {
1722 case InsertMergeReplace:
1723 case InsertMergeTruncateExisting:
1724 case InsertMergeTruncateAddition:
1725 case InsertMergeExtend:
1726 /* delete the existing note, the new one will cover it */
1727 to_be_deleted.insert (*i);
1743 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1744 remove_note_unlocked (*i);
1747 cmd->side_effect_remove (*i);
1751 if (set_note_time) {
1753 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1755 note->set_time (note_time);
1758 if (set_note_length) {
1760 cmd->change (note, NoteDiffCommand::Length, note_length);
1762 note->set_length (note_length);
1769 MidiModel::insert_merge_policy () const
1771 /* XXX ultimately this should be a per-track or even per-model policy */
1772 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1775 return ms->session().config.get_insert_merge_policy ();
1779 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1781 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1787 _midi_source_connections.drop_connections ();
1791 s->InterpolationChanged.connect_same_thread (
1792 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1795 s->AutomationStateChanged.connect_same_thread (
1796 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1800 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1801 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1802 * appropriate ControlList.
1804 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1805 * or the other is listened to by the GUI.
1808 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1810 Glib::Mutex::Lock lm (_control_lock);
1811 control(p)->list()->set_interpolation (s);
1814 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1815 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1818 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1820 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1823 ms->set_interpolation_of (p, s);
1827 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1829 Glib::Mutex::Lock lm (_control_lock);
1830 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1831 al->set_automation_state (s);
1835 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1837 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1839 ms->set_automation_state_of (p, s);
1842 boost::shared_ptr<Evoral::Control>
1843 MidiModel::control_factory (Evoral::Parameter const & p)
1845 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1847 /* Set up newly created control's lists to the appropriate interpolation and
1848 automation state from our source.
1851 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1854 c->list()->set_interpolation (ms->interpolation_of (p));
1856 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1859 al->set_automation_state (ms->automation_state_of (p));
1864 boost::shared_ptr<const MidiSource>
1865 MidiModel::midi_source ()
1867 return _midi_source.lock ();
1870 /** Moves notes, patch changes, controllers and sys-ex to insert silence at the start of the model.
1871 * Adds commands to the session's current undo stack to reflect the movements.
1874 MidiModel::insert_silence_at_start (TimeType t)
1876 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1881 if (!notes().empty ()) {
1882 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1884 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1885 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1888 apply_command_as_subcommand (s->session(), c);
1893 if (!patch_changes().empty ()) {
1894 PatchChangeDiffCommand* c = new_patch_change_diff_command ("insert silence");
1896 for (PatchChanges::const_iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1897 c->change_time (*i, (*i)->time() + t);
1900 apply_command_as_subcommand (s->session(), c);
1905 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1906 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1907 XMLNode& before = ac->alist()->get_state ();
1908 i->second->list()->shift (0, t);
1909 XMLNode& after = ac->alist()->get_state ();
1910 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1915 if (!sysexes().empty()) {
1916 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1918 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1919 c->change (*i, (*i)->time() + t);
1922 apply_command_as_subcommand (s->session(), c);
1926 /** Transpose notes in a time range by a given number of semitones. Notes
1927 * will be clamped at 0 and 127 if the transposition would make them exceed
1930 * @param from Start time.
1931 * @param end End time.
1932 * @param semitones Number of semitones to transpose by (+ve is higher, -ve is lower).
1935 MidiModel::transpose (TimeType from, TimeType to, int semitones)
1937 boost::shared_ptr<const MidiSource> s = midi_source ();
1939 NoteDiffCommand* c = new_note_diff_command (_("transpose"));
1941 for (Notes::iterator i = notes().begin(); i != notes().end(); ++i) {
1943 if ((*i)->time() >= to) {
1948 } else if ((*i)->time() >= from) {
1950 int new_note = (*i)->note() + semitones;
1954 } else if (new_note > 127) {
1958 c->change (*i, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
1963 apply_command (s->session (), c);
1967 MidiModel::control_list_marked_dirty ()
1969 AutomatableSequence<Evoral::MusicalTime>::control_list_marked_dirty ();
1971 ContentsChanged (); /* EMIT SIGNAL */