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,
154 if (new_value == note->note()) {
157 change.old_value = note->note();
160 if (new_value == note->velocity()) {
163 change.old_value = note->velocity();
166 if (new_value == note->channel()) {
169 change.old_value = note->channel();
174 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
178 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
184 change.property = prop;
185 change.new_value = new_value;
187 _changes.push_back (change);
191 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
200 fatal << "MidiModel::NoteDiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
204 if (Evoral::musical_time_equal (note->time(), new_time)) {
207 change.old_time = note->time();
210 if (Evoral::musical_time_equal (note->length(), new_time)) {
213 change.old_time = note->length();
218 change.property = prop;
219 change.new_time = new_time;
221 _changes.push_back (change);
224 MidiModel::NoteDiffCommand &
225 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand& other)
227 if (this == &other) {
231 if (_model != other._model) {
235 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
236 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
237 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
238 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
244 MidiModel::NoteDiffCommand::operator() ()
247 MidiModel::WriteLock lock(_model->edit_lock());
249 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
250 if (!_model->add_note_unlocked(*i)) {
251 /* failed to add it, so don't leave it in the removed list, to
252 avoid apparent errors on undo.
254 _removed_notes.remove (*i);
258 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
259 _model->remove_note_unlocked(*i);
262 /* notes we modify in a way that requires remove-then-add to maintain ordering */
263 set<NotePtr> temporary_removals;
265 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
266 Property prop = i->property;
269 if (temporary_removals.find (i->note) == temporary_removals.end()) {
270 _model->remove_note_unlocked (i->note);
271 temporary_removals.insert (i->note);
273 i->note->set_note (i->new_value);
277 if (temporary_removals.find (i->note) == temporary_removals.end()) {
278 _model->remove_note_unlocked (i->note);
279 temporary_removals.insert (i->note);
282 i->note->set_time (i->new_time);
286 if (temporary_removals.find (i->note) == temporary_removals.end()) {
287 _model->remove_note_unlocked (i->note);
288 temporary_removals.insert (i->note);
290 i->note->set_channel (i->new_value);
293 /* no remove-then-add required for these properties, since we do not index them
297 i->note->set_velocity (i->new_value);
301 i->note->set_length (i->new_time);
308 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
309 NoteDiffCommand side_effects (model(), "side effects");
310 _model->add_note_unlocked (*i, &side_effects);
311 *this += side_effects;
314 if (!side_effect_removals.empty()) {
316 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
317 cerr << "\t" << *i << ' ' << **i << endl;
322 _model->ContentsChanged(); /* EMIT SIGNAL */
326 MidiModel::NoteDiffCommand::undo ()
329 MidiModel::WriteLock lock(_model->edit_lock());
331 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
332 _model->remove_note_unlocked(*i);
335 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
336 _model->add_note_unlocked(*i);
339 /* notes we modify in a way that requires remove-then-add to maintain ordering */
340 set<NotePtr> temporary_removals;
342 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
343 Property prop = i->property;
346 if (temporary_removals.find (i->note) == temporary_removals.end()) {
347 _model->remove_note_unlocked (i->note);
348 temporary_removals.insert (i->note);
350 i->note->set_note (i->old_value);
353 i->note->set_velocity (i->old_value);
356 if (temporary_removals.find (i->note) == temporary_removals.end()) {
357 _model->remove_note_unlocked (i->note);
358 temporary_removals.insert (i->note);
360 i->note->set_time (i->old_time);
363 i->note->set_length (i->old_time);
366 if (temporary_removals.find (i->note) == temporary_removals.end()) {
367 _model->remove_note_unlocked (i->note);
368 temporary_removals.insert (i->note);
370 i->note->set_channel (i->old_value);
375 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
376 _model->add_note_unlocked (*i);
379 /* finally add back notes that were removed by the "do". we don't care
380 about side effects here since the model should be back to its original
381 state once this is done.
384 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
385 _model->add_note_unlocked (*i);
389 _model->ContentsChanged(); /* EMIT SIGNAL */
393 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
395 XMLNode* xml_note = new XMLNode("note");
398 ostringstream id_str(ios::ate);
399 id_str << int(note->id());
400 xml_note->add_property("id", id_str.str());
404 ostringstream note_str(ios::ate);
405 note_str << int(note->note());
406 xml_note->add_property("note", note_str.str());
410 ostringstream channel_str(ios::ate);
411 channel_str << int(note->channel());
412 xml_note->add_property("channel", channel_str.str());
416 ostringstream time_str(ios::ate);
417 time_str << note->time();
418 xml_note->add_property("time", time_str.str());
422 ostringstream length_str(ios::ate);
423 length_str << note->length();
424 xml_note->add_property("length", length_str.str());
428 ostringstream velocity_str(ios::ate);
429 velocity_str << (unsigned int) note->velocity();
430 xml_note->add_property("velocity", velocity_str.str());
436 Evoral::Sequence<MidiModel::TimeType>::NotePtr
437 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
441 unsigned int channel;
444 unsigned int velocity;
447 if ((prop = xml_note->property("id")) != 0) {
448 istringstream id_str(prop->value());
451 error << "note information missing ID value" << endmsg;
455 if ((prop = xml_note->property("note")) != 0) {
456 istringstream note_str(prop->value());
459 warning << "note information missing note value" << endmsg;
463 if ((prop = xml_note->property("channel")) != 0) {
464 istringstream channel_str(prop->value());
465 channel_str >> channel;
467 warning << "note information missing channel" << endmsg;
471 if ((prop = xml_note->property("time")) != 0) {
472 istringstream time_str(prop->value());
475 warning << "note information missing time" << endmsg;
479 if ((prop = xml_note->property("length")) != 0) {
480 istringstream length_str(prop->value());
481 length_str >> length;
483 warning << "note information missing length" << endmsg;
487 if ((prop = xml_note->property("velocity")) != 0) {
488 istringstream velocity_str(prop->value());
489 velocity_str >> velocity;
491 warning << "note information missing velocity" << endmsg;
495 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
496 note_ptr->set_id (id);
502 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
504 XMLNode* xml_change = new XMLNode("Change");
506 /* first, the change itself */
508 xml_change->add_property ("property", enum_2_string (change.property));
511 ostringstream old_value_str (ios::ate);
512 if (change.property == StartTime || change.property == Length) {
513 old_value_str << change.old_time;
515 old_value_str << (unsigned int) change.old_value;
517 xml_change->add_property ("old", old_value_str.str());
521 ostringstream new_value_str (ios::ate);
522 if (change.property == StartTime || change.property == Length) {
523 new_value_str << change.new_time;
525 new_value_str << (unsigned int) change.new_value;
527 xml_change->add_property ("new", new_value_str.str());
530 ostringstream id_str;
531 id_str << change.note->id();
532 xml_change->add_property ("id", id_str.str());
537 MidiModel::NoteDiffCommand::NoteChange
538 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
543 if ((prop = xml_change->property("property")) != 0) {
544 change.property = (Property) string_2_enum (prop->value(), change.property);
546 fatal << "!!!" << endmsg;
550 if ((prop = xml_change->property ("id")) == 0) {
551 error << _("No NoteID found for note property change - ignored") << endmsg;
555 gint note_id = atoi (prop->value().c_str());
557 if ((prop = xml_change->property ("old")) != 0) {
558 istringstream old_str (prop->value());
559 if (change.property == StartTime || change.property == Length) {
560 old_str >> change.old_time;
562 int integer_value_so_that_istream_does_the_right_thing;
563 old_str >> integer_value_so_that_istream_does_the_right_thing;
564 change.old_value = integer_value_so_that_istream_does_the_right_thing;
567 fatal << "!!!" << endmsg;
571 if ((prop = xml_change->property ("new")) != 0) {
572 istringstream new_str (prop->value());
573 if (change.property == StartTime || change.property == Length) {
574 new_str >> change.new_time;
576 int integer_value_so_that_istream_does_the_right_thing;
577 new_str >> integer_value_so_that_istream_does_the_right_thing;
578 change.new_value = integer_value_so_that_istream_does_the_right_thing;
581 fatal << "!!!" << endmsg;
585 /* we must point at the instance of the note that is actually in the model.
586 so go look for it ...
589 change.note = _model->find_note (note_id);
592 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
600 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
602 if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
608 _added_notes.clear();
609 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
611 XMLNodeList notes = added_notes->children();
612 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
613 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
619 _removed_notes.clear();
620 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
622 XMLNodeList notes = removed_notes->children();
623 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
624 boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
632 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
635 XMLNodeList notes = changed_notes->children();
636 transform (notes.begin(), notes.end(), back_inserter(_changes),
637 boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
641 /* side effect removals caused by changes */
643 side_effect_removals.clear();
645 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
647 if (side_effect_notes) {
648 XMLNodeList notes = side_effect_notes->children();
649 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
650 side_effect_removals.insert (unmarshal_note (*n));
658 MidiModel::NoteDiffCommand::get_state ()
660 XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
661 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
663 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
664 for_each(_changes.begin(), _changes.end(),
666 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
667 boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
669 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
670 for_each(_added_notes.begin(), _added_notes.end(),
672 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
673 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
675 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
676 for_each(_removed_notes.begin(), _removed_notes.end(),
678 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
679 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
681 /* if this command had side-effects, store that state too
684 if (!side_effect_removals.empty()) {
685 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
686 for_each(side_effect_removals.begin(), side_effect_removals.end(),
688 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
689 boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
692 return *diff_command;
695 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
696 : DiffCommand (m, "")
699 set_state (node, Stateful::loading_state_version);
703 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
708 change.property = Time;
709 change.old_time = s->time ();
710 change.new_time = new_time;
712 _changes.push_back (change);
716 MidiModel::SysExDiffCommand::operator() ()
719 MidiModel::WriteLock lock (_model->edit_lock ());
721 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
722 switch (i->property) {
724 i->sysex->set_time (i->new_time);
729 _model->ContentsChanged (); /* EMIT SIGNAL */
733 MidiModel::SysExDiffCommand::undo ()
736 MidiModel::WriteLock lock (_model->edit_lock ());
738 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
739 switch (i->property) {
741 i->sysex->set_time (i->old_time);
748 _model->ContentsChanged(); /* EMIT SIGNAL */
752 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
754 XMLNode* xml_change = new XMLNode ("Change");
756 /* first, the change itself */
758 xml_change->add_property ("property", enum_2_string (change.property));
761 ostringstream old_value_str (ios::ate);
762 old_value_str << change.old_time;
763 xml_change->add_property ("old", old_value_str.str());
767 ostringstream new_value_str (ios::ate);
768 new_value_str << change.new_time;
769 xml_change->add_property ("new", new_value_str.str());
772 ostringstream id_str;
773 id_str << change.sysex->id();
774 xml_change->add_property ("id", id_str.str());
779 MidiModel::SysExDiffCommand::Change
780 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
785 if ((prop = xml_change->property ("property")) != 0) {
786 change.property = (Property) string_2_enum (prop->value(), change.property);
788 fatal << "!!!" << endmsg;
792 if ((prop = xml_change->property ("id")) == 0) {
793 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
797 gint sysex_id = atoi (prop->value().c_str());
799 if ((prop = xml_change->property ("old")) != 0) {
800 istringstream old_str (prop->value());
801 old_str >> change.old_time;
803 fatal << "!!!" << endmsg;
807 if ((prop = xml_change->property ("new")) != 0) {
808 istringstream new_str (prop->value());
809 new_str >> change.new_time;
811 fatal << "!!!" << endmsg;
815 /* we must point at the instance of the sysex that is actually in the model.
816 so go look for it ...
819 change.sysex = _model->find_sysex (sysex_id);
822 warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg;
830 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
832 if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
840 XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
842 if (changed_sysexes) {
843 XMLNodeList sysexes = changed_sysexes->children();
844 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
845 boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
853 MidiModel::SysExDiffCommand::get_state ()
855 XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
856 diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
858 XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
859 for_each (_changes.begin(), _changes.end(),
861 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
862 boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
864 return *diff_command;
867 /** Write all of the model to a MidiSource (i.e. save the model).
868 * This is different from manually using read to write to a source in that
869 * note off events are written regardless of the track mode. This is so the
870 * user can switch a recorded track (with note durations from some instrument)
871 * to percussive, save, reload, then switch it back to sustained without
872 * destroying the original note durations.
874 * Similarly, control events are written without interpolation (as with the
878 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
880 ReadLock lock(read_lock());
882 const bool old_percussive = percussive();
883 set_percussive(false);
885 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
888 source->drop_model();
889 source->mark_streaming_midi_write_started (note_mode(), ms->timeline_position ());
891 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
892 source->append_event_unlocked_beats(*i);
895 set_percussive(old_percussive);
896 source->mark_streaming_write_completed();
903 /** very similar to ::write_to() but writes to the model's own
904 existing midi_source, without making it call MidiSource::drop_model().
905 the caller is a MidiSource that needs to catch up with the state
909 MidiModel::sync_to_source ()
911 ReadLock lock(read_lock());
913 const bool old_percussive = percussive();
914 set_percussive(false);
916 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
919 ms->mark_streaming_midi_write_started (note_mode(), ms->timeline_position());
921 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
922 ms->append_event_unlocked_beats(*i);
925 set_percussive (old_percussive);
926 ms->mark_streaming_write_completed ();
933 /** Write part or all of the model to a MidiSource (i.e. save the model).
934 * This is different from manually using read to write to a source in that
935 * note off events are written regardless of the track mode. This is so the
936 * user can switch a recorded track (with note durations from some instrument)
937 * to percussive, save, reload, then switch it back to sustained without
938 * destroying the original note durations.
941 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
943 ReadLock lock(read_lock());
944 MidiStateTracker mst;
945 Evoral::MusicalTime extra_note_on_time = end_time;
947 const bool old_percussive = percussive();
948 set_percussive(false);
950 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
953 source->drop_model();
954 source->mark_streaming_midi_write_started (note_mode(), ms->timeline_position());
956 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
957 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
959 if (ev.time() >= begin_time && ev.time() < end_time) {
961 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
962 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
969 if (mev->is_note_off()) {
971 if (!mst.active (mev->note(), mev->channel())) {
973 /* add a note-on at the start of the range we're writing
974 to the file. velocity is just an arbitary reasonable value.
977 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
978 on.set_type (mev->type());
979 on.set_note (mev->note());
980 on.set_channel (mev->channel());
981 on.set_velocity (mev->velocity());
983 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
984 source->append_event_unlocked_beats (on);
985 mst.add (on.note(), on.channel());
987 extra_note_on_time += 1.0/128.0;
990 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
991 source->append_event_unlocked_beats (*i);
992 mst.remove (mev->note(), mev->channel());
995 } else if (mev->is_note_on()) {
996 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
997 mst.add (mev->note(), mev->channel());
998 source->append_event_unlocked_beats(*i);
1001 cerr << "MIDI other event type\n";
1002 source->append_event_unlocked_beats(*i);
1007 mst.resolve_notes (*source, end_time);
1009 set_percussive(old_percussive);
1010 source->mark_streaming_write_completed();
1018 MidiModel::get_state()
1020 XMLNode *node = new XMLNode("MidiModel");
1024 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1025 MidiModel::find_note (NotePtr other)
1027 Notes::iterator l = notes().lower_bound(other);
1029 if (l != notes().end()) {
1030 for (; (*l)->time() == other->time(); ++l) {
1031 /* NB: compare note contents, not note pointers.
1032 If "other" was a ptr to a note already in
1033 the model, we wouldn't be looking for it,
1036 if (**l == *other) {
1045 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1046 MidiModel::find_note (gint note_id)
1048 /* used only for looking up notes when reloading history from disk,
1049 so we don't care about performance *too* much.
1052 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1053 if ((*l)->id() == note_id) {
1061 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1062 MidiModel::find_sysex (gint sysex_id)
1064 /* used only for looking up notes when reloading history from disk,
1065 so we don't care about performance *too* much.
1068 for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1069 if ((*l)->id() == sysex_id) {
1074 return boost::shared_ptr<Evoral::Event<TimeType> > ();
1077 /** Lock and invalidate the source.
1078 * This should be used by commands and editing things
1080 MidiModel::WriteLock
1081 MidiModel::edit_lock()
1083 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1086 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock (ms->mutex());
1087 ms->invalidate(); // Release cached iterator's read lock on model
1088 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1091 /** Lock just the model, the source lock must already be held.
1092 * This should only be called from libardour/evoral places
1094 MidiModel::WriteLock
1095 MidiModel::write_lock()
1097 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1100 assert (!ms->mutex().trylock ());
1101 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
1105 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1107 using namespace Evoral;
1109 if (_writing || insert_merge_policy() == InsertMergeRelax) {
1113 NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1115 TimeType sa = note->time();
1116 TimeType ea = note->end_time();
1118 const Pitches& p (pitches (note->channel()));
1119 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
1120 set<NotePtr> to_be_deleted;
1121 bool set_note_length = false;
1122 bool set_note_time = false;
1123 TimeType note_time = note->time();
1124 TimeType note_length = note->length();
1126 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1128 for (Pitches::const_iterator i = p.lower_bound (search_note);
1129 i != p.end() && (*i)->note() == note->note(); ++i) {
1131 TimeType sb = (*i)->time();
1132 TimeType eb = (*i)->end_time();
1133 OverlapType overlap = OverlapNone;
1136 if ((sb > sa) && (eb <= ea)) {
1137 overlap = OverlapInternal;
1138 } else if ((eb >= sa) && (eb <= ea)) {
1139 overlap = OverlapStart;
1140 } else if ((sb > sa) && (sb <= ea)) {
1141 overlap = OverlapEnd;
1142 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1143 overlap = OverlapExternal;
1149 DEBUG_TRACE (DEBUG::Sequence, string_compose ("\toverlap is %1 for (%2,%3) vs (%4,%5)\n", enum_2_string(overlap),
1152 if (insert_merge_policy() == InsertMergeReject) {
1153 DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1159 cerr << "OverlapStart\n";
1160 /* existing note covers start of new note */
1161 switch (insert_merge_policy()) {
1162 case InsertMergeReplace:
1163 to_be_deleted.insert (*i);
1165 case InsertMergeTruncateExisting:
1167 cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1169 (*i)->set_length (note->time() - (*i)->time());
1171 case InsertMergeTruncateAddition:
1172 set_note_time = true;
1173 set_note_length = true;
1174 note_time = (*i)->time() + (*i)->length();
1175 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1177 case InsertMergeExtend:
1179 cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1181 (*i)->set_length (note->end_time() - (*i)->time());
1182 return -1; /* do not add the new note */
1192 cerr << "OverlapEnd\n";
1193 /* existing note covers end of new note */
1194 switch (insert_merge_policy()) {
1195 case InsertMergeReplace:
1196 to_be_deleted.insert (*i);
1199 case InsertMergeTruncateExisting:
1200 /* resetting the start time of the existing note
1201 is a problem because of time ordering.
1205 case InsertMergeTruncateAddition:
1206 set_note_length = true;
1207 note_length = min (note_length, ((*i)->time() - note->time()));
1210 case InsertMergeExtend:
1211 /* we can't reset the time of the existing note because
1212 that will corrupt time ordering. So remove the
1213 existing note and change the position/length
1214 of the new note (which has not been added yet)
1216 to_be_deleted.insert (*i);
1217 set_note_length = true;
1218 note_length = min (note_length, (*i)->end_time() - note->time());
1227 case OverlapExternal:
1228 cerr << "OverlapExt\n";
1229 /* existing note overlaps all the new note */
1230 switch (insert_merge_policy()) {
1231 case InsertMergeReplace:
1232 to_be_deleted.insert (*i);
1234 case InsertMergeTruncateExisting:
1235 case InsertMergeTruncateAddition:
1236 case InsertMergeExtend:
1237 /* cannot add in this case */
1246 case OverlapInternal:
1247 cerr << "OverlapInt\n";
1248 /* new note fully overlaps an existing note */
1249 switch (insert_merge_policy()) {
1250 case InsertMergeReplace:
1251 case InsertMergeTruncateExisting:
1252 case InsertMergeTruncateAddition:
1253 case InsertMergeExtend:
1254 /* delete the existing note, the new one will cover it */
1255 to_be_deleted.insert (*i);
1271 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1272 remove_note_unlocked (*i);
1275 cmd->side_effect_remove (*i);
1279 if (set_note_time) {
1281 cmd->change (note, NoteDiffCommand::StartTime, note_time);
1283 note->set_time (note_time);
1286 if (set_note_length) {
1288 cmd->change (note, NoteDiffCommand::Length, note_length);
1290 note->set_length (note_length);
1297 MidiModel::insert_merge_policy () const
1299 /* XXX ultimately this should be a per-track or even per-model policy */
1300 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1303 return ms->session().config.get_insert_merge_policy ();
1307 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1309 boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1315 _midi_source_connections.drop_connections ();
1319 s->InterpolationChanged.connect_same_thread (
1320 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1323 s->AutomationStateChanged.connect_same_thread (
1324 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1328 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1329 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1330 * appropriate ControlList.
1332 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1333 * or the other is listened to by the GUI.
1336 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1338 Glib::Mutex::Lock lm (_control_lock);
1339 control(p)->list()->set_interpolation (s);
1342 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1343 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1346 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1348 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1351 ms->set_interpolation_of (p, s);
1355 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1357 Glib::Mutex::Lock lm (_control_lock);
1358 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1359 al->set_automation_state (s);
1363 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1365 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1367 ms->set_automation_state_of (p, s);
1370 boost::shared_ptr<Evoral::Control>
1371 MidiModel::control_factory (Evoral::Parameter const & p)
1373 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1375 /* Set up newly created control's lists to the appropriate interpolation and
1376 automation state from our source.
1379 boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1382 c->list()->set_interpolation (ms->interpolation_of (p));
1384 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1387 al->set_automation_state (ms->automation_state_of (p));
1392 boost::shared_ptr<const MidiSource>
1393 MidiModel::midi_source ()
1395 return _midi_source.lock ();
1398 /** Moves notes, controllers and sys-ex to insert silence at the start of the model.
1399 * Adds commands to the session's current undo stack to reflect the movements.
1402 MidiModel::insert_silence_at_start (TimeType t)
1404 boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1409 if (!notes().empty ()) {
1410 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1412 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1413 c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1416 apply_command_as_subcommand (s->session(), c);
1421 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1422 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1423 XMLNode& before = ac->alist()->get_state ();
1424 i->second->list()->shift (0, t);
1425 XMLNode& after = ac->alist()->get_state ();
1426 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1431 if (!sysexes().empty()) {
1432 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1434 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1435 c->change (*i, (*i)->time() + t);
1438 apply_command_as_subcommand (s->session(), c);