2 Copyright (C) 2007 Paul Davis
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(), ms->timeline_position ());
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(), ms->timeline_position());
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(), ms->timeline_position());
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 ("\toverlap is %1 for (%2,%3) vs (%4,%5)\n", enum_2_string(overlap),
1612 if (insert_merge_policy() == InsertMergeReject) {
1613 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1619 cerr << "OverlapStart\n";
1620 /* existing note covers start of new note */
1621 switch (insert_merge_policy()) {
1622 case InsertMergeReplace:
1623 to_be_deleted.insert (*i);
1625 case InsertMergeTruncateExisting:
1627 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1629 (*i)->set_length (note->time() - (*i)->time());
1631 case InsertMergeTruncateAddition:
1632 set_note_time = true;
1633 set_note_length = true;
1634 note_time = (*i)->time() + (*i)->length();
1635 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1637 case InsertMergeExtend:
1639 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1641 (*i)->set_length (note->end_time() - (*i)->time());
1642 return -1; /* do not add the new note */
1652 cerr << "OverlapEnd\n";
1653 /* existing note covers end of new note */
1654 switch (insert_merge_policy()) {
1655 case InsertMergeReplace:
1656 to_be_deleted.insert (*i);
1659 case InsertMergeTruncateExisting:
1660 /* resetting the start time of the existing note
1661 is a problem because of time ordering.
1665 case InsertMergeTruncateAddition:
1666 set_note_length = true;
1667 note_length = min (note_length, ((*i)->time() - note->time()));
1670 case InsertMergeExtend:
1671 /* we can't reset the time of the existing note because
1672 that will corrupt time ordering. So remove the
1673 existing note and change the position/length
1674 of the new note (which has not been added yet)
1676 to_be_deleted.insert (*i);
1677 set_note_length = true;
1678 note_length = min (note_length, (*i)->end_time() - note->time());
1687 case OverlapExternal:
1688 cerr << "OverlapExt\n";
1689 /* existing note overlaps all the new note */
1690 switch (insert_merge_policy()) {
1691 case InsertMergeReplace:
1692 to_be_deleted.insert (*i);
1694 case InsertMergeTruncateExisting:
1695 case InsertMergeTruncateAddition:
1696 case InsertMergeExtend:
1697 /* cannot add in this case */
1706 case OverlapInternal:
1707 cerr << "OverlapInt\n";
1708 /* new note fully overlaps an existing note */
1709 switch (insert_merge_policy()) {
1710 case InsertMergeReplace:
1711 case InsertMergeTruncateExisting:
1712 case InsertMergeTruncateAddition:
1713 case InsertMergeExtend:
1714 /* delete the existing note, the new one will cover it */
1715 to_be_deleted.insert (*i);
1731 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1732 remove_note_unlocked (*i);
1735 cmd->side_effect_remove (*i);
1739 if (set_note_time) {
1741 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1743 note->set_time (note_time);
1746 if (set_note_length) {
1748 cmd->change (note, NoteDiffCommand::Length, note_length);
1750 note->set_length (note_length);
1757 MidiModel::insert_merge_policy () const
1759 /* XXX ultimately this should be a per-track or even per-model policy */
1760 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1763 return ms->session().config.get_insert_merge_policy ();
1767 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1769 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1775 _midi_source_connections.drop_connections ();
1779 s->InterpolationChanged.connect_same_thread (
1780 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1783 s->AutomationStateChanged.connect_same_thread (
1784 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1788 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1789 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1790 * appropriate ControlList.
1792 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1793 * or the other is listened to by the GUI.
1796 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1798 Glib::Mutex::Lock lm (_control_lock);
1799 control(p)->list()->set_interpolation (s);
1802 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1803 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1806 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1808 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1811 ms->set_interpolation_of (p, s);
1815 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1817 Glib::Mutex::Lock lm (_control_lock);
1818 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1819 al->set_automation_state (s);
1823 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1825 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1827 ms->set_automation_state_of (p, s);
1830 boost::shared_ptr<Evoral::Control>
1831 MidiModel::control_factory (Evoral::Parameter const & p)
1833 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1835 /* Set up newly created control's lists to the appropriate interpolation and
1836 automation state from our source.
1839 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1842 c->list()->set_interpolation (ms->interpolation_of (p));
1844 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1847 al->set_automation_state (ms->automation_state_of (p));
1852 boost::shared_ptr<const MidiSource>
1853 MidiModel::midi_source ()
1855 return _midi_source.lock ();
1858 /** Moves notes, controllers and sys-ex to insert silence at the start of the model.
1859 * Adds commands to the session's current undo stack to reflect the movements.
1862 MidiModel::insert_silence_at_start (TimeType t)
1864 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1869 if (!notes().empty ()) {
1870 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1872 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1873 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1876 apply_command_as_subcommand (s->session(), c);
1881 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1882 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1883 XMLNode& before = ac->alist()->get_state ();
1884 i->second->list()->shift (0, t);
1885 XMLNode& after = ac->alist()->get_state ();
1886 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1891 if (!sysexes().empty()) {
1892 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1894 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1895 c->change (*i, (*i)->time() + t);
1898 apply_command_as_subcommand (s->session(), c);