Fix undo when notes are changed and then removed by the overlap checker (#3995).
[ardour2.git] / libs / ardour / midi_model.cc
blobb23631da0e8ebbe4b009637c0ebc1bf0f2e98462
1 /*
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.
21 #include <set>
22 #include <iostream>
23 #include <algorithm>
24 #include <stdexcept>
25 #include <stdint.h>
26 #include "pbd/error.h"
27 #include "pbd/enumwriter.h"
28 #include "pbd/compose.h"
29 #include "midi++/events.h"
31 #include "ardour/midi_model.h"
32 #include "ardour/midi_source.h"
33 #include "ardour/midi_state_tracker.h"
34 #include "ardour/smf_source.h"
35 #include "ardour/types.h"
36 #include "ardour/session.h"
37 #include "ardour/midi_automation_list_binder.h"
39 #include "i18n.h"
41 using namespace std;
42 using namespace ARDOUR;
43 using namespace PBD;
45 MidiModel::MidiModel (boost::shared_ptr<MidiSource> s)
46 : AutomatableSequence<TimeType>(s->session())
48 set_midi_source (s);
51 /** Start a new NoteDiff command.
53 * This has no side-effects on the model or Session, the returned command
54 * can be held on to for as long as the caller wishes, or discarded without
55 * formality, until apply_command is called and ownership is taken.
57 MidiModel::NoteDiffCommand*
58 MidiModel::new_note_diff_command (const string name)
60 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
61 assert (ms);
63 return new NoteDiffCommand (ms->model(), name);
66 /** Start a new SysExDiff command */
67 MidiModel::SysExDiffCommand*
68 MidiModel::new_sysex_diff_command (const string name)
70 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
71 assert (ms);
73 return new SysExDiffCommand (ms->model(), name);
76 /** Start a new PatchChangeDiff command */
77 MidiModel::PatchChangeDiffCommand*
78 MidiModel::new_patch_change_diff_command (const string name)
80 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
81 assert (ms);
83 return new PatchChangeDiffCommand (ms->model(), name);
87 /** Apply a command.
89 * Ownership of cmd is taken, it must not be deleted by the caller.
90 * The command will constitute one item on the undo stack.
92 void
93 MidiModel::apply_command(Session& session, Command* cmd)
95 session.begin_reversible_command(cmd->name());
96 (*cmd)();
97 session.commit_reversible_command(cmd);
98 set_edited(true);
101 /** Apply a command as part of a larger reversible transaction
103 * Ownership of cmd is taken, it must not be deleted by the caller.
104 * The command will constitute one item on the undo stack.
106 void
107 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
109 (*cmd)();
110 session.add_command(cmd);
111 set_edited(true);
114 /************** DIFF COMMAND ********************/
116 #define NOTE_DIFF_COMMAND_ELEMENT "NoteDiffCommand"
117 #define DIFF_NOTES_ELEMENT "ChangedNotes"
118 #define ADDED_NOTES_ELEMENT "AddedNotes"
119 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
120 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
121 #define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand"
122 #define DIFF_SYSEXES_ELEMENT "ChangedSysExes"
123 #define PATCH_CHANGE_DIFF_COMMAND_ELEMENT "PatchChangeDiffCommand"
124 #define ADDED_PATCH_CHANGES_ELEMENT "AddedPatchChanges"
125 #define REMOVED_PATCH_CHANGES_ELEMENT "RemovedPatchChanges"
126 #define DIFF_PATCH_CHANGES_ELEMENT "ChangedPatchChanges"
128 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
129 : Command (name)
130 , _model (m)
131 , _name (name)
133 assert(_model);
136 MidiModel::NoteDiffCommand::NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
137 : DiffCommand (m, "")
139 assert (_model);
140 set_state (node, Stateful::loading_state_version);
143 void
144 MidiModel::NoteDiffCommand::add (const NotePtr note)
146 _removed_notes.remove(note);
147 _added_notes.push_back(note);
150 void
151 MidiModel::NoteDiffCommand::remove (const NotePtr note)
153 _added_notes.remove(note);
154 _removed_notes.push_back(note);
157 void
158 MidiModel::NoteDiffCommand::side_effect_remove (const NotePtr note)
160 side_effect_removals.insert (note);
163 void
164 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
165 uint8_t new_value)
167 assert (note);
169 NoteChange change;
171 switch (prop) {
172 case NoteNumber:
173 if (new_value == note->note()) {
174 return;
176 change.old_value = note->note();
177 break;
178 case Velocity:
179 if (new_value == note->velocity()) {
180 return;
182 change.old_value = note->velocity();
183 break;
184 case Channel:
185 if (new_value == note->channel()) {
186 return;
188 change.old_value = note->channel();
189 break;
192 case StartTime:
193 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
194 /*NOTREACHED*/
195 break;
196 case Length:
197 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
198 /*NOTREACHED*/
199 break;
202 change.note = note;
203 change.property = prop;
204 change.new_value = new_value;
206 _changes.push_back (change);
209 void
210 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
211 TimeType new_time)
213 assert (note);
215 NoteChange change;
217 switch (prop) {
218 case NoteNumber:
219 case Channel:
220 case Velocity:
221 fatal << "MidiModel::NoteDiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
222 break;
224 case StartTime:
225 if (Evoral::musical_time_equal (note->time(), new_time)) {
226 return;
228 change.old_time = note->time();
229 break;
230 case Length:
231 if (Evoral::musical_time_equal (note->length(), new_time)) {
232 return;
234 change.old_time = note->length();
235 break;
238 change.note = note;
239 change.property = prop;
240 change.new_time = new_time;
242 _changes.push_back (change);
245 MidiModel::NoteDiffCommand &
246 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand& other)
248 if (this == &other) {
249 return *this;
252 if (_model != other._model) {
253 return *this;
256 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
257 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
258 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
259 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
261 return *this;
264 void
265 MidiModel::NoteDiffCommand::operator() ()
268 MidiModel::WriteLock lock(_model->edit_lock());
270 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
271 if (!_model->add_note_unlocked(*i)) {
272 /* failed to add it, so don't leave it in the removed list, to
273 avoid apparent errors on undo.
275 _removed_notes.remove (*i);
279 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
280 _model->remove_note_unlocked(*i);
283 /* notes we modify in a way that requires remove-then-add to maintain ordering */
284 set<NotePtr> temporary_removals;
286 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
287 Property prop = i->property;
288 switch (prop) {
289 case NoteNumber:
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_note (i->new_value);
295 break;
297 case StartTime:
298 if (temporary_removals.find (i->note) == temporary_removals.end()) {
299 _model->remove_note_unlocked (i->note);
300 temporary_removals.insert (i->note);
302 i->note->set_time (i->new_time);
303 break;
305 case Channel:
306 if (temporary_removals.find (i->note) == temporary_removals.end()) {
307 _model->remove_note_unlocked (i->note);
308 temporary_removals.insert (i->note);
310 i->note->set_channel (i->new_value);
311 break;
313 /* no remove-then-add required for these properties, since we do not index them
316 case Velocity:
317 i->note->set_velocity (i->new_value);
318 break;
320 case Length:
321 i->note->set_length (i->new_time);
322 break;
327 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
328 NoteDiffCommand side_effects (model(), "side effects");
329 if (_model->add_note_unlocked (*i, &side_effects)) {
330 /* The note was re-added ok */
331 *this += side_effects;
332 } else {
333 /* The note that we removed earlier could not be re-added. This change record
334 must say that the note was removed. We'll keep the changes we made, though,
335 as if the note is re-added by the undo the changes must also be undone.
337 _removed_notes.push_back (*i);
341 if (!side_effect_removals.empty()) {
342 cerr << "SER: \n";
343 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
344 cerr << "\t" << *i << ' ' << **i << endl;
349 _model->ContentsChanged(); /* EMIT SIGNAL */
352 void
353 MidiModel::NoteDiffCommand::undo ()
356 MidiModel::WriteLock lock(_model->edit_lock());
358 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
359 _model->remove_note_unlocked(*i);
362 /* Apply changes first; this is important in the case of a note change which
363 resulted in the note being removed by the overlap checker. If the overlap
364 checker removes a note, it will be in _removed_notes. We are going to re-add
365 it below, but first we must undo the changes we made so that the overlap
366 checker doesn't refuse the re-add.
369 /* notes we modify in a way that requires remove-then-add to maintain ordering */
370 set<NotePtr> temporary_removals;
372 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
373 Property prop = i->property;
374 switch (prop) {
376 case NoteNumber:
377 if (
378 temporary_removals.find (i->note) == temporary_removals.end() &&
379 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()
382 /* We only need to mark this note for re-add if (a) we haven't
383 already marked it and (b) it isn't on the _removed_notes
384 list (which means that it has already been removed and it
385 will be re-added anyway)
388 _model->remove_note_unlocked (i->note);
389 temporary_removals.insert (i->note);
391 i->note->set_note (i->old_value);
392 break;
394 case StartTime:
395 if (
396 temporary_removals.find (i->note) == temporary_removals.end() &&
397 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()
400 /* See above ... */
402 _model->remove_note_unlocked (i->note);
403 temporary_removals.insert (i->note);
405 i->note->set_time (i->old_time);
406 break;
408 case Channel:
409 if (
410 temporary_removals.find (i->note) == temporary_removals.end() &&
411 find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()
414 /* See above ... */
416 _model->remove_note_unlocked (i->note);
417 temporary_removals.insert (i->note);
419 i->note->set_channel (i->old_value);
420 break;
422 /* no remove-then-add required for these properties, since we do not index them
425 case Velocity:
426 i->note->set_velocity (i->old_value);
427 break;
429 case Length:
430 i->note->set_length (i->old_time);
431 break;
435 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
436 _model->add_note_unlocked(*i);
439 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
440 _model->add_note_unlocked (*i);
443 /* finally add back notes that were removed by the "do". we don't care
444 about side effects here since the model should be back to its original
445 state once this is done.
448 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
449 _model->add_note_unlocked (*i);
453 _model->ContentsChanged(); /* EMIT SIGNAL */
456 XMLNode&
457 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
459 XMLNode* xml_note = new XMLNode("note");
462 ostringstream id_str(ios::ate);
463 id_str << int(note->id());
464 xml_note->add_property("id", id_str.str());
468 ostringstream note_str(ios::ate);
469 note_str << int(note->note());
470 xml_note->add_property("note", note_str.str());
474 ostringstream channel_str(ios::ate);
475 channel_str << int(note->channel());
476 xml_note->add_property("channel", channel_str.str());
480 ostringstream time_str(ios::ate);
481 time_str << note->time();
482 xml_note->add_property("time", time_str.str());
486 ostringstream length_str(ios::ate);
487 length_str << note->length();
488 xml_note->add_property("length", length_str.str());
492 ostringstream velocity_str(ios::ate);
493 velocity_str << (unsigned int) note->velocity();
494 xml_note->add_property("velocity", velocity_str.str());
497 return *xml_note;
500 Evoral::Sequence<MidiModel::TimeType>::NotePtr
501 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
503 unsigned int note;
504 XMLProperty* prop;
505 unsigned int channel;
506 unsigned int time;
507 unsigned int length;
508 unsigned int velocity;
509 gint id;
511 if ((prop = xml_note->property("id")) != 0) {
512 istringstream id_str(prop->value());
513 id_str >> id;
514 } else {
515 error << "note information missing ID value" << endmsg;
516 id = -1;
519 if ((prop = xml_note->property("note")) != 0) {
520 istringstream note_str(prop->value());
521 note_str >> note;
522 } else {
523 warning << "note information missing note value" << endmsg;
524 note = 127;
527 if ((prop = xml_note->property("channel")) != 0) {
528 istringstream channel_str(prop->value());
529 channel_str >> channel;
530 } else {
531 warning << "note information missing channel" << endmsg;
532 channel = 0;
535 if ((prop = xml_note->property("time")) != 0) {
536 istringstream time_str(prop->value());
537 time_str >> time;
538 } else {
539 warning << "note information missing time" << endmsg;
540 time = 0;
543 if ((prop = xml_note->property("length")) != 0) {
544 istringstream length_str(prop->value());
545 length_str >> length;
546 } else {
547 warning << "note information missing length" << endmsg;
548 length = 1;
551 if ((prop = xml_note->property("velocity")) != 0) {
552 istringstream velocity_str(prop->value());
553 velocity_str >> velocity;
554 } else {
555 warning << "note information missing velocity" << endmsg;
556 velocity = 127;
559 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
560 note_ptr->set_id (id);
562 return note_ptr;
565 XMLNode&
566 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
568 XMLNode* xml_change = new XMLNode("Change");
570 /* first, the change itself */
572 xml_change->add_property ("property", enum_2_string (change.property));
575 ostringstream old_value_str (ios::ate);
576 if (change.property == StartTime || change.property == Length) {
577 old_value_str << change.old_time;
578 } else {
579 old_value_str << (unsigned int) change.old_value;
581 xml_change->add_property ("old", old_value_str.str());
585 ostringstream new_value_str (ios::ate);
586 if (change.property == StartTime || change.property == Length) {
587 new_value_str << change.new_time;
588 } else {
589 new_value_str << (unsigned int) change.new_value;
591 xml_change->add_property ("new", new_value_str.str());
594 ostringstream id_str;
595 id_str << change.note->id();
596 xml_change->add_property ("id", id_str.str());
598 return *xml_change;
601 MidiModel::NoteDiffCommand::NoteChange
602 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
604 XMLProperty* prop;
605 NoteChange change;
607 if ((prop = xml_change->property("property")) != 0) {
608 change.property = (Property) string_2_enum (prop->value(), change.property);
609 } else {
610 fatal << "!!!" << endmsg;
611 /*NOTREACHED*/
614 if ((prop = xml_change->property ("id")) == 0) {
615 error << _("No NoteID found for note property change - ignored") << endmsg;
616 return change;
619 gint note_id = atoi (prop->value().c_str());
621 if ((prop = xml_change->property ("old")) != 0) {
622 istringstream old_str (prop->value());
623 if (change.property == StartTime || change.property == Length) {
624 old_str >> change.old_time;
625 } else {
626 int integer_value_so_that_istream_does_the_right_thing;
627 old_str >> integer_value_so_that_istream_does_the_right_thing;
628 change.old_value = integer_value_so_that_istream_does_the_right_thing;
630 } else {
631 fatal << "!!!" << endmsg;
632 /*NOTREACHED*/
635 if ((prop = xml_change->property ("new")) != 0) {
636 istringstream new_str (prop->value());
637 if (change.property == StartTime || change.property == Length) {
638 new_str >> change.new_time;
639 } else {
640 int integer_value_so_that_istream_does_the_right_thing;
641 new_str >> integer_value_so_that_istream_does_the_right_thing;
642 change.new_value = integer_value_so_that_istream_does_the_right_thing;
644 } else {
645 fatal << "!!!" << endmsg;
646 /*NOTREACHED*/
649 /* we must point at the instance of the note that is actually in the model.
650 so go look for it ...
653 change.note = _model->find_note (note_id);
655 if (!change.note) {
656 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
657 return change;
660 return change;
664 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
666 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
667 return 1;
670 /* additions */
672 _added_notes.clear();
673 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
674 if (added_notes) {
675 XMLNodeList notes = added_notes->children();
676 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
677 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
681 /* removals */
683 _removed_notes.clear();
684 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
685 if (removed_notes) {
686 XMLNodeList notes = removed_notes->children();
687 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
688 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
692 /* changes */
694 _changes.clear();
696 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
698 if (changed_notes) {
699 XMLNodeList notes = changed_notes->children();
700 transform (notes.begin(), notes.end(), back_inserter(_changes),
701 boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
705 /* side effect removals caused by changes */
707 side_effect_removals.clear();
709 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
711 if (side_effect_notes) {
712 XMLNodeList notes = side_effect_notes->children();
713 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
714 side_effect_removals.insert (unmarshal_note (*n));
718 return 0;
721 XMLNode&
722 MidiModel::NoteDiffCommand::get_state ()
724 XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
725 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
727 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
728 for_each(_changes.begin(), _changes.end(),
729 boost::bind (
730 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
731 boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
733 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
734 for_each(_added_notes.begin(), _added_notes.end(),
735 boost::bind(
736 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
737 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
739 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
740 for_each(_removed_notes.begin(), _removed_notes.end(),
741 boost::bind (
742 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
743 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
745 /* if this command had side-effects, store that state too
748 if (!side_effect_removals.empty()) {
749 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
750 for_each(side_effect_removals.begin(), side_effect_removals.end(),
751 boost::bind (
752 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
753 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
756 return *diff_command;
759 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
760 : DiffCommand (m, "")
762 assert (_model);
763 set_state (node, Stateful::loading_state_version);
766 void
767 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
769 Change change;
771 change.sysex = s;
772 change.property = Time;
773 change.old_time = s->time ();
774 change.new_time = new_time;
776 _changes.push_back (change);
779 void
780 MidiModel::SysExDiffCommand::operator() ()
783 MidiModel::WriteLock lock (_model->edit_lock ());
785 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
786 switch (i->property) {
787 case Time:
788 i->sysex->set_time (i->new_time);
793 _model->ContentsChanged (); /* EMIT SIGNAL */
796 void
797 MidiModel::SysExDiffCommand::undo ()
800 MidiModel::WriteLock lock (_model->edit_lock ());
802 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
803 switch (i->property) {
804 case Time:
805 i->sysex->set_time (i->old_time);
806 break;
812 _model->ContentsChanged(); /* EMIT SIGNAL */
815 XMLNode&
816 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
818 XMLNode* xml_change = new XMLNode ("Change");
820 /* first, the change itself */
822 xml_change->add_property ("property", enum_2_string (change.property));
825 ostringstream old_value_str (ios::ate);
826 old_value_str << change.old_time;
827 xml_change->add_property ("old", old_value_str.str());
831 ostringstream new_value_str (ios::ate);
832 new_value_str << change.new_time;
833 xml_change->add_property ("new", new_value_str.str());
836 ostringstream id_str;
837 id_str << change.sysex->id();
838 xml_change->add_property ("id", id_str.str());
840 return *xml_change;
843 MidiModel::SysExDiffCommand::Change
844 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
846 XMLProperty* prop;
847 Change change;
849 if ((prop = xml_change->property ("property")) != 0) {
850 change.property = (Property) string_2_enum (prop->value(), change.property);
851 } else {
852 fatal << "!!!" << endmsg;
853 /*NOTREACHED*/
856 if ((prop = xml_change->property ("id")) == 0) {
857 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
858 return change;
861 gint sysex_id = atoi (prop->value().c_str());
863 if ((prop = xml_change->property ("old")) != 0) {
864 istringstream old_str (prop->value());
865 old_str >> change.old_time;
866 } else {
867 fatal << "!!!" << endmsg;
868 /*NOTREACHED*/
871 if ((prop = xml_change->property ("new")) != 0) {
872 istringstream new_str (prop->value());
873 new_str >> change.new_time;
874 } else {
875 fatal << "!!!" << endmsg;
876 /*NOTREACHED*/
879 /* we must point at the instance of the sysex that is actually in the model.
880 so go look for it ...
883 change.sysex = _model->find_sysex (sysex_id);
885 if (!change.sysex) {
886 warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg;
887 return change;
890 return change;
894 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
896 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
897 return 1;
900 /* changes */
902 _changes.clear();
904 XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
906 if (changed_sysexes) {
907 XMLNodeList sysexes = changed_sysexes->children();
908 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
909 boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
913 return 0;
916 XMLNode&
917 MidiModel::SysExDiffCommand::get_state ()
919 XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
920 diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
922 XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
923 for_each (_changes.begin(), _changes.end(),
924 boost::bind (
925 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
926 boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
928 return *diff_command;
931 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
932 : DiffCommand (m, name)
934 assert (_model);
937 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
938 : DiffCommand (m, "")
940 assert (_model);
941 set_state (node, Stateful::loading_state_version);
944 void
945 MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
947 _added.push_back (p);
950 void
951 MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
953 _removed.push_back (p);
956 void
957 MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
959 Change c;
960 c.property = Time;
961 c.patch = patch;
962 c.old_time = patch->time ();
963 c.new_time = t;
965 _changes.push_back (c);
968 void
969 MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
971 Change c;
972 c.property = Channel;
973 c.patch = patch;
974 c.old_channel = patch->channel ();
975 c.new_channel = channel;
977 _changes.push_back (c);
980 void
981 MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
983 Change c;
984 c.property = Program;
985 c.patch = patch;
986 c.old_program = patch->program ();
987 c.new_program = program;
989 _changes.push_back (c);
992 void
993 MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
995 Change c;
996 c.property = Bank;
997 c.patch = patch;
998 c.old_bank = patch->bank ();
999 c.new_bank = bank;
1001 _changes.push_back (c);
1004 void
1005 MidiModel::PatchChangeDiffCommand::operator() ()
1008 MidiModel::WriteLock lock (_model->edit_lock ());
1010 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1011 _model->add_patch_change_unlocked (*i);
1014 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1015 _model->remove_patch_change_unlocked (*i);
1018 set<PatchChangePtr> temporary_removals;
1020 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1021 switch (i->property) {
1022 case Time:
1023 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1024 _model->remove_patch_change_unlocked (i->patch);
1025 temporary_removals.insert (i->patch);
1027 i->patch->set_time (i->new_time);
1028 break;
1030 case Channel:
1031 i->patch->set_channel (i->new_channel);
1032 break;
1034 case Program:
1035 i->patch->set_program (i->new_program);
1036 break;
1038 case Bank:
1039 i->patch->set_bank (i->new_bank);
1040 break;
1044 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1045 _model->add_patch_change_unlocked (*i);
1049 _model->ContentsChanged (); /* EMIT SIGNAL */
1052 void
1053 MidiModel::PatchChangeDiffCommand::undo ()
1056 MidiModel::WriteLock lock (_model->edit_lock());
1058 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1059 _model->remove_patch_change_unlocked (*i);
1062 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1063 _model->add_patch_change_unlocked (*i);
1066 set<PatchChangePtr> temporary_removals;
1068 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1069 switch (i->property) {
1070 case Time:
1071 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1072 _model->remove_patch_change_unlocked (i->patch);
1073 temporary_removals.insert (i->patch);
1075 i->patch->set_time (i->old_time);
1076 break;
1078 case Channel:
1079 i->patch->set_channel (i->old_channel);
1080 break;
1082 case Program:
1083 i->patch->set_program (i->old_program);
1084 break;
1086 case Bank:
1087 i->patch->set_bank (i->old_bank);
1088 break;
1092 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1093 _model->add_patch_change_unlocked (*i);
1098 _model->ContentsChanged (); /* EMIT SIGNAL */
1101 XMLNode &
1102 MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
1104 XMLNode* n = new XMLNode ("patch-change");
1107 ostringstream s (ios::ate);
1108 s << int (p->id ());
1109 n->add_property ("id", s.str());
1113 ostringstream s (ios::ate);
1114 s << p->time ();
1115 n->add_property ("time", s.str ());
1119 ostringstream s (ios::ate);
1120 s << int (p->channel ());
1121 n->add_property ("channel", s.str ());
1125 ostringstream s (ios::ate);
1126 s << int (p->program ());
1127 n->add_property ("program", s.str ());
1131 ostringstream s (ios::ate);
1132 s << int (p->bank ());
1133 n->add_property ("bank", s.str ());
1136 return *n;
1139 XMLNode&
1140 MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
1142 XMLNode* n = new XMLNode (X_("Change"));
1144 n->add_property (X_("property"), enum_2_string (c.property));
1147 ostringstream s (ios::ate);
1148 if (c.property == Time) {
1149 s << c.old_time;
1150 } else if (c.property == Channel) {
1151 s << c.old_channel;
1152 } else if (c.property == Program) {
1153 s << int (c.old_program);
1154 } else if (c.property == Bank) {
1155 s << c.old_bank;
1158 n->add_property (X_("old"), s.str ());
1162 ostringstream s (ios::ate);
1164 if (c.property == Time) {
1165 s << c.new_time;
1166 } else if (c.property == Channel) {
1167 s << c.new_channel;
1168 } else if (c.property == Program) {
1169 s << int (c.new_program);
1170 } else if (c.property == Bank) {
1171 s << c.new_bank;
1174 n->add_property (X_("new"), s.str ());
1178 ostringstream s;
1179 s << c.patch->id ();
1180 n->add_property ("id", s.str ());
1183 return *n;
1186 MidiModel::PatchChangePtr
1187 MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
1189 XMLProperty* prop;
1190 Evoral::event_id_t id;
1191 Evoral::MusicalTime time = 0;
1192 uint8_t channel = 0;
1193 uint8_t program = 0;
1194 int bank = 0;
1196 if ((prop = n->property ("id")) != 0) {
1197 istringstream s (prop->value());
1198 s >> id;
1201 if ((prop = n->property ("time")) != 0) {
1202 istringstream s (prop->value ());
1203 s >> time;
1206 if ((prop = n->property ("channel")) != 0) {
1207 istringstream s (prop->value ());
1208 s >> channel;
1211 if ((prop = n->property ("program")) != 0) {
1212 istringstream s (prop->value ());
1213 s >> program;
1216 if ((prop = n->property ("bank")) != 0) {
1217 istringstream s (prop->value ());
1218 s >> bank;
1221 PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
1222 p->set_id (id);
1223 return p;
1226 MidiModel::PatchChangeDiffCommand::Change
1227 MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
1229 XMLProperty* prop;
1230 Change c;
1232 prop = n->property ("property");
1233 assert (prop);
1234 c.property = (Property) string_2_enum (prop->value(), c.property);
1236 prop = n->property ("id");
1237 assert (prop);
1238 Evoral::event_id_t const id = atoi (prop->value().c_str());
1240 prop = n->property ("old");
1241 assert (prop);
1243 istringstream s (prop->value ());
1244 if (c.property == Time) {
1245 s >> c.old_time;
1246 } else if (c.property == Channel) {
1247 s >> c.old_channel;
1248 } else if (c.property == Program) {
1249 s >> c.old_program;
1250 } else if (c.property == Bank) {
1251 s >> c.old_bank;
1255 prop = n->property ("new");
1256 assert (prop);
1258 istringstream s (prop->value ());
1259 if (c.property == Time) {
1260 s >> c.new_time;
1261 } else if (c.property == Channel) {
1262 s >> c.new_channel;
1263 } else if (c.property == Program) {
1264 s >> c.new_program;
1265 } else if (c.property == Bank) {
1266 s >> c.new_bank;
1270 c.patch = _model->find_patch_change (id);
1271 assert (c.patch);
1273 return c;
1277 MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
1279 if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
1280 return 1;
1283 _added.clear ();
1284 XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
1285 if (added) {
1286 XMLNodeList p = added->children ();
1287 transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1290 _removed.clear ();
1291 XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
1292 if (removed) {
1293 XMLNodeList p = removed->children ();
1294 transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1297 _changes.clear ();
1298 XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
1299 if (changed) {
1300 XMLNodeList p = changed->children ();
1301 transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
1304 return 0;
1307 XMLNode &
1308 MidiModel::PatchChangeDiffCommand::get_state ()
1310 XMLNode* diff_command = new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT);
1311 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
1313 XMLNode* added = diff_command->add_child (ADDED_PATCH_CHANGES_ELEMENT);
1314 for_each (_added.begin(), _added.end(),
1315 boost::bind (
1316 boost::bind (&XMLNode::add_child_nocopy, added, _1),
1317 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1321 XMLNode* removed = diff_command->add_child (REMOVED_PATCH_CHANGES_ELEMENT);
1322 for_each (_removed.begin(), _removed.end(),
1323 boost::bind (
1324 boost::bind (&XMLNode::add_child_nocopy, removed, _1),
1325 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1329 XMLNode* changes = diff_command->add_child (DIFF_PATCH_CHANGES_ELEMENT);
1330 for_each (_changes.begin(), _changes.end(),
1331 boost::bind (
1332 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
1333 boost::bind (&PatchChangeDiffCommand::marshal_change, this, _1)
1337 return *diff_command;
1340 /** Write all of the model to a MidiSource (i.e. save the model).
1341 * This is different from manually using read to write to a source in that
1342 * note off events are written regardless of the track mode. This is so the
1343 * user can switch a recorded track (with note durations from some instrument)
1344 * to percussive, save, reload, then switch it back to sustained without
1345 * destroying the original note durations.
1347 * Similarly, control events are written without interpolation (as with the
1348 * `Discrete' mode).
1350 bool
1351 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
1353 ReadLock lock(read_lock());
1355 const bool old_percussive = percussive();
1356 set_percussive(false);
1358 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1359 assert (ms);
1361 source->drop_model();
1362 source->mark_streaming_midi_write_started (note_mode());
1364 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1365 source->append_event_unlocked_beats(*i);
1368 set_percussive(old_percussive);
1369 source->mark_streaming_write_completed();
1371 set_edited(false);
1373 return true;
1376 /** very similar to ::write_to() but writes to the model's own
1377 existing midi_source, without making it call MidiSource::drop_model().
1378 the caller is a MidiSource that needs to catch up with the state
1379 of the model.
1381 bool
1382 MidiModel::sync_to_source ()
1384 ReadLock lock(read_lock());
1386 const bool old_percussive = percussive();
1387 set_percussive(false);
1389 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1390 assert (ms);
1392 ms->mark_streaming_midi_write_started (note_mode());
1394 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1395 ms->append_event_unlocked_beats(*i);
1398 set_percussive (old_percussive);
1399 ms->mark_streaming_write_completed ();
1401 set_edited (false);
1403 return true;
1406 /** Write part or all of the model to a MidiSource (i.e. save the model).
1407 * This is different from manually using read to write to a source in that
1408 * note off events are written regardless of the track mode. This is so the
1409 * user can switch a recorded track (with note durations from some instrument)
1410 * to percussive, save, reload, then switch it back to sustained without
1411 * destroying the original note durations.
1413 bool
1414 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
1416 ReadLock lock(read_lock());
1417 MidiStateTracker mst;
1418 Evoral::MusicalTime extra_note_on_time = end_time;
1420 const bool old_percussive = percussive();
1421 set_percussive(false);
1423 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1424 assert (ms);
1426 source->drop_model();
1427 source->mark_streaming_midi_write_started (note_mode());
1429 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1430 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
1432 if (ev.time() >= begin_time && ev.time() < end_time) {
1434 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
1435 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
1437 if (!mev) {
1438 continue;
1442 if (mev->is_note_off()) {
1444 if (!mst.active (mev->note(), mev->channel())) {
1446 /* add a note-on at the start of the range we're writing
1447 to the file. velocity is just an arbitary reasonable value.
1450 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
1451 on.set_type (mev->type());
1452 on.set_note (mev->note());
1453 on.set_channel (mev->channel());
1454 on.set_velocity (mev->velocity());
1456 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
1457 source->append_event_unlocked_beats (on);
1458 mst.add (on.note(), on.channel());
1459 mst.dump (cerr);
1460 extra_note_on_time += 1.0/128.0;
1463 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
1464 source->append_event_unlocked_beats (*i);
1465 mst.remove (mev->note(), mev->channel());
1466 mst.dump (cerr);
1468 } else if (mev->is_note_on()) {
1469 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
1470 mst.add (mev->note(), mev->channel());
1471 source->append_event_unlocked_beats(*i);
1472 mst.dump (cerr);
1473 } else {
1474 cerr << "MIDI other event type\n";
1475 source->append_event_unlocked_beats(*i);
1480 mst.resolve_notes (*source, end_time);
1482 set_percussive(old_percussive);
1483 source->mark_streaming_write_completed();
1485 set_edited(false);
1487 return true;
1490 XMLNode&
1491 MidiModel::get_state()
1493 XMLNode *node = new XMLNode("MidiModel");
1494 return *node;
1497 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1498 MidiModel::find_note (NotePtr other)
1500 Notes::iterator l = notes().lower_bound(other);
1502 if (l != notes().end()) {
1503 for (; (*l)->time() == other->time(); ++l) {
1504 /* NB: compare note contents, not note pointers.
1505 If "other" was a ptr to a note already in
1506 the model, we wouldn't be looking for it,
1507 would we now?
1509 if (**l == *other) {
1510 return *l;
1515 return NotePtr();
1518 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1519 MidiModel::find_note (gint note_id)
1521 /* used only for looking up notes when reloading history from disk,
1522 so we don't care about performance *too* much.
1525 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1526 if ((*l)->id() == note_id) {
1527 return *l;
1531 return NotePtr();
1534 MidiModel::PatchChangePtr
1535 MidiModel::find_patch_change (Evoral::event_id_t id)
1537 for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1538 if ((*i)->id() == id) {
1539 return *i;
1543 return PatchChangePtr ();
1546 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1547 MidiModel::find_sysex (gint sysex_id)
1549 /* used only for looking up notes when reloading history from disk,
1550 so we don't care about performance *too* much.
1553 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1554 if ((*l)->id() == sysex_id) {
1555 return *l;
1559 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1562 /** Lock and invalidate the source.
1563 * This should be used by commands and editing things
1565 MidiModel::WriteLock
1566 MidiModel::edit_lock()
1568 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1569 assert (ms);
1571 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock (ms->mutex());
1572 ms->invalidate(); // Release cached iterator's read lock on model
1573 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1576 /** Lock just the model, the source lock must already be held.
1577 * This should only be called from libardour/evoral places
1579 MidiModel::WriteLock
1580 MidiModel::write_lock()
1582 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1583 assert (ms);
1585 assert (!ms->mutex().trylock ());
1586 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
1590 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1592 using namespace Evoral;
1594 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1595 return 0;
1598 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1600 TimeType sa = note->time();
1601 TimeType ea = note->end_time();
1603 const Pitches& p (pitches (note->channel()));
1604 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
1605 set<NotePtr> to_be_deleted;
1606 bool set_note_length = false;
1607 bool set_note_time = false;
1608 TimeType note_time = note->time();
1609 TimeType note_length = note->length();
1611 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1613 for (Pitches::const_iterator i = p.lower_bound (search_note);
1614 i != p.end() && (*i)->note() == note->note(); ++i) {
1616 TimeType sb = (*i)->time();
1617 TimeType eb = (*i)->end_time();
1618 OverlapType overlap = OverlapNone;
1620 if ((sb > sa) && (eb <= ea)) {
1621 overlap = OverlapInternal;
1622 } else if ((eb >= sa) && (eb <= ea)) {
1623 overlap = OverlapStart;
1624 } else if ((sb > sa) && (sb <= ea)) {
1625 overlap = OverlapEnd;
1626 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1627 overlap = OverlapExternal;
1628 } else {
1629 /* no overlap */
1630 continue;
1633 DEBUG_TRACE (DEBUG::Sequence, string_compose (
1634 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1635 enum_2_string(overlap), sa, ea, sb, eb));
1637 if (insert_merge_policy() == InsertMergeReject) {
1638 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1639 return -1;
1642 switch (overlap) {
1643 case OverlapStart:
1644 cerr << "OverlapStart\n";
1645 /* existing note covers start of new note */
1646 switch (insert_merge_policy()) {
1647 case InsertMergeReplace:
1648 to_be_deleted.insert (*i);
1649 break;
1650 case InsertMergeTruncateExisting:
1651 if (cmd) {
1652 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1654 (*i)->set_length (note->time() - (*i)->time());
1655 break;
1656 case InsertMergeTruncateAddition:
1657 set_note_time = true;
1658 set_note_length = true;
1659 note_time = (*i)->time() + (*i)->length();
1660 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1661 break;
1662 case InsertMergeExtend:
1663 if (cmd) {
1664 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1666 (*i)->set_length (note->end_time() - (*i)->time());
1667 return -1; /* do not add the new note */
1668 break;
1669 default:
1670 /*NOTREACHED*/
1671 /* stupid gcc */
1672 break;
1674 break;
1676 case OverlapEnd:
1677 cerr << "OverlapEnd\n";
1678 /* existing note covers end of new note */
1679 switch (insert_merge_policy()) {
1680 case InsertMergeReplace:
1681 to_be_deleted.insert (*i);
1682 break;
1684 case InsertMergeTruncateExisting:
1685 /* resetting the start time of the existing note
1686 is a problem because of time ordering.
1688 break;
1690 case InsertMergeTruncateAddition:
1691 set_note_length = true;
1692 note_length = min (note_length, ((*i)->time() - note->time()));
1693 break;
1695 case InsertMergeExtend:
1696 /* we can't reset the time of the existing note because
1697 that will corrupt time ordering. So remove the
1698 existing note and change the position/length
1699 of the new note (which has not been added yet)
1701 to_be_deleted.insert (*i);
1702 set_note_length = true;
1703 note_length = min (note_length, (*i)->end_time() - note->time());
1704 break;
1705 default:
1706 /*NOTREACHED*/
1707 /* stupid gcc */
1708 break;
1710 break;
1712 case OverlapExternal:
1713 cerr << "OverlapExt\n";
1714 /* existing note overlaps all the new note */
1715 switch (insert_merge_policy()) {
1716 case InsertMergeReplace:
1717 to_be_deleted.insert (*i);
1718 break;
1719 case InsertMergeTruncateExisting:
1720 case InsertMergeTruncateAddition:
1721 case InsertMergeExtend:
1722 /* cannot add in this case */
1723 return -1;
1724 default:
1725 /*NOTREACHED*/
1726 /* stupid gcc */
1727 break;
1729 break;
1731 case OverlapInternal:
1732 cerr << "OverlapInt\n";
1733 /* new note fully overlaps an existing note */
1734 switch (insert_merge_policy()) {
1735 case InsertMergeReplace:
1736 case InsertMergeTruncateExisting:
1737 case InsertMergeTruncateAddition:
1738 case InsertMergeExtend:
1739 /* delete the existing note, the new one will cover it */
1740 to_be_deleted.insert (*i);
1741 break;
1742 default:
1743 /*NOTREACHED*/
1744 /* stupid gcc */
1745 break;
1747 break;
1749 default:
1750 /*NOTREACHED*/
1751 /* stupid gcc */
1752 break;
1756 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1757 remove_note_unlocked (*i);
1759 if (cmd) {
1760 cmd->side_effect_remove (*i);
1764 if (set_note_time) {
1765 if (cmd) {
1766 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1768 note->set_time (note_time);
1771 if (set_note_length) {
1772 if (cmd) {
1773 cmd->change (note, NoteDiffCommand::Length, note_length);
1775 note->set_length (note_length);
1778 return 0;
1781 InsertMergePolicy
1782 MidiModel::insert_merge_policy () const
1784 /* XXX ultimately this should be a per-track or even per-model policy */
1785 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1786 assert (ms);
1788 return ms->session().config.get_insert_merge_policy ();
1791 void
1792 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1794 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1796 if (old) {
1797 old->invalidate ();
1800 _midi_source_connections.drop_connections ();
1802 _midi_source = s;
1804 s->InterpolationChanged.connect_same_thread (
1805 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1808 s->AutomationStateChanged.connect_same_thread (
1809 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1813 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1814 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1815 * appropriate ControlList.
1817 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1818 * or the other is listened to by the GUI.
1820 void
1821 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1823 Glib::Mutex::Lock lm (_control_lock);
1824 control(p)->list()->set_interpolation (s);
1827 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1828 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1830 void
1831 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1833 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1834 assert (ms);
1836 ms->set_interpolation_of (p, s);
1839 void
1840 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1842 Glib::Mutex::Lock lm (_control_lock);
1843 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1844 al->set_automation_state (s);
1847 void
1848 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1850 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1851 assert (ms);
1852 ms->set_automation_state_of (p, s);
1855 boost::shared_ptr<Evoral::Control>
1856 MidiModel::control_factory (Evoral::Parameter const & p)
1858 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1860 /* Set up newly created control's lists to the appropriate interpolation and
1861 automation state from our source.
1864 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1865 assert (ms);
1867 c->list()->set_interpolation (ms->interpolation_of (p));
1869 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1870 assert (al);
1872 al->set_automation_state (ms->automation_state_of (p));
1874 return c;
1877 boost::shared_ptr<const MidiSource>
1878 MidiModel::midi_source ()
1880 return _midi_source.lock ();
1883 /** Moves notes, controllers and sys-ex to insert silence at the start of the model.
1884 * Adds commands to the session's current undo stack to reflect the movements.
1886 void
1887 MidiModel::insert_silence_at_start (TimeType t)
1889 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1890 assert (s);
1892 /* Notes */
1894 if (!notes().empty ()) {
1895 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1897 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1898 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1901 apply_command_as_subcommand (s->session(), c);
1904 /* Controllers */
1906 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1907 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1908 XMLNode& before = ac->alist()->get_state ();
1909 i->second->list()->shift (0, t);
1910 XMLNode& after = ac->alist()->get_state ();
1911 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1914 /* Sys-ex */
1916 if (!sysexes().empty()) {
1917 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1919 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1920 c->change (*i, (*i)->time() + t);
1923 apply_command_as_subcommand (s->session(), c);
1927 /** Transpose notes in a time range by a given number of semitones. Notes
1928 * will be clamped at 0 and 127 if the transposition would make them exceed
1929 * that range.
1931 * @param from Start time.
1932 * @param end End time.
1933 * @param semitones Number of semitones to transpose by (+ve is higher, -ve is lower).
1935 void
1936 MidiModel::transpose (TimeType from, TimeType to, int semitones)
1938 boost::shared_ptr<const MidiSource> s = midi_source ();
1940 NoteDiffCommand* c = new_note_diff_command (_("transpose"));
1942 for (Notes::iterator i = notes().begin(); i != notes().end(); ++i) {
1944 if ((*i)->time() >= to) {
1946 /* finished */
1947 break;
1949 } else if ((*i)->time() >= from) {
1951 int new_note = (*i)->note() + semitones;
1953 if (new_note < 0) {
1954 new_note = 0;
1955 } else if (new_note > 127) {
1956 new_note = 127;
1959 c->change (*i, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
1964 apply_command (s->session (), c);