use -r argument with JACK if realtime is not requested in engine dialog (also applied...
[ArdourMidi.git] / libs / ardour / midi_model.cc
blob6922384e71736d4d86aa52ba10c402b8a2912775
1 /*
2 Copyright (C) 2007 Paul Davis
3 Author: Dave 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 #define __STDC_LIMIT_MACROS 1
22 #include <set>
23 #include <iostream>
24 #include <algorithm>
25 #include <stdexcept>
26 #include <stdint.h>
27 #include "pbd/error.h"
28 #include "pbd/enumwriter.h"
29 #include "midi++/events.h"
31 #include "ardour/midi_model.h"
32 #include "ardour/midi_source.h"
33 #include "ardour/midi_state_tracker.h"
34 #include "ardour/smf_source.h"
35 #include "ardour/types.h"
36 #include "ardour/session.h"
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
42 MidiModel::MidiModel(MidiSource* s)
43 : AutomatableSequence<TimeType>(s->session())
44 , _midi_source (0)
46 set_midi_source (s);
49 /** Start a new Diff command.
51 * This has no side-effects on the model or Session, the returned command
52 * can be held on to for as long as the caller wishes, or discarded without
53 * formality, until apply_command is called and ownership is taken.
55 MidiModel::DiffCommand*
56 MidiModel::new_diff_command(const string name)
58 DiffCommand* cmd = new DiffCommand(_midi_source->model(), name);
59 return cmd;
62 /** Apply a command.
64 * Ownership of cmd is taken, it must not be deleted by the caller.
65 * The command will constitute one item on the undo stack.
67 void
68 MidiModel::apply_command(Session& session, Command* cmd)
70 session.begin_reversible_command(cmd->name());
71 (*cmd)();
72 session.commit_reversible_command(cmd);
73 set_edited(true);
76 /** Apply a command as part of a larger reversible transaction
78 * Ownership of cmd is taken, it must not be deleted by the caller.
79 * The command will constitute one item on the undo stack.
81 void
82 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
84 (*cmd)();
85 session.add_command(cmd);
86 set_edited(true);
89 /************** DIFF COMMAND ********************/
91 #define DIFF_COMMAND_ELEMENT "DiffCommand"
92 #define DIFF_NOTES_ELEMENT "ChangedNotes"
93 #define ADDED_NOTES_ELEMENT "AddedNotes"
94 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
95 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
97 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
98 : Command(name)
99 , _model(m)
100 , _name(name)
102 assert(_model);
105 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
106 : _model(m)
108 assert(_model);
109 set_state(node, Stateful::loading_state_version);
112 void
113 MidiModel::DiffCommand::add(const NotePtr note)
115 _removed_notes.remove(note);
116 _added_notes.push_back(note);
119 void
120 MidiModel::DiffCommand::remove(const NotePtr note)
122 _added_notes.remove(note);
123 _removed_notes.push_back(note);
126 void
127 MidiModel::DiffCommand::side_effect_remove(const NotePtr note)
129 side_effect_removals.insert (note);
132 void
133 MidiModel::DiffCommand::change(const NotePtr note, Property prop,
134 uint8_t new_value)
136 NoteChange change;
138 switch (prop) {
139 case NoteNumber:
140 if (new_value == note->note()) {
141 return;
143 change.old_value = note->note();
144 break;
145 case Velocity:
146 if (new_value == note->velocity()) {
147 return;
149 change.old_value = note->velocity();
150 break;
151 case Channel:
152 if (new_value == note->channel()) {
153 return;
155 change.old_value = note->channel();
156 break;
159 case StartTime:
160 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
161 /*NOTREACHED*/
162 break;
163 case Length:
164 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
165 /*NOTREACHED*/
166 break;
169 change.note = note;
170 change.property = prop;
171 change.new_value = new_value;
173 _changes.push_back (change);
176 void
177 MidiModel::DiffCommand::change(const NotePtr note, Property prop,
178 TimeType new_time)
180 NoteChange change;
182 switch (prop) {
183 case NoteNumber:
184 case Channel:
185 case Velocity:
186 fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
187 break;
189 case StartTime:
190 if (Evoral::musical_time_equal (note->time(), new_time)) {
191 return;
193 change.old_time = note->time();
194 break;
195 case Length:
196 if (Evoral::musical_time_equal (note->length(), new_time)) {
197 return;
199 change.old_time = note->length();
200 break;
203 change.note = note;
204 change.property = prop;
205 change.new_time = new_time;
207 _changes.push_back (change);
210 MidiModel::DiffCommand&
211 MidiModel::DiffCommand::operator+= (const DiffCommand& other)
213 if (this == &other) {
214 return *this;
217 if (_model != other._model) {
218 return *this;
221 _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
222 _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
223 side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
224 _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
226 return *this;
229 void
230 MidiModel::DiffCommand::operator()()
233 MidiModel::WriteLock lock(_model->edit_lock());
235 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
236 if (!_model->add_note_unlocked(*i)) {
237 /* failed to add it, so don't leave it in the removed list, to
238 avoid apparent errors on undo.
240 _removed_notes.remove (*i);
244 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
245 _model->remove_note_unlocked(*i);
248 /* notes we modify in a way that requires remove-then-add to maintain ordering */
249 set<NotePtr> temporary_removals;
251 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
252 Property prop = i->property;
253 switch (prop) {
254 case NoteNumber:
255 if (temporary_removals.find (i->note) == temporary_removals.end()) {
256 _model->remove_note_unlocked (i->note);
257 temporary_removals.insert (i->note);
259 i->note->set_note (i->new_value);
260 break;
262 case StartTime:
263 if (temporary_removals.find (i->note) == temporary_removals.end()) {
264 _model->remove_note_unlocked (i->note);
265 temporary_removals.insert (i->note);
268 i->note->set_time (i->new_time);
269 break;
271 case Channel:
272 if (temporary_removals.find (i->note) == temporary_removals.end()) {
273 _model->remove_note_unlocked (i->note);
274 temporary_removals.insert (i->note);
276 i->note->set_channel (i->new_value);
277 break;
279 /* no remove-then-add required for these properties, since we do not index them
282 case Velocity:
283 i->note->set_velocity (i->new_value);
284 break;
286 case Length:
287 i->note->set_length (i->new_time);
288 break;
294 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
295 DiffCommand side_effects (model(), "side effects");
296 _model->add_note_unlocked (*i, &side_effects);
297 *this += side_effects;
300 if (!side_effect_removals.empty()) {
301 cerr << "SER: \n";
302 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
303 cerr << "\t" << *i << ' ' << **i << endl;
308 _model->ContentsChanged(); /* EMIT SIGNAL */
311 void
312 MidiModel::DiffCommand::undo()
315 MidiModel::WriteLock lock(_model->edit_lock());
317 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
318 _model->remove_note_unlocked(*i);
321 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
322 _model->add_note_unlocked(*i);
325 /* notes we modify in a way that requires remove-then-add to maintain ordering */
326 set<NotePtr> temporary_removals;
328 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
329 Property prop = i->property;
330 switch (prop) {
331 case NoteNumber:
332 if (temporary_removals.find (i->note) == temporary_removals.end()) {
333 _model->remove_note_unlocked (i->note);
334 temporary_removals.insert (i->note);
336 i->note->set_note (i->old_value);
337 break;
338 case Velocity:
339 i->note->set_velocity (i->old_value);
340 break;
341 case StartTime:
342 if (temporary_removals.find (i->note) == temporary_removals.end()) {
343 _model->remove_note_unlocked (i->note);
344 temporary_removals.insert (i->note);
346 i->note->set_time (i->old_time);
347 break;
348 case Length:
349 i->note->set_length (i->old_time);
350 break;
351 case Channel:
352 if (temporary_removals.find (i->note) == temporary_removals.end()) {
353 _model->remove_note_unlocked (i->note);
354 temporary_removals.insert (i->note);
356 i->note->set_channel (i->old_value);
357 break;
361 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
362 _model->add_note_unlocked (*i);
365 /* finally add back notes that were removed by the "do". we don't care
366 about side effects here since the model should be back to its original
367 state once this is done.
370 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
371 _model->add_note_unlocked (*i);
375 _model->ContentsChanged(); /* EMIT SIGNAL */
378 XMLNode&
379 MidiModel::DiffCommand::marshal_note(const NotePtr note)
381 XMLNode* xml_note = new XMLNode("note");
383 cerr << "Marshalling note: " << *note << endl;
386 ostringstream id_str(ios::ate);
387 id_str << int(note->id());
388 xml_note->add_property("id", id_str.str());
392 ostringstream note_str(ios::ate);
393 note_str << int(note->note());
394 xml_note->add_property("note", note_str.str());
398 ostringstream channel_str(ios::ate);
399 channel_str << int(note->channel());
400 xml_note->add_property("channel", channel_str.str());
404 ostringstream time_str(ios::ate);
405 time_str << note->time();
406 xml_note->add_property("time", time_str.str());
410 ostringstream length_str(ios::ate);
411 length_str << note->length();
412 xml_note->add_property("length", length_str.str());
416 ostringstream velocity_str(ios::ate);
417 velocity_str << (unsigned int) note->velocity();
418 xml_note->add_property("velocity", velocity_str.str());
421 return *xml_note;
424 Evoral::Sequence<MidiModel::TimeType>::NotePtr
425 MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
427 unsigned int note;
428 XMLProperty* prop;
429 unsigned int channel;
430 unsigned int time;
431 unsigned int length;
432 unsigned int velocity;
433 gint id;
435 if ((prop = xml_note->property("id")) != 0) {
436 istringstream id_str(prop->value());
437 id_str >> id;
438 } else {
439 error << "note information missing ID value" << endmsg;
440 id = -1;
443 if ((prop = xml_note->property("note")) != 0) {
444 istringstream note_str(prop->value());
445 note_str >> note;
446 } else {
447 warning << "note information missing note value" << endmsg;
448 note = 127;
451 if ((prop = xml_note->property("channel")) != 0) {
452 istringstream channel_str(prop->value());
453 channel_str >> channel;
454 } else {
455 warning << "note information missing channel" << endmsg;
456 channel = 0;
459 if ((prop = xml_note->property("time")) != 0) {
460 istringstream time_str(prop->value());
461 time_str >> time;
462 } else {
463 warning << "note information missing time" << endmsg;
464 time = 0;
467 if ((prop = xml_note->property("length")) != 0) {
468 istringstream length_str(prop->value());
469 length_str >> length;
470 } else {
471 warning << "note information missing length" << endmsg;
472 length = 1;
475 if ((prop = xml_note->property("velocity")) != 0) {
476 istringstream velocity_str(prop->value());
477 velocity_str >> velocity;
478 } else {
479 warning << "note information missing velocity" << endmsg;
480 velocity = 127;
483 NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
484 note_ptr->set_id (id);
486 return note_ptr;
489 XMLNode&
490 MidiModel::DiffCommand::marshal_change(const NoteChange& change)
492 XMLNode* xml_change = new XMLNode("Change");
494 /* first, the change itself */
496 xml_change->add_property ("property", enum_2_string (change.property));
499 ostringstream old_value_str (ios::ate);
500 if (change.property == StartTime || change.property == Length) {
501 old_value_str << change.old_time;
502 } else {
503 old_value_str << (unsigned int) change.old_value;
505 xml_change->add_property ("old", old_value_str.str());
509 ostringstream new_value_str (ios::ate);
510 if (change.property == StartTime || change.property == Length) {
511 new_value_str << change.new_time;
512 } else {
513 new_value_str << (unsigned int) change.new_value;
515 xml_change->add_property ("new", new_value_str.str());
518 ostringstream id_str;
519 id_str << change.note->id();
520 xml_change->add_property ("id", id_str.str());
522 return *xml_change;
525 MidiModel::DiffCommand::NoteChange
526 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
528 XMLProperty* prop;
529 NoteChange change;
531 if ((prop = xml_change->property("property")) != 0) {
532 change.property = (Property) string_2_enum (prop->value(), change.property);
533 } else {
534 fatal << "!!!" << endmsg;
535 /*NOTREACHED*/
538 if ((prop = xml_change->property ("id")) == 0) {
539 error << _("No NoteID found for note property change - ignored") << endmsg;
540 return change;
543 gint note_id = atoi (prop->value().c_str());
545 if ((prop = xml_change->property ("old")) != 0) {
546 istringstream old_str (prop->value());
547 if (change.property == StartTime || change.property == Length) {
548 old_str >> change.old_time;
549 } else {
550 int integer_value_so_that_istream_does_the_right_thing;
551 old_str >> integer_value_so_that_istream_does_the_right_thing;
552 change.old_value = integer_value_so_that_istream_does_the_right_thing;
554 } else {
555 fatal << "!!!" << endmsg;
556 /*NOTREACHED*/
559 if ((prop = xml_change->property ("new")) != 0) {
560 istringstream new_str (prop->value());
561 if (change.property == StartTime || change.property == Length) {
562 new_str >> change.new_time;
563 } else {
564 int integer_value_so_that_istream_does_the_right_thing;
565 new_str >> integer_value_so_that_istream_does_the_right_thing;
566 change.new_value = integer_value_so_that_istream_does_the_right_thing;
568 } else {
569 fatal << "!!!" << endmsg;
570 /*NOTREACHED*/
573 /* we must point at the instance of the note that is actually in the model.
574 so go look for it ...
577 change.note = _model->find_note (note_id);
579 if (!change.note) {
580 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
581 return change;
584 return change;
588 MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
590 if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
591 return 1;
594 /* additions */
596 _added_notes.clear();
597 XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
598 if (added_notes) {
599 XMLNodeList notes = added_notes->children();
600 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
601 boost::bind (&DiffCommand::unmarshal_note, this, _1));
605 /* removals */
607 _removed_notes.clear();
608 XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
609 if (removed_notes) {
610 XMLNodeList notes = removed_notes->children();
611 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
612 boost::bind (&DiffCommand::unmarshal_note, this, _1));
616 /* changes */
618 _changes.clear();
620 XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
622 if (changed_notes) {
623 XMLNodeList notes = changed_notes->children();
624 transform (notes.begin(), notes.end(), back_inserter(_changes),
625 boost::bind (&DiffCommand::unmarshal_change, this, _1));
629 /* side effect removals caused by changes */
631 side_effect_removals.clear();
633 XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
635 if (side_effect_notes) {
636 XMLNodeList notes = side_effect_notes->children();
637 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
638 side_effect_removals.insert (unmarshal_note (*n));
642 return 0;
645 XMLNode&
646 MidiModel::DiffCommand::get_state ()
648 XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
649 diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
651 XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
652 for_each(_changes.begin(), _changes.end(),
653 boost::bind (
654 boost::bind (&XMLNode::add_child_nocopy, changes, _1),
655 boost::bind (&DiffCommand::marshal_change, this, _1)));
657 XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
658 for_each(_added_notes.begin(), _added_notes.end(),
659 boost::bind(
660 boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
661 boost::bind (&DiffCommand::marshal_note, this, _1)));
663 XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
664 for_each(_removed_notes.begin(), _removed_notes.end(),
665 boost::bind (
666 boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
667 boost::bind (&DiffCommand::marshal_note, this, _1)));
669 /* if this command had side-effects, store that state too
672 if (!side_effect_removals.empty()) {
673 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
674 for_each(side_effect_removals.begin(), side_effect_removals.end(),
675 boost::bind (
676 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
677 boost::bind (&DiffCommand::marshal_note, this, _1)));
680 return *diff_command;
684 /** Write all of the model to a MidiSource (i.e. save the model).
685 * This is different from manually using read to write to a source in that
686 * note off events are written regardless of the track mode. This is so the
687 * user can switch a recorded track (with note durations from some instrument)
688 * to percussive, save, reload, then switch it back to sustained without
689 * destroying the original note durations.
691 * Similarly, control events are written without interpolation (as with the
692 * `Discrete' mode).
694 bool
695 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
697 ReadLock lock(read_lock());
699 const bool old_percussive = percussive();
700 set_percussive(false);
702 source->drop_model();
703 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
705 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
706 source->append_event_unlocked_beats(*i);
709 set_percussive(old_percussive);
710 source->mark_streaming_write_completed();
712 set_edited(false);
714 return true;
717 /** very similar to ::write_to() but writes to the model's own
718 existing midi_source, without making it call MidiSource::drop_model().
719 the caller is a MidiSource that needs to catch up with the state
720 of the model.
722 bool
723 MidiModel::sync_to_source ()
725 ReadLock lock(read_lock());
727 const bool old_percussive = percussive();
728 set_percussive(false);
730 _midi_source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
732 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
733 _midi_source->append_event_unlocked_beats(*i);
736 set_percussive (old_percussive);
737 _midi_source->mark_streaming_write_completed ();
739 set_edited (false);
741 return true;
744 /** Write part or all of the model to a MidiSource (i.e. save the model).
745 * This is different from manually using read to write to a source in that
746 * note off events are written regardless of the track mode. This is so the
747 * user can switch a recorded track (with note durations from some instrument)
748 * to percussive, save, reload, then switch it back to sustained without
749 * destroying the original note durations.
751 bool
752 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
754 ReadLock lock(read_lock());
755 MidiStateTracker mst;
756 Evoral::MusicalTime extra_note_on_time = end_time;
758 const bool old_percussive = percussive();
759 set_percussive(false);
761 source->drop_model();
762 source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
764 for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
765 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
767 if (ev.time() >= begin_time && ev.time() < end_time) {
769 const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
770 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
772 if (!mev) {
773 continue;
777 if (mev->is_note_off()) {
779 if (!mst.active (mev->note(), mev->channel())) {
781 /* add a note-on at the start of the range we're writing
782 to the file. velocity is just an arbitary reasonable value.
785 Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
786 on.set_type (mev->type());
787 on.set_note (mev->note());
788 on.set_channel (mev->channel());
789 on.set_velocity (mev->velocity());
791 cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
792 source->append_event_unlocked_beats (on);
793 mst.add (on.note(), on.channel());
794 mst.dump (cerr);
795 extra_note_on_time += 1.0/128.0;
798 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
799 source->append_event_unlocked_beats (*i);
800 mst.remove (mev->note(), mev->channel());
801 mst.dump (cerr);
803 } else if (mev->is_note_on()) {
804 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
805 mst.add (mev->note(), mev->channel());
806 source->append_event_unlocked_beats(*i);
807 mst.dump (cerr);
808 } else {
809 cerr << "MIDI other event type\n";
810 source->append_event_unlocked_beats(*i);
815 mst.resolve_notes (*source, end_time);
817 set_percussive(old_percussive);
818 source->mark_streaming_write_completed();
820 set_edited(false);
822 return true;
825 XMLNode&
826 MidiModel::get_state()
828 XMLNode *node = new XMLNode("MidiModel");
829 return *node;
832 Evoral::Sequence<MidiModel::TimeType>::NotePtr
833 MidiModel::find_note (NotePtr other)
835 Notes::iterator l = notes().lower_bound(other);
837 if (l != notes().end()) {
838 for (; (*l)->time() == other->time(); ++l) {
839 /* NB: compare note contents, not note pointers.
840 If "other" was a ptr to a note already in
841 the model, we wouldn't be looking for it,
842 would we now?
844 if (**l == *other) {
845 return *l;
850 return NotePtr();
853 Evoral::Sequence<MidiModel::TimeType>::NotePtr
854 MidiModel::find_note (gint note_id)
856 /* used only for looking up notes when reloading history from disk,
857 so we don't care about performance *too* much.
860 for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
861 if ((*l)->id() == note_id) {
862 return *l;
866 return NotePtr();
869 /** Lock and invalidate the source.
870 * This should be used by commands and editing things
872 MidiModel::WriteLock
873 MidiModel::edit_lock()
875 Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
876 _midi_source->invalidate(); // Release cached iterator's read lock on model
877 return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
880 /** Lock just the model, the source lock must already be held.
881 * This should only be called from libardour/evoral places
883 MidiModel::WriteLock
884 MidiModel::write_lock()
886 assert(!_midi_source->mutex().trylock());
887 return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
891 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
893 using namespace Evoral;
895 if (_writing || insert_merge_policy() == InsertMergeRelax) {
896 return 0;
899 DiffCommand* cmd = static_cast<DiffCommand*>(arg);
901 TimeType sa = note->time();
902 TimeType ea = note->end_time();
904 const Pitches& p (pitches (note->channel()));
905 NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
906 set<NotePtr> to_be_deleted;
907 bool set_note_length = false;
908 bool set_note_time = false;
909 TimeType note_time = note->time();
910 TimeType note_length = note->length();
912 for (Pitches::const_iterator i = p.lower_bound (search_note);
913 i != p.end() && (*i)->note() == note->note(); ++i) {
915 TimeType sb = (*i)->time();
916 TimeType eb = (*i)->end_time();
917 OverlapType overlap = OverlapNone;
919 if ((sb > sa) && (eb <= ea)) {
920 overlap = OverlapInternal;
921 } else if ((eb >= sa) && (eb <= ea)) {
922 overlap = OverlapStart;
923 } else if ((sb > sa) && (sb <= ea)) {
924 overlap = OverlapEnd;
925 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
926 overlap = OverlapExternal;
927 } else {
928 /* no overlap */
929 continue;
932 if (insert_merge_policy() == InsertMergeReject) {
933 return -1;
936 switch (overlap) {
937 case OverlapStart:
938 cerr << "OverlapStart\n";
939 /* existing note covers start of new note */
940 switch (insert_merge_policy()) {
941 case InsertMergeReplace:
942 to_be_deleted.insert (*i);
943 break;
944 case InsertMergeTruncateExisting:
945 if (cmd) {
946 cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
948 (*i)->set_length (note->time() - (*i)->time());
949 break;
950 case InsertMergeTruncateAddition:
951 set_note_time = true;
952 set_note_length = true;
953 note_time = (*i)->time() + (*i)->length();
954 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
955 break;
956 case InsertMergeExtend:
957 if (cmd) {
958 cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
960 (*i)->set_length (note->end_time() - (*i)->time());
961 return -1; /* do not add the new note */
962 break;
963 default:
964 /*NOTREACHED*/
965 /* stupid gcc */
966 break;
968 break;
970 case OverlapEnd:
971 cerr << "OverlapEnd\n";
972 /* existing note covers end of new note */
973 switch (insert_merge_policy()) {
974 case InsertMergeReplace:
975 to_be_deleted.insert (*i);
976 break;
978 case InsertMergeTruncateExisting:
979 /* resetting the start time of the existing note
980 is a problem because of time ordering.
982 break;
984 case InsertMergeTruncateAddition:
985 set_note_length = true;
986 note_length = min (note_length, ((*i)->time() - note->time()));
987 break;
989 case InsertMergeExtend:
990 /* we can't reset the time of the existing note because
991 that will corrupt time ordering. So remove the
992 existing note and change the position/length
993 of the new note (which has not been added yet)
995 to_be_deleted.insert (*i);
996 set_note_length = true;
997 note_length = min (note_length, (*i)->end_time() - note->time());
998 break;
999 default:
1000 /*NOTREACHED*/
1001 /* stupid gcc */
1002 break;
1004 break;
1006 case OverlapExternal:
1007 cerr << "OverlapExt\n";
1008 /* existing note overlaps all the new note */
1009 switch (insert_merge_policy()) {
1010 case InsertMergeReplace:
1011 to_be_deleted.insert (*i);
1012 break;
1013 case InsertMergeTruncateExisting:
1014 case InsertMergeTruncateAddition:
1015 case InsertMergeExtend:
1016 /* cannot add in this case */
1017 return -1;
1018 default:
1019 /*NOTREACHED*/
1020 /* stupid gcc */
1021 break;
1023 break;
1025 case OverlapInternal:
1026 cerr << "OverlapInt\n";
1027 /* new note fully overlaps an existing note */
1028 switch (insert_merge_policy()) {
1029 case InsertMergeReplace:
1030 case InsertMergeTruncateExisting:
1031 case InsertMergeTruncateAddition:
1032 case InsertMergeExtend:
1033 /* delete the existing note, the new one will cover it */
1034 to_be_deleted.insert (*i);
1035 break;
1036 default:
1037 /*NOTREACHED*/
1038 /* stupid gcc */
1039 break;
1041 break;
1043 default:
1044 /*NOTREACHED*/
1045 /* stupid gcc */
1046 break;
1050 for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1051 remove_note_unlocked (*i);
1053 if (cmd) {
1054 cmd->side_effect_remove (*i);
1058 if (set_note_time) {
1059 if (cmd) {
1060 cmd->change (note, DiffCommand::StartTime, note_time);
1062 note->set_time (note_time);
1065 if (set_note_length) {
1066 if (cmd) {
1067 cmd->change (note, DiffCommand::Length, note_length);
1069 note->set_length (note_length);
1072 return 0;
1075 InsertMergePolicy
1076 MidiModel::insert_merge_policy () const
1078 /* XXX ultimately this should be a per-track or even per-model policy */
1080 return _midi_source->session().config.get_insert_merge_policy();
1083 void
1084 MidiModel::set_midi_source (MidiSource* s)
1086 if (_midi_source) {
1087 _midi_source->invalidate ();
1090 _midi_source_connections.drop_connections ();
1092 _midi_source = s;
1094 _midi_source->InterpolationChanged.connect_same_thread (
1095 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1099 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1100 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1101 * appropriate ControlList.
1103 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and the
1104 * MidiSource's InterpolationChanged signal is listened to by the GUI.
1106 void
1107 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1109 Glib::Mutex::Lock lm (_control_lock);
1110 control(p)->list()->set_interpolation (s);
1113 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1114 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1116 void
1117 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1119 _midi_source->set_interpolation_of (p, s);
1122 boost::shared_ptr<Evoral::Control>
1123 MidiModel::control_factory (Evoral::Parameter const & p)
1125 boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1127 /* Set up newly created control's lists to the appropriate interpolation state
1128 from our source.
1131 assert (_midi_source);
1133 c->list()->set_interpolation (_midi_source->interpolation_of (p));
1135 return c;