a95c0958036ff095ca985db21529367f8e4b3d7e
[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          /* XXX ultimately this should be a per-track or even per-model policy */
1108
1109          return _midi_source->session().config.get_insert_merge_policy();
1110 }
1111                         
1112 void
1113 MidiModel::set_midi_source (MidiSource* s)
1114 {
1115         _midi_source->invalidate ();
1116         _midi_source = s;
1117 }