Reset fades on regions copied from time ranges in other regions (#4035).
[ardour2.git] / libs / ardour / midi_model.cc
blob1297b0c3ea831f4694e04c123280d9cc18850d28
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);
303 i->note->set_time (i->new_time);
304 break;
306 case Channel:
307 if (temporary_removals.find (i->note) == temporary_removals.end()) {
308 _model->remove_note_unlocked (i->note);
309 temporary_removals.insert (i->note);
311 i->note->set_channel (i->new_value);
312 break;
314 /* no remove-then-add required for these properties, since we do not index them
317 case Velocity:
318 i->note->set_velocity (i->new_value);
319 break;
321 case Length:
322 i->note->set_length (i->new_time);
323 break;
329 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
330 NoteDiffCommand side_effects (model(), "side effects");
331 if (_model->add_note_unlocked (*i, &side_effects)) {
332 /* The note was re-added ok */
333 *this += side_effects;
334 } else {
335 /* The note that we removed earlier could not be re-added. This change record
336 must say that the note was removed. It is an un-note.
339 /* We didn't change it... */
340 for (ChangeList::iterator j = _changes.begin(); j != _changes.end(); ) {
342 ChangeList::iterator k = j;
343 ++k;
345 if (*i == j->note) {
346 _changes.erase (j);
349 j = k;
352 /* ...in fact, we removed it */
353 _removed_notes.push_back (*i);
357 if (!side_effect_removals.empty()) {
358 cerr << "SER: \n";
359 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
360 cerr << "\t" << *i << ' ' << **i << endl;
365 _model->ContentsChanged(); /* EMIT SIGNAL */
368 void
369 MidiModel::NoteDiffCommand::undo ()
372 MidiModel::WriteLock lock(_model->edit_lock());
374 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
375 _model->remove_note_unlocked(*i);
378 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
379 _model->add_note_unlocked(*i);
382 /* notes we modify in a way that requires remove-then-add to maintain ordering */
383 set<NotePtr> temporary_removals;
385 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
386 Property prop = i->property;
387 switch (prop) {
388 case NoteNumber:
389 if (temporary_removals.find (i->note) == temporary_removals.end()) {
390 _model->remove_note_unlocked (i->note);
391 temporary_removals.insert (i->note);
393 i->note->set_note (i->old_value);
394 break;
395 case Velocity:
396 i->note->set_velocity (i->old_value);
397 break;
398 case StartTime:
399 if (temporary_removals.find (i->note) == temporary_removals.end()) {
400 _model->remove_note_unlocked (i->note);
401 temporary_removals.insert (i->note);
403 i->note->set_time (i->old_time);
404 break;
405 case Length:
406 i->note->set_length (i->old_time);
407 break;
408 case Channel:
409 if (temporary_removals.find (i->note) == temporary_removals.end()) {
410 _model->remove_note_unlocked (i->note);
411 temporary_removals.insert (i->note);
413 i->note->set_channel (i->old_value);
414 break;
418 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
419 _model->add_note_unlocked (*i);
422 /* finally add back notes that were removed by the "do". we don't care
423 about side effects here since the model should be back to its original
424 state once this is done.
427 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
428 _model->add_note_unlocked (*i);
432 _model->ContentsChanged(); /* EMIT SIGNAL */
435 XMLNode&
436 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
438 XMLNode* xml_note = new XMLNode("note");
441 ostringstream id_str(ios::ate);
442 id_str << int(note->id());
443 xml_note->add_property("id", id_str.str());
447 ostringstream note_str(ios::ate);
448 note_str << int(note->note());
449 xml_note->add_property("note", note_str.str());
453 ostringstream channel_str(ios::ate);
454 channel_str << int(note->channel());
455 xml_note->add_property("channel", channel_str.str());
459 ostringstream time_str(ios::ate);
460 time_str << note->time();
461 xml_note->add_property("time", time_str.str());
465 ostringstream length_str(ios::ate);
466 length_str << note->length();
467 xml_note->add_property("length", length_str.str());
471 ostringstream velocity_str(ios::ate);
472 velocity_str << (unsigned int) note->velocity();
473 xml_note->add_property("velocity", velocity_str.str());
476 return *xml_note;
479 Evoral::Sequence<MidiModel::TimeType>::NotePtr
480 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
482 unsigned int note;
483 XMLProperty* prop;
484 unsigned int channel;
485 unsigned int time;
486 unsigned int length;
487 unsigned int velocity;
488 gint id;
490 if ((prop = xml_note->property("id")) != 0) {
491 istringstream id_str(prop->value());
492 id_str >> id;
493 } else {
494 error << "note information missing ID value" << endmsg;
495 id = -1;
498 if ((prop = xml_note->property("note")) != 0) {
499 istringstream note_str(prop->value());
500 note_str >> note;
501 } else {
502 warning << "note information missing note value" << endmsg;
503 note = 127;
506 if ((prop = xml_note->property("channel")) != 0) {
507 istringstream channel_str(prop->value());
508 channel_str >> channel;
509 } else {
510 warning << "note information missing channel" << endmsg;
511 channel = 0;
514 if ((prop = xml_note->property("time")) != 0) {
515 istringstream time_str(prop->value());
516 time_str >> time;
517 } else {
518 warning << "note information missing time" << endmsg;
519 time = 0;
522 if ((prop = xml_note->property("length")) != 0) {
523 istringstream length_str(prop->value());
524 length_str >> length;
525 } else {
526 warning << "note information missing length" << endmsg;
527 length = 1;
530 if ((prop = xml_note->property("velocity")) != 0) {
531 istringstream velocity_str(prop->value());
532 velocity_str >> velocity;
533 } else {
534 warning << "note information missing velocity" << endmsg;
535 velocity = 127;
538 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
539 note_ptr->set_id (id);
541 return note_ptr;
544 XMLNode&
545 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
547 XMLNode* xml_change = new XMLNode("Change");
549 /* first, the change itself */
551 xml_change->add_property ("property", enum_2_string (change.property));
554 ostringstream old_value_str (ios::ate);
555 if (change.property == StartTime || change.property == Length) {
556 old_value_str << change.old_time;
557 } else {
558 old_value_str << (unsigned int) change.old_value;
560 xml_change->add_property ("old", old_value_str.str());
564 ostringstream new_value_str (ios::ate);
565 if (change.property == StartTime || change.property == Length) {
566 new_value_str << change.new_time;
567 } else {
568 new_value_str << (unsigned int) change.new_value;
570 xml_change->add_property ("new", new_value_str.str());
573 ostringstream id_str;
574 id_str << change.note->id();
575 xml_change->add_property ("id", id_str.str());
577 return *xml_change;
580 MidiModel::NoteDiffCommand::NoteChange
581 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
583 XMLProperty* prop;
584 NoteChange change;
586 if ((prop = xml_change->property("property")) != 0) {
587 change.property = (Property) string_2_enum (prop->value(), change.property);
588 } else {
589 fatal << "!!!" << endmsg;
590 /*NOTREACHED*/
593 if ((prop = xml_change->property ("id")) == 0) {
594 error << _("No NoteID found for note property change - ignored") << endmsg;
595 return change;
598 gint note_id = atoi (prop->value().c_str());
600 if ((prop = xml_change->property ("old")) != 0) {
601 istringstream old_str (prop->value());
602 if (change.property == StartTime || change.property == Length) {
603 old_str >> change.old_time;
604 } else {
605 int integer_value_so_that_istream_does_the_right_thing;
606 old_str >> integer_value_so_that_istream_does_the_right_thing;
607 change.old_value = integer_value_so_that_istream_does_the_right_thing;
609 } else {
610 fatal << "!!!" << endmsg;
611 /*NOTREACHED*/
614 if ((prop = xml_change->property ("new")) != 0) {
615 istringstream new_str (prop->value());
616 if (change.property == StartTime || change.property == Length) {
617 new_str >> change.new_time;
618 } else {
619 int integer_value_so_that_istream_does_the_right_thing;
620 new_str >> integer_value_so_that_istream_does_the_right_thing;
621 change.new_value = integer_value_so_that_istream_does_the_right_thing;
623 } else {
624 fatal << "!!!" << endmsg;
625 /*NOTREACHED*/
628 /* we must point at the instance of the note that is actually in the model.
629 so go look for it ...
632 change.note = _model->find_note (note_id);
634 if (!change.note) {
635 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
636 return change;
639 return change;
643 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
645 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
646 return 1;
649 /* additions */
651 _added_notes.clear();
652 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
653 if (added_notes) {
654 XMLNodeList notes = added_notes->children();
655 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
656 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
660 /* removals */
662 _removed_notes.clear();
663 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
664 if (removed_notes) {
665 XMLNodeList notes = removed_notes->children();
666 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
667 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
671 /* changes */
673 _changes.clear();
675 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
677 if (changed_notes) {
678 XMLNodeList notes = changed_notes->children();
679 transform (notes.begin(), notes.end(), back_inserter(_changes),
680 boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
684 /* side effect removals caused by changes */
686 side_effect_removals.clear();
688 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
690 if (side_effect_notes) {
691 XMLNodeList notes = side_effect_notes->children();
692 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
693 side_effect_removals.insert (unmarshal_note (*n));
697 return 0;
700 XMLNode&
701 MidiModel::NoteDiffCommand::get_state ()
703 XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
704 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
706 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
707 for_each(_changes.begin(), _changes.end(),
708 boost::bind (
709 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
710 boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
712 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
713 for_each(_added_notes.begin(), _added_notes.end(),
714 boost::bind(
715 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
716 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
718 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
719 for_each(_removed_notes.begin(), _removed_notes.end(),
720 boost::bind (
721 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
722 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
724 /* if this command had side-effects, store that state too
727 if (!side_effect_removals.empty()) {
728 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
729 for_each(side_effect_removals.begin(), side_effect_removals.end(),
730 boost::bind (
731 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
732 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
735 return *diff_command;
738 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
739 : DiffCommand (m, "")
741 assert (_model);
742 set_state (node, Stateful::loading_state_version);
745 void
746 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
748 Change change;
750 change.sysex = s;
751 change.property = Time;
752 change.old_time = s->time ();
753 change.new_time = new_time;
755 _changes.push_back (change);
758 void
759 MidiModel::SysExDiffCommand::operator() ()
762 MidiModel::WriteLock lock (_model->edit_lock ());
764 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
765 switch (i->property) {
766 case Time:
767 i->sysex->set_time (i->new_time);
772 _model->ContentsChanged (); /* EMIT SIGNAL */
775 void
776 MidiModel::SysExDiffCommand::undo ()
779 MidiModel::WriteLock lock (_model->edit_lock ());
781 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
782 switch (i->property) {
783 case Time:
784 i->sysex->set_time (i->old_time);
785 break;
791 _model->ContentsChanged(); /* EMIT SIGNAL */
794 XMLNode&
795 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
797 XMLNode* xml_change = new XMLNode ("Change");
799 /* first, the change itself */
801 xml_change->add_property ("property", enum_2_string (change.property));
804 ostringstream old_value_str (ios::ate);
805 old_value_str << change.old_time;
806 xml_change->add_property ("old", old_value_str.str());
810 ostringstream new_value_str (ios::ate);
811 new_value_str << change.new_time;
812 xml_change->add_property ("new", new_value_str.str());
815 ostringstream id_str;
816 id_str << change.sysex->id();
817 xml_change->add_property ("id", id_str.str());
819 return *xml_change;
822 MidiModel::SysExDiffCommand::Change
823 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
825 XMLProperty* prop;
826 Change change;
828 if ((prop = xml_change->property ("property")) != 0) {
829 change.property = (Property) string_2_enum (prop->value(), change.property);
830 } else {
831 fatal << "!!!" << endmsg;
832 /*NOTREACHED*/
835 if ((prop = xml_change->property ("id")) == 0) {
836 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
837 return change;
840 gint sysex_id = atoi (prop->value().c_str());
842 if ((prop = xml_change->property ("old")) != 0) {
843 istringstream old_str (prop->value());
844 old_str >> change.old_time;
845 } else {
846 fatal << "!!!" << endmsg;
847 /*NOTREACHED*/
850 if ((prop = xml_change->property ("new")) != 0) {
851 istringstream new_str (prop->value());
852 new_str >> change.new_time;
853 } else {
854 fatal << "!!!" << endmsg;
855 /*NOTREACHED*/
858 /* we must point at the instance of the sysex that is actually in the model.
859 so go look for it ...
862 change.sysex = _model->find_sysex (sysex_id);
864 if (!change.sysex) {
865 warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg;
866 return change;
869 return change;
873 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
875 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
876 return 1;
879 /* changes */
881 _changes.clear();
883 XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
885 if (changed_sysexes) {
886 XMLNodeList sysexes = changed_sysexes->children();
887 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
888 boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
892 return 0;
895 XMLNode&
896 MidiModel::SysExDiffCommand::get_state ()
898 XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
899 diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
901 XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
902 for_each (_changes.begin(), _changes.end(),
903 boost::bind (
904 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
905 boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
907 return *diff_command;
910 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
911 : DiffCommand (m, name)
913 assert (_model);
916 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
917 : DiffCommand (m, "")
919 assert (_model);
920 set_state (node, Stateful::loading_state_version);
923 void
924 MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
926 _added.push_back (p);
929 void
930 MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
932 _removed.push_back (p);
935 void
936 MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
938 Change c;
939 c.property = Time;
940 c.patch = patch;
941 c.old_time = patch->time ();
942 c.new_time = t;
944 _changes.push_back (c);
947 void
948 MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
950 Change c;
951 c.property = Channel;
952 c.patch = patch;
953 c.old_channel = patch->channel ();
954 c.new_channel = channel;
956 _changes.push_back (c);
959 void
960 MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
962 Change c;
963 c.property = Program;
964 c.patch = patch;
965 c.old_program = patch->program ();
966 c.new_program = program;
968 _changes.push_back (c);
971 void
972 MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
974 Change c;
975 c.property = Bank;
976 c.patch = patch;
977 c.old_bank = patch->bank ();
978 c.new_bank = bank;
980 _changes.push_back (c);
983 void
984 MidiModel::PatchChangeDiffCommand::operator() ()
987 MidiModel::WriteLock lock (_model->edit_lock ());
989 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
990 _model->add_patch_change_unlocked (*i);
993 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
994 _model->remove_patch_change_unlocked (*i);
997 set<PatchChangePtr> temporary_removals;
999 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1000 switch (i->property) {
1001 case Time:
1002 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1003 _model->remove_patch_change_unlocked (i->patch);
1004 temporary_removals.insert (i->patch);
1006 i->patch->set_time (i->new_time);
1007 break;
1009 case Channel:
1010 i->patch->set_channel (i->new_channel);
1011 break;
1013 case Program:
1014 i->patch->set_program (i->new_program);
1015 break;
1017 case Bank:
1018 i->patch->set_bank (i->new_bank);
1019 break;
1023 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1024 _model->add_patch_change_unlocked (*i);
1028 _model->ContentsChanged (); /* EMIT SIGNAL */
1031 void
1032 MidiModel::PatchChangeDiffCommand::undo ()
1035 MidiModel::WriteLock lock (_model->edit_lock());
1037 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1038 _model->remove_patch_change_unlocked (*i);
1041 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1042 _model->add_patch_change_unlocked (*i);
1045 set<PatchChangePtr> temporary_removals;
1047 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1048 switch (i->property) {
1049 case Time:
1050 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1051 _model->remove_patch_change_unlocked (i->patch);
1052 temporary_removals.insert (i->patch);
1054 i->patch->set_time (i->old_time);
1055 break;
1057 case Channel:
1058 i->patch->set_channel (i->old_channel);
1059 break;
1061 case Program:
1062 i->patch->set_program (i->old_program);
1063 break;
1065 case Bank:
1066 i->patch->set_bank (i->old_bank);
1067 break;
1071 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1072 _model->add_patch_change_unlocked (*i);
1077 _model->ContentsChanged (); /* EMIT SIGNAL */
1080 XMLNode &
1081 MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
1083 XMLNode* n = new XMLNode ("patch-change");
1086 ostringstream s (ios::ate);
1087 s << int (p->id ());
1088 n->add_property ("id", s.str());
1092 ostringstream s (ios::ate);
1093 s << p->time ();
1094 n->add_property ("time", s.str ());
1098 ostringstream s (ios::ate);
1099 s << int (p->channel ());
1100 n->add_property ("channel", s.str ());
1104 ostringstream s (ios::ate);
1105 s << int (p->program ());
1106 n->add_property ("program", s.str ());
1110 ostringstream s (ios::ate);
1111 s << int (p->bank ());
1112 n->add_property ("bank", s.str ());
1115 return *n;
1118 XMLNode&
1119 MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
1121 XMLNode* n = new XMLNode (X_("Change"));
1123 n->add_property (X_("property"), enum_2_string (c.property));
1126 ostringstream s (ios::ate);
1127 if (c.property == Time) {
1128 s << c.old_time;
1129 } else if (c.property == Channel) {
1130 s << c.old_channel;
1131 } else if (c.property == Program) {
1132 s << int (c.old_program);
1133 } else if (c.property == Bank) {
1134 s << c.old_bank;
1137 n->add_property (X_("old"), s.str ());
1141 ostringstream s (ios::ate);
1143 if (c.property == Time) {
1144 s << c.new_time;
1145 } else if (c.property == Channel) {
1146 s << c.new_channel;
1147 } else if (c.property == Program) {
1148 s << int (c.new_program);
1149 } else if (c.property == Bank) {
1150 s << c.new_bank;
1153 n->add_property (X_("new"), s.str ());
1157 ostringstream s;
1158 s << c.patch->id ();
1159 n->add_property ("id", s.str ());
1162 return *n;
1165 MidiModel::PatchChangePtr
1166 MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
1168 XMLProperty* prop;
1169 Evoral::event_id_t id;
1170 Evoral::MusicalTime time = 0;
1171 uint8_t channel = 0;
1172 uint8_t program = 0;
1173 int bank = 0;
1175 if ((prop = n->property ("id")) != 0) {
1176 istringstream s (prop->value());
1177 s >> id;
1180 if ((prop = n->property ("time")) != 0) {
1181 istringstream s (prop->value ());
1182 s >> time;
1185 if ((prop = n->property ("channel")) != 0) {
1186 istringstream s (prop->value ());
1187 s >> channel;
1190 if ((prop = n->property ("program")) != 0) {
1191 istringstream s (prop->value ());
1192 s >> program;
1195 if ((prop = n->property ("bank")) != 0) {
1196 istringstream s (prop->value ());
1197 s >> bank;
1200 PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
1201 p->set_id (id);
1202 return p;
1205 MidiModel::PatchChangeDiffCommand::Change
1206 MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
1208 XMLProperty* prop;
1209 Change c;
1211 prop = n->property ("property");
1212 assert (prop);
1213 c.property = (Property) string_2_enum (prop->value(), c.property);
1215 prop = n->property ("id");
1216 assert (prop);
1217 Evoral::event_id_t const id = atoi (prop->value().c_str());
1219 prop = n->property ("old");
1220 assert (prop);
1222 istringstream s (prop->value ());
1223 if (c.property == Time) {
1224 s >> c.old_time;
1225 } else if (c.property == Channel) {
1226 s >> c.old_channel;
1227 } else if (c.property == Program) {
1228 s >> c.old_program;
1229 } else if (c.property == Bank) {
1230 s >> c.old_bank;
1234 prop = n->property ("new");
1235 assert (prop);
1237 istringstream s (prop->value ());
1238 if (c.property == Time) {
1239 s >> c.new_time;
1240 } else if (c.property == Channel) {
1241 s >> c.new_channel;
1242 } else if (c.property == Program) {
1243 s >> c.new_program;
1244 } else if (c.property == Bank) {
1245 s >> c.new_bank;
1249 c.patch = _model->find_patch_change (id);
1250 assert (c.patch);
1252 return c;
1256 MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
1258 if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
1259 return 1;
1262 _added.clear ();
1263 XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
1264 if (added) {
1265 XMLNodeList p = added->children ();
1266 transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1269 _removed.clear ();
1270 XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
1271 if (removed) {
1272 XMLNodeList p = removed->children ();
1273 transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1276 _changes.clear ();
1277 XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
1278 if (changed) {
1279 XMLNodeList p = changed->children ();
1280 transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
1283 return 0;
1286 XMLNode &
1287 MidiModel::PatchChangeDiffCommand::get_state ()
1289 XMLNode* diff_command = new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT);
1290 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
1292 XMLNode* added = diff_command->add_child (ADDED_PATCH_CHANGES_ELEMENT);
1293 for_each (_added.begin(), _added.end(),
1294 boost::bind (
1295 boost::bind (&XMLNode::add_child_nocopy, added, _1),
1296 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1300 XMLNode* removed = diff_command->add_child (REMOVED_PATCH_CHANGES_ELEMENT);
1301 for_each (_removed.begin(), _removed.end(),
1302 boost::bind (
1303 boost::bind (&XMLNode::add_child_nocopy, removed, _1),
1304 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1308 XMLNode* changes = diff_command->add_child (DIFF_PATCH_CHANGES_ELEMENT);
1309 for_each (_changes.begin(), _changes.end(),
1310 boost::bind (
1311 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
1312 boost::bind (&PatchChangeDiffCommand::marshal_change, this, _1)
1316 return *diff_command;
1319 /** Write all of the model to a MidiSource (i.e. save the model).
1320 * This is different from manually using read to write to a source in that
1321 * note off events are written regardless of the track mode. This is so the
1322 * user can switch a recorded track (with note durations from some instrument)
1323 * to percussive, save, reload, then switch it back to sustained without
1324 * destroying the original note durations.
1326 * Similarly, control events are written without interpolation (as with the
1327 * `Discrete' mode).
1329 bool
1330 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
1332 ReadLock lock(read_lock());
1334 const bool old_percussive = percussive();
1335 set_percussive(false);
1337 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1338 assert (ms);
1340 source->drop_model();
1341 source->mark_streaming_midi_write_started (note_mode());
1343 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1344 source->append_event_unlocked_beats(*i);
1347 set_percussive(old_percussive);
1348 source->mark_streaming_write_completed();
1350 set_edited(false);
1352 return true;
1355 /** very similar to ::write_to() but writes to the model's own
1356 existing midi_source, without making it call MidiSource::drop_model().
1357 the caller is a MidiSource that needs to catch up with the state
1358 of the model.
1360 bool
1361 MidiModel::sync_to_source ()
1363 ReadLock lock(read_lock());
1365 const bool old_percussive = percussive();
1366 set_percussive(false);
1368 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1369 assert (ms);
1371 ms->mark_streaming_midi_write_started (note_mode());
1373 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1374 ms->append_event_unlocked_beats(*i);
1377 set_percussive (old_percussive);
1378 ms->mark_streaming_write_completed ();
1380 set_edited (false);
1382 return true;
1385 /** Write part or all of the model to a MidiSource (i.e. save the model).
1386 * This is different from manually using read to write to a source in that
1387 * note off events are written regardless of the track mode. This is so the
1388 * user can switch a recorded track (with note durations from some instrument)
1389 * to percussive, save, reload, then switch it back to sustained without
1390 * destroying the original note durations.
1392 bool
1393 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
1395 ReadLock lock(read_lock());
1396 MidiStateTracker mst;
1397 Evoral::MusicalTime extra_note_on_time = end_time;
1399 const bool old_percussive = percussive();
1400 set_percussive(false);
1402 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1403 assert (ms);
1405 source->drop_model();
1406 source->mark_streaming_midi_write_started (note_mode());
1408 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1409 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
1411 if (ev.time() >= begin_time && ev.time() < end_time) {
1413 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
1414 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
1416 if (!mev) {
1417 continue;
1421 if (mev->is_note_off()) {
1423 if (!mst.active (mev->note(), mev->channel())) {
1425 /* add a note-on at the start of the range we're writing
1426 to the file. velocity is just an arbitary reasonable value.
1429 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
1430 on.set_type (mev->type());
1431 on.set_note (mev->note());
1432 on.set_channel (mev->channel());
1433 on.set_velocity (mev->velocity());
1435 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
1436 source->append_event_unlocked_beats (on);
1437 mst.add (on.note(), on.channel());
1438 mst.dump (cerr);
1439 extra_note_on_time += 1.0/128.0;
1442 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
1443 source->append_event_unlocked_beats (*i);
1444 mst.remove (mev->note(), mev->channel());
1445 mst.dump (cerr);
1447 } else if (mev->is_note_on()) {
1448 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
1449 mst.add (mev->note(), mev->channel());
1450 source->append_event_unlocked_beats(*i);
1451 mst.dump (cerr);
1452 } else {
1453 cerr << "MIDI other event type\n";
1454 source->append_event_unlocked_beats(*i);
1459 mst.resolve_notes (*source, end_time);
1461 set_percussive(old_percussive);
1462 source->mark_streaming_write_completed();
1464 set_edited(false);
1466 return true;
1469 XMLNode&
1470 MidiModel::get_state()
1472 XMLNode *node = new XMLNode("MidiModel");
1473 return *node;
1476 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1477 MidiModel::find_note (NotePtr other)
1479 Notes::iterator l = notes().lower_bound(other);
1481 if (l != notes().end()) {
1482 for (; (*l)->time() == other->time(); ++l) {
1483 /* NB: compare note contents, not note pointers.
1484 If "other" was a ptr to a note already in
1485 the model, we wouldn't be looking for it,
1486 would we now?
1488 if (**l == *other) {
1489 return *l;
1494 return NotePtr();
1497 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1498 MidiModel::find_note (gint note_id)
1500 /* used only for looking up notes when reloading history from disk,
1501 so we don't care about performance *too* much.
1504 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1505 if ((*l)->id() == note_id) {
1506 return *l;
1510 return NotePtr();
1513 MidiModel::PatchChangePtr
1514 MidiModel::find_patch_change (Evoral::event_id_t id)
1516 for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1517 if ((*i)->id() == id) {
1518 return *i;
1522 return PatchChangePtr ();
1525 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1526 MidiModel::find_sysex (gint sysex_id)
1528 /* used only for looking up notes when reloading history from disk,
1529 so we don't care about performance *too* much.
1532 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1533 if ((*l)->id() == sysex_id) {
1534 return *l;
1538 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1541 /** Lock and invalidate the source.
1542 * This should be used by commands and editing things
1544 MidiModel::WriteLock
1545 MidiModel::edit_lock()
1547 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1548 assert (ms);
1550 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock (ms->mutex());
1551 ms->invalidate(); // Release cached iterator's read lock on model
1552 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1555 /** Lock just the model, the source lock must already be held.
1556 * This should only be called from libardour/evoral places
1558 MidiModel::WriteLock
1559 MidiModel::write_lock()
1561 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1562 assert (ms);
1564 assert (!ms->mutex().trylock ());
1565 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
1569 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1571 using namespace Evoral;
1573 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1574 return 0;
1577 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1579 TimeType sa = note->time();
1580 TimeType ea = note->end_time();
1582 const Pitches& p (pitches (note->channel()));
1583 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
1584 set<NotePtr> to_be_deleted;
1585 bool set_note_length = false;
1586 bool set_note_time = false;
1587 TimeType note_time = note->time();
1588 TimeType note_length = note->length();
1590 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1592 for (Pitches::const_iterator i = p.lower_bound (search_note);
1593 i != p.end() && (*i)->note() == note->note(); ++i) {
1595 TimeType sb = (*i)->time();
1596 TimeType eb = (*i)->end_time();
1597 OverlapType overlap = OverlapNone;
1599 if ((sb > sa) && (eb <= ea)) {
1600 overlap = OverlapInternal;
1601 } else if ((eb >= sa) && (eb <= ea)) {
1602 overlap = OverlapStart;
1603 } else if ((sb > sa) && (sb <= ea)) {
1604 overlap = OverlapEnd;
1605 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1606 overlap = OverlapExternal;
1607 } else {
1608 /* no overlap */
1609 continue;
1612 DEBUG_TRACE (DEBUG::Sequence, string_compose (
1613 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1614 enum_2_string(overlap), sa, ea, sb, eb));
1616 if (insert_merge_policy() == InsertMergeReject) {
1617 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1618 return -1;
1621 switch (overlap) {
1622 case OverlapStart:
1623 cerr << "OverlapStart\n";
1624 /* existing note covers start of new note */
1625 switch (insert_merge_policy()) {
1626 case InsertMergeReplace:
1627 to_be_deleted.insert (*i);
1628 break;
1629 case InsertMergeTruncateExisting:
1630 if (cmd) {
1631 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1633 (*i)->set_length (note->time() - (*i)->time());
1634 break;
1635 case InsertMergeTruncateAddition:
1636 set_note_time = true;
1637 set_note_length = true;
1638 note_time = (*i)->time() + (*i)->length();
1639 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1640 break;
1641 case InsertMergeExtend:
1642 if (cmd) {
1643 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1645 (*i)->set_length (note->end_time() - (*i)->time());
1646 return -1; /* do not add the new note */
1647 break;
1648 default:
1649 /*NOTREACHED*/
1650 /* stupid gcc */
1651 break;
1653 break;
1655 case OverlapEnd:
1656 cerr << "OverlapEnd\n";
1657 /* existing note covers end of new note */
1658 switch (insert_merge_policy()) {
1659 case InsertMergeReplace:
1660 to_be_deleted.insert (*i);
1661 break;
1663 case InsertMergeTruncateExisting:
1664 /* resetting the start time of the existing note
1665 is a problem because of time ordering.
1667 break;
1669 case InsertMergeTruncateAddition:
1670 set_note_length = true;
1671 note_length = min (note_length, ((*i)->time() - note->time()));
1672 break;
1674 case InsertMergeExtend:
1675 /* we can't reset the time of the existing note because
1676 that will corrupt time ordering. So remove the
1677 existing note and change the position/length
1678 of the new note (which has not been added yet)
1680 to_be_deleted.insert (*i);
1681 set_note_length = true;
1682 note_length = min (note_length, (*i)->end_time() - note->time());
1683 break;
1684 default:
1685 /*NOTREACHED*/
1686 /* stupid gcc */
1687 break;
1689 break;
1691 case OverlapExternal:
1692 cerr << "OverlapExt\n";
1693 /* existing note overlaps all the new note */
1694 switch (insert_merge_policy()) {
1695 case InsertMergeReplace:
1696 to_be_deleted.insert (*i);
1697 break;
1698 case InsertMergeTruncateExisting:
1699 case InsertMergeTruncateAddition:
1700 case InsertMergeExtend:
1701 /* cannot add in this case */
1702 return -1;
1703 default:
1704 /*NOTREACHED*/
1705 /* stupid gcc */
1706 break;
1708 break;
1710 case OverlapInternal:
1711 cerr << "OverlapInt\n";
1712 /* new note fully overlaps an existing note */
1713 switch (insert_merge_policy()) {
1714 case InsertMergeReplace:
1715 case InsertMergeTruncateExisting:
1716 case InsertMergeTruncateAddition:
1717 case InsertMergeExtend:
1718 /* delete the existing note, the new one will cover it */
1719 to_be_deleted.insert (*i);
1720 break;
1721 default:
1722 /*NOTREACHED*/
1723 /* stupid gcc */
1724 break;
1726 break;
1728 default:
1729 /*NOTREACHED*/
1730 /* stupid gcc */
1731 break;
1735 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1736 remove_note_unlocked (*i);
1738 if (cmd) {
1739 cmd->side_effect_remove (*i);
1743 if (set_note_time) {
1744 if (cmd) {
1745 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1747 note->set_time (note_time);
1750 if (set_note_length) {
1751 if (cmd) {
1752 cmd->change (note, NoteDiffCommand::Length, note_length);
1754 note->set_length (note_length);
1757 return 0;
1760 InsertMergePolicy
1761 MidiModel::insert_merge_policy () const
1763 /* XXX ultimately this should be a per-track or even per-model policy */
1764 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1765 assert (ms);
1767 return ms->session().config.get_insert_merge_policy ();
1770 void
1771 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1773 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1775 if (old) {
1776 old->invalidate ();
1779 _midi_source_connections.drop_connections ();
1781 _midi_source = s;
1783 s->InterpolationChanged.connect_same_thread (
1784 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1787 s->AutomationStateChanged.connect_same_thread (
1788 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1792 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1793 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1794 * appropriate ControlList.
1796 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1797 * or the other is listened to by the GUI.
1799 void
1800 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1802 Glib::Mutex::Lock lm (_control_lock);
1803 control(p)->list()->set_interpolation (s);
1806 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1807 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1809 void
1810 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1812 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1813 assert (ms);
1815 ms->set_interpolation_of (p, s);
1818 void
1819 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1821 Glib::Mutex::Lock lm (_control_lock);
1822 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1823 al->set_automation_state (s);
1826 void
1827 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1829 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1830 assert (ms);
1831 ms->set_automation_state_of (p, s);
1834 boost::shared_ptr<Evoral::Control>
1835 MidiModel::control_factory (Evoral::Parameter const & p)
1837 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1839 /* Set up newly created control's lists to the appropriate interpolation and
1840 automation state from our source.
1843 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1844 assert (ms);
1846 c->list()->set_interpolation (ms->interpolation_of (p));
1848 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1849 assert (al);
1851 al->set_automation_state (ms->automation_state_of (p));
1853 return c;
1856 boost::shared_ptr<const MidiSource>
1857 MidiModel::midi_source ()
1859 return _midi_source.lock ();
1862 /** Moves notes, controllers and sys-ex to insert silence at the start of the model.
1863 * Adds commands to the session's current undo stack to reflect the movements.
1865 void
1866 MidiModel::insert_silence_at_start (TimeType t)
1868 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1869 assert (s);
1871 /* Notes */
1873 if (!notes().empty ()) {
1874 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1876 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1877 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1880 apply_command_as_subcommand (s->session(), c);
1883 /* Controllers */
1885 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1886 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1887 XMLNode& before = ac->alist()->get_state ();
1888 i->second->list()->shift (0, t);
1889 XMLNode& after = ac->alist()->get_state ();
1890 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1893 /* Sys-ex */
1895 if (!sysexes().empty()) {
1896 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1898 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1899 c->change (*i, (*i)->time() + t);
1902 apply_command_as_subcommand (s->session(), c);
1906 /** Transpose notes in a time range by a given number of semitones. Notes
1907 * will be clamped at 0 and 127 if the transposition would make them exceed
1908 * that range.
1910 * @param from Start time.
1911 * @param end End time.
1912 * @param semitones Number of semitones to transpose by (+ve is higher, -ve is lower).
1914 void
1915 MidiModel::transpose (TimeType from, TimeType to, int semitones)
1917 boost::shared_ptr<const MidiSource> s = midi_source ();
1919 NoteDiffCommand* c = new_note_diff_command (_("transpose"));
1921 for (Notes::iterator i = notes().begin(); i != notes().end(); ++i) {
1923 if ((*i)->time() >= to) {
1925 /* finished */
1926 break;
1928 } else if ((*i)->time() >= from) {
1930 int new_note = (*i)->note() + semitones;
1932 if (new_note < 0) {
1933 new_note = 0;
1934 } else if (new_note > 127) {
1935 new_note = 127;
1938 c->change (*i, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
1943 apply_command (s->session (), c);