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.
26 #include "pbd/error.h"
27 #include "pbd/enumwriter.h"
28 #include "midi++/events.h"
30 #include "ardour/midi_model.h"
31 #include "ardour/midi_source.h"
32 #include "ardour/midi_state_tracker.h"
33 #include "ardour/smf_source.h"
34 #include "ardour/types.h"
35 #include "ardour/session.h"
36 #include "ardour/midi_automation_list_binder.h"
39 using namespace ARDOUR;
42 MidiModel::MidiModel (boost::shared_ptr<MidiSource> s)
43 : AutomatableSequence<TimeType>(s->session())
48 /** Start a new NoteDiff command.
50 * This has no side-effects on the model or Session, the returned command
51 * can be held on to for as long as the caller wishes, or discarded without
52 * formality, until apply_command is called and ownership is taken.
54 MidiModel::NoteDiffCommand*
55 MidiModel::new_note_diff_command (const string name)
57 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
60 return new NoteDiffCommand (ms->model(), name);
63 /** Start a new SysExDiff command */
64 MidiModel::SysExDiffCommand*
65 MidiModel::new_sysex_diff_command (const string name)
67 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
70 return new SysExDiffCommand (ms->model(), name);
73 /** Start a new PatchChangeDiff command */
74 MidiModel::PatchChangeDiffCommand*
75 MidiModel::new_patch_change_diff_command (const string name)
77 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
80 return new PatchChangeDiffCommand (ms->model(), name);
86 * Ownership of cmd is taken, it must not be deleted by the caller.
87 * The command will constitute one item on the undo stack.
90 MidiModel::apply_command(Session& session, Command* cmd)
92 session.begin_reversible_command(cmd->name());
94 session.commit_reversible_command(cmd);
98 /** Apply a command as part of a larger reversible transaction
100 * Ownership of cmd is taken, it must not be deleted by the caller.
101 * The command will constitute one item on the undo stack.
104 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
107 session.add_command(cmd);
111 /************** DIFF COMMAND ********************/
113 #define NOTE_DIFF_COMMAND_ELEMENT "NoteDiffCommand"
114 #define DIFF_NOTES_ELEMENT "ChangedNotes"
115 #define ADDED_NOTES_ELEMENT "AddedNotes"
116 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
117 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
118 #define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand"
119 #define DIFF_SYSEXES_ELEMENT "ChangedSysExes"
120 #define PATCH_CHANGE_DIFF_COMMAND_ELEMENT "PatchChangeDiffCommand"
121 #define ADDED_PATCH_CHANGES_ELEMENT "AddedPatchChanges"
122 #define REMOVED_PATCH_CHANGES_ELEMENT "RemovedPatchChanges"
123 #define DIFF_PATCH_CHANGES_ELEMENT "ChangedPatchChanges"
125 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
133 MidiModel::NoteDiffCommand::NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
134 : DiffCommand (m, "")
137 set_state (node, Stateful::loading_state_version);
141 MidiModel::NoteDiffCommand::add (const NotePtr note)
143 _removed_notes.remove(note);
144 _added_notes.push_back(note);
148 MidiModel::NoteDiffCommand::remove (const NotePtr note)
150 _added_notes.remove(note);
151 _removed_notes.push_back(note);
155 MidiModel::NoteDiffCommand::side_effect_remove (const NotePtr note)
157 side_effect_removals.insert (note);
161 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
170 if (new_value == note->note()) {
173 change.old_value = note->note();
176 if (new_value == note->velocity()) {
179 change.old_value = note->velocity();
182 if (new_value == note->channel()) {
185 change.old_value = note->channel();
190 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
194 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
200 change.property = prop;
201 change.new_value = new_value;
203 _changes.push_back (change);
207 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
218 fatal << "MidiModel::NoteDiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
222 if (Evoral::musical_time_equal (note->time(), new_time)) {
225 change.old_time = note->time();
228 if (Evoral::musical_time_equal (note->length(), new_time)) {
231 change.old_time = note->length();
236 change.property = prop;
237 change.new_time = new_time;
239 _changes.push_back (change);
242 MidiModel::NoteDiffCommand &
243 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand& other)
245 if (this == &other) {
249 if (_model != other._model) {
253 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
254 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
255 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
256 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
262 MidiModel::NoteDiffCommand::operator() ()
265 MidiModel::WriteLock lock(_model->edit_lock());
267 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
268 if (!_model->add_note_unlocked(*i)) {
269 /* failed to add it, so don't leave it in the removed list, to
270 avoid apparent errors on undo.
272 _removed_notes.remove (*i);
276 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
277 _model->remove_note_unlocked(*i);
280 /* notes we modify in a way that requires remove-then-add to maintain ordering */
281 set<NotePtr> temporary_removals;
283 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
284 Property prop = i->property;
287 if (temporary_removals.find (i->note) == temporary_removals.end()) {
288 _model->remove_note_unlocked (i->note);
289 temporary_removals.insert (i->note);
291 i->note->set_note (i->new_value);
295 if (temporary_removals.find (i->note) == temporary_removals.end()) {
296 _model->remove_note_unlocked (i->note);
297 temporary_removals.insert (i->note);
300 i->note->set_time (i->new_time);
304 if (temporary_removals.find (i->note) == temporary_removals.end()) {
305 _model->remove_note_unlocked (i->note);
306 temporary_removals.insert (i->note);
308 i->note->set_channel (i->new_value);
311 /* no remove-then-add required for these properties, since we do not index them
315 i->note->set_velocity (i->new_value);
319 i->note->set_length (i->new_time);
326 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
327 NoteDiffCommand side_effects (model(), "side effects");
328 if (_model->add_note_unlocked (*i, &side_effects)) {
329 /* The note was re-added ok */
330 *this += side_effects;
332 /* The note that we removed earlier could not be re-added. This change record
333 must say that the note was removed. It is an un-note.
336 /* We didn't change it... */
337 for (ChangeList::iterator j = _changes.begin(); j != _changes.end(); ) {
339 ChangeList::iterator k = j;
349 /* ...in fact, we removed it */
350 _removed_notes.push_back (*i);
354 if (!side_effect_removals.empty()) {
356 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
357 cerr << "\t" << *i << ' ' << **i << endl;
362 _model->ContentsChanged(); /* EMIT SIGNAL */
366 MidiModel::NoteDiffCommand::undo ()
369 MidiModel::WriteLock lock(_model->edit_lock());
371 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
372 _model->remove_note_unlocked(*i);
375 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
376 _model->add_note_unlocked(*i);
379 /* notes we modify in a way that requires remove-then-add to maintain ordering */
380 set<NotePtr> temporary_removals;
382 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
383 Property prop = i->property;
386 if (temporary_removals.find (i->note) == temporary_removals.end()) {
387 _model->remove_note_unlocked (i->note);
388 temporary_removals.insert (i->note);
390 i->note->set_note (i->old_value);
393 i->note->set_velocity (i->old_value);
396 if (temporary_removals.find (i->note) == temporary_removals.end()) {
397 _model->remove_note_unlocked (i->note);
398 temporary_removals.insert (i->note);
400 i->note->set_time (i->old_time);
403 i->note->set_length (i->old_time);
406 if (temporary_removals.find (i->note) == temporary_removals.end()) {
407 _model->remove_note_unlocked (i->note);
408 temporary_removals.insert (i->note);
410 i->note->set_channel (i->old_value);
415 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
416 _model->add_note_unlocked (*i);
419 /* finally add back notes that were removed by the "do". we don't care
420 about side effects here since the model should be back to its original
421 state once this is done.
424 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
425 _model->add_note_unlocked (*i);
429 _model->ContentsChanged(); /* EMIT SIGNAL */
433 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
435 XMLNode* xml_note = new XMLNode("note");
438 ostringstream id_str(ios::ate);
439 id_str << int(note->id());
440 xml_note->add_property("id", id_str.str());
444 ostringstream note_str(ios::ate);
445 note_str << int(note->note());
446 xml_note->add_property("note", note_str.str());
450 ostringstream channel_str(ios::ate);
451 channel_str << int(note->channel());
452 xml_note->add_property("channel", channel_str.str());
456 ostringstream time_str(ios::ate);
457 time_str << note->time();
458 xml_note->add_property("time", time_str.str());
462 ostringstream length_str(ios::ate);
463 length_str << note->length();
464 xml_note->add_property("length", length_str.str());
468 ostringstream velocity_str(ios::ate);
469 velocity_str << (unsigned int) note->velocity();
470 xml_note->add_property("velocity", velocity_str.str());
476 Evoral::Sequence<MidiModel::TimeType>::NotePtr
477 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
481 unsigned int channel;
484 unsigned int velocity;
487 if ((prop = xml_note->property("id")) != 0) {
488 istringstream id_str(prop->value());
491 error << "note information missing ID value" << endmsg;
495 if ((prop = xml_note->property("note")) != 0) {
496 istringstream note_str(prop->value());
499 warning << "note information missing note value" << endmsg;
503 if ((prop = xml_note->property("channel")) != 0) {
504 istringstream channel_str(prop->value());
505 channel_str >> channel;
507 warning << "note information missing channel" << endmsg;
511 if ((prop = xml_note->property("time")) != 0) {
512 istringstream time_str(prop->value());
515 warning << "note information missing time" << endmsg;
519 if ((prop = xml_note->property("length")) != 0) {
520 istringstream length_str(prop->value());
521 length_str >> length;
523 warning << "note information missing length" << endmsg;
527 if ((prop = xml_note->property("velocity")) != 0) {
528 istringstream velocity_str(prop->value());
529 velocity_str >> velocity;
531 warning << "note information missing velocity" << endmsg;
535 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
536 note_ptr->set_id (id);
542 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
544 XMLNode* xml_change = new XMLNode("Change");
546 /* first, the change itself */
548 xml_change->add_property ("property", enum_2_string (change.property));
551 ostringstream old_value_str (ios::ate);
552 if (change.property == StartTime || change.property == Length) {
553 old_value_str << change.old_time;
555 old_value_str << (unsigned int) change.old_value;
557 xml_change->add_property ("old", old_value_str.str());
561 ostringstream new_value_str (ios::ate);
562 if (change.property == StartTime || change.property == Length) {
563 new_value_str << change.new_time;
565 new_value_str << (unsigned int) change.new_value;
567 xml_change->add_property ("new", new_value_str.str());
570 ostringstream id_str;
571 id_str << change.note->id();
572 xml_change->add_property ("id", id_str.str());
577 MidiModel::NoteDiffCommand::NoteChange
578 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
583 if ((prop = xml_change->property("property")) != 0) {
584 change.property = (Property) string_2_enum (prop->value(), change.property);
586 fatal << "!!!" << endmsg;
590 if ((prop = xml_change->property ("id")) == 0) {
591 error << _("No NoteID found for note property change - ignored") << endmsg;
595 gint note_id = atoi (prop->value().c_str());
597 if ((prop = xml_change->property ("old")) != 0) {
598 istringstream old_str (prop->value());
599 if (change.property == StartTime || change.property == Length) {
600 old_str >> change.old_time;
602 int integer_value_so_that_istream_does_the_right_thing;
603 old_str >> integer_value_so_that_istream_does_the_right_thing;
604 change.old_value = integer_value_so_that_istream_does_the_right_thing;
607 fatal << "!!!" << endmsg;
611 if ((prop = xml_change->property ("new")) != 0) {
612 istringstream new_str (prop->value());
613 if (change.property == StartTime || change.property == Length) {
614 new_str >> change.new_time;
616 int integer_value_so_that_istream_does_the_right_thing;
617 new_str >> integer_value_so_that_istream_does_the_right_thing;
618 change.new_value = integer_value_so_that_istream_does_the_right_thing;
621 fatal << "!!!" << endmsg;
625 /* we must point at the instance of the note that is actually in the model.
626 so go look for it ...
629 change.note = _model->find_note (note_id);
632 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
640 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
642 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
648 _added_notes.clear();
649 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
651 XMLNodeList notes = added_notes->children();
652 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
653 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
659 _removed_notes.clear();
660 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
662 XMLNodeList notes = removed_notes->children();
663 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
664 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
672 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
675 XMLNodeList notes = changed_notes->children();
676 transform (notes.begin(), notes.end(), back_inserter(_changes),
677 boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
681 /* side effect removals caused by changes */
683 side_effect_removals.clear();
685 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
687 if (side_effect_notes) {
688 XMLNodeList notes = side_effect_notes->children();
689 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
690 side_effect_removals.insert (unmarshal_note (*n));
698 MidiModel::NoteDiffCommand::get_state ()
700 XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
701 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
703 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
704 for_each(_changes.begin(), _changes.end(),
706 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
707 boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
709 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
710 for_each(_added_notes.begin(), _added_notes.end(),
712 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
713 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
715 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
716 for_each(_removed_notes.begin(), _removed_notes.end(),
718 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
719 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
721 /* if this command had side-effects, store that state too
724 if (!side_effect_removals.empty()) {
725 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
726 for_each(side_effect_removals.begin(), side_effect_removals.end(),
728 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
729 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
732 return *diff_command;
735 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
736 : DiffCommand (m, "")
739 set_state (node, Stateful::loading_state_version);
743 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
748 change.property = Time;
749 change.old_time = s->time ();
750 change.new_time = new_time;
752 _changes.push_back (change);
756 MidiModel::SysExDiffCommand::operator() ()
759 MidiModel::WriteLock lock (_model->edit_lock ());
761 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
762 switch (i->property) {
764 i->sysex->set_time (i->new_time);
769 _model->ContentsChanged (); /* EMIT SIGNAL */
773 MidiModel::SysExDiffCommand::undo ()
776 MidiModel::WriteLock lock (_model->edit_lock ());
778 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
779 switch (i->property) {
781 i->sysex->set_time (i->old_time);
788 _model->ContentsChanged(); /* EMIT SIGNAL */
792 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
794 XMLNode* xml_change = new XMLNode ("Change");
796 /* first, the change itself */
798 xml_change->add_property ("property", enum_2_string (change.property));
801 ostringstream old_value_str (ios::ate);
802 old_value_str << change.old_time;
803 xml_change->add_property ("old", old_value_str.str());
807 ostringstream new_value_str (ios::ate);
808 new_value_str << change.new_time;
809 xml_change->add_property ("new", new_value_str.str());
812 ostringstream id_str;
813 id_str << change.sysex->id();
814 xml_change->add_property ("id", id_str.str());
819 MidiModel::SysExDiffCommand::Change
820 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
825 if ((prop = xml_change->property ("property")) != 0) {
826 change.property = (Property) string_2_enum (prop->value(), change.property);
828 fatal << "!!!" << endmsg;
832 if ((prop = xml_change->property ("id")) == 0) {
833 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
837 gint sysex_id = atoi (prop->value().c_str());
839 if ((prop = xml_change->property ("old")) != 0) {
840 istringstream old_str (prop->value());
841 old_str >> change.old_time;
843 fatal << "!!!" << endmsg;
847 if ((prop = xml_change->property ("new")) != 0) {
848 istringstream new_str (prop->value());
849 new_str >> change.new_time;
851 fatal << "!!!" << endmsg;
855 /* we must point at the instance of the sysex that is actually in the model.
856 so go look for it ...
859 change.sysex = _model->find_sysex (sysex_id);
862 warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg;
870 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
872 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
880 XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
882 if (changed_sysexes) {
883 XMLNodeList sysexes = changed_sysexes->children();
884 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
885 boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
893 MidiModel::SysExDiffCommand::get_state ()
895 XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
896 diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
898 XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
899 for_each (_changes.begin(), _changes.end(),
901 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
902 boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
904 return *diff_command;
907 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
908 : DiffCommand (m, name)
913 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
914 : DiffCommand (m, "")
917 set_state (node, Stateful::loading_state_version);
921 MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
923 _added.push_back (p);
927 MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
929 _removed.push_back (p);
933 MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
938 c.old_time = patch->time ();
941 _changes.push_back (c);
945 MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
948 c.property = Channel;
950 c.old_channel = patch->channel ();
951 c.new_channel = channel;
953 _changes.push_back (c);
957 MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
960 c.property = Program;
962 c.old_program = patch->program ();
963 c.new_program = program;
965 _changes.push_back (c);
969 MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
974 c.old_bank = patch->bank ();
977 _changes.push_back (c);
981 MidiModel::PatchChangeDiffCommand::operator() ()
984 MidiModel::WriteLock lock (_model->edit_lock ());
986 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
987 _model->add_patch_change_unlocked (*i);
990 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
991 _model->remove_patch_change_unlocked (*i);
994 set<PatchChangePtr> temporary_removals;
996 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
997 switch (i->property) {
999 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1000 _model->remove_patch_change_unlocked (i->patch);
1001 temporary_removals.insert (i->patch);
1003 i->patch->set_time (i->new_time);
1007 i->patch->set_channel (i->new_channel);
1011 i->patch->set_program (i->new_program);
1015 i->patch->set_bank (i->new_bank);
1020 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1021 _model->add_patch_change_unlocked (*i);
1025 _model->ContentsChanged (); /* EMIT SIGNAL */
1029 MidiModel::PatchChangeDiffCommand::undo ()
1032 MidiModel::WriteLock lock (_model->edit_lock());
1034 for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
1035 _model->remove_patch_change_unlocked (*i);
1038 for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
1039 _model->add_patch_change_unlocked (*i);
1042 set<PatchChangePtr> temporary_removals;
1044 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
1045 switch (i->property) {
1047 if (temporary_removals.find (i->patch) == temporary_removals.end()) {
1048 _model->remove_patch_change_unlocked (i->patch);
1049 temporary_removals.insert (i->patch);
1051 i->patch->set_time (i->old_time);
1055 i->patch->set_channel (i->old_channel);
1059 i->patch->set_program (i->old_program);
1063 i->patch->set_bank (i->old_bank);
1068 for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
1069 _model->add_patch_change_unlocked (*i);
1074 _model->ContentsChanged (); /* EMIT SIGNAL */
1078 MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
1080 XMLNode* n = new XMLNode ("patch-change");
1083 ostringstream s (ios::ate);
1084 s << int (p->id ());
1085 n->add_property ("id", s.str());
1089 ostringstream s (ios::ate);
1091 n->add_property ("time", s.str ());
1095 ostringstream s (ios::ate);
1096 s << int (p->channel ());
1097 n->add_property ("channel", s.str ());
1101 ostringstream s (ios::ate);
1102 s << int (p->program ());
1103 n->add_property ("program", s.str ());
1107 ostringstream s (ios::ate);
1108 s << int (p->bank ());
1109 n->add_property ("bank", s.str ());
1116 MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
1118 XMLNode* n = new XMLNode (X_("Change"));
1120 n->add_property (X_("property"), enum_2_string (c.property));
1123 ostringstream s (ios::ate);
1124 if (c.property == Time) {
1126 } else if (c.property == Channel) {
1128 } else if (c.property == Program) {
1129 s << int (c.old_program);
1130 } else if (c.property == Bank) {
1134 n->add_property (X_("old"), s.str ());
1138 ostringstream s (ios::ate);
1140 if (c.property == Time) {
1142 } else if (c.property == Channel) {
1144 } else if (c.property == Program) {
1145 s << int (c.new_program);
1146 } else if (c.property == Bank) {
1150 n->add_property (X_("new"), s.str ());
1155 s << c.patch->id ();
1156 n->add_property ("id", s.str ());
1162 MidiModel::PatchChangePtr
1163 MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
1166 Evoral::event_id_t id;
1167 Evoral::MusicalTime time = 0;
1168 uint8_t channel = 0;
1169 uint8_t program = 0;
1172 if ((prop = n->property ("id")) != 0) {
1173 istringstream s (prop->value());
1177 if ((prop = n->property ("time")) != 0) {
1178 istringstream s (prop->value ());
1182 if ((prop = n->property ("channel")) != 0) {
1183 istringstream s (prop->value ());
1187 if ((prop = n->property ("program")) != 0) {
1188 istringstream s (prop->value ());
1192 if ((prop = n->property ("bank")) != 0) {
1193 istringstream s (prop->value ());
1197 PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
1202 MidiModel::PatchChangeDiffCommand::Change
1203 MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
1208 prop = n->property ("property");
1210 c.property = (Property) string_2_enum (prop->value(), c.property);
1212 prop = n->property ("id");
1214 Evoral::event_id_t const id = atoi (prop->value().c_str());
1216 prop = n->property ("old");
1219 istringstream s (prop->value ());
1220 if (c.property == Time) {
1222 } else if (c.property == Channel) {
1224 } else if (c.property == Program) {
1226 } else if (c.property == Bank) {
1231 prop = n->property ("new");
1234 istringstream s (prop->value ());
1235 if (c.property == Time) {
1237 } else if (c.property == Channel) {
1239 } else if (c.property == Program) {
1241 } else if (c.property == Bank) {
1246 c.patch = _model->find_patch_change (id);
1253 MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
1255 if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
1260 XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
1262 XMLNodeList p = added->children ();
1263 transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1267 XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
1269 XMLNodeList p = removed->children ();
1270 transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1274 XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
1276 XMLNodeList p = changed->children ();
1277 transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
1284 MidiModel::PatchChangeDiffCommand::get_state ()
1286 XMLNode* diff_command = new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT);
1287 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
1289 XMLNode* added = diff_command->add_child (ADDED_PATCH_CHANGES_ELEMENT);
1290 for_each (_added.begin(), _added.end(),
1292 boost::bind (&XMLNode::add_child_nocopy, added, _1),
1293 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1297 XMLNode* removed = diff_command->add_child (REMOVED_PATCH_CHANGES_ELEMENT);
1298 for_each (_removed.begin(), _removed.end(),
1300 boost::bind (&XMLNode::add_child_nocopy, removed, _1),
1301 boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
1305 XMLNode* changes = diff_command->add_child (DIFF_PATCH_CHANGES_ELEMENT);
1306 for_each (_changes.begin(), _changes.end(),
1308 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
1309 boost::bind (&PatchChangeDiffCommand::marshal_change, this, _1)
1313 return *diff_command;
1316 /** Write all of the model to a MidiSource (i.e. save the model).
1317 * This is different from manually using read to write to a source in that
1318 * note off events are written regardless of the track mode. This is so the
1319 * user can switch a recorded track (with note durations from some instrument)
1320 * to percussive, save, reload, then switch it back to sustained without
1321 * destroying the original note durations.
1323 * Similarly, control events are written without interpolation (as with the
1327 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
1329 ReadLock lock(read_lock());
1331 const bool old_percussive = percussive();
1332 set_percussive(false);
1334 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1337 source->drop_model();
1338 source->mark_streaming_midi_write_started (note_mode());
1340 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1341 source->append_event_unlocked_beats(*i);
1344 set_percussive(old_percussive);
1345 source->mark_streaming_write_completed();
1352 /** very similar to ::write_to() but writes to the model's own
1353 existing midi_source, without making it call MidiSource::drop_model().
1354 the caller is a MidiSource that needs to catch up with the state
1358 MidiModel::sync_to_source ()
1360 ReadLock lock(read_lock());
1362 const bool old_percussive = percussive();
1363 set_percussive(false);
1365 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1368 ms->mark_streaming_midi_write_started (note_mode());
1370 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1371 ms->append_event_unlocked_beats(*i);
1374 set_percussive (old_percussive);
1375 ms->mark_streaming_write_completed ();
1382 /** Write part or all of the model to a MidiSource (i.e. save the model).
1383 * This is different from manually using read to write to a source in that
1384 * note off events are written regardless of the track mode. This is so the
1385 * user can switch a recorded track (with note durations from some instrument)
1386 * to percussive, save, reload, then switch it back to sustained without
1387 * destroying the original note durations.
1390 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
1392 ReadLock lock(read_lock());
1393 MidiStateTracker mst;
1394 Evoral::MusicalTime extra_note_on_time = end_time;
1396 const bool old_percussive = percussive();
1397 set_percussive(false);
1399 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1402 source->drop_model();
1403 source->mark_streaming_midi_write_started (note_mode());
1405 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
1406 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
1408 if (ev.time() >= begin_time && ev.time() < end_time) {
1410 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
1411 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
1418 if (mev->is_note_off()) {
1420 if (!mst.active (mev->note(), mev->channel())) {
1422 /* add a note-on at the start of the range we're writing
1423 to the file. velocity is just an arbitary reasonable value.
1426 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
1427 on.set_type (mev->type());
1428 on.set_note (mev->note());
1429 on.set_channel (mev->channel());
1430 on.set_velocity (mev->velocity());
1432 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
1433 source->append_event_unlocked_beats (on);
1434 mst.add (on.note(), on.channel());
1436 extra_note_on_time += 1.0/128.0;
1439 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
1440 source->append_event_unlocked_beats (*i);
1441 mst.remove (mev->note(), mev->channel());
1444 } else if (mev->is_note_on()) {
1445 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
1446 mst.add (mev->note(), mev->channel());
1447 source->append_event_unlocked_beats(*i);
1450 cerr << "MIDI other event type\n";
1451 source->append_event_unlocked_beats(*i);
1456 mst.resolve_notes (*source, end_time);
1458 set_percussive(old_percussive);
1459 source->mark_streaming_write_completed();
1467 MidiModel::get_state()
1469 XMLNode *node = new XMLNode("MidiModel");
1473 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1474 MidiModel::find_note (NotePtr other)
1476 Notes::iterator l = notes().lower_bound(other);
1478 if (l != notes().end()) {
1479 for (; (*l)->time() == other->time(); ++l) {
1480 /* NB: compare note contents, not note pointers.
1481 If "other" was a ptr to a note already in
1482 the model, we wouldn't be looking for it,
1485 if (**l == *other) {
1494 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1495 MidiModel::find_note (gint note_id)
1497 /* used only for looking up notes when reloading history from disk,
1498 so we don't care about performance *too* much.
1501 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1502 if ((*l)->id() == note_id) {
1510 MidiModel::PatchChangePtr
1511 MidiModel::find_patch_change (Evoral::event_id_t id)
1513 for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
1514 if ((*i)->id() == id) {
1519 return PatchChangePtr ();
1522 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1523 MidiModel::find_sysex (gint sysex_id)
1525 /* used only for looking up notes when reloading history from disk,
1526 so we don't care about performance *too* much.
1529 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1530 if ((*l)->id() == sysex_id) {
1535 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1538 /** Lock and invalidate the source.
1539 * This should be used by commands and editing things
1541 MidiModel::WriteLock
1542 MidiModel::edit_lock()
1544 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1547 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock (ms->mutex());
1548 ms->invalidate(); // Release cached iterator's read lock on model
1549 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1552 /** Lock just the model, the source lock must already be held.
1553 * This should only be called from libardour/evoral places
1555 MidiModel::WriteLock
1556 MidiModel::write_lock()
1558 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1561 assert (!ms->mutex().trylock ());
1562 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
1566 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1568 using namespace Evoral;
1570 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1574 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1576 TimeType sa = note->time();
1577 TimeType ea = note->end_time();
1579 const Pitches& p (pitches (note->channel()));
1580 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
1581 set<NotePtr> to_be_deleted;
1582 bool set_note_length = false;
1583 bool set_note_time = false;
1584 TimeType note_time = note->time();
1585 TimeType note_length = note->length();
1587 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1589 for (Pitches::const_iterator i = p.lower_bound (search_note);
1590 i != p.end() && (*i)->note() == note->note(); ++i) {
1592 TimeType sb = (*i)->time();
1593 TimeType eb = (*i)->end_time();
1594 OverlapType overlap = OverlapNone;
1596 if ((sb > sa) && (eb <= ea)) {
1597 overlap = OverlapInternal;
1598 } else if ((eb >= sa) && (eb <= ea)) {
1599 overlap = OverlapStart;
1600 } else if ((sb > sa) && (sb <= ea)) {
1601 overlap = OverlapEnd;
1602 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1603 overlap = OverlapExternal;
1609 DEBUG_TRACE (DEBUG::Sequence, string_compose (
1610 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1611 enum_2_string(overlap), sa, ea, sb, eb));
1613 if (insert_merge_policy() == InsertMergeReject) {
1614 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1620 cerr << "OverlapStart\n";
1621 /* existing note covers start of new note */
1622 switch (insert_merge_policy()) {
1623 case InsertMergeReplace:
1624 to_be_deleted.insert (*i);
1626 case InsertMergeTruncateExisting:
1628 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1630 (*i)->set_length (note->time() - (*i)->time());
1632 case InsertMergeTruncateAddition:
1633 set_note_time = true;
1634 set_note_length = true;
1635 note_time = (*i)->time() + (*i)->length();
1636 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1638 case InsertMergeExtend:
1640 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1642 (*i)->set_length (note->end_time() - (*i)->time());
1643 return -1; /* do not add the new note */
1653 cerr << "OverlapEnd\n";
1654 /* existing note covers end of new note */
1655 switch (insert_merge_policy()) {
1656 case InsertMergeReplace:
1657 to_be_deleted.insert (*i);
1660 case InsertMergeTruncateExisting:
1661 /* resetting the start time of the existing note
1662 is a problem because of time ordering.
1666 case InsertMergeTruncateAddition:
1667 set_note_length = true;
1668 note_length = min (note_length, ((*i)->time() - note->time()));
1671 case InsertMergeExtend:
1672 /* we can't reset the time of the existing note because
1673 that will corrupt time ordering. So remove the
1674 existing note and change the position/length
1675 of the new note (which has not been added yet)
1677 to_be_deleted.insert (*i);
1678 set_note_length = true;
1679 note_length = min (note_length, (*i)->end_time() - note->time());
1688 case OverlapExternal:
1689 cerr << "OverlapExt\n";
1690 /* existing note overlaps all the new note */
1691 switch (insert_merge_policy()) {
1692 case InsertMergeReplace:
1693 to_be_deleted.insert (*i);
1695 case InsertMergeTruncateExisting:
1696 case InsertMergeTruncateAddition:
1697 case InsertMergeExtend:
1698 /* cannot add in this case */
1707 case OverlapInternal:
1708 cerr << "OverlapInt\n";
1709 /* new note fully overlaps an existing note */
1710 switch (insert_merge_policy()) {
1711 case InsertMergeReplace:
1712 case InsertMergeTruncateExisting:
1713 case InsertMergeTruncateAddition:
1714 case InsertMergeExtend:
1715 /* delete the existing note, the new one will cover it */
1716 to_be_deleted.insert (*i);
1732 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1733 remove_note_unlocked (*i);
1736 cmd->side_effect_remove (*i);
1740 if (set_note_time) {
1742 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1744 note->set_time (note_time);
1747 if (set_note_length) {
1749 cmd->change (note, NoteDiffCommand::Length, note_length);
1751 note->set_length (note_length);
1758 MidiModel::insert_merge_policy () const
1760 /* XXX ultimately this should be a per-track or even per-model policy */
1761 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1764 return ms->session().config.get_insert_merge_policy ();
1768 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1770 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1776 _midi_source_connections.drop_connections ();
1780 s->InterpolationChanged.connect_same_thread (
1781 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1784 s->AutomationStateChanged.connect_same_thread (
1785 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1789 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1790 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1791 * appropriate ControlList.
1793 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1794 * or the other is listened to by the GUI.
1797 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1799 Glib::Mutex::Lock lm (_control_lock);
1800 control(p)->list()->set_interpolation (s);
1803 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1804 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1807 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1809 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1812 ms->set_interpolation_of (p, s);
1816 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1818 Glib::Mutex::Lock lm (_control_lock);
1819 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1820 al->set_automation_state (s);
1824 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1826 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1828 ms->set_automation_state_of (p, s);
1831 boost::shared_ptr<Evoral::Control>
1832 MidiModel::control_factory (Evoral::Parameter const & p)
1834 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1836 /* Set up newly created control's lists to the appropriate interpolation and
1837 automation state from our source.
1840 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1843 c->list()->set_interpolation (ms->interpolation_of (p));
1845 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1848 al->set_automation_state (ms->automation_state_of (p));
1853 boost::shared_ptr<const MidiSource>
1854 MidiModel::midi_source ()
1856 return _midi_source.lock ();
1859 /** Moves notes, controllers and sys-ex to insert silence at the start of the model.
1860 * Adds commands to the session's current undo stack to reflect the movements.
1863 MidiModel::insert_silence_at_start (TimeType t)
1865 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1870 if (!notes().empty ()) {
1871 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1873 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1874 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1877 apply_command_as_subcommand (s->session(), c);
1882 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1883 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1884 XMLNode& before = ac->alist()->get_state ();
1885 i->second->list()->shift (0, t);
1886 XMLNode& after = ac->alist()->get_state ();
1887 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1892 if (!sysexes().empty()) {
1893 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1895 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1896 c->change (*i, (*i)->time() + t);
1899 apply_command_as_subcommand (s->session(), c);