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);
76 * Ownership of cmd is taken, it must not be deleted by the caller.
77 * The command will constitute one item on the undo stack.
80 MidiModel::apply_command(Session& session, Command* cmd)
82 session.begin_reversible_command(cmd->name());
84 session.commit_reversible_command(cmd);
88 /** Apply a command as part of a larger reversible transaction
90 * Ownership of cmd is taken, it must not be deleted by the caller.
91 * The command will constitute one item on the undo stack.
94 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
97 session.add_command(cmd);
101 /************** DIFF COMMAND ********************/
103 #define NOTE_DIFF_COMMAND_ELEMENT "NoteDiffCommand"
104 #define DIFF_NOTES_ELEMENT "ChangedNotes"
105 #define ADDED_NOTES_ELEMENT "AddedNotes"
106 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
107 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
108 #define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand"
109 #define DIFF_SYSEXES_ELEMENT "ChangedSysExes"
111 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
119 MidiModel::NoteDiffCommand::NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
120 : DiffCommand (m, "")
123 set_state (node, Stateful::loading_state_version);
127 MidiModel::NoteDiffCommand::add (const NotePtr note)
129 _removed_notes.remove(note);
130 _added_notes.push_back(note);
134 MidiModel::NoteDiffCommand::remove (const NotePtr note)
136 _added_notes.remove(note);
137 _removed_notes.push_back(note);
141 MidiModel::NoteDiffCommand::side_effect_remove (const NotePtr note)
143 side_effect_removals.insert (note);
147 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
156 if (new_value == note->note()) {
159 change.old_value = note->note();
162 if (new_value == note->velocity()) {
165 change.old_value = note->velocity();
168 if (new_value == note->channel()) {
171 change.old_value = note->channel();
176 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
180 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
186 change.property = prop;
187 change.new_value = new_value;
189 _changes.push_back (change);
193 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
204 fatal << "MidiModel::NoteDiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
208 if (Evoral::musical_time_equal (note->time(), new_time)) {
211 change.old_time = note->time();
214 if (Evoral::musical_time_equal (note->length(), new_time)) {
217 change.old_time = note->length();
222 change.property = prop;
223 change.new_time = new_time;
225 _changes.push_back (change);
228 MidiModel::NoteDiffCommand &
229 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand& other)
231 if (this == &other) {
235 if (_model != other._model) {
239 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
240 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
241 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
242 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
248 MidiModel::NoteDiffCommand::operator() ()
251 MidiModel::WriteLock lock(_model->edit_lock());
253 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
254 if (!_model->add_note_unlocked(*i)) {
255 /* failed to add it, so don't leave it in the removed list, to
256 avoid apparent errors on undo.
258 _removed_notes.remove (*i);
262 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
263 _model->remove_note_unlocked(*i);
266 /* notes we modify in a way that requires remove-then-add to maintain ordering */
267 set<NotePtr> temporary_removals;
269 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
270 Property prop = i->property;
273 if (temporary_removals.find (i->note) == temporary_removals.end()) {
274 _model->remove_note_unlocked (i->note);
275 temporary_removals.insert (i->note);
277 i->note->set_note (i->new_value);
281 if (temporary_removals.find (i->note) == temporary_removals.end()) {
282 _model->remove_note_unlocked (i->note);
283 temporary_removals.insert (i->note);
286 i->note->set_time (i->new_time);
290 if (temporary_removals.find (i->note) == temporary_removals.end()) {
291 _model->remove_note_unlocked (i->note);
292 temporary_removals.insert (i->note);
294 i->note->set_channel (i->new_value);
297 /* no remove-then-add required for these properties, since we do not index them
301 i->note->set_velocity (i->new_value);
305 i->note->set_length (i->new_time);
312 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
313 NoteDiffCommand side_effects (model(), "side effects");
314 if (_model->add_note_unlocked (*i, &side_effects)) {
315 /* The note was re-added ok */
316 *this += side_effects;
318 /* The note that we removed earlier could not be re-added. This change record
319 must say that the note was removed. It is an un-note.
322 /* We didn't change it... */
323 for (ChangeList::iterator j = _changes.begin(); j != _changes.end(); ) {
325 ChangeList::iterator k = j;
335 /* ...in fact, we removed it */
336 _removed_notes.push_back (*i);
340 if (!side_effect_removals.empty()) {
342 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
343 cerr << "\t" << *i << ' ' << **i << endl;
348 _model->ContentsChanged(); /* EMIT SIGNAL */
352 MidiModel::NoteDiffCommand::undo ()
355 MidiModel::WriteLock lock(_model->edit_lock());
357 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
358 _model->remove_note_unlocked(*i);
361 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
362 _model->add_note_unlocked(*i);
365 /* notes we modify in a way that requires remove-then-add to maintain ordering */
366 set<NotePtr> temporary_removals;
368 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
369 Property prop = i->property;
372 if (temporary_removals.find (i->note) == temporary_removals.end()) {
373 _model->remove_note_unlocked (i->note);
374 temporary_removals.insert (i->note);
376 i->note->set_note (i->old_value);
379 i->note->set_velocity (i->old_value);
382 if (temporary_removals.find (i->note) == temporary_removals.end()) {
383 _model->remove_note_unlocked (i->note);
384 temporary_removals.insert (i->note);
386 i->note->set_time (i->old_time);
389 i->note->set_length (i->old_time);
392 if (temporary_removals.find (i->note) == temporary_removals.end()) {
393 _model->remove_note_unlocked (i->note);
394 temporary_removals.insert (i->note);
396 i->note->set_channel (i->old_value);
401 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
402 _model->add_note_unlocked (*i);
405 /* finally add back notes that were removed by the "do". we don't care
406 about side effects here since the model should be back to its original
407 state once this is done.
410 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
411 _model->add_note_unlocked (*i);
415 _model->ContentsChanged(); /* EMIT SIGNAL */
419 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
421 XMLNode* xml_note = new XMLNode("note");
424 ostringstream id_str(ios::ate);
425 id_str << int(note->id());
426 xml_note->add_property("id", id_str.str());
430 ostringstream note_str(ios::ate);
431 note_str << int(note->note());
432 xml_note->add_property("note", note_str.str());
436 ostringstream channel_str(ios::ate);
437 channel_str << int(note->channel());
438 xml_note->add_property("channel", channel_str.str());
442 ostringstream time_str(ios::ate);
443 time_str << note->time();
444 xml_note->add_property("time", time_str.str());
448 ostringstream length_str(ios::ate);
449 length_str << note->length();
450 xml_note->add_property("length", length_str.str());
454 ostringstream velocity_str(ios::ate);
455 velocity_str << (unsigned int) note->velocity();
456 xml_note->add_property("velocity", velocity_str.str());
462 Evoral::Sequence<MidiModel::TimeType>::NotePtr
463 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
467 unsigned int channel;
470 unsigned int velocity;
473 if ((prop = xml_note->property("id")) != 0) {
474 istringstream id_str(prop->value());
477 error << "note information missing ID value" << endmsg;
481 if ((prop = xml_note->property("note")) != 0) {
482 istringstream note_str(prop->value());
485 warning << "note information missing note value" << endmsg;
489 if ((prop = xml_note->property("channel")) != 0) {
490 istringstream channel_str(prop->value());
491 channel_str >> channel;
493 warning << "note information missing channel" << endmsg;
497 if ((prop = xml_note->property("time")) != 0) {
498 istringstream time_str(prop->value());
501 warning << "note information missing time" << endmsg;
505 if ((prop = xml_note->property("length")) != 0) {
506 istringstream length_str(prop->value());
507 length_str >> length;
509 warning << "note information missing length" << endmsg;
513 if ((prop = xml_note->property("velocity")) != 0) {
514 istringstream velocity_str(prop->value());
515 velocity_str >> velocity;
517 warning << "note information missing velocity" << endmsg;
521 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
522 note_ptr->set_id (id);
528 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
530 XMLNode* xml_change = new XMLNode("Change");
532 /* first, the change itself */
534 xml_change->add_property ("property", enum_2_string (change.property));
537 ostringstream old_value_str (ios::ate);
538 if (change.property == StartTime || change.property == Length) {
539 old_value_str << change.old_time;
541 old_value_str << (unsigned int) change.old_value;
543 xml_change->add_property ("old", old_value_str.str());
547 ostringstream new_value_str (ios::ate);
548 if (change.property == StartTime || change.property == Length) {
549 new_value_str << change.new_time;
551 new_value_str << (unsigned int) change.new_value;
553 xml_change->add_property ("new", new_value_str.str());
556 ostringstream id_str;
557 id_str << change.note->id();
558 xml_change->add_property ("id", id_str.str());
563 MidiModel::NoteDiffCommand::NoteChange
564 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
569 if ((prop = xml_change->property("property")) != 0) {
570 change.property = (Property) string_2_enum (prop->value(), change.property);
572 fatal << "!!!" << endmsg;
576 if ((prop = xml_change->property ("id")) == 0) {
577 error << _("No NoteID found for note property change - ignored") << endmsg;
581 gint note_id = atoi (prop->value().c_str());
583 if ((prop = xml_change->property ("old")) != 0) {
584 istringstream old_str (prop->value());
585 if (change.property == StartTime || change.property == Length) {
586 old_str >> change.old_time;
588 int integer_value_so_that_istream_does_the_right_thing;
589 old_str >> integer_value_so_that_istream_does_the_right_thing;
590 change.old_value = integer_value_so_that_istream_does_the_right_thing;
593 fatal << "!!!" << endmsg;
597 if ((prop = xml_change->property ("new")) != 0) {
598 istringstream new_str (prop->value());
599 if (change.property == StartTime || change.property == Length) {
600 new_str >> change.new_time;
602 int integer_value_so_that_istream_does_the_right_thing;
603 new_str >> integer_value_so_that_istream_does_the_right_thing;
604 change.new_value = integer_value_so_that_istream_does_the_right_thing;
607 fatal << "!!!" << endmsg;
611 /* we must point at the instance of the note that is actually in the model.
612 so go look for it ...
615 change.note = _model->find_note (note_id);
618 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
626 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
628 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
634 _added_notes.clear();
635 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
637 XMLNodeList notes = added_notes->children();
638 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
639 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
645 _removed_notes.clear();
646 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
648 XMLNodeList notes = removed_notes->children();
649 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
650 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
658 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
661 XMLNodeList notes = changed_notes->children();
662 transform (notes.begin(), notes.end(), back_inserter(_changes),
663 boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
667 /* side effect removals caused by changes */
669 side_effect_removals.clear();
671 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
673 if (side_effect_notes) {
674 XMLNodeList notes = side_effect_notes->children();
675 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
676 side_effect_removals.insert (unmarshal_note (*n));
684 MidiModel::NoteDiffCommand::get_state ()
686 XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
687 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
689 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
690 for_each(_changes.begin(), _changes.end(),
692 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
693 boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
695 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
696 for_each(_added_notes.begin(), _added_notes.end(),
698 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
699 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
701 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
702 for_each(_removed_notes.begin(), _removed_notes.end(),
704 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
705 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
707 /* if this command had side-effects, store that state too
710 if (!side_effect_removals.empty()) {
711 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
712 for_each(side_effect_removals.begin(), side_effect_removals.end(),
714 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
715 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
718 return *diff_command;
721 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
722 : DiffCommand (m, "")
725 set_state (node, Stateful::loading_state_version);
729 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
734 change.property = Time;
735 change.old_time = s->time ();
736 change.new_time = new_time;
738 _changes.push_back (change);
742 MidiModel::SysExDiffCommand::operator() ()
745 MidiModel::WriteLock lock (_model->edit_lock ());
747 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
748 switch (i->property) {
750 i->sysex->set_time (i->new_time);
755 _model->ContentsChanged (); /* EMIT SIGNAL */
759 MidiModel::SysExDiffCommand::undo ()
762 MidiModel::WriteLock lock (_model->edit_lock ());
764 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
765 switch (i->property) {
767 i->sysex->set_time (i->old_time);
774 _model->ContentsChanged(); /* EMIT SIGNAL */
778 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
780 XMLNode* xml_change = new XMLNode ("Change");
782 /* first, the change itself */
784 xml_change->add_property ("property", enum_2_string (change.property));
787 ostringstream old_value_str (ios::ate);
788 old_value_str << change.old_time;
789 xml_change->add_property ("old", old_value_str.str());
793 ostringstream new_value_str (ios::ate);
794 new_value_str << change.new_time;
795 xml_change->add_property ("new", new_value_str.str());
798 ostringstream id_str;
799 id_str << change.sysex->id();
800 xml_change->add_property ("id", id_str.str());
805 MidiModel::SysExDiffCommand::Change
806 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
811 if ((prop = xml_change->property ("property")) != 0) {
812 change.property = (Property) string_2_enum (prop->value(), change.property);
814 fatal << "!!!" << endmsg;
818 if ((prop = xml_change->property ("id")) == 0) {
819 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
823 gint sysex_id = atoi (prop->value().c_str());
825 if ((prop = xml_change->property ("old")) != 0) {
826 istringstream old_str (prop->value());
827 old_str >> change.old_time;
829 fatal << "!!!" << endmsg;
833 if ((prop = xml_change->property ("new")) != 0) {
834 istringstream new_str (prop->value());
835 new_str >> change.new_time;
837 fatal << "!!!" << endmsg;
841 /* we must point at the instance of the sysex that is actually in the model.
842 so go look for it ...
845 change.sysex = _model->find_sysex (sysex_id);
848 warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg;
856 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
858 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
866 XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
868 if (changed_sysexes) {
869 XMLNodeList sysexes = changed_sysexes->children();
870 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
871 boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
879 MidiModel::SysExDiffCommand::get_state ()
881 XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
882 diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
884 XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
885 for_each (_changes.begin(), _changes.end(),
887 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
888 boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
890 return *diff_command;
893 /** Write all of the model to a MidiSource (i.e. save the model).
894 * This is different from manually using read to write to a source in that
895 * note off events are written regardless of the track mode. This is so the
896 * user can switch a recorded track (with note durations from some instrument)
897 * to percussive, save, reload, then switch it back to sustained without
898 * destroying the original note durations.
900 * Similarly, control events are written without interpolation (as with the
904 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
906 ReadLock lock(read_lock());
908 const bool old_percussive = percussive();
909 set_percussive(false);
911 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
914 source->drop_model();
915 source->mark_streaming_midi_write_started (note_mode(), ms->timeline_position ());
917 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
918 source->append_event_unlocked_beats(*i);
921 set_percussive(old_percussive);
922 source->mark_streaming_write_completed();
929 /** very similar to ::write_to() but writes to the model's own
930 existing midi_source, without making it call MidiSource::drop_model().
931 the caller is a MidiSource that needs to catch up with the state
935 MidiModel::sync_to_source ()
937 ReadLock lock(read_lock());
939 const bool old_percussive = percussive();
940 set_percussive(false);
942 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
945 ms->mark_streaming_midi_write_started (note_mode(), ms->timeline_position());
947 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
948 ms->append_event_unlocked_beats(*i);
951 set_percussive (old_percussive);
952 ms->mark_streaming_write_completed ();
959 /** Write part or all of the model to a MidiSource (i.e. save the model).
960 * This is different from manually using read to write to a source in that
961 * note off events are written regardless of the track mode. This is so the
962 * user can switch a recorded track (with note durations from some instrument)
963 * to percussive, save, reload, then switch it back to sustained without
964 * destroying the original note durations.
967 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
969 ReadLock lock(read_lock());
970 MidiStateTracker mst;
971 Evoral::MusicalTime extra_note_on_time = end_time;
973 const bool old_percussive = percussive();
974 set_percussive(false);
976 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
979 source->drop_model();
980 source->mark_streaming_midi_write_started (note_mode(), ms->timeline_position());
982 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
983 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
985 if (ev.time() >= begin_time && ev.time() < end_time) {
987 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
988 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
995 if (mev->is_note_off()) {
997 if (!mst.active (mev->note(), mev->channel())) {
999 /* add a note-on at the start of the range we're writing
1000 to the file. velocity is just an arbitary reasonable value.
1003 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
1004 on.set_type (mev->type());
1005 on.set_note (mev->note());
1006 on.set_channel (mev->channel());
1007 on.set_velocity (mev->velocity());
1009 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
1010 source->append_event_unlocked_beats (on);
1011 mst.add (on.note(), on.channel());
1013 extra_note_on_time += 1.0/128.0;
1016 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
1017 source->append_event_unlocked_beats (*i);
1018 mst.remove (mev->note(), mev->channel());
1021 } else if (mev->is_note_on()) {
1022 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
1023 mst.add (mev->note(), mev->channel());
1024 source->append_event_unlocked_beats(*i);
1027 cerr << "MIDI other event type\n";
1028 source->append_event_unlocked_beats(*i);
1033 mst.resolve_notes (*source, end_time);
1035 set_percussive(old_percussive);
1036 source->mark_streaming_write_completed();
1044 MidiModel::get_state()
1046 XMLNode *node = new XMLNode("MidiModel");
1050 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1051 MidiModel::find_note (NotePtr other)
1053 Notes::iterator l = notes().lower_bound(other);
1055 if (l != notes().end()) {
1056 for (; (*l)->time() == other->time(); ++l) {
1057 /* NB: compare note contents, not note pointers.
1058 If "other" was a ptr to a note already in
1059 the model, we wouldn't be looking for it,
1062 if (**l == *other) {
1071 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1072 MidiModel::find_note (gint note_id)
1074 /* used only for looking up notes when reloading history from disk,
1075 so we don't care about performance *too* much.
1078 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1079 if ((*l)->id() == note_id) {
1087 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1088 MidiModel::find_sysex (gint sysex_id)
1090 /* used only for looking up notes when reloading history from disk,
1091 so we don't care about performance *too* much.
1094 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1095 if ((*l)->id() == sysex_id) {
1100 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1103 /** Lock and invalidate the source.
1104 * This should be used by commands and editing things
1106 MidiModel::WriteLock
1107 MidiModel::edit_lock()
1109 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1112 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock (ms->mutex());
1113 ms->invalidate(); // Release cached iterator's read lock on model
1114 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1117 /** Lock just the model, the source lock must already be held.
1118 * This should only be called from libardour/evoral places
1120 MidiModel::WriteLock
1121 MidiModel::write_lock()
1123 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1126 assert (!ms->mutex().trylock ());
1127 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
1131 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1133 using namespace Evoral;
1135 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1139 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1141 TimeType sa = note->time();
1142 TimeType ea = note->end_time();
1144 const Pitches& p (pitches (note->channel()));
1145 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
1146 set<NotePtr> to_be_deleted;
1147 bool set_note_length = false;
1148 bool set_note_time = false;
1149 TimeType note_time = note->time();
1150 TimeType note_length = note->length();
1152 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1154 for (Pitches::const_iterator i = p.lower_bound (search_note);
1155 i != p.end() && (*i)->note() == note->note(); ++i) {
1157 TimeType sb = (*i)->time();
1158 TimeType eb = (*i)->end_time();
1159 OverlapType overlap = OverlapNone;
1161 if ((sb > sa) && (eb <= ea)) {
1162 overlap = OverlapInternal;
1163 } else if ((eb >= sa) && (eb <= ea)) {
1164 overlap = OverlapStart;
1165 } else if ((sb > sa) && (sb <= ea)) {
1166 overlap = OverlapEnd;
1167 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1168 overlap = OverlapExternal;
1174 DEBUG_TRACE (DEBUG::Sequence, string_compose ("\toverlap is %1 for (%2,%3) vs (%4,%5)\n", enum_2_string(overlap),
1177 if (insert_merge_policy() == InsertMergeReject) {
1178 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1184 cerr << "OverlapStart\n";
1185 /* existing note covers start of new note */
1186 switch (insert_merge_policy()) {
1187 case InsertMergeReplace:
1188 to_be_deleted.insert (*i);
1190 case InsertMergeTruncateExisting:
1192 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1194 (*i)->set_length (note->time() - (*i)->time());
1196 case InsertMergeTruncateAddition:
1197 set_note_time = true;
1198 set_note_length = true;
1199 note_time = (*i)->time() + (*i)->length();
1200 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1202 case InsertMergeExtend:
1204 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1206 (*i)->set_length (note->end_time() - (*i)->time());
1207 return -1; /* do not add the new note */
1217 cerr << "OverlapEnd\n";
1218 /* existing note covers end of new note */
1219 switch (insert_merge_policy()) {
1220 case InsertMergeReplace:
1221 to_be_deleted.insert (*i);
1224 case InsertMergeTruncateExisting:
1225 /* resetting the start time of the existing note
1226 is a problem because of time ordering.
1230 case InsertMergeTruncateAddition:
1231 set_note_length = true;
1232 note_length = min (note_length, ((*i)->time() - note->time()));
1235 case InsertMergeExtend:
1236 /* we can't reset the time of the existing note because
1237 that will corrupt time ordering. So remove the
1238 existing note and change the position/length
1239 of the new note (which has not been added yet)
1241 to_be_deleted.insert (*i);
1242 set_note_length = true;
1243 note_length = min (note_length, (*i)->end_time() - note->time());
1252 case OverlapExternal:
1253 cerr << "OverlapExt\n";
1254 /* existing note overlaps all the new note */
1255 switch (insert_merge_policy()) {
1256 case InsertMergeReplace:
1257 to_be_deleted.insert (*i);
1259 case InsertMergeTruncateExisting:
1260 case InsertMergeTruncateAddition:
1261 case InsertMergeExtend:
1262 /* cannot add in this case */
1271 case OverlapInternal:
1272 cerr << "OverlapInt\n";
1273 /* new note fully overlaps an existing note */
1274 switch (insert_merge_policy()) {
1275 case InsertMergeReplace:
1276 case InsertMergeTruncateExisting:
1277 case InsertMergeTruncateAddition:
1278 case InsertMergeExtend:
1279 /* delete the existing note, the new one will cover it */
1280 to_be_deleted.insert (*i);
1296 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1297 remove_note_unlocked (*i);
1300 cmd->side_effect_remove (*i);
1304 if (set_note_time) {
1306 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1308 note->set_time (note_time);
1311 if (set_note_length) {
1313 cmd->change (note, NoteDiffCommand::Length, note_length);
1315 note->set_length (note_length);
1322 MidiModel::insert_merge_policy () const
1324 /* XXX ultimately this should be a per-track or even per-model policy */
1325 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1328 return ms->session().config.get_insert_merge_policy ();
1332 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1334 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1340 _midi_source_connections.drop_connections ();
1344 s->InterpolationChanged.connect_same_thread (
1345 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1348 s->AutomationStateChanged.connect_same_thread (
1349 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1353 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1354 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1355 * appropriate ControlList.
1357 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1358 * or the other is listened to by the GUI.
1361 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1363 Glib::Mutex::Lock lm (_control_lock);
1364 control(p)->list()->set_interpolation (s);
1367 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1368 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1371 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1373 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1376 ms->set_interpolation_of (p, s);
1380 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1382 Glib::Mutex::Lock lm (_control_lock);
1383 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1384 al->set_automation_state (s);
1388 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1390 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1392 ms->set_automation_state_of (p, s);
1395 boost::shared_ptr<Evoral::Control>
1396 MidiModel::control_factory (Evoral::Parameter const & p)
1398 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1400 /* Set up newly created control's lists to the appropriate interpolation and
1401 automation state from our source.
1404 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1407 c->list()->set_interpolation (ms->interpolation_of (p));
1409 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1412 al->set_automation_state (ms->automation_state_of (p));
1417 boost::shared_ptr<const MidiSource>
1418 MidiModel::midi_source ()
1420 return _midi_source.lock ();
1423 /** Moves notes, controllers and sys-ex to insert silence at the start of the model.
1424 * Adds commands to the session's current undo stack to reflect the movements.
1427 MidiModel::insert_silence_at_start (TimeType t)
1429 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1434 if (!notes().empty ()) {
1435 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1437 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1438 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1441 apply_command_as_subcommand (s->session(), c);
1446 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1447 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1448 XMLNode& before = ac->alist()->get_state ();
1449 i->second->list()->shift (0, t);
1450 XMLNode& after = ac->alist()->get_state ();
1451 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1456 if (!sysexes().empty()) {
1457 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1459 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1460 c->change (*i, (*i)->time() + t);
1463 apply_command_as_subcommand (s->session(), c);