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