rationale pathways that add notes to Sequence<T> so that there is only final insertio...
[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
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 Delta 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::DeltaCommand*
55 MidiModel::new_delta_command(const string name)
56 {
57         DeltaCommand* cmd = new DeltaCommand(_midi_source->model(), name);
58         return cmd;
59 }
60
61 /** Start a new Diff command.
62  *
63  * This has no side-effects on the model or Session, the returned command
64  * can be held on to for as long as the caller wishes, or discarded without
65  * formality, until apply_command is called and ownership is taken.
66  */
67 MidiModel::DiffCommand*
68 MidiModel::new_diff_command(const string name)
69 {
70         DiffCommand* cmd = new DiffCommand(_midi_source->model(), name);
71         return cmd;
72 }
73
74 /** Apply a command.
75  *
76  * Ownership of cmd is taken, it must not be deleted by the caller.
77  * The command will constitute one item on the undo stack.
78  */
79 void
80 MidiModel::apply_command(Session& session, Command* cmd)
81 {
82         session.begin_reversible_command(cmd->name());
83         (*cmd)();
84         session.commit_reversible_command(cmd);
85         set_edited(true);
86 }
87
88 /** Apply a command as part of a larger reversible transaction
89  *
90  * Ownership of cmd is taken, it must not be deleted by the caller.
91  * The command will constitute one item on the undo stack.
92  */
93 void
94 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
95 {
96         (*cmd)();
97         session.add_command(cmd);
98         set_edited(true);
99 }
100
101
102 // DeltaCommand
103
104 MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
105         : Command(name)
106         , _model(m)
107         , _name(name)
108 {
109         assert(_model);
110 }
111
112 MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
113         : _model(m)
114 {
115         assert(_model);
116         set_state(node, Stateful::loading_state_version);
117 }
118
119 void
120 MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note<TimeType> > note)
121 {
122         _removed_notes.remove(note);
123         _added_notes.push_back(note);
124 }
125
126 void
127 MidiModel::DeltaCommand::remove(const boost::shared_ptr< Evoral::Note<TimeType> > note)
128 {
129         _added_notes.remove(note);
130         _removed_notes.push_back(note);
131 }
132
133 void
134 MidiModel::DeltaCommand::operator()()
135 {
136         // This could be made much faster by using a priority_queue for added and
137         // removed notes (or sort here), and doing a single iteration over _model
138
139         MidiModel::WriteLock lock(_model->edit_lock());
140
141         for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
142                 _model->add_note_unlocked(*i);
143         }
144
145         for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
146                 _model->remove_note_unlocked(*i);
147         }
148
149         lock.reset();
150         _model->ContentsChanged(); /* EMIT SIGNAL */
151 }
152
153 void
154 MidiModel::DeltaCommand::undo()
155 {
156         // This could be made much faster by using a priority_queue for added and
157         // removed notes (or sort here), and doing a single iteration over _model
158
159         MidiModel::WriteLock lock(_model->edit_lock());;
160
161         for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
162                 _model->remove_note_unlocked(*i);
163         }
164
165         for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
166                 _model->add_note_unlocked(*i);
167         }
168
169         lock.reset();
170         _model->ContentsChanged(); /* EMIT SIGNAL */
171 }
172
173 XMLNode&
174 MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note<TimeType> > note)
175 {
176         XMLNode* xml_note = new XMLNode("note");
177         ostringstream note_str(ios::ate);
178         note_str << int(note->note());
179         xml_note->add_property("note", note_str.str());
180
181         ostringstream channel_str(ios::ate);
182         channel_str << int(note->channel());
183         xml_note->add_property("channel", channel_str.str());
184
185         ostringstream time_str(ios::ate);
186         time_str << int(note->time());
187         xml_note->add_property("time", time_str.str());
188
189         ostringstream length_str(ios::ate);
190         length_str <<(unsigned int) note->length();
191         xml_note->add_property("length", length_str.str());
192
193         ostringstream velocity_str(ios::ate);
194         velocity_str << (unsigned int) note->velocity();
195         xml_note->add_property("velocity", velocity_str.str());
196
197         return *xml_note;
198 }
199
200 boost::shared_ptr< Evoral::Note<MidiModel::TimeType> >
201 MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
202 {
203         unsigned int note;
204         XMLProperty* prop;
205         unsigned int channel;
206         unsigned int time;
207         unsigned int length;
208         unsigned int velocity;
209
210         if ((prop = xml_note->property("note")) != 0) {
211                 istringstream note_str(prop->value());
212                 note_str >> note;
213         } else {
214                 warning << "note information missing note value" << endmsg;
215                 note = 127;
216         }
217
218         if ((prop = xml_note->property("channel")) != 0) {
219                 istringstream channel_str(prop->value());
220                 channel_str >> channel;
221         } else {
222                 warning << "note information missing channel" << endmsg;
223                 channel = 0;
224         }
225
226         if ((prop = xml_note->property("time")) != 0) {
227                 istringstream time_str(prop->value());
228                 time_str >> time;
229         } else {
230                 warning << "note information missing time" << endmsg;
231                 time = 0;
232         }
233
234         if ((prop = xml_note->property("length")) != 0) {
235                 istringstream length_str(prop->value());
236                 length_str >> length;
237         } else {
238                 warning << "note information missing length" << endmsg;
239                 length = 1;
240         }
241
242         if ((prop = xml_note->property("velocity")) != 0) {
243                 istringstream velocity_str(prop->value());
244                 velocity_str >> velocity;
245         } else {
246                 warning << "note information missing velocity" << endmsg;
247                 velocity = 127;
248         }
249
250         boost::shared_ptr< Evoral::Note<TimeType> > note_ptr(new Evoral::Note<TimeType>(
251                         channel, time, length, note, velocity));
252         return note_ptr;
253 }
254
255 #define ADDED_NOTES_ELEMENT "AddedNotes"
256 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
257 #define DELTA_COMMAND_ELEMENT "DeltaCommand"
258
259 int
260 MidiModel::DeltaCommand::set_state (const XMLNode& delta_command, int /*version*/)
261 {
262         if (delta_command.name() != string(DELTA_COMMAND_ELEMENT)) {
263                 return 1;
264         }
265
266         _added_notes.clear();
267         XMLNode* added_notes = delta_command.child(ADDED_NOTES_ELEMENT);
268         if (added_notes) {
269                 XMLNodeList notes = added_notes->children();
270                 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
271                           boost::bind (&DeltaCommand::unmarshal_note, this, _1));
272         }
273
274         _removed_notes.clear();
275         XMLNode* removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT);
276         if (removed_notes) {
277                 XMLNodeList notes = removed_notes->children();
278                 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
279                           boost::bind (&DeltaCommand::unmarshal_note, this, _1));
280         }
281
282         return 0;
283 }
284
285 XMLNode&
286 MidiModel::DeltaCommand::get_state()
287 {
288         XMLNode* delta_command = new XMLNode(DELTA_COMMAND_ELEMENT);
289         delta_command->add_property("midi-source", _model->midi_source()->id().to_s());
290
291         XMLNode* added_notes = delta_command->add_child(ADDED_NOTES_ELEMENT);
292         for_each(_added_notes.begin(), _added_notes.end(), 
293                  boost::bind(
294                          boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
295                          boost::bind (&DeltaCommand::marshal_note, this, _1)));
296
297         XMLNode* removed_notes = delta_command->add_child(REMOVED_NOTES_ELEMENT);
298         for_each(_removed_notes.begin(), _removed_notes.end(), 
299                  boost::bind (
300                          boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
301                          boost::bind (&DeltaCommand::marshal_note, this, _1)));
302
303         return *delta_command;
304 }
305
306 /************** DIFF COMMAND ********************/
307
308 #define DIFF_NOTES_ELEMENT "ChangedNotes"
309 #define DIFF_COMMAND_ELEMENT "DiffCommand"
310
311 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
312         : Command(name)
313         , _model(m)
314         , _name(name)
315 {
316         assert(_model);
317 }
318
319 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
320         : _model(m)
321 {
322         assert(_model);
323         set_state(node, Stateful::loading_state_version);
324 }
325
326 void
327 MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop,
328                                uint8_t new_value)
329 {
330         NoteChange change;
331
332         switch (prop) {
333         case NoteNumber:
334                 if (new_value == note->note()) {
335                         return;
336                 }
337                 change.old_value = note->note();
338                 break;
339         case Velocity:
340                 if (new_value == note->velocity()) {
341                         return;
342                 }
343                 change.old_value = note->velocity();
344                 break;
345         case Channel:
346                 if (new_value == note->channel()) {
347                         return;
348                 }
349                 change.old_value = note->channel();
350                 break;
351
352
353         case StartTime:
354                 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
355                 /*NOTREACHED*/
356                 break;
357         case Length:
358                 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
359                 /*NOTREACHED*/
360                 break;
361         }
362
363         change.note = note;
364         change.property = prop;
365         change.new_value = new_value;
366
367         _changes.push_back (change);
368 }
369
370 void
371 MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop,
372                                TimeType new_time)
373 {
374         NoteChange change;
375
376         switch (prop) {
377         case NoteNumber:
378         case Channel:
379         case Velocity:
380                 fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
381                 break;
382
383         case StartTime:
384                 if (Evoral::musical_time_equal (note->time(), new_time)) {
385                         return;
386                 }
387                 change.old_time = note->time();
388                 break;
389         case Length:
390                 if (Evoral::musical_time_equal (note->length(), new_time)) {
391                         return;
392                 }
393                 change.old_time = note->length();
394                 break;
395         }
396
397         change.note = note;
398         change.property = prop;
399         change.new_time = new_time;
400
401         _changes.push_back (change);
402 }
403
404 void
405 MidiModel::DiffCommand::operator()()
406 {
407         {
408                 MidiModel::WriteLock lock(_model->edit_lock());
409                 
410                 set<boost::shared_ptr<Evoral::Note<TimeType> > > removed_notes;
411
412                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
413                         Property prop = i->property;
414                         switch (prop) {
415                         case NoteNumber:
416                                 if (removed_notes.find (i->note) == removed_notes.end()) {
417                                         _model->remove_note_unlocked (i->note);
418                                         removed_notes.insert (i->note);
419                                 }
420                                 i->note->set_note (i->new_value);
421                                 break;
422                         case Velocity:
423                                 i->note->set_velocity (i->new_value);
424                                 break;
425                         case StartTime:
426                                 if (removed_notes.find (i->note) == removed_notes.end()) {
427                                         _model->remove_note_unlocked (i->note);
428                                         removed_notes.insert (i->note);
429
430                                 }
431                                 i->note->set_time (i->new_time);
432                                 break;
433                         case Length:
434                                 i->note->set_length (i->new_time);
435                                 break;
436                         case Channel:
437                                 if (removed_notes.find (i->note) == removed_notes.end()) {
438                                         _model->remove_note_unlocked (i->note);
439                                         removed_notes.insert (i->note);
440                                 }
441                                 i->note->set_channel (i->new_value);
442                                 break;
443                         }
444                 }
445
446                 for (set<boost::shared_ptr<Evoral::Note<TimeType> > >::iterator i = removed_notes.begin(); i != removed_notes.end(); ++i) {
447                         _model->add_note_unlocked (*i);
448                 }
449         }
450
451         _model->ContentsChanged(); /* EMIT SIGNAL */
452 }
453
454 void
455 MidiModel::DiffCommand::undo()
456 {
457         {
458                 MidiModel::WriteLock lock(_model->edit_lock());
459                 
460                 set<boost::shared_ptr<Evoral::Note<TimeType> > > removed_notes;
461
462                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
463                         Property prop = i->property;
464                         switch (prop) {
465                         case NoteNumber:
466                                 if (removed_notes.find (i->note) == removed_notes.end()) {
467                                         _model->remove_note_unlocked (i->note);
468                                         removed_notes.insert (i->note);
469                                 }
470                                 i->note->set_note (i->old_value);
471                                 break;
472                         case Velocity:
473                                 i->note->set_velocity (i->old_value);
474                                 break;
475                         case StartTime:
476                                 if (removed_notes.find (i->note) == removed_notes.end()) {
477                                         _model->remove_note_unlocked (i->note);
478                                         removed_notes.insert (i->note);
479                                 }
480                                 i->note->set_time (i->old_time);
481                                 break;
482                         case Length:
483                                 i->note->set_length (i->old_time);
484                                 break;
485                         case Channel:
486                                 if (removed_notes.find (i->note) == removed_notes.end()) {
487                                         _model->remove_note_unlocked (i->note);
488                                         removed_notes.insert (i->note);
489                                 }
490                                 i->note->set_channel (i->old_value);
491                                 break;
492                         }
493                 }
494
495                 for (set<boost::shared_ptr<Evoral::Note<TimeType> > >::iterator i = removed_notes.begin(); i != removed_notes.end(); ++i) {
496                         _model->add_note_unlocked (*i);
497                 }
498         }
499
500         _model->ContentsChanged(); /* EMIT SIGNAL */
501 }
502
503 XMLNode&
504 MidiModel::DiffCommand::marshal_change(const NoteChange& change)
505 {
506         XMLNode* xml_change = new XMLNode("change");
507
508         /* first, the change itself */
509
510         xml_change->add_property ("property", enum_2_string (change.property));
511
512         {
513                 ostringstream old_value_str (ios::ate);
514                 if (change.property == StartTime || change.property == Length) {
515                         old_value_str << change.old_time;
516                 } else {
517                         old_value_str << (unsigned int) change.old_value;
518                 }
519                 xml_change->add_property ("old", old_value_str.str());
520         }
521
522         {
523                 ostringstream new_value_str (ios::ate);
524                 if (change.property == StartTime || change.property == Length) {
525                         new_value_str << change.new_time;
526                 } else {
527                         new_value_str << (unsigned int) change.new_value;
528                 }
529                 xml_change->add_property ("new", new_value_str.str());
530         }
531
532         /* now the rest of the note */
533
534         const SMFSource* smf = dynamic_cast<const SMFSource*> (_model->midi_source());
535
536         if (change.property != NoteNumber) {
537                 ostringstream note_str;
538                 note_str << int(change.note->note());
539                 xml_change->add_property("note", note_str.str());
540         }
541
542         if (change.property != Channel) {
543                 ostringstream channel_str;
544                 channel_str << int(change.note->channel());
545                 xml_change->add_property("channel", channel_str.str());
546         }
547
548         if (change.property != StartTime) {
549                 ostringstream time_str;
550                 if (smf) {
551                         time_str << smf->round_to_file_precision (change.note->time());
552                 } else {
553                         time_str << change.note->time();
554                 }
555                 xml_change->add_property("time", time_str.str());
556         }
557
558         if (change.property != Length) {
559                 ostringstream length_str;
560                 if (smf) {
561                         length_str << smf->round_to_file_precision (change.note->length());
562                 } else {
563                         length_str << change.note->length();
564                 }
565                 xml_change->add_property ("length", length_str.str());
566         }
567
568         if (change.property != Velocity) {
569                 ostringstream velocity_str;
570                 velocity_str << int (change.note->velocity());
571                 xml_change->add_property("velocity", velocity_str.str());
572         }
573
574         return *xml_change;
575 }
576
577 MidiModel::DiffCommand::NoteChange
578 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
579 {
580         XMLProperty* prop;
581         NoteChange change;
582         unsigned int note;
583         unsigned int channel;
584         unsigned int velocity;
585         Evoral::MusicalTime time;
586         Evoral::MusicalTime length;
587
588         if ((prop = xml_change->property("property")) != 0) {
589                 change.property = (Property) string_2_enum (prop->value(), change.property);
590         } else {
591                 fatal << "!!!" << endmsg;
592                 /*NOTREACHED*/
593         }
594
595         if ((prop = xml_change->property ("old")) != 0) {
596                 istringstream old_str (prop->value());
597                 if (change.property == StartTime || change.property == Length) {
598                         old_str >> change.old_time;
599                 } else {
600                         int integer_value_so_that_istream_does_the_right_thing;
601                         old_str >> integer_value_so_that_istream_does_the_right_thing;
602                         change.old_value = integer_value_so_that_istream_does_the_right_thing;
603                 }
604         } else {
605                 fatal << "!!!" << endmsg;
606                 /*NOTREACHED*/
607         }
608
609         if ((prop = xml_change->property ("new")) != 0) {
610                 istringstream new_str (prop->value());
611                 if (change.property == StartTime || change.property == Length) {
612                         new_str >> change.new_time;
613                 } else {
614                         int integer_value_so_that_istream_does_the_right_thing;
615                         new_str >> integer_value_so_that_istream_does_the_right_thing;
616                         change.new_value = integer_value_so_that_istream_does_the_right_thing;
617                 }
618         } else {
619                 fatal << "!!!" << endmsg;
620                 /*NOTREACHED*/
621         }
622
623         if (change.property != NoteNumber) {
624                 if ((prop = xml_change->property("note")) != 0) {
625                         istringstream note_str(prop->value());
626                         note_str >> note;
627                 } else {
628                         warning << "note information missing note value" << endmsg;
629                         note = 127;
630                 }
631         } else {
632                 note = change.new_value;
633         }
634
635         if (change.property != Channel) {
636                 if ((prop = xml_change->property("channel")) != 0) {
637                         istringstream channel_str(prop->value());
638                         channel_str >> channel;
639                 } else {
640                         warning << "note information missing channel" << endmsg;
641                         channel = 0;
642                 }
643         } else {
644                 channel = change.new_value;
645         }
646
647         if (change.property != StartTime) {
648                 if ((prop = xml_change->property("time")) != 0) {
649                         istringstream time_str(prop->value());
650                         time_str >> time;
651                 } else {
652                         warning << "note information missing time" << endmsg;
653                         time = 0;
654                 }
655         } else {
656                 time = change.new_time;
657         }
658
659         if (change.property != Length) {
660                 if ((prop = xml_change->property("length")) != 0) {
661                         istringstream length_str(prop->value());
662                         length_str >> length;
663                 } else {
664                         warning << "note information missing length" << endmsg;
665                         length = 1;
666                 }
667         } else {
668                 length = change.new_time;
669         }
670
671         if (change.property != Velocity) {
672                 if ((prop = xml_change->property("velocity")) != 0) {
673                         istringstream velocity_str(prop->value());
674                         velocity_str >> velocity;
675                 } else {
676                         warning << "note information missing velocity" << endmsg;
677                         velocity = 127;
678                 }
679         } else {
680                 velocity = change.new_value;
681         }
682
683         /* we must point at the instance of the note that is actually in the model.
684            so go look for it ...
685         */
686
687         boost::shared_ptr<Evoral::Note<TimeType> > new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
688
689         change.note = _model->find_note (new_note);
690
691         if (!change.note) {
692                 warning << "MIDI note " << *new_note << " not found in model - programmers should investigate this" << endmsg;
693                 /* use the actual new note */
694                 change.note = new_note;
695         }
696
697         return change;
698 }
699
700 int
701 MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
702 {
703         if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
704                 return 1;
705         }
706
707         _changes.clear();
708
709         XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
710
711         if (changed_notes) {
712                 XMLNodeList notes = changed_notes->children();
713                 transform (notes.begin(), notes.end(), back_inserter(_changes),
714                            boost::bind (&DiffCommand::unmarshal_change, this, _1));
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         return *diff_command;
734 }
735
736 /** Write all of the model to a MidiSource (i.e. save the model).
737  * This is different from manually using read to write to a source in that
738  * note off events are written regardless of the track mode.  This is so the
739  * user can switch a recorded track (with note durations from some instrument)
740  * to percussive, save, reload, then switch it back to sustained without
741  * destroying the original note durations.
742  */
743 bool
744 MidiModel::write_to (boost::shared_ptr<MidiSource> source)
745 {
746         ReadLock lock(read_lock());
747
748         const bool old_percussive = percussive();
749         set_percussive(false);
750
751         source->drop_model();
752         source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
753
754         for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
755                 source->append_event_unlocked_beats(*i);
756         }
757
758         set_percussive(old_percussive);
759         source->mark_streaming_write_completed();
760
761         set_edited(false);
762
763         return true;
764 }
765
766 /** Write part or all of the model to a MidiSource (i.e. save the model).
767  * This is different from manually using read to write to a source in that
768  * note off events are written regardless of the track mode.  This is so the
769  * user can switch a recorded track (with note durations from some instrument)
770  * to percussive, save, reload, then switch it back to sustained without
771  * destroying the original note durations.
772  */
773 bool
774 MidiModel::write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
775 {
776         ReadLock lock(read_lock());
777         MidiStateTracker mst;
778         Evoral::MusicalTime extra_note_on_time = end_time;
779
780         const bool old_percussive = percussive();
781         set_percussive(false);
782
783         source->drop_model();
784         source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
785
786         for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
787                 const Evoral::Event<Evoral::MusicalTime>& ev (*i);
788                 
789                 if (ev.time() >= begin_time && ev.time() < end_time) {
790
791                         const Evoral::MIDIEvent<Evoral::MusicalTime>* mev = 
792                                 static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
793                         
794                         if (!mev) {
795                                 continue;
796                         }
797
798
799                         if (mev->is_note_off()) {
800                                 
801                                 if (!mst.active (mev->note(), mev->channel())) {
802                                         
803                                         /* add a note-on at the start of the range we're writing
804                                            to the file. velocity is just an arbitary reasonable value.
805                                         */
806                                         
807                                         Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
808                                         on.set_type (mev->type());
809                                         on.set_note (mev->note());
810                                         on.set_channel (mev->channel());
811                                         on.set_velocity (mev->velocity());
812                                         
813                                         cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
814                                         source->append_event_unlocked_beats (on);
815                                         mst.add (on.note(), on.channel());
816                                         mst.dump (cerr);
817                                         extra_note_on_time += 1.0/128.0;
818                                 }
819                                         
820                                 cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
821                                 source->append_event_unlocked_beats (*i);
822                                 mst.remove (mev->note(), mev->channel());
823                                 mst.dump (cerr);
824                                         
825                         } else if (mev->is_note_on()) {
826                                 cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
827                                 mst.add (mev->note(), mev->channel());
828                                 source->append_event_unlocked_beats(*i);
829                                 mst.dump (cerr);
830                         } else {
831                                 cerr << "MIDI other event type\n";
832                                 source->append_event_unlocked_beats(*i);
833                         }
834                 }
835         }
836
837         mst.resolve_notes (*source, end_time);
838
839         set_percussive(old_percussive);
840         source->mark_streaming_write_completed();
841
842         set_edited(false);
843
844         return true;
845 }
846
847 XMLNode&
848 MidiModel::get_state()
849 {
850         XMLNode *node = new XMLNode("MidiModel");
851         return *node;
852 }
853
854 boost::shared_ptr<Evoral::Note<MidiModel::TimeType> >
855 MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other)
856 {
857         Notes::iterator l = notes().lower_bound(other);
858
859         if (l != notes().end()) {
860                 for (; (*l)->time() == other->time(); ++l) {
861                         /* NB: compare note contents, not note pointers.
862                            If "other" was a ptr to a note already in
863                            the model, we wouldn't be looking for it,
864                            would we now?
865                          */
866                         if (**l == *other) {
867                                 return *l;
868                         }
869                 }
870         }
871
872         return boost::shared_ptr<Evoral::Note<TimeType> >();
873 }
874
875 /** Lock and invalidate the source.
876  * This should be used by commands and editing things
877  */
878 MidiModel::WriteLock
879 MidiModel::edit_lock()
880 {
881         Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
882         _midi_source->invalidate(); // Release cached iterator's read lock on model
883         return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
884 }
885
886 /** Lock just the model, the source lock must already be held.
887  * This should only be called from libardour/evoral places
888  */
889 MidiModel::WriteLock
890 MidiModel::write_lock()
891 {
892         assert(!_midi_source->mutex().trylock());
893         return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
894 }