2 Copyright (C) 2007 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "pbd/error.h"
27 #include "pbd/enumwriter.h"
28 #include "pbd/compose.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"
37 #include "ardour/midi_automation_list_binder.h"
42 using namespace ARDOUR
;
45 MidiModel::MidiModel (boost::shared_ptr
<MidiSource
> s
)
46 : AutomatableSequence
<TimeType
>(s
->session())
51 /** Start a new NoteDiff command.
53 * This has no side-effects on the model or Session, the returned command
54 * can be held on to for as long as the caller wishes, or discarded without
55 * formality, until apply_command is called and ownership is taken.
57 MidiModel::NoteDiffCommand
*
58 MidiModel::new_note_diff_command (const string name
)
60 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
63 return new NoteDiffCommand (ms
->model(), name
);
66 /** Start a new SysExDiff command */
67 MidiModel::SysExDiffCommand
*
68 MidiModel::new_sysex_diff_command (const string name
)
70 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
73 return new SysExDiffCommand (ms
->model(), name
);
76 /** Start a new PatchChangeDiff command */
77 MidiModel::PatchChangeDiffCommand
*
78 MidiModel::new_patch_change_diff_command (const string name
)
80 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
83 return new PatchChangeDiffCommand (ms
->model(), name
);
89 * Ownership of cmd is taken, it must not be deleted by the caller.
90 * The command will constitute one item on the undo stack.
93 MidiModel::apply_command(Session
& session
, Command
* cmd
)
95 session
.begin_reversible_command(cmd
->name());
97 session
.commit_reversible_command(cmd
);
101 /** Apply a command as part of a larger reversible transaction
103 * Ownership of cmd is taken, it must not be deleted by the caller.
104 * The command will constitute one item on the undo stack.
107 MidiModel::apply_command_as_subcommand(Session
& session
, Command
* cmd
)
110 session
.add_command(cmd
);
114 /************** DIFF COMMAND ********************/
116 #define NOTE_DIFF_COMMAND_ELEMENT "NoteDiffCommand"
117 #define DIFF_NOTES_ELEMENT "ChangedNotes"
118 #define ADDED_NOTES_ELEMENT "AddedNotes"
119 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
120 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
121 #define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand"
122 #define DIFF_SYSEXES_ELEMENT "ChangedSysExes"
123 #define PATCH_CHANGE_DIFF_COMMAND_ELEMENT "PatchChangeDiffCommand"
124 #define ADDED_PATCH_CHANGES_ELEMENT "AddedPatchChanges"
125 #define REMOVED_PATCH_CHANGES_ELEMENT "RemovedPatchChanges"
126 #define DIFF_PATCH_CHANGES_ELEMENT "ChangedPatchChanges"
128 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr
<MidiModel
> m
, const std::string
& name
)
136 MidiModel::NoteDiffCommand::NoteDiffCommand (boost::shared_ptr
<MidiModel
> m
, const XMLNode
& node
)
137 : DiffCommand (m
, "")
140 set_state (node
, Stateful::loading_state_version
);
144 MidiModel::NoteDiffCommand::add (const NotePtr note
)
146 _removed_notes
.remove(note
);
147 _added_notes
.push_back(note
);
151 MidiModel::NoteDiffCommand::remove (const NotePtr note
)
153 _added_notes
.remove(note
);
154 _removed_notes
.push_back(note
);
158 MidiModel::NoteDiffCommand::side_effect_remove (const NotePtr note
)
160 side_effect_removals
.insert (note
);
164 MidiModel::NoteDiffCommand::change (const NotePtr note
, Property prop
,
173 if (new_value
== note
->note()) {
176 change
.old_value
= note
->note();
179 if (new_value
== note
->velocity()) {
182 change
.old_value
= note
->velocity();
185 if (new_value
== note
->channel()) {
188 change
.old_value
= note
->channel();
193 fatal
<< "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg
;
197 fatal
<< "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg
;
203 change
.property
= prop
;
204 change
.new_value
= new_value
;
206 _changes
.push_back (change
);
210 MidiModel::NoteDiffCommand::change (const NotePtr note
, Property prop
,
221 fatal
<< "MidiModel::NoteDiffCommand::change() with time argument called for note, channel or velocity" << endmsg
;
225 if (Evoral::musical_time_equal (note
->time(), new_time
)) {
228 change
.old_time
= note
->time();
231 if (Evoral::musical_time_equal (note
->length(), new_time
)) {
234 change
.old_time
= note
->length();
239 change
.property
= prop
;
240 change
.new_time
= new_time
;
242 _changes
.push_back (change
);
245 MidiModel::NoteDiffCommand
&
246 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand
& other
)
248 if (this == &other
) {
252 if (_model
!= other
._model
) {
256 _added_notes
.insert (_added_notes
.end(), other
._added_notes
.begin(), other
._added_notes
.end());
257 _removed_notes
.insert (_removed_notes
.end(), other
._removed_notes
.begin(), other
._removed_notes
.end());
258 side_effect_removals
.insert (other
.side_effect_removals
.begin(), other
.side_effect_removals
.end());
259 _changes
.insert (_changes
.end(), other
._changes
.begin(), other
._changes
.end());
265 MidiModel::NoteDiffCommand::operator() ()
268 MidiModel::WriteLock
lock(_model
->edit_lock());
270 for (NoteList::iterator i
= _added_notes
.begin(); i
!= _added_notes
.end(); ++i
) {
271 if (!_model
->add_note_unlocked(*i
)) {
272 /* failed to add it, so don't leave it in the removed list, to
273 avoid apparent errors on undo.
275 _removed_notes
.remove (*i
);
279 for (NoteList::iterator i
= _removed_notes
.begin(); i
!= _removed_notes
.end(); ++i
) {
280 _model
->remove_note_unlocked(*i
);
283 /* notes we modify in a way that requires remove-then-add to maintain ordering */
284 set
<NotePtr
> temporary_removals
;
286 for (ChangeList::iterator i
= _changes
.begin(); i
!= _changes
.end(); ++i
) {
287 Property prop
= i
->property
;
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_note (i
->new_value
);
298 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
299 _model
->remove_note_unlocked (i
->note
);
300 temporary_removals
.insert (i
->note
);
303 i
->note
->set_time (i
->new_time
);
307 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
308 _model
->remove_note_unlocked (i
->note
);
309 temporary_removals
.insert (i
->note
);
311 i
->note
->set_channel (i
->new_value
);
314 /* no remove-then-add required for these properties, since we do not index them
318 i
->note
->set_velocity (i
->new_value
);
322 i
->note
->set_length (i
->new_time
);
329 for (set
<NotePtr
>::iterator i
= temporary_removals
.begin(); i
!= temporary_removals
.end(); ++i
) {
330 NoteDiffCommand
side_effects (model(), "side effects");
331 if (_model
->add_note_unlocked (*i
, &side_effects
)) {
332 /* The note was re-added ok */
333 *this += side_effects
;
335 /* The note that we removed earlier could not be re-added. This change record
336 must say that the note was removed. It is an un-note.
339 /* We didn't change it... */
340 for (ChangeList::iterator j
= _changes
.begin(); j
!= _changes
.end(); ) {
342 ChangeList::iterator k
= j
;
352 /* ...in fact, we removed it */
353 _removed_notes
.push_back (*i
);
357 if (!side_effect_removals
.empty()) {
359 for (set
<NotePtr
>::iterator i
= side_effect_removals
.begin(); i
!= side_effect_removals
.end(); ++i
) {
360 cerr
<< "\t" << *i
<< ' ' << **i
<< endl
;
365 _model
->ContentsChanged(); /* EMIT SIGNAL */
369 MidiModel::NoteDiffCommand::undo ()
372 MidiModel::WriteLock
lock(_model
->edit_lock());
374 for (NoteList::iterator i
= _added_notes
.begin(); i
!= _added_notes
.end(); ++i
) {
375 _model
->remove_note_unlocked(*i
);
378 for (NoteList::iterator i
= _removed_notes
.begin(); i
!= _removed_notes
.end(); ++i
) {
379 _model
->add_note_unlocked(*i
);
382 /* notes we modify in a way that requires remove-then-add to maintain ordering */
383 set
<NotePtr
> temporary_removals
;
385 for (ChangeList::iterator i
= _changes
.begin(); i
!= _changes
.end(); ++i
) {
386 Property prop
= i
->property
;
389 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
390 _model
->remove_note_unlocked (i
->note
);
391 temporary_removals
.insert (i
->note
);
393 i
->note
->set_note (i
->old_value
);
396 i
->note
->set_velocity (i
->old_value
);
399 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
400 _model
->remove_note_unlocked (i
->note
);
401 temporary_removals
.insert (i
->note
);
403 i
->note
->set_time (i
->old_time
);
406 i
->note
->set_length (i
->old_time
);
409 if (temporary_removals
.find (i
->note
) == temporary_removals
.end()) {
410 _model
->remove_note_unlocked (i
->note
);
411 temporary_removals
.insert (i
->note
);
413 i
->note
->set_channel (i
->old_value
);
418 for (set
<NotePtr
>::iterator i
= temporary_removals
.begin(); i
!= temporary_removals
.end(); ++i
) {
419 _model
->add_note_unlocked (*i
);
422 /* finally add back notes that were removed by the "do". we don't care
423 about side effects here since the model should be back to its original
424 state once this is done.
427 for (set
<NotePtr
>::iterator i
= side_effect_removals
.begin(); i
!= side_effect_removals
.end(); ++i
) {
428 _model
->add_note_unlocked (*i
);
432 _model
->ContentsChanged(); /* EMIT SIGNAL */
436 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note
)
438 XMLNode
* xml_note
= new XMLNode("note");
441 ostringstream
id_str(ios::ate
);
442 id_str
<< int(note
->id());
443 xml_note
->add_property("id", id_str
.str());
447 ostringstream
note_str(ios::ate
);
448 note_str
<< int(note
->note());
449 xml_note
->add_property("note", note_str
.str());
453 ostringstream
channel_str(ios::ate
);
454 channel_str
<< int(note
->channel());
455 xml_note
->add_property("channel", channel_str
.str());
459 ostringstream
time_str(ios::ate
);
460 time_str
<< note
->time();
461 xml_note
->add_property("time", time_str
.str());
465 ostringstream
length_str(ios::ate
);
466 length_str
<< note
->length();
467 xml_note
->add_property("length", length_str
.str());
471 ostringstream
velocity_str(ios::ate
);
472 velocity_str
<< (unsigned int) note
->velocity();
473 xml_note
->add_property("velocity", velocity_str
.str());
479 Evoral::Sequence
<MidiModel::TimeType
>::NotePtr
480 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode
*xml_note
)
484 unsigned int channel
;
487 unsigned int velocity
;
490 if ((prop
= xml_note
->property("id")) != 0) {
491 istringstream
id_str(prop
->value());
494 error
<< "note information missing ID value" << endmsg
;
498 if ((prop
= xml_note
->property("note")) != 0) {
499 istringstream
note_str(prop
->value());
502 warning
<< "note information missing note value" << endmsg
;
506 if ((prop
= xml_note
->property("channel")) != 0) {
507 istringstream
channel_str(prop
->value());
508 channel_str
>> channel
;
510 warning
<< "note information missing channel" << endmsg
;
514 if ((prop
= xml_note
->property("time")) != 0) {
515 istringstream
time_str(prop
->value());
518 warning
<< "note information missing time" << endmsg
;
522 if ((prop
= xml_note
->property("length")) != 0) {
523 istringstream
length_str(prop
->value());
524 length_str
>> length
;
526 warning
<< "note information missing length" << endmsg
;
530 if ((prop
= xml_note
->property("velocity")) != 0) {
531 istringstream
velocity_str(prop
->value());
532 velocity_str
>> velocity
;
534 warning
<< "note information missing velocity" << endmsg
;
538 NotePtr
note_ptr(new Evoral::Note
<TimeType
>(channel
, time
, length
, note
, velocity
));
539 note_ptr
->set_id (id
);
545 MidiModel::NoteDiffCommand::marshal_change (const NoteChange
& change
)
547 XMLNode
* xml_change
= new XMLNode("Change");
549 /* first, the change itself */
551 xml_change
->add_property ("property", enum_2_string (change
.property
));
554 ostringstream
old_value_str (ios::ate
);
555 if (change
.property
== StartTime
|| change
.property
== Length
) {
556 old_value_str
<< change
.old_time
;
558 old_value_str
<< (unsigned int) change
.old_value
;
560 xml_change
->add_property ("old", old_value_str
.str());
564 ostringstream
new_value_str (ios::ate
);
565 if (change
.property
== StartTime
|| change
.property
== Length
) {
566 new_value_str
<< change
.new_time
;
568 new_value_str
<< (unsigned int) change
.new_value
;
570 xml_change
->add_property ("new", new_value_str
.str());
573 ostringstream id_str
;
574 id_str
<< change
.note
->id();
575 xml_change
->add_property ("id", id_str
.str());
580 MidiModel::NoteDiffCommand::NoteChange
581 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode
*xml_change
)
586 if ((prop
= xml_change
->property("property")) != 0) {
587 change
.property
= (Property
) string_2_enum (prop
->value(), change
.property
);
589 fatal
<< "!!!" << endmsg
;
593 if ((prop
= xml_change
->property ("id")) == 0) {
594 error
<< _("No NoteID found for note property change - ignored") << endmsg
;
598 gint note_id
= atoi (prop
->value().c_str());
600 if ((prop
= xml_change
->property ("old")) != 0) {
601 istringstream
old_str (prop
->value());
602 if (change
.property
== StartTime
|| change
.property
== Length
) {
603 old_str
>> change
.old_time
;
605 int integer_value_so_that_istream_does_the_right_thing
;
606 old_str
>> integer_value_so_that_istream_does_the_right_thing
;
607 change
.old_value
= integer_value_so_that_istream_does_the_right_thing
;
610 fatal
<< "!!!" << endmsg
;
614 if ((prop
= xml_change
->property ("new")) != 0) {
615 istringstream
new_str (prop
->value());
616 if (change
.property
== StartTime
|| change
.property
== Length
) {
617 new_str
>> change
.new_time
;
619 int integer_value_so_that_istream_does_the_right_thing
;
620 new_str
>> integer_value_so_that_istream_does_the_right_thing
;
621 change
.new_value
= integer_value_so_that_istream_does_the_right_thing
;
624 fatal
<< "!!!" << endmsg
;
628 /* we must point at the instance of the note that is actually in the model.
629 so go look for it ...
632 change
.note
= _model
->find_note (note_id
);
635 warning
<< "MIDI note #" << note_id
<< " not found in model - programmers should investigate this" << endmsg
;
643 MidiModel::NoteDiffCommand::set_state (const XMLNode
& diff_command
, int /*version*/)
645 if (diff_command
.name() != string (NOTE_DIFF_COMMAND_ELEMENT
)) {
651 _added_notes
.clear();
652 XMLNode
* added_notes
= diff_command
.child(ADDED_NOTES_ELEMENT
);
654 XMLNodeList notes
= added_notes
->children();
655 transform(notes
.begin(), notes
.end(), back_inserter(_added_notes
),
656 boost::bind (&NoteDiffCommand::unmarshal_note
, this, _1
));
662 _removed_notes
.clear();
663 XMLNode
* removed_notes
= diff_command
.child(REMOVED_NOTES_ELEMENT
);
665 XMLNodeList notes
= removed_notes
->children();
666 transform(notes
.begin(), notes
.end(), back_inserter(_removed_notes
),
667 boost::bind (&NoteDiffCommand::unmarshal_note
, this, _1
));
675 XMLNode
* changed_notes
= diff_command
.child(DIFF_NOTES_ELEMENT
);
678 XMLNodeList notes
= changed_notes
->children();
679 transform (notes
.begin(), notes
.end(), back_inserter(_changes
),
680 boost::bind (&NoteDiffCommand::unmarshal_change
, this, _1
));
684 /* side effect removals caused by changes */
686 side_effect_removals
.clear();
688 XMLNode
* side_effect_notes
= diff_command
.child(SIDE_EFFECT_REMOVALS_ELEMENT
);
690 if (side_effect_notes
) {
691 XMLNodeList notes
= side_effect_notes
->children();
692 for (XMLNodeList::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
693 side_effect_removals
.insert (unmarshal_note (*n
));
701 MidiModel::NoteDiffCommand::get_state ()
703 XMLNode
* diff_command
= new XMLNode (NOTE_DIFF_COMMAND_ELEMENT
);
704 diff_command
->add_property("midi-source", _model
->midi_source()->id().to_s());
706 XMLNode
* changes
= diff_command
->add_child(DIFF_NOTES_ELEMENT
);
707 for_each(_changes
.begin(), _changes
.end(),
709 boost::bind (&XMLNode::add_child_nocopy
, changes
, _1
),
710 boost::bind (&NoteDiffCommand::marshal_change
, this, _1
)));
712 XMLNode
* added_notes
= diff_command
->add_child(ADDED_NOTES_ELEMENT
);
713 for_each(_added_notes
.begin(), _added_notes
.end(),
715 boost::bind (&XMLNode::add_child_nocopy
, added_notes
, _1
),
716 boost::bind (&NoteDiffCommand::marshal_note
, this, _1
)));
718 XMLNode
* removed_notes
= diff_command
->add_child(REMOVED_NOTES_ELEMENT
);
719 for_each(_removed_notes
.begin(), _removed_notes
.end(),
721 boost::bind (&XMLNode::add_child_nocopy
, removed_notes
, _1
),
722 boost::bind (&NoteDiffCommand::marshal_note
, this, _1
)));
724 /* if this command had side-effects, store that state too
727 if (!side_effect_removals
.empty()) {
728 XMLNode
* side_effect_notes
= diff_command
->add_child(SIDE_EFFECT_REMOVALS_ELEMENT
);
729 for_each(side_effect_removals
.begin(), side_effect_removals
.end(),
731 boost::bind (&XMLNode::add_child_nocopy
, side_effect_notes
, _1
),
732 boost::bind (&NoteDiffCommand::marshal_note
, this, _1
)));
735 return *diff_command
;
738 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr
<MidiModel
> m
, const XMLNode
& node
)
739 : DiffCommand (m
, "")
742 set_state (node
, Stateful::loading_state_version
);
746 MidiModel::SysExDiffCommand::change (boost::shared_ptr
<Evoral::Event
<TimeType
> > s
, TimeType new_time
)
751 change
.property
= Time
;
752 change
.old_time
= s
->time ();
753 change
.new_time
= new_time
;
755 _changes
.push_back (change
);
759 MidiModel::SysExDiffCommand::operator() ()
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
->new_time
);
772 _model
->ContentsChanged (); /* EMIT SIGNAL */
776 MidiModel::SysExDiffCommand::undo ()
779 MidiModel::WriteLock
lock (_model
->edit_lock ());
781 for (ChangeList::iterator i
= _changes
.begin(); i
!= _changes
.end(); ++i
) {
782 switch (i
->property
) {
784 i
->sysex
->set_time (i
->old_time
);
791 _model
->ContentsChanged(); /* EMIT SIGNAL */
795 MidiModel::SysExDiffCommand::marshal_change (const Change
& change
)
797 XMLNode
* xml_change
= new XMLNode ("Change");
799 /* first, the change itself */
801 xml_change
->add_property ("property", enum_2_string (change
.property
));
804 ostringstream
old_value_str (ios::ate
);
805 old_value_str
<< change
.old_time
;
806 xml_change
->add_property ("old", old_value_str
.str());
810 ostringstream
new_value_str (ios::ate
);
811 new_value_str
<< change
.new_time
;
812 xml_change
->add_property ("new", new_value_str
.str());
815 ostringstream id_str
;
816 id_str
<< change
.sysex
->id();
817 xml_change
->add_property ("id", id_str
.str());
822 MidiModel::SysExDiffCommand::Change
823 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode
*xml_change
)
828 if ((prop
= xml_change
->property ("property")) != 0) {
829 change
.property
= (Property
) string_2_enum (prop
->value(), change
.property
);
831 fatal
<< "!!!" << endmsg
;
835 if ((prop
= xml_change
->property ("id")) == 0) {
836 error
<< _("No SysExID found for sys-ex property change - ignored") << endmsg
;
840 gint sysex_id
= atoi (prop
->value().c_str());
842 if ((prop
= xml_change
->property ("old")) != 0) {
843 istringstream
old_str (prop
->value());
844 old_str
>> change
.old_time
;
846 fatal
<< "!!!" << endmsg
;
850 if ((prop
= xml_change
->property ("new")) != 0) {
851 istringstream
new_str (prop
->value());
852 new_str
>> change
.new_time
;
854 fatal
<< "!!!" << endmsg
;
858 /* we must point at the instance of the sysex that is actually in the model.
859 so go look for it ...
862 change
.sysex
= _model
->find_sysex (sysex_id
);
865 warning
<< "Sys-ex #" << sysex_id
<< " not found in model - programmers should investigate this" << endmsg
;
873 MidiModel::SysExDiffCommand::set_state (const XMLNode
& diff_command
, int /*version*/)
875 if (diff_command
.name() != string (SYSEX_DIFF_COMMAND_ELEMENT
)) {
883 XMLNode
* changed_sysexes
= diff_command
.child (DIFF_SYSEXES_ELEMENT
);
885 if (changed_sysexes
) {
886 XMLNodeList sysexes
= changed_sysexes
->children();
887 transform (sysexes
.begin(), sysexes
.end(), back_inserter (_changes
),
888 boost::bind (&SysExDiffCommand::unmarshal_change
, this, _1
));
896 MidiModel::SysExDiffCommand::get_state ()
898 XMLNode
* diff_command
= new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT
);
899 diff_command
->add_property ("midi-source", _model
->midi_source()->id().to_s());
901 XMLNode
* changes
= diff_command
->add_child(DIFF_SYSEXES_ELEMENT
);
902 for_each (_changes
.begin(), _changes
.end(),
904 boost::bind (&XMLNode::add_child_nocopy
, changes
, _1
),
905 boost::bind (&SysExDiffCommand::marshal_change
, this, _1
)));
907 return *diff_command
;
910 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr
<MidiModel
> m
, const string
& name
)
911 : DiffCommand (m
, name
)
916 MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr
<MidiModel
> m
, const XMLNode
& node
)
917 : DiffCommand (m
, "")
920 set_state (node
, Stateful::loading_state_version
);
924 MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p
)
926 _added
.push_back (p
);
930 MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p
)
932 _removed
.push_back (p
);
936 MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch
, TimeType t
)
941 c
.old_time
= patch
->time ();
944 _changes
.push_back (c
);
948 MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch
, uint8_t channel
)
951 c
.property
= Channel
;
953 c
.old_channel
= patch
->channel ();
954 c
.new_channel
= channel
;
956 _changes
.push_back (c
);
960 MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch
, uint8_t program
)
963 c
.property
= Program
;
965 c
.old_program
= patch
->program ();
966 c
.new_program
= program
;
968 _changes
.push_back (c
);
972 MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch
, int bank
)
977 c
.old_bank
= patch
->bank ();
980 _changes
.push_back (c
);
984 MidiModel::PatchChangeDiffCommand::operator() ()
987 MidiModel::WriteLock
lock (_model
->edit_lock ());
989 for (list
<PatchChangePtr
>::iterator i
= _added
.begin(); i
!= _added
.end(); ++i
) {
990 _model
->add_patch_change_unlocked (*i
);
993 for (list
<PatchChangePtr
>::iterator i
= _removed
.begin(); i
!= _removed
.end(); ++i
) {
994 _model
->remove_patch_change_unlocked (*i
);
997 set
<PatchChangePtr
> temporary_removals
;
999 for (ChangeList::iterator i
= _changes
.begin(); i
!= _changes
.end(); ++i
) {
1000 switch (i
->property
) {
1002 if (temporary_removals
.find (i
->patch
) == temporary_removals
.end()) {
1003 _model
->remove_patch_change_unlocked (i
->patch
);
1004 temporary_removals
.insert (i
->patch
);
1006 i
->patch
->set_time (i
->new_time
);
1010 i
->patch
->set_channel (i
->new_channel
);
1014 i
->patch
->set_program (i
->new_program
);
1018 i
->patch
->set_bank (i
->new_bank
);
1023 for (set
<PatchChangePtr
>::iterator i
= temporary_removals
.begin(); i
!= temporary_removals
.end(); ++i
) {
1024 _model
->add_patch_change_unlocked (*i
);
1028 _model
->ContentsChanged (); /* EMIT SIGNAL */
1032 MidiModel::PatchChangeDiffCommand::undo ()
1035 MidiModel::WriteLock
lock (_model
->edit_lock());
1037 for (list
<PatchChangePtr
>::iterator i
= _added
.begin(); i
!= _added
.end(); ++i
) {
1038 _model
->remove_patch_change_unlocked (*i
);
1041 for (list
<PatchChangePtr
>::iterator i
= _removed
.begin(); i
!= _removed
.end(); ++i
) {
1042 _model
->add_patch_change_unlocked (*i
);
1045 set
<PatchChangePtr
> temporary_removals
;
1047 for (ChangeList::iterator i
= _changes
.begin(); i
!= _changes
.end(); ++i
) {
1048 switch (i
->property
) {
1050 if (temporary_removals
.find (i
->patch
) == temporary_removals
.end()) {
1051 _model
->remove_patch_change_unlocked (i
->patch
);
1052 temporary_removals
.insert (i
->patch
);
1054 i
->patch
->set_time (i
->old_time
);
1058 i
->patch
->set_channel (i
->old_channel
);
1062 i
->patch
->set_program (i
->old_program
);
1066 i
->patch
->set_bank (i
->old_bank
);
1071 for (set
<PatchChangePtr
>::iterator i
= temporary_removals
.begin(); i
!= temporary_removals
.end(); ++i
) {
1072 _model
->add_patch_change_unlocked (*i
);
1077 _model
->ContentsChanged (); /* EMIT SIGNAL */
1081 MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p
)
1083 XMLNode
* n
= new XMLNode ("patch-change");
1086 ostringstream
s (ios::ate
);
1087 s
<< int (p
->id ());
1088 n
->add_property ("id", s
.str());
1092 ostringstream
s (ios::ate
);
1094 n
->add_property ("time", s
.str ());
1098 ostringstream
s (ios::ate
);
1099 s
<< int (p
->channel ());
1100 n
->add_property ("channel", s
.str ());
1104 ostringstream
s (ios::ate
);
1105 s
<< int (p
->program ());
1106 n
->add_property ("program", s
.str ());
1110 ostringstream
s (ios::ate
);
1111 s
<< int (p
->bank ());
1112 n
->add_property ("bank", s
.str ());
1119 MidiModel::PatchChangeDiffCommand::marshal_change (const Change
& c
)
1121 XMLNode
* n
= new XMLNode (X_("Change"));
1123 n
->add_property (X_("property"), enum_2_string (c
.property
));
1126 ostringstream
s (ios::ate
);
1127 if (c
.property
== Time
) {
1129 } else if (c
.property
== Channel
) {
1131 } else if (c
.property
== Program
) {
1132 s
<< int (c
.old_program
);
1133 } else if (c
.property
== Bank
) {
1137 n
->add_property (X_("old"), s
.str ());
1141 ostringstream
s (ios::ate
);
1143 if (c
.property
== Time
) {
1145 } else if (c
.property
== Channel
) {
1147 } else if (c
.property
== Program
) {
1148 s
<< int (c
.new_program
);
1149 } else if (c
.property
== Bank
) {
1153 n
->add_property (X_("new"), s
.str ());
1158 s
<< c
.patch
->id ();
1159 n
->add_property ("id", s
.str ());
1165 MidiModel::PatchChangePtr
1166 MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode
* n
)
1169 Evoral::event_id_t id
;
1170 Evoral::MusicalTime time
= 0;
1171 uint8_t channel
= 0;
1172 uint8_t program
= 0;
1175 if ((prop
= n
->property ("id")) != 0) {
1176 istringstream
s (prop
->value());
1180 if ((prop
= n
->property ("time")) != 0) {
1181 istringstream
s (prop
->value ());
1185 if ((prop
= n
->property ("channel")) != 0) {
1186 istringstream
s (prop
->value ());
1190 if ((prop
= n
->property ("program")) != 0) {
1191 istringstream
s (prop
->value ());
1195 if ((prop
= n
->property ("bank")) != 0) {
1196 istringstream
s (prop
->value ());
1200 PatchChangePtr
p (new Evoral::PatchChange
<TimeType
> (time
, channel
, program
, bank
));
1205 MidiModel::PatchChangeDiffCommand::Change
1206 MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode
* n
)
1211 prop
= n
->property ("property");
1213 c
.property
= (Property
) string_2_enum (prop
->value(), c
.property
);
1215 prop
= n
->property ("id");
1217 Evoral::event_id_t
const id
= atoi (prop
->value().c_str());
1219 prop
= n
->property ("old");
1222 istringstream
s (prop
->value ());
1223 if (c
.property
== Time
) {
1225 } else if (c
.property
== Channel
) {
1227 } else if (c
.property
== Program
) {
1229 } else if (c
.property
== Bank
) {
1234 prop
= n
->property ("new");
1237 istringstream
s (prop
->value ());
1238 if (c
.property
== Time
) {
1240 } else if (c
.property
== Channel
) {
1242 } else if (c
.property
== Program
) {
1244 } else if (c
.property
== Bank
) {
1249 c
.patch
= _model
->find_patch_change (id
);
1256 MidiModel::PatchChangeDiffCommand::set_state (const XMLNode
& diff_command
, int /*version*/)
1258 if (diff_command
.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT
) {
1263 XMLNode
* added
= diff_command
.child (ADDED_PATCH_CHANGES_ELEMENT
);
1265 XMLNodeList p
= added
->children ();
1266 transform (p
.begin(), p
.end(), back_inserter (_added
), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change
, this, _1
));
1270 XMLNode
* removed
= diff_command
.child (REMOVED_PATCH_CHANGES_ELEMENT
);
1272 XMLNodeList p
= removed
->children ();
1273 transform (p
.begin(), p
.end(), back_inserter (_removed
), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change
, this, _1
));
1277 XMLNode
* changed
= diff_command
.child (DIFF_PATCH_CHANGES_ELEMENT
);
1279 XMLNodeList p
= changed
->children ();
1280 transform (p
.begin(), p
.end(), back_inserter (_changes
), boost::bind (&PatchChangeDiffCommand::unmarshal_change
, this, _1
));
1287 MidiModel::PatchChangeDiffCommand::get_state ()
1289 XMLNode
* diff_command
= new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT
);
1290 diff_command
->add_property("midi-source", _model
->midi_source()->id().to_s());
1292 XMLNode
* added
= diff_command
->add_child (ADDED_PATCH_CHANGES_ELEMENT
);
1293 for_each (_added
.begin(), _added
.end(),
1295 boost::bind (&XMLNode::add_child_nocopy
, added
, _1
),
1296 boost::bind (&PatchChangeDiffCommand::marshal_patch_change
, this, _1
)
1300 XMLNode
* removed
= diff_command
->add_child (REMOVED_PATCH_CHANGES_ELEMENT
);
1301 for_each (_removed
.begin(), _removed
.end(),
1303 boost::bind (&XMLNode::add_child_nocopy
, removed
, _1
),
1304 boost::bind (&PatchChangeDiffCommand::marshal_patch_change
, this, _1
)
1308 XMLNode
* changes
= diff_command
->add_child (DIFF_PATCH_CHANGES_ELEMENT
);
1309 for_each (_changes
.begin(), _changes
.end(),
1311 boost::bind (&XMLNode::add_child_nocopy
, changes
, _1
),
1312 boost::bind (&PatchChangeDiffCommand::marshal_change
, this, _1
)
1316 return *diff_command
;
1319 /** Write all of the model to a MidiSource (i.e. save the model).
1320 * This is different from manually using read to write to a source in that
1321 * note off events are written regardless of the track mode. This is so the
1322 * user can switch a recorded track (with note durations from some instrument)
1323 * to percussive, save, reload, then switch it back to sustained without
1324 * destroying the original note durations.
1326 * Similarly, control events are written without interpolation (as with the
1330 MidiModel::write_to (boost::shared_ptr
<MidiSource
> source
)
1332 ReadLock
lock(read_lock());
1334 const bool old_percussive
= percussive();
1335 set_percussive(false);
1337 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
1340 source
->drop_model();
1341 source
->mark_streaming_midi_write_started (note_mode());
1343 for (Evoral::Sequence
<TimeType
>::const_iterator i
= begin(0, true); i
!= end(); ++i
) {
1344 source
->append_event_unlocked_beats(*i
);
1347 set_percussive(old_percussive
);
1348 source
->mark_streaming_write_completed();
1355 /** very similar to ::write_to() but writes to the model's own
1356 existing midi_source, without making it call MidiSource::drop_model().
1357 the caller is a MidiSource that needs to catch up with the state
1361 MidiModel::sync_to_source ()
1363 ReadLock
lock(read_lock());
1365 const bool old_percussive
= percussive();
1366 set_percussive(false);
1368 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
1371 ms
->mark_streaming_midi_write_started (note_mode());
1373 for (Evoral::Sequence
<TimeType
>::const_iterator i
= begin(0, true); i
!= end(); ++i
) {
1374 ms
->append_event_unlocked_beats(*i
);
1377 set_percussive (old_percussive
);
1378 ms
->mark_streaming_write_completed ();
1385 /** Write part or all of the model to a MidiSource (i.e. save the model).
1386 * This is different from manually using read to write to a source in that
1387 * note off events are written regardless of the track mode. This is so the
1388 * user can switch a recorded track (with note durations from some instrument)
1389 * to percussive, save, reload, then switch it back to sustained without
1390 * destroying the original note durations.
1393 MidiModel::write_section_to (boost::shared_ptr
<MidiSource
> source
, Evoral::MusicalTime begin_time
, Evoral::MusicalTime end_time
)
1395 ReadLock
lock(read_lock());
1396 MidiStateTracker mst
;
1397 Evoral::MusicalTime extra_note_on_time
= end_time
;
1399 const bool old_percussive
= percussive();
1400 set_percussive(false);
1402 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
1405 source
->drop_model();
1406 source
->mark_streaming_midi_write_started (note_mode());
1408 for (Evoral::Sequence
<TimeType
>::const_iterator i
= begin(0, true); i
!= end(); ++i
) {
1409 const Evoral::Event
<Evoral::MusicalTime
>& ev (*i
);
1411 if (ev
.time() >= begin_time
&& ev
.time() < end_time
) {
1413 const Evoral::MIDIEvent
<Evoral::MusicalTime
>* mev
=
1414 static_cast<const Evoral::MIDIEvent
<Evoral::MusicalTime
>* > (&ev
);
1421 if (mev
->is_note_off()) {
1423 if (!mst
.active (mev
->note(), mev
->channel())) {
1425 /* add a note-on at the start of the range we're writing
1426 to the file. velocity is just an arbitary reasonable value.
1429 Evoral::MIDIEvent
<Evoral::MusicalTime
> on (mev
->event_type(), extra_note_on_time
, 3, 0, true);
1430 on
.set_type (mev
->type());
1431 on
.set_note (mev
->note());
1432 on
.set_channel (mev
->channel());
1433 on
.set_velocity (mev
->velocity());
1435 cerr
<< "Add note on for odd note off, note = " << (int) on
.note() << endl
;
1436 source
->append_event_unlocked_beats (on
);
1437 mst
.add (on
.note(), on
.channel());
1439 extra_note_on_time
+= 1.0/128.0;
1442 cerr
<< "MIDI Note off (note = " << (int) mev
->note() << endl
;
1443 source
->append_event_unlocked_beats (*i
);
1444 mst
.remove (mev
->note(), mev
->channel());
1447 } else if (mev
->is_note_on()) {
1448 cerr
<< "MIDI Note on (note = " << (int) mev
->note() << endl
;
1449 mst
.add (mev
->note(), mev
->channel());
1450 source
->append_event_unlocked_beats(*i
);
1453 cerr
<< "MIDI other event type\n";
1454 source
->append_event_unlocked_beats(*i
);
1459 mst
.resolve_notes (*source
, end_time
);
1461 set_percussive(old_percussive
);
1462 source
->mark_streaming_write_completed();
1470 MidiModel::get_state()
1472 XMLNode
*node
= new XMLNode("MidiModel");
1476 Evoral::Sequence
<MidiModel::TimeType
>::NotePtr
1477 MidiModel::find_note (NotePtr other
)
1479 Notes::iterator l
= notes().lower_bound(other
);
1481 if (l
!= notes().end()) {
1482 for (; (*l
)->time() == other
->time(); ++l
) {
1483 /* NB: compare note contents, not note pointers.
1484 If "other" was a ptr to a note already in
1485 the model, we wouldn't be looking for it,
1488 if (**l
== *other
) {
1497 Evoral::Sequence
<MidiModel::TimeType
>::NotePtr
1498 MidiModel::find_note (gint note_id
)
1500 /* used only for looking up notes when reloading history from disk,
1501 so we don't care about performance *too* much.
1504 for (Notes::iterator l
= notes().begin(); l
!= notes().end(); ++l
) {
1505 if ((*l
)->id() == note_id
) {
1513 MidiModel::PatchChangePtr
1514 MidiModel::find_patch_change (Evoral::event_id_t id
)
1516 for (PatchChanges::iterator i
= patch_changes().begin(); i
!= patch_changes().end(); ++i
) {
1517 if ((*i
)->id() == id
) {
1522 return PatchChangePtr ();
1525 boost::shared_ptr
<Evoral::Event
<MidiModel::TimeType
> >
1526 MidiModel::find_sysex (gint sysex_id
)
1528 /* used only for looking up notes when reloading history from disk,
1529 so we don't care about performance *too* much.
1532 for (SysExes::iterator l
= sysexes().begin(); l
!= sysexes().end(); ++l
) {
1533 if ((*l
)->id() == sysex_id
) {
1538 return boost::shared_ptr
<Evoral::Event
<TimeType
> > ();
1541 /** Lock and invalidate the source.
1542 * This should be used by commands and editing things
1544 MidiModel::WriteLock
1545 MidiModel::edit_lock()
1547 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
1550 Glib::Mutex::Lock
* source_lock
= new Glib::Mutex::Lock (ms
->mutex());
1551 ms
->invalidate(); // Release cached iterator's read lock on model
1552 return WriteLock(new WriteLockImpl(source_lock
, _lock
, _control_lock
));
1555 /** Lock just the model, the source lock must already be held.
1556 * This should only be called from libardour/evoral places
1558 MidiModel::WriteLock
1559 MidiModel::write_lock()
1561 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
1564 assert (!ms
->mutex().trylock ());
1565 return WriteLock(new WriteLockImpl(NULL
, _lock
, _control_lock
));
1569 MidiModel::resolve_overlaps_unlocked (const NotePtr note
, void* arg
)
1571 using namespace Evoral
;
1573 if (_writing
|| insert_merge_policy() == InsertMergeRelax
) {
1577 NoteDiffCommand
* cmd
= static_cast<NoteDiffCommand
*>(arg
);
1579 TimeType sa
= note
->time();
1580 TimeType ea
= note
->end_time();
1582 const Pitches
& p (pitches (note
->channel()));
1583 NotePtr
search_note(new Note
<TimeType
>(0, 0, 0, note
->note()));
1584 set
<NotePtr
> to_be_deleted
;
1585 bool set_note_length
= false;
1586 bool set_note_time
= false;
1587 TimeType note_time
= note
->time();
1588 TimeType note_length
= note
->length();
1590 DEBUG_TRACE (DEBUG::Sequence
, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note
->note(), note
->time()));
1592 for (Pitches::const_iterator i
= p
.lower_bound (search_note
);
1593 i
!= p
.end() && (*i
)->note() == note
->note(); ++i
) {
1595 TimeType sb
= (*i
)->time();
1596 TimeType eb
= (*i
)->end_time();
1597 OverlapType overlap
= OverlapNone
;
1599 if ((sb
> sa
) && (eb
<= ea
)) {
1600 overlap
= OverlapInternal
;
1601 } else if ((eb
>= sa
) && (eb
<= ea
)) {
1602 overlap
= OverlapStart
;
1603 } else if ((sb
> sa
) && (sb
<= ea
)) {
1604 overlap
= OverlapEnd
;
1605 } else if ((sa
>= sb
) && (sa
<= eb
) && (ea
<= eb
)) {
1606 overlap
= OverlapExternal
;
1612 DEBUG_TRACE (DEBUG::Sequence
, string_compose (
1613 "\toverlap is %1 for (%2,%3) vs (%4,%5)\n",
1614 enum_2_string(overlap
), sa
, ea
, sb
, eb
));
1616 if (insert_merge_policy() == InsertMergeReject
) {
1617 DEBUG_TRACE (DEBUG::Sequence
, string_compose ("%1 just reject\n", this));
1623 cerr
<< "OverlapStart\n";
1624 /* existing note covers start of new note */
1625 switch (insert_merge_policy()) {
1626 case InsertMergeReplace
:
1627 to_be_deleted
.insert (*i
);
1629 case InsertMergeTruncateExisting
:
1631 cmd
->change (*i
, NoteDiffCommand::Length
, (note
->time() - (*i
)->time()));
1633 (*i
)->set_length (note
->time() - (*i
)->time());
1635 case InsertMergeTruncateAddition
:
1636 set_note_time
= true;
1637 set_note_length
= true;
1638 note_time
= (*i
)->time() + (*i
)->length();
1639 note_length
= min (note_length
, (*i
)->length() - ((*i
)->end_time() - note
->time()));
1641 case InsertMergeExtend
:
1643 cmd
->change ((*i
), NoteDiffCommand::Length
, note
->end_time() - (*i
)->time());
1645 (*i
)->set_length (note
->end_time() - (*i
)->time());
1646 return -1; /* do not add the new note */
1656 cerr
<< "OverlapEnd\n";
1657 /* existing note covers end of new note */
1658 switch (insert_merge_policy()) {
1659 case InsertMergeReplace
:
1660 to_be_deleted
.insert (*i
);
1663 case InsertMergeTruncateExisting
:
1664 /* resetting the start time of the existing note
1665 is a problem because of time ordering.
1669 case InsertMergeTruncateAddition
:
1670 set_note_length
= true;
1671 note_length
= min (note_length
, ((*i
)->time() - note
->time()));
1674 case InsertMergeExtend
:
1675 /* we can't reset the time of the existing note because
1676 that will corrupt time ordering. So remove the
1677 existing note and change the position/length
1678 of the new note (which has not been added yet)
1680 to_be_deleted
.insert (*i
);
1681 set_note_length
= true;
1682 note_length
= min (note_length
, (*i
)->end_time() - note
->time());
1691 case OverlapExternal
:
1692 cerr
<< "OverlapExt\n";
1693 /* existing note overlaps all the new note */
1694 switch (insert_merge_policy()) {
1695 case InsertMergeReplace
:
1696 to_be_deleted
.insert (*i
);
1698 case InsertMergeTruncateExisting
:
1699 case InsertMergeTruncateAddition
:
1700 case InsertMergeExtend
:
1701 /* cannot add in this case */
1710 case OverlapInternal
:
1711 cerr
<< "OverlapInt\n";
1712 /* new note fully overlaps an existing note */
1713 switch (insert_merge_policy()) {
1714 case InsertMergeReplace
:
1715 case InsertMergeTruncateExisting
:
1716 case InsertMergeTruncateAddition
:
1717 case InsertMergeExtend
:
1718 /* delete the existing note, the new one will cover it */
1719 to_be_deleted
.insert (*i
);
1735 for (set
<NotePtr
>::iterator i
= to_be_deleted
.begin(); i
!= to_be_deleted
.end(); ++i
) {
1736 remove_note_unlocked (*i
);
1739 cmd
->side_effect_remove (*i
);
1743 if (set_note_time
) {
1745 cmd
->change (note
, NoteDiffCommand::StartTime
, note_time
);
1747 note
->set_time (note_time
);
1750 if (set_note_length
) {
1752 cmd
->change (note
, NoteDiffCommand::Length
, note_length
);
1754 note
->set_length (note_length
);
1761 MidiModel::insert_merge_policy () const
1763 /* XXX ultimately this should be a per-track or even per-model policy */
1764 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
1767 return ms
->session().config
.get_insert_merge_policy ();
1771 MidiModel::set_midi_source (boost::shared_ptr
<MidiSource
> s
)
1773 boost::shared_ptr
<MidiSource
> old
= _midi_source
.lock ();
1779 _midi_source_connections
.drop_connections ();
1783 s
->InterpolationChanged
.connect_same_thread (
1784 _midi_source_connections
, boost::bind (&MidiModel::source_interpolation_changed
, this, _1
, _2
)
1787 s
->AutomationStateChanged
.connect_same_thread (
1788 _midi_source_connections
, boost::bind (&MidiModel::source_automation_state_changed
, this, _1
, _2
)
1792 /** The source has signalled that the interpolation style for a parameter has changed. In order to
1793 * keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1794 * appropriate ControlList.
1796 * The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1797 * or the other is listened to by the GUI.
1800 MidiModel::source_interpolation_changed (Evoral::Parameter p
, Evoral::ControlList::InterpolationStyle s
)
1802 Glib::Mutex::Lock
lm (_control_lock
);
1803 control(p
)->list()->set_interpolation (s
);
1806 /** A ControlList has signalled that its interpolation style has changed. Again, in order to keep
1807 * MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1810 MidiModel::control_list_interpolation_changed (Evoral::Parameter p
, Evoral::ControlList::InterpolationStyle s
)
1812 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
1815 ms
->set_interpolation_of (p
, s
);
1819 MidiModel::source_automation_state_changed (Evoral::Parameter p
, AutoState s
)
1821 Glib::Mutex::Lock
lm (_control_lock
);
1822 boost::shared_ptr
<AutomationList
> al
= boost::dynamic_pointer_cast
<AutomationList
> (control(p
)->list ());
1823 al
->set_automation_state (s
);
1827 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p
, AutoState s
)
1829 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
1831 ms
->set_automation_state_of (p
, s
);
1834 boost::shared_ptr
<Evoral::Control
>
1835 MidiModel::control_factory (Evoral::Parameter
const & p
)
1837 boost::shared_ptr
<Evoral::Control
> c
= Automatable::control_factory (p
);
1839 /* Set up newly created control's lists to the appropriate interpolation and
1840 automation state from our source.
1843 boost::shared_ptr
<MidiSource
> ms
= _midi_source
.lock ();
1846 c
->list()->set_interpolation (ms
->interpolation_of (p
));
1848 boost::shared_ptr
<AutomationList
> al
= boost::dynamic_pointer_cast
<AutomationList
> (c
->list ());
1851 al
->set_automation_state (ms
->automation_state_of (p
));
1856 boost::shared_ptr
<const MidiSource
>
1857 MidiModel::midi_source ()
1859 return _midi_source
.lock ();
1862 /** Moves notes, controllers and sys-ex to insert silence at the start of the model.
1863 * Adds commands to the session's current undo stack to reflect the movements.
1866 MidiModel::insert_silence_at_start (TimeType t
)
1868 boost::shared_ptr
<MidiSource
> s
= _midi_source
.lock ();
1873 if (!notes().empty ()) {
1874 NoteDiffCommand
* c
= new_note_diff_command ("insert silence");
1876 for (Notes::const_iterator i
= notes().begin(); i
!= notes().end(); ++i
) {
1877 c
->change (*i
, NoteDiffCommand::StartTime
, (*i
)->time() + t
);
1880 apply_command_as_subcommand (s
->session(), c
);
1885 for (Controls::iterator i
= controls().begin(); i
!= controls().end(); ++i
) {
1886 boost::shared_ptr
<AutomationControl
> ac
= boost::dynamic_pointer_cast
<AutomationControl
> (i
->second
);
1887 XMLNode
& before
= ac
->alist()->get_state ();
1888 i
->second
->list()->shift (0, t
);
1889 XMLNode
& after
= ac
->alist()->get_state ();
1890 s
->session().add_command (new MementoCommand
<AutomationList
> (new MidiAutomationListBinder (s
, i
->first
), &before
, &after
));
1895 if (!sysexes().empty()) {
1896 SysExDiffCommand
* c
= new_sysex_diff_command ("insert silence");
1898 for (SysExes::iterator i
= sysexes().begin(); i
!= sysexes().end(); ++i
) {
1899 c
->change (*i
, (*i
)->time() + t
);
1902 apply_command_as_subcommand (s
->session(), c
);
1906 /** Transpose notes in a time range by a given number of semitones. Notes
1907 * will be clamped at 0 and 127 if the transposition would make them exceed
1910 * @param from Start time.
1911 * @param end End time.
1912 * @param semitones Number of semitones to transpose by (+ve is higher, -ve is lower).
1915 MidiModel::transpose (TimeType from
, TimeType to
, int semitones
)
1917 boost::shared_ptr
<const MidiSource
> s
= midi_source ();
1919 NoteDiffCommand
* c
= new_note_diff_command (_("transpose"));
1921 for (Notes::iterator i
= notes().begin(); i
!= notes().end(); ++i
) {
1923 if ((*i
)->time() >= to
) {
1928 } else if ((*i
)->time() >= from
) {
1930 int new_note
= (*i
)->note() + semitones
;
1934 } else if (new_note
> 127) {
1938 c
->change (*i
, NoteDiffCommand::NoteNumber
, (uint8_t) new_note
);
1943 apply_command (s
->session (), c
);