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