Differentiate between pitch-shift (for audio) and transpose (for MIDI). Fixes #3940.
[ardour2.git] / libs / ardour / midi_model.cc
blobe1a955d43c833d18cf9462fbeb54dbe8822abd39
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 "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"
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
42 MidiModel::MidiModel (boost::shared_ptr<MidiSource> s)
43 : AutomatableSequence<TimeType>(s->session())
45 set_midi_source (s);
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 ();
58 assert (ms);
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 ();
68 assert (ms);
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 ();
78 assert (ms);
80 return new PatchChangeDiffCommand (ms->model(), name);
84 /** Apply a command.
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.
89 void
90 MidiModel::apply_command(Session& session, Command* cmd)
92 session.begin_reversible_command(cmd->name());
93 (*cmd)();
94 session.commit_reversible_command(cmd);
95 set_edited(true);
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.
103 void
104 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
106 (*cmd)();
107 session.add_command(cmd);
108 set_edited(true);
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)
126 : Command (name)
127 , _model (m)
128 , _name (name)
130 assert(_model);
133 MidiModel::NoteDiffCommand::NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
134 : DiffCommand (m, "")
136 assert (_model);
137 set_state (node, Stateful::loading_state_version);
140 void
141 MidiModel::NoteDiffCommand::add (const NotePtr note)
143 _removed_notes.remove(note);
144 _added_notes.push_back(note);
147 void
148 MidiModel::NoteDiffCommand::remove (const NotePtr note)
150 _added_notes.remove(note);
151 _removed_notes.push_back(note);
154 void
155 MidiModel::NoteDiffCommand::side_effect_remove (const NotePtr note)
157 side_effect_removals.insert (note);
160 void
161 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
162 uint8_t new_value)
164 assert (note);
166 NoteChange change;
168 switch (prop) {
169 case NoteNumber:
170 if (new_value == note->note()) {
171 return;
173 change.old_value = note->note();
174 break;
175 case Velocity:
176 if (new_value == note->velocity()) {
177 return;
179 change.old_value = note->velocity();
180 break;
181 case Channel:
182 if (new_value == note->channel()) {
183 return;
185 change.old_value = note->channel();
186 break;
189 case StartTime:
190 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
191 /*NOTREACHED*/
192 break;
193 case Length:
194 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
195 /*NOTREACHED*/
196 break;
199 change.note = note;
200 change.property = prop;
201 change.new_value = new_value;
203 _changes.push_back (change);
206 void
207 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
208 TimeType new_time)
210 assert (note);
212 NoteChange change;
214 switch (prop) {
215 case NoteNumber:
216 case Channel:
217 case Velocity:
218 fatal << "MidiModel::NoteDiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
219 break;
221 case StartTime:
222 if (Evoral::musical_time_equal (note->time(), new_time)) {
223 return;
225 change.old_time = note->time();
226 break;
227 case Length:
228 if (Evoral::musical_time_equal (note->length(), new_time)) {
229 return;
231 change.old_time = note->length();
232 break;
235 change.note = note;
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) {
246 return *this;
249 if (_model != other._model) {
250 return *this;
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());
258 return *this;
261 void
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;
285 switch (prop) {
286 case NoteNumber:
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);
292 break;
294 case StartTime:
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);
301 break;
303 case Channel:
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);
309 break;
311 /* no remove-then-add required for these properties, since we do not index them
314 case Velocity:
315 i->note->set_velocity (i->new_value);
316 break;
318 case Length:
319 i->note->set_length (i->new_time);
320 break;
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;
331 } else {
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;
340 ++k;
342 if (*i == j->note) {
343 _changes.erase (j);
346 j = k;
349 /* ...in fact, we removed it */
350 _removed_notes.push_back (*i);
354 if (!side_effect_removals.empty()) {
355 cerr << "SER: \n";
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 */
365 void
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;
384 switch (prop) {
385 case NoteNumber:
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);
391 break;
392 case Velocity:
393 i->note->set_velocity (i->old_value);
394 break;
395 case StartTime:
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);
401 break;
402 case Length:
403 i->note->set_length (i->old_time);
404 break;
405 case Channel:
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);
411 break;
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 */
432 XMLNode&
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());
473 return *xml_note;
476 Evoral::Sequence<MidiModel::TimeType>::NotePtr
477 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
479 unsigned int note;
480 XMLProperty* prop;
481 unsigned int channel;
482 unsigned int time;
483 unsigned int length;
484 unsigned int velocity;
485 gint id;
487 if ((prop = xml_note->property("id")) != 0) {
488 istringstream id_str(prop->value());
489 id_str >> id;
490 } else {
491 error << "note information missing ID value" << endmsg;
492 id = -1;
495 if ((prop = xml_note->property("note")) != 0) {
496 istringstream note_str(prop->value());
497 note_str >> note;
498 } else {
499 warning << "note information missing note value" << endmsg;
500 note = 127;
503 if ((prop = xml_note->property("channel")) != 0) {
504 istringstream channel_str(prop->value());
505 channel_str >> channel;
506 } else {
507 warning << "note information missing channel" << endmsg;
508 channel = 0;
511 if ((prop = xml_note->property("time")) != 0) {
512 istringstream time_str(prop->value());
513 time_str >> time;
514 } else {
515 warning << "note information missing time" << endmsg;
516 time = 0;
519 if ((prop = xml_note->property("length")) != 0) {
520 istringstream length_str(prop->value());
521 length_str >> length;
522 } else {
523 warning << "note information missing length" << endmsg;
524 length = 1;
527 if ((prop = xml_note->property("velocity")) != 0) {
528 istringstream velocity_str(prop->value());
529 velocity_str >> velocity;
530 } else {
531 warning << "note information missing velocity" << endmsg;
532 velocity = 127;
535 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
536 note_ptr->set_id (id);
538 return note_ptr;
541 XMLNode&
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;
554 } else {
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;
564 } else {
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());
574 return *xml_change;
577 MidiModel::NoteDiffCommand::NoteChange
578 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
580 XMLProperty* prop;
581 NoteChange change;
583 if ((prop = xml_change->property("property")) != 0) {
584 change.property = (Property) string_2_enum (prop->value(), change.property);
585 } else {
586 fatal << "!!!" << endmsg;
587 /*NOTREACHED*/
590 if ((prop = xml_change->property ("id")) == 0) {
591 error << _("No NoteID found for note property change - ignored") << endmsg;
592 return change;
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;
601 } else {
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;
606 } else {
607 fatal << "!!!" << endmsg;
608 /*NOTREACHED*/
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;
615 } else {
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;
620 } else {
621 fatal << "!!!" << endmsg;
622 /*NOTREACHED*/
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);
631 if (!change.note) {
632 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
633 return change;
636 return change;
640 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
642 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
643 return 1;
646 /* additions */
648 _added_notes.clear();
649 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
650 if (added_notes) {
651 XMLNodeList notes = added_notes->children();
652 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
653 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
657 /* removals */
659 _removed_notes.clear();
660 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
661 if (removed_notes) {
662 XMLNodeList notes = removed_notes->children();
663 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
664 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
668 /* changes */
670 _changes.clear();
672 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
674 if (changed_notes) {
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));
694 return 0;
697 XMLNode&
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(),
705 boost::bind (
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(),
711 boost::bind(
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(),
717 boost::bind (
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(),
727 boost::bind (
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, "")
738 assert (_model);
739 set_state (node, Stateful::loading_state_version);
742 void
743 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
745 Change change;
747 change.sysex = s;
748 change.property = Time;
749 change.old_time = s->time ();
750 change.new_time = new_time;
752 _changes.push_back (change);
755 void
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) {
763 case Time:
764 i->sysex->set_time (i->new_time);
769 _model->ContentsChanged (); /* EMIT SIGNAL */
772 void
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) {
780 case Time:
781 i->sysex->set_time (i->old_time);
782 break;
788 _model->ContentsChanged(); /* EMIT SIGNAL */
791 XMLNode&
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());
816 return *xml_change;
819 MidiModel::SysExDiffCommand::Change
820 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
822 XMLProperty* prop;
823 Change change;
825 if ((prop = xml_change->property ("property")) != 0) {
826 change.property = (Property) string_2_enum (prop->value(), change.property);
827 } else {
828 fatal << "!!!" << endmsg;
829 /*NOTREACHED*/
832 if ((prop = xml_change->property ("id")) == 0) {
833 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
834 return change;
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;
842 } else {
843 fatal << "!!!" << endmsg;
844 /*NOTREACHED*/
847 if ((prop = xml_change->property ("new")) != 0) {
848 istringstream new_str (prop->value());
849 new_str >> change.new_time;
850 } else {
851 fatal << "!!!" << endmsg;
852 /*NOTREACHED*/
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);
861 if (!change.sysex) {
862 warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg;
863 return change;
866 return change;
870 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
872 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
873 return 1;
876 /* changes */
878 _changes.clear();
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));
889 return 0;
892 XMLNode&
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(),
900 boost::bind (
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)
910 assert (_model);
913 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
914 : DiffCommand (m, "")
916 assert (_model);
917 set_state (node, Stateful::loading_state_version);
920 void
921 MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
923 _added.push_back (p);
926 void
927 MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
929 _removed.push_back (p);
932 void
933 MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
935 Change c;
936 c.property = Time;
937 c.patch = patch;
938 c.old_time = patch->time ();
939 c.new_time = t;
941 _changes.push_back (c);
944 void
945 MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
947 Change c;
948 c.property = Channel;
949 c.patch = patch;
950 c.old_channel = patch->channel ();
951 c.new_channel = channel;
953 _changes.push_back (c);
956 void
957 MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
959 Change c;
960 c.property = Program;
961 c.patch = patch;
962 c.old_program = patch->program ();
963 c.new_program = program;
965 _changes.push_back (c);
968 void
969 MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
971 Change c;
972 c.property = Bank;
973 c.patch = patch;
974 c.old_bank = patch->bank ();
975 c.new_bank = bank;
977 _changes.push_back (c);
980 void
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) {
998 case Time:
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);
1004 break;
1006 case Channel:
1007 i->patch->set_channel (i->new_channel);
1008 break;
1010 case Program:
1011 i->patch->set_program (i->new_program);
1012 break;
1014 case Bank:
1015 i->patch->set_bank (i->new_bank);
1016 break;
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 */
1028 void
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) {
1046 case Time:
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);
1052 break;
1054 case Channel:
1055 i->patch->set_channel (i->old_channel);
1056 break;
1058 case Program:
1059 i->patch->set_program (i->old_program);
1060 break;
1062 case Bank:
1063 i->patch->set_bank (i->old_bank);
1064 break;
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 */
1077 XMLNode &
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);
1090 s << p->time ();
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 ());
1112 return *n;
1115 XMLNode&
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) {
1125 s << c.old_time;
1126 } else if (c.property == Channel) {
1127 s << c.old_channel;
1128 } else if (c.property == Program) {
1129 s << int (c.old_program);
1130 } else if (c.property == Bank) {
1131 s << c.old_bank;
1134 n->add_property (X_("old"), s.str ());
1138 ostringstream s (ios::ate);
1140 if (c.property == Time) {
1141 s << c.new_time;
1142 } else if (c.property == Channel) {
1143 s << c.new_channel;
1144 } else if (c.property == Program) {
1145 s << int (c.new_program);
1146 } else if (c.property == Bank) {
1147 s << c.new_bank;
1150 n->add_property (X_("new"), s.str ());
1154 ostringstream s;
1155 s << c.patch->id ();
1156 n->add_property ("id", s.str ());
1159 return *n;
1162 MidiModel::PatchChangePtr
1163 MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
1165 XMLProperty* prop;
1166 Evoral::event_id_t id;
1167 Evoral::MusicalTime time = 0;
1168 uint8_t channel = 0;
1169 uint8_t program = 0;
1170 int bank = 0;
1172 if ((prop = n->property ("id")) != 0) {
1173 istringstream s (prop->value());
1174 s >> id;
1177 if ((prop = n->property ("time")) != 0) {
1178 istringstream s (prop->value ());
1179 s >> time;
1182 if ((prop = n->property ("channel")) != 0) {
1183 istringstream s (prop->value ());
1184 s >> channel;
1187 if ((prop = n->property ("program")) != 0) {
1188 istringstream s (prop->value ());
1189 s >> program;
1192 if ((prop = n->property ("bank")) != 0) {
1193 istringstream s (prop->value ());
1194 s >> bank;
1197 PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
1198 p->set_id (id);
1199 return p;
1202 MidiModel::PatchChangeDiffCommand::Change
1203 MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
1205 XMLProperty* prop;
1206 Change c;
1208 prop = n->property ("property");
1209 assert (prop);
1210 c.property = (Property) string_2_enum (prop->value(), c.property);
1212 prop = n->property ("id");
1213 assert (prop);
1214 Evoral::event_id_t const id = atoi (prop->value().c_str());
1216 prop = n->property ("old");
1217 assert (prop);
1219 istringstream s (prop->value ());
1220 if (c.property == Time) {
1221 s >> c.old_time;
1222 } else if (c.property == Channel) {
1223 s >> c.old_channel;
1224 } else if (c.property == Program) {
1225 s >> c.old_program;
1226 } else if (c.property == Bank) {
1227 s >> c.old_bank;
1231 prop = n->property ("new");
1232 assert (prop);
1234 istringstream s (prop->value ());
1235 if (c.property == Time) {
1236 s >> c.new_time;
1237 } else if (c.property == Channel) {
1238 s >> c.new_channel;
1239 } else if (c.property == Program) {
1240 s >> c.new_program;
1241 } else if (c.property == Bank) {
1242 s >> c.new_bank;
1246 c.patch = _model->find_patch_change (id);
1247 assert (c.patch);
1249 return c;
1253 MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
1255 if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
1256 return 1;
1259 _added.clear ();
1260 XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
1261 if (added) {
1262 XMLNodeList p = added->children ();
1263 transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1266 _removed.clear ();
1267 XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
1268 if (removed) {
1269 XMLNodeList p = removed->children ();
1270 transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
1273 _changes.clear ();
1274 XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
1275 if (changed) {
1276 XMLNodeList p = changed->children ();
1277 transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
1280 return 0;
1283 XMLNode &
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(),
1291 boost::bind (
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(),
1299 boost::bind (
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(),
1307 boost::bind (
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
1324 * `Discrete' mode).
1326 bool
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 ();
1335 assert (ms);
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();
1347 set_edited(false);
1349 return true;
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
1355 of the model.
1357 bool
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 ();
1366 assert (ms);
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 ();
1377 set_edited (false);
1379 return true;
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.
1389 bool
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 ();
1400 assert (ms);
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);
1413 if (!mev) {
1414 continue;
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());
1435 mst.dump (cerr);
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());
1442 mst.dump (cerr);
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);
1448 mst.dump (cerr);
1449 } else {
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();
1461 set_edited(false);
1463 return true;
1466 XMLNode&
1467 MidiModel::get_state()
1469 XMLNode *node = new XMLNode("MidiModel");
1470 return *node;
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,
1483 would we now?
1485 if (**l == *other) {
1486 return *l;
1491 return NotePtr();
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) {
1503 return *l;
1507 return NotePtr();
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) {
1515 return *i;
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) {
1531 return *l;
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 ();
1545 assert (ms);
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 ();
1559 assert (ms);
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) {
1571 return 0;
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;
1604 } else {
1605 /* no overlap */
1606 continue;
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));
1615 return -1;
1618 switch (overlap) {
1619 case OverlapStart:
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);
1625 break;
1626 case InsertMergeTruncateExisting:
1627 if (cmd) {
1628 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1630 (*i)->set_length (note->time() - (*i)->time());
1631 break;
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()));
1637 break;
1638 case InsertMergeExtend:
1639 if (cmd) {
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 */
1644 break;
1645 default:
1646 /*NOTREACHED*/
1647 /* stupid gcc */
1648 break;
1650 break;
1652 case OverlapEnd:
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);
1658 break;
1660 case InsertMergeTruncateExisting:
1661 /* resetting the start time of the existing note
1662 is a problem because of time ordering.
1664 break;
1666 case InsertMergeTruncateAddition:
1667 set_note_length = true;
1668 note_length = min (note_length, ((*i)->time() - note->time()));
1669 break;
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());
1680 break;
1681 default:
1682 /*NOTREACHED*/
1683 /* stupid gcc */
1684 break;
1686 break;
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);
1694 break;
1695 case InsertMergeTruncateExisting:
1696 case InsertMergeTruncateAddition:
1697 case InsertMergeExtend:
1698 /* cannot add in this case */
1699 return -1;
1700 default:
1701 /*NOTREACHED*/
1702 /* stupid gcc */
1703 break;
1705 break;
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);
1717 break;
1718 default:
1719 /*NOTREACHED*/
1720 /* stupid gcc */
1721 break;
1723 break;
1725 default:
1726 /*NOTREACHED*/
1727 /* stupid gcc */
1728 break;
1732 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1733 remove_note_unlocked (*i);
1735 if (cmd) {
1736 cmd->side_effect_remove (*i);
1740 if (set_note_time) {
1741 if (cmd) {
1742 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1744 note->set_time (note_time);
1747 if (set_note_length) {
1748 if (cmd) {
1749 cmd->change (note, NoteDiffCommand::Length, note_length);
1751 note->set_length (note_length);
1754 return 0;
1757 InsertMergePolicy
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 ();
1762 assert (ms);
1764 return ms->session().config.get_insert_merge_policy ();
1767 void
1768 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1770 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1772 if (old) {
1773 old->invalidate ();
1776 _midi_source_connections.drop_connections ();
1778 _midi_source = s;
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.
1796 void
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.
1806 void
1807 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1809 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1810 assert (ms);
1812 ms->set_interpolation_of (p, s);
1815 void
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);
1823 void
1824 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1826 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1827 assert (ms);
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 ();
1841 assert (ms);
1843 c->list()->set_interpolation (ms->interpolation_of (p));
1845 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1846 assert (al);
1848 al->set_automation_state (ms->automation_state_of (p));
1850 return c;
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.
1862 void
1863 MidiModel::insert_silence_at_start (TimeType t)
1865 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1866 assert (s);
1868 /* Notes */
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);
1880 /* Controllers */
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));
1890 /* Sys-ex */
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);
1903 /** Transpose notes in a time range by a given number of semitones. Notes
1904 * will be clamped at 0 and 127 if the transposition would make them exceed
1905 * that range.
1907 * @param from Start time.
1908 * @param end End time.
1909 * @param semitones Number of semitones to transpose by (+ve is higher, -ve is lower).
1911 void
1912 MidiModel::transpose (TimeType from, TimeType to, int semitones)
1914 boost::shared_ptr<const MidiSource> s = midi_source ();
1916 NoteDiffCommand* c = new_note_diff_command (_("transpose"));
1918 for (Notes::iterator i = notes().begin(); i != notes().end(); ++i) {
1920 if ((*i)->time() >= to) {
1922 /* finished */
1923 break;
1925 } else if ((*i)->time() >= from) {
1927 int new_note = (*i)->note() + semitones;
1929 if (new_note < 0) {
1930 new_note = 0;
1931 } else if (new_note > 127) {
1932 new_note = 127;
1935 c->change (*i, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
1940 apply_command (s->session (), c);