do not crash when loading old history files with MIDI edits; add all notes in region...
[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/types.h"
34 #include "ardour/session.h"
35
36 using namespace std;
37 using namespace ARDOUR;
38 using namespace PBD;
39
40 MidiModel::MidiModel(MidiSource* s, size_t size)
41         : AutomatableSequence<TimeType>(s->session(), size)
42         , _midi_source(s)
43 {
44 }
45
46 /** Start a new Delta command.
47  *
48  * This has no side-effects on the model or Session, the returned command
49  * can be held on to for as long as the caller wishes, or discarded without
50  * formality, until apply_command is called and ownership is taken.
51  */
52 MidiModel::DeltaCommand*
53 MidiModel::new_delta_command(const string name)
54 {
55         DeltaCommand* cmd = new DeltaCommand(_midi_source->model(), name);
56         return cmd;
57 }
58
59 /** Start a new Diff command.
60  *
61  * This has no side-effects on the model or Session, the returned command
62  * can be held on to for as long as the caller wishes, or discarded without
63  * formality, until apply_command is called and ownership is taken.
64  */
65 MidiModel::DiffCommand*
66 MidiModel::new_diff_command(const string name)
67 {
68         DiffCommand* cmd = new DiffCommand(_midi_source->model(), name);
69         return cmd;
70 }
71
72 /** Apply a command.
73  *
74  * Ownership of cmd is taken, it must not be deleted by the caller.
75  * The command will constitute one item on the undo stack.
76  */
77 void
78 MidiModel::apply_command(Session& session, Command* cmd)
79 {
80         session.begin_reversible_command(cmd->name());
81         (*cmd)();
82         session.commit_reversible_command(cmd);
83         set_edited(true);
84 }
85
86 /** Apply a command as part of a larger reversible transaction
87  *
88  * Ownership of cmd is taken, it must not be deleted by the caller.
89  * The command will constitute one item on the undo stack.
90  */
91 void
92 MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
93 {
94         (*cmd)();
95         session.add_command(cmd);
96         set_edited(true);
97 }
98
99
100 // DeltaCommand
101
102 MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
103         : Command(name)
104         , _model(m)
105         , _name(name)
106 {
107         assert(_model);
108 }
109
110 MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
111         : _model(m)
112 {
113         assert(_model);
114         set_state(node);
115 }
116
117 void
118 MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note<TimeType> > note)
119 {
120         _removed_notes.remove(note);
121         _added_notes.push_back(note);
122 }
123
124 void
125 MidiModel::DeltaCommand::remove(const boost::shared_ptr< Evoral::Note<TimeType> > note)
126 {
127         _added_notes.remove(note);
128         _removed_notes.push_back(note);
129 }
130
131 void
132 MidiModel::DeltaCommand::operator()()
133 {
134         // This could be made much faster by using a priority_queue for added and
135         // removed notes (or sort here), and doing a single iteration over _model
136
137         Glib::Mutex::Lock lm (_model->_midi_source->mutex());
138         _model->_midi_source->invalidate(); // release model read lock
139         _model->write_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         _model->write_unlock();
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         Glib::Mutex::Lock lm (_model->_midi_source->mutex());
160         _model->_midi_source->invalidate(); // release model read lock
161         _model->write_lock();
162
163         for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
164                 _model->remove_note_unlocked(*i);
165         }
166
167         for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
168                 _model->add_note_unlocked(*i);
169         }
170
171         _model->write_unlock();
172         _model->ContentsChanged(); /* EMIT SIGNAL */
173 }
174
175 XMLNode&
176 MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note<TimeType> > note)
177 {
178         XMLNode* xml_note = new XMLNode("note");
179         ostringstream note_str(ios::ate);
180         note_str << int(note->note());
181         xml_note->add_property("note", note_str.str());
182
183         ostringstream channel_str(ios::ate);
184         channel_str << int(note->channel());
185         xml_note->add_property("channel", channel_str.str());
186
187         ostringstream time_str(ios::ate);
188         time_str << int(note->time());
189         xml_note->add_property("time", time_str.str());
190
191         ostringstream length_str(ios::ate);
192         length_str <<(unsigned int) note->length();
193         xml_note->add_property("length", length_str.str());
194
195         ostringstream velocity_str(ios::ate);
196         velocity_str << (unsigned int) note->velocity();
197         xml_note->add_property("velocity", velocity_str.str());
198
199         return *xml_note;
200 }
201
202 boost::shared_ptr< Evoral::Note<MidiModel::TimeType> >
203 MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
204 {
205         unsigned int note;
206         XMLProperty* prop;
207         unsigned int channel;
208         unsigned int time;
209         unsigned int length;
210         unsigned int velocity;
211
212         if ((prop = xml_note->property("note")) != 0) {
213                 istringstream note_str(prop->value());
214                 note_str >> note;
215         } else {
216                 warning << "note information missing note value" << endmsg;
217                 note = 127;
218         }
219
220         if ((prop = xml_note->property("channel")) != 0) {
221                 istringstream channel_str(prop->value());
222                 channel_str >> channel;
223         } else {
224                 warning << "note information missing channel" << endmsg;
225                 channel = 0;
226         }
227
228         if ((prop = xml_note->property("time")) != 0) {
229                 istringstream time_str(prop->value());
230                 time_str >> time;
231         } else {
232                 warning << "note information missing time" << endmsg;
233                 time = 0;
234         }
235
236         if ((prop = xml_note->property("length")) != 0) {
237                 istringstream length_str(prop->value());
238                 length_str >> length;
239         } else {
240                 warning << "note information missing length" << endmsg;
241                 length = 1;
242         }
243
244         if ((prop = xml_note->property("velocity")) != 0) {
245                 istringstream velocity_str(prop->value());
246                 velocity_str >> velocity;
247         } else {
248                 warning << "note information missing velocity" << endmsg;
249                 velocity = 127;
250         }
251
252         boost::shared_ptr< Evoral::Note<TimeType> > note_ptr(new Evoral::Note<TimeType>(
253                         channel, time, length, note, velocity));
254         return note_ptr;
255 }
256
257 #define ADDED_NOTES_ELEMENT "AddedNotes"
258 #define REMOVED_NOTES_ELEMENT "RemovedNotes"
259 #define DELTA_COMMAND_ELEMENT "DeltaCommand"
260
261 int
262 MidiModel::DeltaCommand::set_state(const XMLNode& delta_command)
263 {
264         if (delta_command.name() != string(DELTA_COMMAND_ELEMENT)) {
265                 return 1;
266         }
267
268         _added_notes.clear();
269         XMLNode* added_notes = delta_command.child(ADDED_NOTES_ELEMENT);
270         if (added_notes) {
271                 XMLNodeList notes = added_notes->children();
272                 transform(notes.begin(), notes.end(), back_inserter(_added_notes),
273                           sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
274         }
275
276         _removed_notes.clear();
277         XMLNode* removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT);
278         if (removed_notes) {
279                 XMLNodeList notes = removed_notes->children();
280                 transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
281                           sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
282         }
283
284         return 0;
285 }
286
287 XMLNode&
288 MidiModel::DeltaCommand::get_state()
289 {
290         XMLNode* delta_command = new XMLNode(DELTA_COMMAND_ELEMENT);
291         delta_command->add_property("midi-source", _model->midi_source()->id().to_s());
292
293         XMLNode* added_notes = delta_command->add_child(ADDED_NOTES_ELEMENT);
294         for_each(_added_notes.begin(), _added_notes.end(), sigc::compose(
295                         sigc::mem_fun(*added_notes, &XMLNode::add_child_nocopy),
296                         sigc::mem_fun(*this, &DeltaCommand::marshal_note)));
297
298         XMLNode* removed_notes = delta_command->add_child(REMOVED_NOTES_ELEMENT);
299         for_each(_removed_notes.begin(), _removed_notes.end(), sigc::compose(
300                         sigc::mem_fun(*removed_notes, &XMLNode::add_child_nocopy),
301                         sigc::mem_fun(*this, &DeltaCommand::marshal_note)));
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);
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         NotePropertyChange change;
331
332         change.note = note;
333         change.property = prop;
334         change.new_value = new_value;
335
336         switch (prop) {
337         case NoteNumber:
338                 change.old_value = note->note();
339                 break;
340         case Velocity:
341                 change.old_value = note->velocity();
342                 break;
343         case StartTime:
344                 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
345                 /*NOTREACHED*/
346                 break;
347         case Length:
348                 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
349                 /*NOTREACHED*/
350                 break;
351         case Channel:
352                 change.old_value = note->channel();
353                 break;
354         }
355
356         _changes.push_back (change);
357 }
358
359 void
360 MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop,
361                                TimeType new_time)
362 {
363         NotePropertyChange change;
364
365         change.note = note;
366         change.property = prop;
367         change.new_time = new_time;
368
369         switch (prop) {
370         case NoteNumber:
371         case Channel:
372         case Velocity:
373                 fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
374                 break;
375         case StartTime:
376                 change.old_time = note->time();
377                 break;
378         case Length:
379                 change.old_time = note->length();
380                 break;
381         }
382
383         _changes.push_back (change);
384 }
385
386 void
387 MidiModel::DiffCommand::operator()()
388 {
389         Glib::Mutex::Lock lm (_model->_midi_source->mutex());
390         _model->_midi_source->invalidate(); // release model read lock
391         _model->write_lock();
392
393         for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
394                 Property prop = i->property;
395                 switch (prop) {
396                 case NoteNumber:
397                         i->note->set_note (i->new_value);
398                         break;
399                 case Velocity:
400                         i->note->set_velocity (i->new_value);
401                         break;
402                 case StartTime:
403                         i->note->set_time (i->new_time);
404                         break;
405                 case Length:
406                         i->note->set_length (i->new_time);
407                         break;
408                 case Channel:
409                         i->note->set_channel (i->new_value);
410                         break;
411                 }
412         }
413         
414         _model->write_unlock();
415         _model->ContentsChanged(); /* EMIT SIGNAL */
416 }
417
418 void
419 MidiModel::DiffCommand::undo()
420 {
421         Glib::Mutex::Lock lm (_model->_midi_source->mutex());
422         _model->_midi_source->invalidate(); // release model read lock
423         _model->write_lock();
424
425         for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
426                 Property prop = i->property;
427                 switch (prop) {
428                 case NoteNumber:
429                         i->note->set_note (i->old_value);
430                         break;
431                 case Velocity:
432                         i->note->set_velocity (i->old_value);
433                         break;
434                 case StartTime:
435                         i->note->set_time (i->old_time);
436                         break;
437                 case Length:
438                         i->note->set_length (i->old_time);
439                         break;
440                 case Channel:
441                         i->note->set_channel (i->old_value);
442                         break;
443                 }
444         }
445
446         _model->write_unlock();
447         _model->ContentsChanged(); /* EMIT SIGNAL */
448 }
449
450 XMLNode&
451 MidiModel::DiffCommand::marshal_change(const NotePropertyChange& change)
452 {
453         XMLNode* xml_change = new XMLNode("change");
454         
455         /* first, the change itself */
456
457         xml_change->add_property ("property", enum_2_string (change.property));
458
459         {
460                 ostringstream old_value_str (ios::ate);
461                 old_value_str << (unsigned int) change.old_value;
462                 xml_change->add_property ("old", old_value_str.str());
463         }
464
465         {
466                 ostringstream new_value_str (ios::ate);
467                 new_value_str << (unsigned int) change.old_value;
468                 xml_change->add_property ("new", new_value_str.str());
469         }
470
471         /* now the rest of the note */
472         
473         if (change.property != NoteNumber) {
474                 ostringstream note_str(ios::ate);
475                 note_str << int(change.note->note());
476                 xml_change->add_property("note", note_str.str());
477         }
478         
479         if (change.property != Channel) {
480                 ostringstream channel_str(ios::ate);
481                 channel_str << int(change.note->channel());
482                 xml_change->add_property("channel", channel_str.str());
483         }
484
485         if (change.property != StartTime) {
486                 ostringstream time_str(ios::ate);
487                 time_str << int(change.note->time());
488                 xml_change->add_property("time", time_str.str());
489         }
490
491         if (change.property != Length) {
492                 ostringstream length_str(ios::ate);
493                 length_str <<(unsigned int) change.note->length();
494                 xml_change->add_property("length", length_str.str());
495         }
496
497         if (change.property != Velocity) {
498                 ostringstream velocity_str(ios::ate);
499                 velocity_str << (unsigned int) change.note->velocity();
500                 xml_change->add_property("velocity", velocity_str.str());
501         }
502
503         return *xml_change;
504 }
505
506 MidiModel::DiffCommand::NotePropertyChange
507 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
508 {
509         XMLProperty* prop;
510         NotePropertyChange change;
511         unsigned int note;
512         unsigned int channel;
513         unsigned int time;
514         unsigned int length;
515         unsigned int velocity;
516
517         if ((prop = xml_change->property("property")) != 0) {
518                 change.property = (Property) string_2_enum (prop->value(), change.property);
519         } else {
520                 fatal << "!!!" << endmsg;
521                 /*NOTREACHED*/
522         }
523
524         if ((prop = xml_change->property ("old")) != 0) {
525                 istringstream old_str (prop->value());
526                 old_str >> change.old_value;
527         } else {
528                 fatal << "!!!" << endmsg;
529                 /*NOTREACHED*/
530         }
531
532         if ((prop = xml_change->property ("new")) != 0) {
533                 istringstream new_str (prop->value());
534                 new_str >> change.new_value;
535         } else {
536                 fatal << "!!!" << endmsg;
537                 /*NOTREACHED*/
538         }
539
540         if (change.property != NoteNumber) {
541                 if ((prop = xml_change->property("note")) != 0) {
542                         istringstream note_str(prop->value());
543                         note_str >> note;
544                 } else {
545                         warning << "note information missing note value" << endmsg;
546                         note = 127;
547                 }
548         } else {
549                 note = change.new_value;
550         }
551
552         if (change.property != Channel) {
553                 if ((prop = xml_change->property("channel")) != 0) {
554                         istringstream channel_str(prop->value());
555                         channel_str >> channel;
556                 } else {
557                         warning << "note information missing channel" << endmsg;
558                         channel = 0;
559                 }
560         } else {
561                 channel = change.new_value;
562         }
563
564         if (change.property != StartTime) {
565                 if ((prop = xml_change->property("time")) != 0) {
566                         istringstream time_str(prop->value());
567                         time_str >> time;
568                 } else {
569                         warning << "note information missing time" << endmsg;
570                         time = 0;
571                 }
572         } else {
573                 time = change.new_value;
574         }
575
576         if (change.property != Length) {
577                 if ((prop = xml_change->property("length")) != 0) {
578                         istringstream length_str(prop->value());
579                         length_str >> length;
580                 } else {
581                         warning << "note information missing length" << endmsg;
582                         length = 1;
583                 }
584         } else {
585                 length = change.new_value;
586         }
587
588         if (change.property != Velocity) {
589                 if ((prop = xml_change->property("velocity")) != 0) {
590                         istringstream velocity_str(prop->value());
591                         velocity_str >> velocity;
592                 } else {
593                         warning << "note information missing velocity" << endmsg;
594                         velocity = 127;
595                 }
596         } else {
597                 velocity = change.new_value;
598         }
599
600         /* we must point at the instance of the note that is actually in the model.
601            so go look for it ...
602         */
603
604         boost::shared_ptr<Evoral::Note<TimeType> > new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
605
606         change.note = _model->find_note (new_note);
607
608         if (!change.note) {
609                 warning << "MIDI note not found in model - programmers should investigate this" << endmsg;
610                 /* use the actual new note */
611                 change.note = new_note;
612         }
613
614         return change;
615 }
616
617 int
618 MidiModel::DiffCommand::set_state(const XMLNode& diff_command)
619 {
620         if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
621                 return 1;
622         }
623
624         _changes.clear();
625
626         XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
627
628         if (changed_notes) {
629                 XMLNodeList notes = changed_notes->children();
630                 
631                 transform (notes.begin(), notes.end(), back_inserter(_changes),
632                            sigc::mem_fun(*this, &DiffCommand::unmarshal_change));
633         }
634
635         return 0;
636 }
637
638 XMLNode&
639 MidiModel::DiffCommand::get_state ()
640 {
641         XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
642         diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
643
644         XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
645         for_each(_changes.begin(), _changes.end(), sigc::compose(
646                          sigc::mem_fun(*changes, &XMLNode::add_child_nocopy),
647                          sigc::mem_fun(*this, &DiffCommand::marshal_change)));
648
649         return *diff_command;
650 }
651
652 /** Write the model to a MidiSource (i.e. save the model).
653  * This is different from manually using read to write to a source in that
654  * note off events are written regardless of the track mode.  This is so the
655  * user can switch a recorded track (with note durations from some instrument)
656  * to percussive, save, reload, then switch it back to sustained without
657  * destroying the original note durations.
658  */
659 bool
660 MidiModel::write_to(boost::shared_ptr<MidiSource> source)
661 {
662         read_lock();
663
664         const bool old_percussive = percussive();
665         set_percussive(false);
666
667         source->drop_model();
668         
669         for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
670                 source->append_event_unlocked_beats(*i);
671         }
672                 
673         set_percussive(old_percussive);
674         
675         read_unlock();
676         set_edited(false);
677
678         return true;
679 }
680
681 XMLNode&
682 MidiModel::get_state()
683 {
684         XMLNode *node = new XMLNode("MidiModel");
685         return *node;
686 }
687
688 boost::shared_ptr<Evoral::Note<MidiModel::TimeType> >
689 MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other) 
690 {
691         Notes::iterator i = find (notes().begin(), notes().end(), other);
692
693         if (i == notes().end()) {
694                 return boost::shared_ptr<Evoral::Note<TimeType> > ();
695         }
696         
697         return *i;
698 }