Fix more broken whitespace.
[ardour.git] / libs / ardour / midi_model.cc
1 /*
2   Copyright (C) 2007 Paul Davis
3   Author: Dave Robillard
4
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.
9
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.
14
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.
18
19 */
20
21 #include <set>
22 #include <iostream>
23 #include <algorithm>
24 #include <stdexcept>
25 #include <stdint.h>
26 #include "pbd/error.h"
27 #include "pbd/enumwriter.h"
28 #include "midi++/events.h"
29
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"
37
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
41
42 MidiModel::MidiModel (boost::shared_ptr<MidiSource> s)
43         : AutomatableSequence<TimeType>(s->session())
44 {
45         set_midi_source (s);
46 }
47
48 /** Start a new NoteDiff command.
49  *
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.
53  */
54 MidiModel::NoteDiffCommand*
55 MidiModel::new_note_diff_command (const string name)
56 {
57         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
58         assert (ms);
59         
60         return new NoteDiffCommand (ms->model(), name);
61 }
62
63 /** Start a new SysExDiff command */
64 MidiModel::SysExDiffCommand*
65 MidiModel::new_sysex_diff_command (const string name)
66 {
67         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
68         assert (ms);
69         
70         return new SysExDiffCommand (ms->model(), name);
71 }
72
73
74 /** Apply a command.
75  *
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.
78  */
79 void
80 MidiModel::apply_command(Session& session, Command* cmd)
81 {
82         session.begin_reversible_command(cmd->name());
83         (*cmd)();
84         session.commit_reversible_command(cmd);
85         set_edited(true);
86 }
87
88 /** Apply a command as part of a larger reversible transaction
89  *
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.
92  */
93 void
94 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
95 {
96         (*cmd)();
97         session.add_command(cmd);
98         set_edited(true);
99 }
100
101 /************** DIFF COMMAND ********************/
102
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"
110
111 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
112         : Command (name)
113         , _model (m)
114         , _name (name)
115 {
116         assert(_model);
117 }
118
119 MidiModel::NoteDiffCommand::NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
120         : DiffCommand (m, "")
121 {
122         assert (_model);
123         set_state (node, Stateful::loading_state_version);
124 }
125
126 void
127 MidiModel::NoteDiffCommand::add (const NotePtr note)
128 {
129         _removed_notes.remove(note);
130         _added_notes.push_back(note);
131 }
132
133 void
134 MidiModel::NoteDiffCommand::remove (const NotePtr note)
135 {
136         _added_notes.remove(note);
137         _removed_notes.push_back(note);
138 }
139
140 void
141 MidiModel::NoteDiffCommand::side_effect_remove (const NotePtr note)
142 {
143         side_effect_removals.insert (note);
144 }
145
146 void
147 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
148                                     uint8_t new_value)
149 {
150         assert (note);
151         
152         NoteChange change;
153         
154         switch (prop) {
155         case NoteNumber:
156                 if (new_value == note->note()) {
157                         return;
158                 }
159                 change.old_value = note->note();
160                 break;
161         case Velocity:
162                 if (new_value == note->velocity()) {
163                         return;
164                 }
165                 change.old_value = note->velocity();
166                 break;
167         case Channel:
168                 if (new_value == note->channel()) {
169                         return;
170                 }
171                 change.old_value = note->channel();
172                 break;
173
174
175         case StartTime:
176                 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
177                 /*NOTREACHED*/
178                 break;
179         case Length:
180                 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
181                 /*NOTREACHED*/
182                 break;
183         }
184
185         change.note = note;
186         change.property = prop;
187         change.new_value = new_value;
188
189         _changes.push_back (change);
190 }
191
192 void
193 MidiModel::NoteDiffCommand::change (const NotePtr note, Property prop,
194                                     TimeType new_time)
195 {
196         assert (note);
197         
198         NoteChange change;
199
200         switch (prop) {
201         case NoteNumber:
202         case Channel:
203         case Velocity:
204                 fatal << "MidiModel::NoteDiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
205                 break;
206
207         case StartTime:
208                 if (Evoral::musical_time_equal (note->time(), new_time)) {
209                         return;
210                 }
211                 change.old_time = note->time();
212                 break;
213         case Length:
214                 if (Evoral::musical_time_equal (note->length(), new_time)) {
215                         return;
216                 }
217                 change.old_time = note->length();
218                 break;
219         }
220
221         change.note = note;
222         change.property = prop;
223         change.new_time = new_time;
224
225         _changes.push_back (change);
226 }
227
228 MidiModel::NoteDiffCommand &
229 MidiModel::NoteDiffCommand::operator+= (const NoteDiffCommand& other)
230 {
231         if (this == &other) {
232                 return *this;
233         }
234
235         if (_model != other._model) {
236                 return *this;
237         }
238
239         _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
240         _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
241         side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
242         _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
243
244         return *this;
245 }
246
247 void
248 MidiModel::NoteDiffCommand::operator() ()
249 {
250         {
251                 MidiModel::WriteLock lock(_model->edit_lock());
252
253                 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
254                         if (!_model->add_note_unlocked(*i)) {
255                                 /* failed to add it, so don't leave it in the removed list, to
256                                    avoid apparent errors on undo.
257                                 */
258                                 _removed_notes.remove (*i);
259                         }
260                 }
261
262                 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
263                         _model->remove_note_unlocked(*i);
264                 }
265
266                 /* notes we modify in a way that requires remove-then-add to maintain ordering */
267                 set<NotePtr> temporary_removals;
268
269                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
270                         Property prop = i->property;
271                         switch (prop) {
272                         case NoteNumber:
273                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
274                                         _model->remove_note_unlocked (i->note);
275                                         temporary_removals.insert (i->note);
276                                 }
277                                 i->note->set_note (i->new_value);
278                                 break;
279
280                         case StartTime:
281                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
282                                         _model->remove_note_unlocked (i->note);
283                                         temporary_removals.insert (i->note);
284
285                                 }
286                                 i->note->set_time (i->new_time);
287                                 break;
288
289                         case Channel:
290                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
291                                         _model->remove_note_unlocked (i->note);
292                                         temporary_removals.insert (i->note);
293                                 }
294                                 i->note->set_channel (i->new_value);
295                                 break;
296
297                                 /* no remove-then-add required for these properties, since we do not index them
298                                  */
299
300                         case Velocity:
301                                 i->note->set_velocity (i->new_value);
302                                 break;
303
304                         case Length:
305                                 i->note->set_length (i->new_time);
306                                 break;
307
308                         }
309                 }
310
311
312                 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
313                         NoteDiffCommand side_effects (model(), "side effects");
314                         if (_model->add_note_unlocked (*i, &side_effects)) {
315                                 /* The note was re-added ok */
316                                 *this += side_effects;
317                         } else {
318                                 /* The note that we removed earlier could not be re-added.  This change record
319                                    must say that the note was removed.  It is an un-note.
320                                 */
321
322                                 /* We didn't change it... */
323                                 for (ChangeList::iterator j = _changes.begin(); j != _changes.end(); ) {
324
325                                         ChangeList::iterator k = j;
326                                         ++k;
327                                         
328                                         if (*i == j->note) {
329                                                 _changes.erase (j);
330                                         }
331
332                                         j = k;
333                                 }
334
335                                 /* ...in fact, we removed it */
336                                 _removed_notes.push_back (*i);
337                         }
338                 }
339
340                 if (!side_effect_removals.empty()) {
341                         cerr << "SER: \n";
342                         for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
343                                 cerr << "\t" << *i << ' ' << **i << endl;
344                         }
345                 }
346         }
347
348         _model->ContentsChanged(); /* EMIT SIGNAL */
349 }
350
351 void
352 MidiModel::NoteDiffCommand::undo ()
353 {
354         {
355                 MidiModel::WriteLock lock(_model->edit_lock());
356
357                 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
358                         _model->remove_note_unlocked(*i);
359                 }
360
361                 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
362                         _model->add_note_unlocked(*i);
363                 }
364
365                 /* notes we modify in a way that requires remove-then-add to maintain ordering */
366                 set<NotePtr> temporary_removals;
367
368                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
369                         Property prop = i->property;
370                         switch (prop) {
371                         case NoteNumber:
372                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
373                                         _model->remove_note_unlocked (i->note);
374                                         temporary_removals.insert (i->note);
375                                 }
376                                 i->note->set_note (i->old_value);
377                                 break;
378                         case Velocity:
379                                 i->note->set_velocity (i->old_value);
380                                 break;
381                         case StartTime:
382                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
383                                         _model->remove_note_unlocked (i->note);
384                                         temporary_removals.insert (i->note);
385                                 }
386                                 i->note->set_time (i->old_time);
387                                 break;
388                         case Length:
389                                 i->note->set_length (i->old_time);
390                                 break;
391                         case Channel:
392                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
393                                         _model->remove_note_unlocked (i->note);
394                                         temporary_removals.insert (i->note);
395                                 }
396                                 i->note->set_channel (i->old_value);
397                                 break;
398                         }
399                 }
400
401                 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
402                         _model->add_note_unlocked (*i);
403                 }
404
405                 /* finally add back notes that were removed by the "do". we don't care
406                    about side effects here since the model should be back to its original
407                    state once this is done.
408                 */
409
410                 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
411                         _model->add_note_unlocked (*i);
412                 }
413         }
414
415         _model->ContentsChanged(); /* EMIT SIGNAL */
416 }
417
418 XMLNode&
419 MidiModel::NoteDiffCommand::marshal_note(const NotePtr note)
420 {
421         XMLNode* xml_note = new XMLNode("note");
422
423         {
424                 ostringstream id_str(ios::ate);
425                 id_str << int(note->id());
426                 xml_note->add_property("id", id_str.str());
427         }
428
429         {
430                 ostringstream note_str(ios::ate);
431                 note_str << int(note->note());
432                 xml_note->add_property("note", note_str.str());
433         }
434
435         {
436                 ostringstream channel_str(ios::ate);
437                 channel_str << int(note->channel());
438                 xml_note->add_property("channel", channel_str.str());
439         }
440
441         {
442                 ostringstream time_str(ios::ate);
443                 time_str << note->time();
444                 xml_note->add_property("time", time_str.str());
445         }
446
447         {
448                 ostringstream length_str(ios::ate);
449                 length_str << note->length();
450                 xml_note->add_property("length", length_str.str());
451         }
452
453         {
454                 ostringstream velocity_str(ios::ate);
455                 velocity_str << (unsigned int) note->velocity();
456                 xml_note->add_property("velocity", velocity_str.str());
457         }
458
459         return *xml_note;
460 }
461
462 Evoral::Sequence<MidiModel::TimeType>::NotePtr
463 MidiModel::NoteDiffCommand::unmarshal_note (XMLNode *xml_note)
464 {
465         unsigned int note;
466         XMLProperty* prop;
467         unsigned int channel;
468         unsigned int time;
469         unsigned int length;
470         unsigned int velocity;
471         gint id;
472
473         if ((prop = xml_note->property("id")) != 0) {
474                 istringstream id_str(prop->value());
475                 id_str >> id;
476         } else {
477                 error << "note information missing ID value" << endmsg;
478                 id = -1;
479         }
480
481         if ((prop = xml_note->property("note")) != 0) {
482                 istringstream note_str(prop->value());
483                 note_str >> note;
484         } else {
485                 warning << "note information missing note value" << endmsg;
486                 note = 127;
487         }
488
489         if ((prop = xml_note->property("channel")) != 0) {
490                 istringstream channel_str(prop->value());
491                 channel_str >> channel;
492         } else {
493                 warning << "note information missing channel" << endmsg;
494                 channel = 0;
495         }
496
497         if ((prop = xml_note->property("time")) != 0) {
498                 istringstream time_str(prop->value());
499                 time_str >> time;
500         } else {
501                 warning << "note information missing time" << endmsg;
502                 time = 0;
503         }
504
505         if ((prop = xml_note->property("length")) != 0) {
506                 istringstream length_str(prop->value());
507                 length_str >> length;
508         } else {
509                 warning << "note information missing length" << endmsg;
510                 length = 1;
511         }
512
513         if ((prop = xml_note->property("velocity")) != 0) {
514                 istringstream velocity_str(prop->value());
515                 velocity_str >> velocity;
516         } else {
517                 warning << "note information missing velocity" << endmsg;
518                 velocity = 127;
519         }
520
521         NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
522         note_ptr->set_id (id);
523
524         return note_ptr;
525 }
526
527 XMLNode&
528 MidiModel::NoteDiffCommand::marshal_change (const NoteChange& change)
529 {
530         XMLNode* xml_change = new XMLNode("Change");
531
532         /* first, the change itself */
533
534         xml_change->add_property ("property", enum_2_string (change.property));
535
536         {
537                 ostringstream old_value_str (ios::ate);
538                 if (change.property == StartTime || change.property == Length) {
539                         old_value_str << change.old_time;
540                 } else {
541                         old_value_str << (unsigned int) change.old_value;
542                 }
543                 xml_change->add_property ("old", old_value_str.str());
544         }
545
546         {
547                 ostringstream new_value_str (ios::ate);
548                 if (change.property == StartTime || change.property == Length) {
549                         new_value_str << change.new_time;
550                 } else {
551                         new_value_str << (unsigned int) change.new_value;
552                 }
553                 xml_change->add_property ("new", new_value_str.str());
554         }
555
556         ostringstream id_str;
557         id_str << change.note->id();
558         xml_change->add_property ("id", id_str.str());
559
560         return *xml_change;
561 }
562
563 MidiModel::NoteDiffCommand::NoteChange
564 MidiModel::NoteDiffCommand::unmarshal_change (XMLNode *xml_change)
565 {
566         XMLProperty* prop;
567         NoteChange change;
568
569         if ((prop = xml_change->property("property")) != 0) {
570                 change.property = (Property) string_2_enum (prop->value(), change.property);
571         } else {
572                 fatal << "!!!" << endmsg;
573                 /*NOTREACHED*/
574         }
575
576         if ((prop = xml_change->property ("id")) == 0) {
577                 error << _("No NoteID found for note property change - ignored") << endmsg;
578                 return change;
579         }
580
581         gint note_id = atoi (prop->value().c_str());
582
583         if ((prop = xml_change->property ("old")) != 0) {
584                 istringstream old_str (prop->value());
585                 if (change.property == StartTime || change.property == Length) {
586                         old_str >> change.old_time;
587                 } else {
588                         int integer_value_so_that_istream_does_the_right_thing;
589                         old_str >> integer_value_so_that_istream_does_the_right_thing;
590                         change.old_value = integer_value_so_that_istream_does_the_right_thing;
591                 }
592         } else {
593                 fatal << "!!!" << endmsg;
594                 /*NOTREACHED*/
595         }
596
597         if ((prop = xml_change->property ("new")) != 0) {
598                 istringstream new_str (prop->value());
599                 if (change.property == StartTime || change.property == Length) {
600                         new_str >> change.new_time;
601                 } else {
602                         int integer_value_so_that_istream_does_the_right_thing;
603                         new_str >> integer_value_so_that_istream_does_the_right_thing;
604                         change.new_value = integer_value_so_that_istream_does_the_right_thing;
605                 }
606         } else {
607                 fatal << "!!!" << endmsg;
608                 /*NOTREACHED*/
609         }
610
611         /* we must point at the instance of the note that is actually in the model.
612            so go look for it ...
613         */
614
615         change.note = _model->find_note (note_id);
616
617         if (!change.note) {
618                 warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
619                 return change;
620         }
621
622         return change;
623 }
624
625 int
626 MidiModel::NoteDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
627 {
628         if (diff_command.name() != string (NOTE_DIFF_COMMAND_ELEMENT)) {
629                 return 1;
630         }
631
632         /* additions */
633
634         _added_notes.clear();
635         XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
636         if (added_notes) {
637                 XMLNodeList notes = added_notes->children();
638                 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
639                           boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
640         }
641
642
643         /* removals */
644
645         _removed_notes.clear();
646         XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
647         if (removed_notes) {
648                 XMLNodeList notes = removed_notes->children();
649                 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
650                           boost::bind (&NoteDiffCommand::unmarshal_note, this, _1));
651         }
652
653
654         /* changes */
655
656         _changes.clear();
657
658         XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
659
660         if (changed_notes) {
661                 XMLNodeList notes = changed_notes->children();
662                 transform (notes.begin(), notes.end(), back_inserter(_changes),
663                            boost::bind (&NoteDiffCommand::unmarshal_change, this, _1));
664
665         }
666
667         /* side effect removals caused by changes */
668
669         side_effect_removals.clear();
670
671         XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
672
673         if (side_effect_notes) {
674                 XMLNodeList notes = side_effect_notes->children();
675                 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
676                         side_effect_removals.insert (unmarshal_note (*n));
677                 }
678         }
679
680         return 0;
681 }
682
683 XMLNode&
684 MidiModel::NoteDiffCommand::get_state ()
685 {
686         XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
687         diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
688
689         XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
690         for_each(_changes.begin(), _changes.end(), 
691                  boost::bind (
692                          boost::bind (&XMLNode::add_child_nocopy, changes, _1),
693                          boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
694
695         XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
696         for_each(_added_notes.begin(), _added_notes.end(), 
697                  boost::bind(
698                          boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
699                          boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
700
701         XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
702         for_each(_removed_notes.begin(), _removed_notes.end(), 
703                  boost::bind (
704                          boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
705                          boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
706
707         /* if this command had side-effects, store that state too 
708          */
709
710         if (!side_effect_removals.empty()) {
711                 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
712                 for_each(side_effect_removals.begin(), side_effect_removals.end(), 
713                          boost::bind (
714                                  boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
715                                  boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
716         }
717
718         return *diff_command;
719 }
720
721 MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
722         : DiffCommand (m, "")
723 {
724         assert (_model);
725         set_state (node, Stateful::loading_state_version);
726 }
727
728 void
729 MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
730 {
731         Change change;
732
733         change.sysex = s;
734         change.property = Time;
735         change.old_time = s->time ();
736         change.new_time = new_time;
737
738         _changes.push_back (change);
739 }
740
741 void
742 MidiModel::SysExDiffCommand::operator() ()
743 {
744         {
745                 MidiModel::WriteLock lock (_model->edit_lock ());
746
747                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
748                         switch (i->property) {
749                         case Time:
750                                 i->sysex->set_time (i->new_time);
751                         }
752                 }
753         }
754
755         _model->ContentsChanged (); /* EMIT SIGNAL */
756 }
757
758 void
759 MidiModel::SysExDiffCommand::undo ()
760 {
761         {
762                 MidiModel::WriteLock lock (_model->edit_lock ());
763
764                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
765                         switch (i->property) {
766                         case Time:
767                                 i->sysex->set_time (i->old_time);
768                                 break;
769                         }
770                 }
771
772         }
773
774         _model->ContentsChanged(); /* EMIT SIGNAL */
775 }
776
777 XMLNode&
778 MidiModel::SysExDiffCommand::marshal_change (const Change& change)
779 {
780         XMLNode* xml_change = new XMLNode ("Change");
781
782         /* first, the change itself */
783
784         xml_change->add_property ("property", enum_2_string (change.property));
785
786         {
787                 ostringstream old_value_str (ios::ate);
788                 old_value_str << change.old_time;
789                 xml_change->add_property ("old", old_value_str.str());
790         }
791
792         {
793                 ostringstream new_value_str (ios::ate);
794                 new_value_str << change.new_time;
795                 xml_change->add_property ("new", new_value_str.str());
796         }
797
798         ostringstream id_str;
799         id_str << change.sysex->id();
800         xml_change->add_property ("id", id_str.str());
801
802         return *xml_change;
803 }
804
805 MidiModel::SysExDiffCommand::Change
806 MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
807 {
808         XMLProperty* prop;
809         Change change;
810
811         if ((prop = xml_change->property ("property")) != 0) {
812                 change.property = (Property) string_2_enum (prop->value(), change.property);
813         } else {
814                 fatal << "!!!" << endmsg;
815                 /*NOTREACHED*/
816         }
817
818         if ((prop = xml_change->property ("id")) == 0) {
819                 error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
820                 return change;
821         }
822
823         gint sysex_id = atoi (prop->value().c_str());
824
825         if ((prop = xml_change->property ("old")) != 0) {
826                 istringstream old_str (prop->value());
827                 old_str >> change.old_time;
828         } else {
829                 fatal << "!!!" << endmsg;
830                 /*NOTREACHED*/
831         }
832
833         if ((prop = xml_change->property ("new")) != 0) {
834                 istringstream new_str (prop->value());
835                 new_str >> change.new_time;
836         } else {
837                 fatal << "!!!" << endmsg;
838                 /*NOTREACHED*/
839         }
840
841         /* we must point at the instance of the sysex that is actually in the model.
842            so go look for it ...
843         */
844
845         change.sysex = _model->find_sysex (sysex_id);
846
847         if (!change.sysex) {
848                 warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg;
849                 return change;
850         }
851
852         return change;
853 }
854
855 int
856 MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
857 {
858         if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
859                 return 1;
860         }
861
862         /* changes */
863
864         _changes.clear();
865
866         XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
867
868         if (changed_sysexes) {
869                 XMLNodeList sysexes = changed_sysexes->children();
870                 transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
871                            boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
872
873         }
874
875         return 0;
876 }
877
878 XMLNode&
879 MidiModel::SysExDiffCommand::get_state ()
880 {
881         XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
882         diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
883
884         XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
885         for_each (_changes.begin(), _changes.end(), 
886                   boost::bind (
887                           boost::bind (&XMLNode::add_child_nocopy, changes, _1),
888                           boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
889
890         return *diff_command;
891 }
892
893 /** Write all of the model to a MidiSource (i.e. save the model).
894  * This is different from manually using read to write to a source in that
895  * note off events are written regardless of the track mode.  This is so the
896  * user can switch a recorded track (with note durations from some instrument)
897  * to percussive, save, reload, then switch it back to sustained without
898  * destroying the original note durations.
899  *
900  * Similarly, control events are written without interpolation (as with the
901  * `Discrete' mode).
902  */
903 bool
904 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
905 {
906         ReadLock lock(read_lock());
907
908         const bool old_percussive = percussive();
909         set_percussive(false);
910
911         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
912         assert (ms);
913         
914         source->drop_model();
915         source->mark_streaming_midi_write_started (note_mode(), ms->timeline_position ());
916
917         for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
918                 source->append_event_unlocked_beats(*i);
919         }
920
921         set_percussive(old_percussive);
922         source->mark_streaming_write_completed();
923
924         set_edited(false);
925         
926         return true;
927 }
928
929 /** very similar to ::write_to() but writes to the model's own
930     existing midi_source, without making it call MidiSource::drop_model().
931     the caller is a MidiSource that needs to catch up with the state
932     of the model.
933 */
934 bool
935 MidiModel::sync_to_source ()
936 {
937         ReadLock lock(read_lock());
938
939         const bool old_percussive = percussive();
940         set_percussive(false);
941
942         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
943         assert (ms);
944         
945         ms->mark_streaming_midi_write_started (note_mode(), ms->timeline_position());
946
947         for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
948                 ms->append_event_unlocked_beats(*i);
949         }
950
951         set_percussive (old_percussive);
952         ms->mark_streaming_write_completed ();
953
954         set_edited (false);
955         
956         return true;
957 }
958
959 /** Write part or all of the model to a MidiSource (i.e. save the model).
960  * This is different from manually using read to write to a source in that
961  * note off events are written regardless of the track mode.  This is so the
962  * user can switch a recorded track (with note durations from some instrument)
963  * to percussive, save, reload, then switch it back to sustained without
964  * destroying the original note durations.
965  */
966 bool
967 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
968 {
969         ReadLock lock(read_lock());
970         MidiStateTracker mst;
971         Evoral::MusicalTime extra_note_on_time = end_time;
972
973         const bool old_percussive = percussive();
974         set_percussive(false);
975
976         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
977         assert (ms);
978         
979         source->drop_model();
980         source->mark_streaming_midi_write_started (note_mode(), ms->timeline_position());
981
982         for (Evoral::Sequence<TimeType>::const_iterator i = begin(0, true); i != end(); ++i) {
983                 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
984
985                 if (ev.time() >= begin_time && ev.time() < end_time) {
986
987                         const Evoral::MIDIEvent<Evoral::MusicalTime>* mev = 
988                                 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
989
990                         if (!mev) {
991                                 continue;
992                         }
993
994
995                         if (mev->is_note_off()) {
996
997                                 if (!mst.active (mev->note(), mev->channel())) {
998
999                                         /* add a note-on at the start of the range we're writing
1000                                            to the file. velocity is just an arbitary reasonable value.
1001                                         */
1002
1003                                         Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
1004                                         on.set_type (mev->type());
1005                                         on.set_note (mev->note());
1006                                         on.set_channel (mev->channel());
1007                                         on.set_velocity (mev->velocity());
1008
1009                                         cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
1010                                         source->append_event_unlocked_beats (on);
1011                                         mst.add (on.note(), on.channel());
1012                                         mst.dump (cerr);
1013                                         extra_note_on_time += 1.0/128.0;
1014                                 }
1015
1016                                 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
1017                                 source->append_event_unlocked_beats (*i);
1018                                 mst.remove (mev->note(), mev->channel());
1019                                 mst.dump (cerr);
1020
1021                         } else if (mev->is_note_on()) {
1022                                 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
1023                                 mst.add (mev->note(), mev->channel());
1024                                 source->append_event_unlocked_beats(*i);
1025                                 mst.dump (cerr);
1026                         } else {
1027                                 cerr << "MIDI other event type\n";
1028                                 source->append_event_unlocked_beats(*i);
1029                         }
1030                 }
1031         }
1032
1033         mst.resolve_notes (*source, end_time);
1034
1035         set_percussive(old_percussive);
1036         source->mark_streaming_write_completed();
1037
1038         set_edited(false);
1039
1040         return true;
1041 }
1042
1043 XMLNode&
1044 MidiModel::get_state()
1045 {
1046         XMLNode *node = new XMLNode("MidiModel");
1047         return *node;
1048 }
1049
1050 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1051 MidiModel::find_note (NotePtr other)
1052 {
1053         Notes::iterator l = notes().lower_bound(other);
1054
1055         if (l != notes().end()) {
1056                 for (; (*l)->time() == other->time(); ++l) {
1057                         /* NB: compare note contents, not note pointers.
1058                            If "other" was a ptr to a note already in
1059                            the model, we wouldn't be looking for it,
1060                            would we now?
1061                         */
1062                         if (**l == *other) {
1063                                 return *l;
1064                         }
1065                 }
1066         }
1067
1068         return NotePtr();
1069 }
1070
1071 Evoral::Sequence<MidiModel::TimeType>::NotePtr
1072 MidiModel::find_note (gint note_id)
1073 {
1074         /* used only for looking up notes when reloading history from disk,
1075            so we don't care about performance *too* much.
1076         */
1077
1078         for (Notes::iterator l = notes().begin(); l != notes().end(); ++l) {
1079                 if ((*l)->id() == note_id) {
1080                         return *l;
1081                 }
1082         }
1083
1084         return NotePtr();
1085 }
1086
1087 boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
1088 MidiModel::find_sysex (gint sysex_id)
1089 {
1090         /* used only for looking up notes when reloading history from disk,
1091            so we don't care about performance *too* much.
1092         */
1093
1094         for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) {
1095                 if ((*l)->id() == sysex_id) {
1096                         return *l;
1097                 }
1098         }
1099
1100         return boost::shared_ptr<Evoral::Event<TimeType> > ();
1101 }
1102
1103 /** Lock and invalidate the source.
1104  * This should be used by commands and editing things
1105  */
1106 MidiModel::WriteLock
1107 MidiModel::edit_lock()
1108 {
1109         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1110         assert (ms);
1111
1112         Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock (ms->mutex());
1113         ms->invalidate(); // Release cached iterator's read lock on model
1114         return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
1115 }
1116
1117 /** Lock just the model, the source lock must already be held.
1118  * This should only be called from libardour/evoral places
1119  */
1120 MidiModel::WriteLock
1121 MidiModel::write_lock()
1122 {
1123         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1124         assert (ms);
1125
1126         assert (!ms->mutex().trylock ());
1127         return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
1128 }
1129
1130 int
1131 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
1132 {
1133         using namespace Evoral;
1134
1135         if (_writing || insert_merge_policy() == InsertMergeRelax) {
1136                 return 0;
1137         }
1138
1139         NoteDiffCommand* cmd = static_cast<NoteDiffCommand*>(arg);
1140
1141         TimeType sa = note->time();
1142         TimeType ea  = note->end_time();
1143
1144         const Pitches& p (pitches (note->channel()));
1145         NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
1146         set<NotePtr> to_be_deleted;
1147         bool set_note_length = false;
1148         bool set_note_time = false;
1149         TimeType note_time = note->time();
1150         TimeType note_length = note->length();
1151
1152         DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 checking overlaps for note %2 @ %3\n", this, (int)note->note(), note->time()));
1153         
1154         for (Pitches::const_iterator i = p.lower_bound (search_note); 
1155              i != p.end() && (*i)->note() == note->note(); ++i) {
1156
1157                 TimeType sb = (*i)->time();
1158                 TimeType eb = (*i)->end_time();
1159                 OverlapType overlap = OverlapNone;
1160
1161                 if ((sb > sa) && (eb <= ea)) {
1162                         overlap = OverlapInternal;
1163                 } else if ((eb >= sa) && (eb <= ea)) {
1164                         overlap = OverlapStart;
1165                 } else if ((sb > sa) && (sb <= ea)) {
1166                         overlap = OverlapEnd;
1167                 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
1168                         overlap = OverlapExternal;
1169                 } else {
1170                         /* no overlap */
1171                         continue;
1172                 }
1173
1174                 DEBUG_TRACE (DEBUG::Sequence, string_compose ("\toverlap is %1 for (%2,%3) vs (%4,%5)\n", enum_2_string(overlap), 
1175                                                               sa, ea, sb, eb));
1176
1177                 if (insert_merge_policy() == InsertMergeReject) {
1178                         DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 just reject\n", this));
1179                         return -1;
1180                 }
1181
1182                 switch (overlap) {
1183                 case OverlapStart:
1184                         cerr << "OverlapStart\n";
1185                         /* existing note covers start of new note */
1186                         switch (insert_merge_policy()) {
1187                         case InsertMergeReplace:
1188                                 to_be_deleted.insert (*i);
1189                                 break;
1190                         case InsertMergeTruncateExisting:
1191                                 if (cmd) {
1192                                         cmd->change (*i, NoteDiffCommand::Length, (note->time() - (*i)->time()));
1193                                 }
1194                                 (*i)->set_length (note->time() - (*i)->time());
1195                                 break;
1196                         case InsertMergeTruncateAddition:
1197                                 set_note_time = true;
1198                                 set_note_length = true;
1199                                 note_time = (*i)->time() + (*i)->length();
1200                                 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1201                                 break;
1202                         case InsertMergeExtend:
1203                                 if (cmd) {
1204                                         cmd->change ((*i), NoteDiffCommand::Length, note->end_time() - (*i)->time());
1205                                 } 
1206                                 (*i)->set_length (note->end_time() - (*i)->time());
1207                                 return -1; /* do not add the new note */
1208                                 break;
1209                         default:
1210                                 /*NOTREACHED*/
1211                                 /* stupid gcc */
1212                                 break;
1213                         }
1214                         break;
1215
1216                 case OverlapEnd:
1217                         cerr << "OverlapEnd\n";
1218                         /* existing note covers end of new note */
1219                         switch (insert_merge_policy()) {
1220                         case InsertMergeReplace:
1221                                 to_be_deleted.insert (*i);
1222                                 break;
1223
1224                         case InsertMergeTruncateExisting:
1225                                 /* resetting the start time of the existing note
1226                                    is a problem because of time ordering.
1227                                 */
1228                                 break;
1229
1230                         case InsertMergeTruncateAddition:
1231                                 set_note_length = true;
1232                                 note_length = min (note_length, ((*i)->time() - note->time()));
1233                                 break;
1234
1235                         case InsertMergeExtend:
1236                                 /* we can't reset the time of the existing note because
1237                                    that will corrupt time ordering. So remove the
1238                                    existing note and change the position/length
1239                                    of the new note (which has not been added yet)
1240                                 */
1241                                 to_be_deleted.insert (*i);
1242                                 set_note_length = true;
1243                                 note_length = min (note_length, (*i)->end_time() - note->time());
1244                                 break;
1245                         default:
1246                                 /*NOTREACHED*/
1247                                 /* stupid gcc */
1248                                 break;
1249                         }
1250                         break;
1251
1252                 case OverlapExternal:
1253                         cerr << "OverlapExt\n";
1254                         /* existing note overlaps all the new note */
1255                         switch (insert_merge_policy()) {
1256                         case InsertMergeReplace:
1257                                 to_be_deleted.insert (*i);
1258                                 break;
1259                         case InsertMergeTruncateExisting:
1260                         case InsertMergeTruncateAddition:
1261                         case InsertMergeExtend:
1262                                 /* cannot add in this case */
1263                                 return -1;
1264                         default:
1265                                 /*NOTREACHED*/
1266                                 /* stupid gcc */
1267                                 break;
1268                         }
1269                         break;
1270
1271                 case OverlapInternal:
1272                         cerr << "OverlapInt\n";
1273                         /* new note fully overlaps an existing note */
1274                         switch (insert_merge_policy()) {
1275                         case InsertMergeReplace:
1276                         case InsertMergeTruncateExisting:
1277                         case InsertMergeTruncateAddition:
1278                         case InsertMergeExtend:
1279                                 /* delete the existing note, the new one will cover it */
1280                                 to_be_deleted.insert (*i);
1281                                 break;
1282                         default:
1283                                 /*NOTREACHED*/
1284                                 /* stupid gcc */
1285                                 break;
1286                         }
1287                         break;
1288
1289                 default:
1290                         /*NOTREACHED*/
1291                         /* stupid gcc */
1292                         break;
1293                 }
1294         }
1295
1296         for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1297                 remove_note_unlocked (*i);
1298
1299                 if (cmd) {
1300                         cmd->side_effect_remove (*i);
1301                 }
1302         }
1303
1304         if (set_note_time) {
1305                 if (cmd) {
1306                         cmd->change (note, NoteDiffCommand::StartTime, note_time);
1307                 } 
1308                 note->set_time (note_time);
1309         }
1310
1311         if (set_note_length) {
1312                 if (cmd) {
1313                         cmd->change (note, NoteDiffCommand::Length, note_length);
1314                 } 
1315                 note->set_length (note_length);
1316         }
1317
1318         return 0;
1319 }
1320
1321 InsertMergePolicy
1322 MidiModel::insert_merge_policy () const 
1323 {
1324         /* XXX ultimately this should be a per-track or even per-model policy */
1325         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1326         assert (ms);
1327
1328         return ms->session().config.get_insert_merge_policy ();
1329 }
1330                         
1331 void
1332 MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
1333 {
1334         boost::shared_ptr<MidiSource> old = _midi_source.lock ();
1335         
1336         if (old) {
1337                 old->invalidate ();
1338         }
1339
1340         _midi_source_connections.drop_connections ();
1341
1342         _midi_source = s;
1343
1344         s->InterpolationChanged.connect_same_thread (
1345                 _midi_source_connections, boost::bind (&MidiModel::source_interpolation_changed, this, _1, _2)
1346                 );
1347
1348         s->AutomationStateChanged.connect_same_thread (
1349                 _midi_source_connections, boost::bind (&MidiModel::source_automation_state_changed, this, _1, _2)
1350                 );
1351 }
1352
1353 /** The source has signalled that the interpolation style for a parameter has changed.  In order to
1354  *  keep MidiSource and ControlList interpolation state the same, we pass this change onto the
1355  *  appropriate ControlList.
1356  *
1357  *  The idea is that MidiSource and the MidiModel's ControlList states are kept in sync, and one
1358  *  or the other is listened to by the GUI.
1359  */
1360 void
1361 MidiModel::source_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1362 {
1363         Glib::Mutex::Lock lm (_control_lock);
1364         control(p)->list()->set_interpolation (s);
1365 }
1366
1367 /** A ControlList has signalled that its interpolation style has changed.  Again, in order to keep
1368  *  MidiSource and ControlList interpolation state in sync, we pass this change onto our MidiSource.
1369  */
1370 void
1371 MidiModel::control_list_interpolation_changed (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
1372 {
1373         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1374         assert (ms);
1375
1376         ms->set_interpolation_of (p, s);
1377 }
1378
1379 void
1380 MidiModel::source_automation_state_changed (Evoral::Parameter p, AutoState s)
1381 {
1382         Glib::Mutex::Lock lm (_control_lock);
1383         boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (control(p)->list ());
1384         al->set_automation_state (s);
1385 }
1386
1387 void
1388 MidiModel::automation_list_automation_state_changed (Evoral::Parameter p, AutoState s)
1389 {
1390         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1391         assert (ms);
1392         ms->set_automation_state_of (p, s);
1393 }
1394
1395 boost::shared_ptr<Evoral::Control>
1396 MidiModel::control_factory (Evoral::Parameter const & p)
1397 {
1398         boost::shared_ptr<Evoral::Control> c = Automatable::control_factory (p);
1399
1400         /* Set up newly created control's lists to the appropriate interpolation and
1401            automation state from our source.
1402         */
1403
1404         boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
1405         assert (ms);
1406
1407         c->list()->set_interpolation (ms->interpolation_of (p));
1408
1409         boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (c->list ());
1410         assert (al);
1411
1412         al->set_automation_state (ms->automation_state_of (p));
1413
1414         return c;
1415 }
1416
1417 boost::shared_ptr<const MidiSource>
1418 MidiModel::midi_source ()
1419 {
1420         return _midi_source.lock ();
1421 }
1422
1423 /** Moves notes, controllers and sys-ex to insert silence at the start of the model.
1424  *  Adds commands to the session's current undo stack to reflect the movements.
1425  */
1426 void
1427 MidiModel::insert_silence_at_start (TimeType t)
1428 {
1429         boost::shared_ptr<MidiSource> s = _midi_source.lock ();
1430         assert (s);
1431         
1432         /* Notes */
1433
1434         if (!notes().empty ()) {
1435                 NoteDiffCommand* c = new_note_diff_command ("insert silence");
1436                 
1437                 for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) {
1438                         c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t);
1439                 }
1440                 
1441                 apply_command_as_subcommand (s->session(), c);
1442         }
1443
1444         /* Controllers */
1445
1446         for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
1447                 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
1448                 XMLNode& before = ac->alist()->get_state ();
1449                 i->second->list()->shift (0, t);
1450                 XMLNode& after = ac->alist()->get_state ();
1451                 s->session().add_command (new MementoCommand<AutomationList> (new MidiAutomationListBinder (s, i->first), &before, &after));
1452         }
1453
1454         /* Sys-ex */
1455
1456         if (!sysexes().empty()) {
1457                 SysExDiffCommand* c = new_sysex_diff_command ("insert silence");
1458
1459                 for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) {
1460                         c->change (*i, (*i)->time() + t);
1461                 }
1462
1463                 apply_command_as_subcommand (s->session(), c);
1464         }
1465 }