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