add MidiModel::sync_to_source() to push model state into its existing source; mark...
[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 #define __STDC_LIMIT_MACROS 1
22 #include <set>
23 #include <iostream>
24 #include <algorithm>
25 #include <stdexcept>
26 #include <stdint.h>
27 #include "pbd/error.h"
28 #include "pbd/enumwriter.h"
29 #include "midi++/events.h"
30
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
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
41
42 MidiModel::MidiModel(MidiSource* s)
43         : AutomatableSequence<TimeType>(s->session())
44         , _midi_source(s)
45 {
46 }
47
48 /** Start a new Diff 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::DiffCommand*
55 MidiModel::new_diff_command(const string name)
56 {
57         DiffCommand* cmd = new DiffCommand(_midi_source->model(), name);
58         return cmd;
59 }
60
61 /** Apply a command.
62  *
63  * Ownership of cmd is taken, it must not be deleted by the caller.
64  * The command will constitute one item on the undo stack.
65  */
66 void
67 MidiModel::apply_command(Session& session, Command* cmd)
68 {
69         session.begin_reversible_command(cmd->name());
70         (*cmd)();
71         session.commit_reversible_command(cmd);
72         set_edited(true);
73 }
74
75 /** Apply a command as part of a larger reversible transaction
76  *
77  * Ownership of cmd is taken, it must not be deleted by the caller.
78  * The command will constitute one item on the undo stack.
79  */
80 void
81 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
82 {
83         (*cmd)();
84         session.add_command(cmd);
85         set_edited(true);
86 }
87
88 /************** DIFF COMMAND ********************/
89
90 #define DIFF_COMMAND_ELEMENT "DiffCommand"
91 #define DIFF_NOTES_ELEMENT "ChangedNotes"
92 #define ADDED_NOTES_ELEMENT "AddedNotes"
93 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
94 #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
95
96 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
97         : Command(name)
98         , _model(m)
99         , _name(name)
100 {
101         assert(_model);
102 }
103
104 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
105         : _model(m)
106 {
107         assert(_model);
108         set_state(node, Stateful::loading_state_version);
109 }
110
111 void
112 MidiModel::DiffCommand::add(const NotePtr note)
113 {
114         _removed_notes.remove(note);
115         _added_notes.push_back(note);
116 }
117
118 void
119 MidiModel::DiffCommand::remove(const NotePtr note)
120 {
121         _added_notes.remove(note);
122         _removed_notes.push_back(note);
123 }
124
125 void
126 MidiModel::DiffCommand::side_effect_remove(const NotePtr note)
127 {
128         side_effect_removals.insert (note);
129 }
130
131 void
132 MidiModel::DiffCommand::change(const NotePtr note, Property prop,
133                                uint8_t new_value)
134 {
135         NoteChange change;
136         
137         switch (prop) {
138         case NoteNumber:
139                 if (new_value == note->note()) {
140                         return;
141                 }
142                 change.old_value = note->note();
143                 break;
144         case Velocity:
145                 if (new_value == note->velocity()) {
146                         return;
147                 }
148                 change.old_value = note->velocity();
149                 break;
150         case Channel:
151                 if (new_value == note->channel()) {
152                         return;
153                 }
154                 change.old_value = note->channel();
155                 break;
156
157
158         case StartTime:
159                 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
160                 /*NOTREACHED*/
161                 break;
162         case Length:
163                 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
164                 /*NOTREACHED*/
165                 break;
166         }
167
168         change.note = note;
169         change.property = prop;
170         change.new_value = new_value;
171
172         _changes.push_back (change);
173 }
174
175 void
176 MidiModel::DiffCommand::change(const NotePtr note, Property prop,
177                                TimeType new_time)
178 {
179         NoteChange change;
180
181         switch (prop) {
182         case NoteNumber:
183         case Channel:
184         case Velocity:
185                 fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
186                 break;
187
188         case StartTime:
189                 if (Evoral::musical_time_equal (note->time(), new_time)) {
190                         return;
191                 }
192                 change.old_time = note->time();
193                 break;
194         case Length:
195                 if (Evoral::musical_time_equal (note->length(), new_time)) {
196                         return;
197                 }
198                 change.old_time = note->length();
199                 break;
200         }
201
202         change.note = note;
203         change.property = prop;
204         change.new_time = new_time;
205
206         _changes.push_back (change);
207 }
208
209 MidiModel::DiffCommand&
210 MidiModel::DiffCommand::operator+= (const DiffCommand& other)
211 {
212         if (this == &other) {
213                 return *this;
214         }
215
216         if (_model != other._model) {
217                 return *this;
218         }
219
220         _added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
221         _removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
222         side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
223         _changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
224
225         return *this;
226 }
227
228 void
229 MidiModel::DiffCommand::operator()()
230 {
231         {
232                 MidiModel::WriteLock lock(_model->edit_lock());
233
234                 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
235                         if (!_model->add_note_unlocked(*i)) {
236                                 /* failed to add it, so don't leave it in the removed list, to
237                                    avoid apparent errors on undo.
238                                 */
239                                 _removed_notes.remove (*i);
240                         }
241                 }
242
243                 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
244                         _model->remove_note_unlocked(*i);
245                 }
246
247                 /* notes we modify in a way that requires remove-then-add to maintain ordering */
248                 set<NotePtr> temporary_removals;
249
250                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
251                         Property prop = i->property;
252                         switch (prop) {
253                         case NoteNumber:
254                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
255                                         _model->remove_note_unlocked (i->note);
256                                         temporary_removals.insert (i->note);
257                                 }
258                                 i->note->set_note (i->new_value);
259                                 break;
260
261                         case StartTime:
262                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
263                                         _model->remove_note_unlocked (i->note);
264                                         temporary_removals.insert (i->note);
265
266                                 }
267                                 i->note->set_time (i->new_time);
268                                 break;
269
270                         case Channel:
271                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
272                                         _model->remove_note_unlocked (i->note);
273                                         temporary_removals.insert (i->note);
274                                 }
275                                 i->note->set_channel (i->new_value);
276                                 break;
277
278                                 /* no remove-then-add required for these properties, since we do not index them
279                                  */
280
281                         case Velocity:
282                                 i->note->set_velocity (i->new_value);
283                                 break;
284
285                         case Length:
286                                 i->note->set_length (i->new_time);
287                                 break;
288
289                         }
290                 }
291
292
293                 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
294                         DiffCommand side_effects (model(), "side effects");
295                         _model->add_note_unlocked (*i, &side_effects);
296                         *this += side_effects;
297                 }
298
299                 if (!side_effect_removals.empty()) {
300                         cerr << "SER: \n";
301                         for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
302                                 cerr << "\t" << *i << ' ' << **i << endl;
303                         }
304                 }
305         }
306
307         _model->ContentsChanged(); /* EMIT SIGNAL */
308 }
309
310 void
311 MidiModel::DiffCommand::undo()
312 {
313         {
314                 MidiModel::WriteLock lock(_model->edit_lock());
315
316                 for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
317                         _model->remove_note_unlocked(*i);
318                 }
319
320                 for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
321                         _model->add_note_unlocked(*i);
322                 }
323
324                 /* notes we modify in a way that requires remove-then-add to maintain ordering */
325                 set<NotePtr> temporary_removals;
326
327                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
328                         Property prop = i->property;
329                         switch (prop) {
330                         case NoteNumber:
331                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
332                                         _model->remove_note_unlocked (i->note);
333                                         temporary_removals.insert (i->note);
334                                 }
335                                 i->note->set_note (i->old_value);
336                                 break;
337                         case Velocity:
338                                 i->note->set_velocity (i->old_value);
339                                 break;
340                         case StartTime:
341                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
342                                         _model->remove_note_unlocked (i->note);
343                                         temporary_removals.insert (i->note);
344                                 }
345                                 i->note->set_time (i->old_time);
346                                 break;
347                         case Length:
348                                 i->note->set_length (i->old_time);
349                                 break;
350                         case Channel:
351                                 if (temporary_removals.find (i->note) == temporary_removals.end()) {
352                                         _model->remove_note_unlocked (i->note);
353                                         temporary_removals.insert (i->note);
354                                 }
355                                 i->note->set_channel (i->old_value);
356                                 break;
357                         }
358                 }
359
360                 for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
361                         _model->add_note_unlocked (*i);
362                 }
363
364                 /* finally add back notes that were removed by the "do". we don't care
365                    about side effects here since the model should be back to its original
366                    state once this is done.
367                 */
368
369                 for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
370                         _model->add_note_unlocked (*i);
371                 }
372         }
373
374         _model->ContentsChanged(); /* EMIT SIGNAL */
375 }
376
377 XMLNode&
378 MidiModel::DiffCommand::marshal_note(const NotePtr note)
379 {
380         XMLNode* xml_note = new XMLNode("note");
381
382         cerr << "Marshalling note: " << *note << endl;
383
384         ostringstream note_str(ios::ate);
385         note_str << int(note->note());
386         xml_note->add_property("note", note_str.str());
387
388         ostringstream channel_str(ios::ate);
389         channel_str << int(note->channel());
390         xml_note->add_property("channel", channel_str.str());
391
392         ostringstream time_str(ios::ate);
393         time_str << note->time();
394         xml_note->add_property("time", time_str.str());
395
396         ostringstream length_str(ios::ate);
397         length_str << note->length();
398         xml_note->add_property("length", length_str.str());
399
400         ostringstream velocity_str(ios::ate);
401         velocity_str << (unsigned int) note->velocity();
402         xml_note->add_property("velocity", velocity_str.str());
403
404         return *xml_note;
405 }
406
407 Evoral::Sequence<MidiModel::TimeType>::NotePtr
408 MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
409 {
410         unsigned int note;
411         XMLProperty* prop;
412         unsigned int channel;
413         unsigned int time;
414         unsigned int length;
415         unsigned int velocity;
416
417         if ((prop = xml_note->property("note")) != 0) {
418                 istringstream note_str(prop->value());
419                 note_str >> note;
420         } else {
421                 warning << "note information missing note value" << endmsg;
422                 note = 127;
423         }
424
425         if ((prop = xml_note->property("channel")) != 0) {
426                 istringstream channel_str(prop->value());
427                 channel_str >> channel;
428         } else {
429                 warning << "note information missing channel" << endmsg;
430                 channel = 0;
431         }
432
433         if ((prop = xml_note->property("time")) != 0) {
434                 istringstream time_str(prop->value());
435                 time_str >> time;
436         } else {
437                 warning << "note information missing time" << endmsg;
438                 time = 0;
439         }
440
441         if ((prop = xml_note->property("length")) != 0) {
442                 istringstream length_str(prop->value());
443                 length_str >> length;
444         } else {
445                 warning << "note information missing length" << endmsg;
446                 length = 1;
447         }
448
449         if ((prop = xml_note->property("velocity")) != 0) {
450                 istringstream velocity_str(prop->value());
451                 velocity_str >> velocity;
452         } else {
453                 warning << "note information missing velocity" << endmsg;
454                 velocity = 127;
455         }
456
457         NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
458
459         return note_ptr;
460 }
461
462 XMLNode&
463 MidiModel::DiffCommand::marshal_change(const NoteChange& change)
464 {
465         XMLNode* xml_change = new XMLNode("change");
466
467         /* first, the change itself */
468
469         xml_change->add_property ("property", enum_2_string (change.property));
470
471         {
472                 ostringstream old_value_str (ios::ate);
473                 if (change.property == StartTime || change.property == Length) {
474                         old_value_str << change.old_time;
475                 } else {
476                         old_value_str << (unsigned int) change.old_value;
477                 }
478                 xml_change->add_property ("old", old_value_str.str());
479         }
480
481         {
482                 ostringstream new_value_str (ios::ate);
483                 if (change.property == StartTime || change.property == Length) {
484                         new_value_str << change.new_time;
485                 } else {
486                         new_value_str << (unsigned int) change.new_value;
487                 }
488                 xml_change->add_property ("new", new_value_str.str());
489         }
490
491         /* now the rest of the note */
492
493         const SMFSource* smf = dynamic_cast<const SMFSource*> (_model->midi_source());
494
495         if (change.property != NoteNumber) {
496                 ostringstream note_str;
497                 note_str << int(change.note->note());
498                 xml_change->add_property("note", note_str.str());
499         }
500
501         if (change.property != Channel) {
502                 ostringstream channel_str;
503                 channel_str << int(change.note->channel());
504                 xml_change->add_property("channel", channel_str.str());
505         }
506
507         if (change.property != StartTime) {
508                 ostringstream time_str;
509                 if (smf) {
510                         time_str << smf->round_to_file_precision (change.note->time());
511                 } else {
512                         time_str << change.note->time();
513                 }
514                 xml_change->add_property("time", time_str.str());
515         }
516
517         if (change.property != Length) {
518                 ostringstream length_str;
519                 if (smf) {
520                         length_str << smf->round_to_file_precision (change.note->length());
521                 } else {
522                         length_str << change.note->length();
523                 }
524                 xml_change->add_property ("length", length_str.str());
525         }
526
527         if (change.property != Velocity) {
528                 ostringstream velocity_str;
529                 velocity_str << int (change.note->velocity());
530                 xml_change->add_property("velocity", velocity_str.str());
531         }
532
533         /* and now notes that were remove as a side-effect */
534
535         return *xml_change;
536 }
537
538 MidiModel::DiffCommand::NoteChange
539 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
540 {
541         XMLProperty* prop;
542         NoteChange change;
543         unsigned int note;
544         unsigned int channel;
545         unsigned int velocity;
546         Evoral::MusicalTime time;
547         Evoral::MusicalTime length;
548
549         if ((prop = xml_change->property("property")) != 0) {
550                 change.property = (Property) string_2_enum (prop->value(), change.property);
551         } else {
552                 fatal << "!!!" << endmsg;
553                 /*NOTREACHED*/
554         }
555
556         if ((prop = xml_change->property ("old")) != 0) {
557                 istringstream old_str (prop->value());
558                 if (change.property == StartTime || change.property == Length) {
559                         old_str >> change.old_time;
560                 } else {
561                         int integer_value_so_that_istream_does_the_right_thing;
562                         old_str >> integer_value_so_that_istream_does_the_right_thing;
563                         change.old_value = integer_value_so_that_istream_does_the_right_thing;
564                 }
565         } else {
566                 fatal << "!!!" << endmsg;
567                 /*NOTREACHED*/
568         }
569
570         if ((prop = xml_change->property ("new")) != 0) {
571                 istringstream new_str (prop->value());
572                 if (change.property == StartTime || change.property == Length) {
573                         new_str >> change.new_time;
574                 } else {
575                         int integer_value_so_that_istream_does_the_right_thing;
576                         new_str >> integer_value_so_that_istream_does_the_right_thing;
577                         change.new_value = integer_value_so_that_istream_does_the_right_thing;
578                 }
579         } else {
580                 fatal << "!!!" << endmsg;
581                 /*NOTREACHED*/
582         }
583
584         if (change.property != NoteNumber) {
585                 if ((prop = xml_change->property("note")) != 0) {
586                         istringstream note_str(prop->value());
587                         note_str >> note;
588                 } else {
589                         warning << "note information missing note value" << endmsg;
590                         note = 127;
591                 }
592         } else {
593                 note = change.new_value;
594         }
595
596         if (change.property != Channel) {
597                 if ((prop = xml_change->property("channel")) != 0) {
598                         istringstream channel_str(prop->value());
599                         channel_str >> channel;
600                 } else {
601                         warning << "note information missing channel" << endmsg;
602                         channel = 0;
603                 }
604         } else {
605                 channel = change.new_value;
606         }
607
608         if (change.property != StartTime) {
609                 if ((prop = xml_change->property("time")) != 0) {
610                         istringstream time_str(prop->value());
611                         time_str >> time;
612                 } else {
613                         warning << "note information missing time" << endmsg;
614                         time = 0;
615                 }
616         } else {
617                 time = change.new_time;
618         }
619
620         if (change.property != Length) {
621                 if ((prop = xml_change->property("length")) != 0) {
622                         istringstream length_str(prop->value());
623                         length_str >> length;
624                 } else {
625                         warning << "note information missing length" << endmsg;
626                         length = 1;
627                 }
628         } else {
629                 length = change.new_time;
630         }
631
632         if (change.property != Velocity) {
633                 if ((prop = xml_change->property("velocity")) != 0) {
634                         istringstream velocity_str(prop->value());
635                         velocity_str >> velocity;
636                 } else {
637                         warning << "note information missing velocity" << endmsg;
638                         velocity = 127;
639                 }
640         } else {
641                 velocity = change.new_value;
642         }
643
644         /* we must point at the instance of the note that is actually in the model.
645            so go look for it ...
646         */
647
648         NotePtr new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
649
650         change.note = _model->find_note (new_note);
651
652         if (!change.note) {
653                 warning << "MIDI note " << *new_note << " not found in model - programmers should investigate this" << endmsg;
654                 /* use the actual new note */
655                 change.note = new_note;
656         }
657
658         return change;
659 }
660
661 int
662 MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
663 {
664         if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
665                 return 1;
666         }
667
668         /* additions */
669
670         _added_notes.clear();
671         XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
672         if (added_notes) {
673                 XMLNodeList notes = added_notes->children();
674                 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
675                           boost::bind (&DiffCommand::unmarshal_note, this, _1));
676         }
677
678
679         /* removals */
680
681         _removed_notes.clear();
682         XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
683         if (removed_notes) {
684                 XMLNodeList notes = removed_notes->children();
685                 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
686                           boost::bind (&DiffCommand::unmarshal_note, this, _1));
687         }
688
689
690         /* changes */
691
692         _changes.clear();
693
694         XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
695
696         if (changed_notes) {
697                 XMLNodeList notes = changed_notes->children();
698                 transform (notes.begin(), notes.end(), back_inserter(_changes),
699                            boost::bind (&DiffCommand::unmarshal_change, this, _1));
700
701         }
702
703         /* side effect removals caused by changes */
704
705         side_effect_removals.clear();
706
707         XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
708
709         if (side_effect_notes) {
710                 XMLNodeList notes = side_effect_notes->children();
711                 for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
712                         side_effect_removals.insert (unmarshal_note (*n));
713                 }
714         }
715
716         return 0;
717 }
718
719 XMLNode&
720 MidiModel::DiffCommand::get_state ()
721 {
722         XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
723         diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
724
725         XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
726         for_each(_changes.begin(), _changes.end(), 
727                  boost::bind (
728                          boost::bind (&XMLNode::add_child_nocopy, changes, _1),
729                          boost::bind (&DiffCommand::marshal_change, this, _1)));
730
731         XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
732         for_each(_added_notes.begin(), _added_notes.end(), 
733                  boost::bind(
734                          boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
735                          boost::bind (&DiffCommand::marshal_note, this, _1)));
736
737         XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
738         for_each(_removed_notes.begin(), _removed_notes.end(), 
739                  boost::bind (
740                          boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
741                          boost::bind (&DiffCommand::marshal_note, this, _1)));
742
743         /* if this command had side-effects, store that state too 
744          */
745
746         if (!side_effect_removals.empty()) {
747                 XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
748                 for_each(side_effect_removals.begin(), side_effect_removals.end(), 
749                          boost::bind (
750                                  boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
751                                  boost::bind (&DiffCommand::marshal_note, this, _1)));
752         }
753
754         return *diff_command;
755 }
756
757
758 /** Write all of the model to a MidiSource (i.e. save the model).
759  * This is different from manually using read to write to a source in that
760  * note off events are written regardless of the track mode.  This is so the
761  * user can switch a recorded track (with note durations from some instrument)
762  * to percussive, save, reload, then switch it back to sustained without
763  * destroying the original note durations.
764  */
765 bool
766 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
767 {
768         ReadLock lock(read_lock());
769
770         const bool old_percussive = percussive();
771         set_percussive(false);
772
773         source->drop_model();
774         source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
775
776         for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
777                 source->append_event_unlocked_beats(*i);
778         }
779
780         set_percussive(old_percussive);
781         source->mark_streaming_write_completed();
782
783         set_edited(false);
784         
785         return true;
786 }
787
788 /** very similar to ::write_to() but writes to the model's own
789     existing midi_source, without making it call MidiSource::drop_model().
790     the caller is a MidiSource that needs to catch up with the state
791     of the model.
792 */
793 bool
794 MidiModel::sync_to_source ()
795 {
796         ReadLock lock(read_lock());
797
798         const bool old_percussive = percussive();
799         set_percussive(false);
800
801         _midi_source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
802
803         for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
804                 _midi_source->append_event_unlocked_beats(*i);
805         }
806
807         set_percussive (old_percussive);
808         _midi_source->mark_streaming_write_completed ();
809
810         set_edited (false);
811         
812         return true;
813 }
814
815 /** Write part or all of the model to a MidiSource (i.e. save the model).
816  * This is different from manually using read to write to a source in that
817  * note off events are written regardless of the track mode.  This is so the
818  * user can switch a recorded track (with note durations from some instrument)
819  * to percussive, save, reload, then switch it back to sustained without
820  * destroying the original note durations.
821  */
822 bool
823 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
824 {
825         ReadLock lock(read_lock());
826         MidiStateTracker mst;
827         Evoral::MusicalTime extra_note_on_time = end_time;
828
829         const bool old_percussive = percussive();
830         set_percussive(false);
831
832         source->drop_model();
833         source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
834
835         for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
836                 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
837
838                 if (ev.time() >= begin_time && ev.time() < end_time) {
839
840                         const Evoral::MIDIEvent<Evoral::MusicalTime>* mev = 
841                                 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
842
843                         if (!mev) {
844                                 continue;
845                         }
846
847
848                         if (mev->is_note_off()) {
849
850                                 if (!mst.active (mev->note(), mev->channel())) {
851
852                                         /* add a note-on at the start of the range we're writing
853                                            to the file. velocity is just an arbitary reasonable value.
854                                         */
855
856                                         Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
857                                         on.set_type (mev->type());
858                                         on.set_note (mev->note());
859                                         on.set_channel (mev->channel());
860                                         on.set_velocity (mev->velocity());
861
862                                         cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
863                                         source->append_event_unlocked_beats (on);
864                                         mst.add (on.note(), on.channel());
865                                         mst.dump (cerr);
866                                         extra_note_on_time += 1.0/128.0;
867                                 }
868
869                                 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
870                                 source->append_event_unlocked_beats (*i);
871                                 mst.remove (mev->note(), mev->channel());
872                                 mst.dump (cerr);
873
874                         } else if (mev->is_note_on()) {
875                                 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
876                                 mst.add (mev->note(), mev->channel());
877                                 source->append_event_unlocked_beats(*i);
878                                 mst.dump (cerr);
879                         } else {
880                                 cerr << "MIDI other event type\n";
881                                 source->append_event_unlocked_beats(*i);
882                         }
883                 }
884         }
885
886         mst.resolve_notes (*source, end_time);
887
888         set_percussive(old_percussive);
889         source->mark_streaming_write_completed();
890
891         set_edited(false);
892
893         return true;
894 }
895
896 XMLNode&
897 MidiModel::get_state()
898 {
899         XMLNode *node = new XMLNode("MidiModel");
900         return *node;
901 }
902
903 Evoral::Sequence<MidiModel::TimeType>::NotePtr
904 MidiModel::find_note (NotePtr other)
905 {
906         Notes::iterator l = notes().lower_bound(other);
907
908         if (l != notes().end()) {
909                 for (; (*l)->time() == other->time(); ++l) {
910                         /* NB: compare note contents, not note pointers.
911                            If "other" was a ptr to a note already in
912                            the model, we wouldn't be looking for it,
913                            would we now?
914                         */
915                         if (**l == *other) {
916                                 return *l;
917                         }
918                 }
919         }
920
921         return NotePtr();
922 }
923
924 /** Lock and invalidate the source.
925  * This should be used by commands and editing things
926  */
927 MidiModel::WriteLock
928 MidiModel::edit_lock()
929 {
930         Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
931         _midi_source->invalidate(); // Release cached iterator's read lock on model
932         return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
933 }
934
935 /** Lock just the model, the source lock must already be held.
936  * This should only be called from libardour/evoral places
937  */
938 MidiModel::WriteLock
939 MidiModel::write_lock()
940 {
941         assert(!_midi_source->mutex().trylock());
942         return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
943 }
944
945 int
946 MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
947 {
948         using namespace Evoral;
949
950         if (_writing || insert_merge_policy() == InsertMergeRelax) {
951                 return 0;
952         }
953
954         DiffCommand* cmd = static_cast<DiffCommand*>(arg);
955
956         TimeType sa = note->time();
957         TimeType ea  = note->end_time();
958
959         const Pitches& p (pitches (note->channel()));
960         NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
961         set<NotePtr> to_be_deleted;
962         bool set_note_length = false;
963         bool set_note_time = false;
964         TimeType note_time = note->time();
965         TimeType note_length = note->length();
966
967         for (Pitches::const_iterator i = p.lower_bound (search_note); 
968              i != p.end() && (*i)->note() == note->note(); ++i) {
969
970                 TimeType sb = (*i)->time();
971                 TimeType eb = (*i)->end_time();
972                 OverlapType overlap = OverlapNone;
973
974                 if ((sb > sa) && (eb <= ea)) {
975                         overlap = OverlapInternal;
976                 } else if ((eb >= sa) && (eb <= ea)) {
977                         overlap = OverlapStart;
978                 } else if ((sb > sa) && (sb <= ea)) {
979                         overlap = OverlapEnd;
980                 } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
981                         overlap = OverlapExternal;
982                 } else {
983                         /* no overlap */
984                         continue;
985                 }
986
987                 if (insert_merge_policy() == InsertMergeReject) {
988                         return -1;
989                 }
990
991                 switch (overlap) {
992                 case OverlapStart:
993                         cerr << "OverlapStart\n";
994                         /* existing note covers start of new note */
995                         switch (insert_merge_policy()) {
996                         case InsertMergeReplace:
997                                 to_be_deleted.insert (*i);
998                                 break;
999                         case InsertMergeTruncateExisting:
1000                                 if (cmd) {
1001                                         cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
1002                                 }
1003                                 (*i)->set_length (note->time() - (*i)->time());
1004                                 break;
1005                         case InsertMergeTruncateAddition:
1006                                 set_note_time = true;
1007                                 set_note_length = true;
1008                                 note_time = (*i)->time() + (*i)->length();
1009                                 note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
1010                                 break;
1011                         case InsertMergeExtend:
1012                                 if (cmd) {
1013                                         cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
1014                                 } 
1015                                 (*i)->set_length (note->end_time() - (*i)->time());
1016                                 return -1; /* do not add the new note */
1017                                 break;
1018                         default:
1019                                 /*NOTREACHED*/
1020                                 /* stupid gcc */
1021                                 break;
1022                         }
1023                         break;
1024
1025                 case OverlapEnd:
1026                         cerr << "OverlapEnd\n";
1027                         /* existing note covers end of new note */
1028                         switch (insert_merge_policy()) {
1029                         case InsertMergeReplace:
1030                                 to_be_deleted.insert (*i);
1031                                 break;
1032
1033                         case InsertMergeTruncateExisting:
1034                                 /* resetting the start time of the existing note
1035                                    is a problem because of time ordering.
1036                                 */
1037                                 break;
1038
1039                         case InsertMergeTruncateAddition:
1040                                 set_note_length = true;
1041                                 note_length = min (note_length, ((*i)->time() - note->time()));
1042                                 break;
1043
1044                         case InsertMergeExtend:
1045                                 /* we can't reset the time of the existing note because
1046                                    that will corrupt time ordering. So remove the
1047                                    existing note and change the position/length
1048                                    of the new note (which has not been added yet)
1049                                 */
1050                                 to_be_deleted.insert (*i);
1051                                 set_note_length = true;
1052                                 note_length = min (note_length, (*i)->end_time() - note->time());
1053                                 break;
1054                         default:
1055                                 /*NOTREACHED*/
1056                                 /* stupid gcc */
1057                                 break;
1058                         }
1059                         break;
1060
1061                 case OverlapExternal:
1062                         cerr << "OverlapExt\n";
1063                         /* existing note overlaps all the new note */
1064                         switch (insert_merge_policy()) {
1065                         case InsertMergeReplace:
1066                                 to_be_deleted.insert (*i);
1067                                 break;
1068                         case InsertMergeTruncateExisting:
1069                         case InsertMergeTruncateAddition:
1070                         case InsertMergeExtend:
1071                                 /* cannot add in this case */
1072                                 return -1;
1073                         default:
1074                                 /*NOTREACHED*/
1075                                 /* stupid gcc */
1076                                 break;
1077                         }
1078                         break;
1079
1080                 case OverlapInternal:
1081                         cerr << "OverlapInt\n";
1082                         /* new note fully overlaps an existing note */
1083                         switch (insert_merge_policy()) {
1084                         case InsertMergeReplace:
1085                         case InsertMergeTruncateExisting:
1086                         case InsertMergeTruncateAddition:
1087                         case InsertMergeExtend:
1088                                 /* delete the existing note, the new one will cover it */
1089                                 to_be_deleted.insert (*i);
1090                                 break;
1091                         default:
1092                                 /*NOTREACHED*/
1093                                 /* stupid gcc */
1094                                 break;
1095                         }
1096                         break;
1097
1098                 default:
1099                         /*NOTREACHED*/
1100                         /* stupid gcc */
1101                         break;
1102                 }
1103         }
1104
1105         for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
1106                 remove_note_unlocked (*i);
1107
1108                 if (cmd) {
1109                         cmd->side_effect_remove (*i);
1110                 }
1111         }
1112
1113         if (set_note_time) {
1114                 if (cmd) {
1115                         cmd->change (note, DiffCommand::StartTime, note_time);
1116                 } 
1117                 note->set_time (note_time);
1118         }
1119
1120         if (set_note_length) {
1121                 if (cmd) {
1122                         cmd->change (note, DiffCommand::Length, note_length);
1123                 } 
1124                 note->set_length (note_length);
1125         }
1126
1127         return 0;
1128 }
1129
1130 InsertMergePolicy
1131 MidiModel::insert_merge_policy () const 
1132 {
1133         /* XXX ultimately this should be a per-track or even per-model policy */
1134
1135         return _midi_source->session().config.get_insert_merge_policy();
1136 }
1137                         
1138 void
1139 MidiModel::set_midi_source (MidiSource* s)
1140 {
1141         _midi_source->invalidate ();
1142         _midi_source = s;
1143 }