more MIDI editing cleanups, mostly fixing subtleties. Will break loading the history...
[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         XMLNodeList notes = added_notes->children();
271         transform(notes.begin(), notes.end(), back_inserter(_added_notes),
272                         sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
273
274         _removed_notes.clear();
275         XMLNode* removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT);
276         notes = removed_notes->children();
277         transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
278                         sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
279
280         return 0;
281 }
282
283 XMLNode&
284 MidiModel::DeltaCommand::get_state()
285 {
286         XMLNode* delta_command = new XMLNode(DELTA_COMMAND_ELEMENT);
287         delta_command->add_property("midi-source", _model->midi_source()->id().to_s());
288
289         XMLNode* added_notes = delta_command->add_child(ADDED_NOTES_ELEMENT);
290         for_each(_added_notes.begin(), _added_notes.end(), sigc::compose(
291                         sigc::mem_fun(*added_notes, &XMLNode::add_child_nocopy),
292                         sigc::mem_fun(*this, &DeltaCommand::marshal_note)));
293
294         XMLNode* removed_notes = delta_command->add_child(REMOVED_NOTES_ELEMENT);
295         for_each(_removed_notes.begin(), _removed_notes.end(), sigc::compose(
296                         sigc::mem_fun(*removed_notes, &XMLNode::add_child_nocopy),
297                         sigc::mem_fun(*this, &DeltaCommand::marshal_note)));
298
299         return *delta_command;
300 }
301
302 /************** DIFF COMMAND ********************/
303
304 #define DIFF_NOTES_ELEMENT "ChangedNotes"
305 #define DIFF_COMMAND_ELEMENT "DiffCommand"
306
307 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
308         : Command(name)
309         , _model(m)
310         , _name(name)
311 {
312         assert(_model);
313 }
314
315 MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
316         : _model(m)
317 {
318         assert(_model);
319         set_state(node);
320 }
321
322 void
323 MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop,
324                                uint8_t new_value)
325 {
326         NotePropertyChange change;
327
328         change.note = note;
329         change.property = prop;
330         change.new_value = new_value;
331
332         switch (prop) {
333         case NoteNumber:
334                 change.old_value = note->note();
335                 break;
336         case Velocity:
337                 change.old_value = note->velocity();
338                 break;
339         case StartTime:
340                 fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg;
341                 /*NOTREACHED*/
342                 break;
343         case Length:
344                 fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg;
345                 /*NOTREACHED*/
346                 break;
347         case Channel:
348                 change.old_value = note->channel();
349                 break;
350         }
351
352         _changes.push_back (change);
353 }
354
355 void
356 MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop,
357                                TimeType new_time)
358 {
359         NotePropertyChange change;
360
361         change.note = note;
362         change.property = prop;
363         change.new_time = new_time;
364
365         switch (prop) {
366         case NoteNumber:
367         case Channel:
368         case Velocity:
369                 fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg;
370                 break;
371         case StartTime:
372                 change.old_time = note->time();
373                 break;
374         case Length:
375                 change.old_time = note->length();
376                 break;
377         }
378
379         _changes.push_back (change);
380 }
381
382 void
383 MidiModel::DiffCommand::operator()()
384 {
385         Glib::Mutex::Lock lm (_model->_midi_source->mutex());
386         _model->_midi_source->invalidate(); // release model read lock
387         _model->write_lock();
388
389         for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
390                 Property prop = i->property;
391                 switch (prop) {
392                 case NoteNumber:
393                         i->note->set_note (i->new_value);
394                         break;
395                 case Velocity:
396                         i->note->set_velocity (i->new_value);
397                         break;
398                 case StartTime:
399                         i->note->set_time (i->new_time);
400                         break;
401                 case Length:
402                         i->note->set_length (i->new_time);
403                         break;
404                 case Channel:
405                         i->note->set_channel (i->new_value);
406                         break;
407                 }
408         }
409         
410         _model->write_unlock();
411         _model->ContentsChanged(); /* EMIT SIGNAL */
412 }
413
414 void
415 MidiModel::DiffCommand::undo()
416 {
417         Glib::Mutex::Lock lm (_model->_midi_source->mutex());
418         _model->_midi_source->invalidate(); // release model read lock
419         _model->write_lock();
420
421         for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
422                 Property prop = i->property;
423                 switch (prop) {
424                 case NoteNumber:
425                         i->note->set_note (i->old_value);
426                         break;
427                 case Velocity:
428                         i->note->set_velocity (i->old_value);
429                         break;
430                 case StartTime:
431                         i->note->set_time (i->old_time);
432                         break;
433                 case Length:
434                         i->note->set_length (i->old_time);
435                         break;
436                 case Channel:
437                         i->note->set_channel (i->old_value);
438                         break;
439                 }
440         }
441
442         _model->write_unlock();
443         _model->ContentsChanged(); /* EMIT SIGNAL */
444 }
445
446 XMLNode&
447 MidiModel::DiffCommand::marshal_change(const NotePropertyChange& change)
448 {
449         XMLNode* xml_change = new XMLNode("change");
450         
451         /* first, the change itself */
452
453         xml_change->add_property ("property", enum_2_string (change.property));
454
455         {
456                 ostringstream old_value_str (ios::ate);
457                 old_value_str << (unsigned int) change.old_value;
458                 xml_change->add_property ("old", old_value_str.str());
459         }
460
461         {
462                 ostringstream new_value_str (ios::ate);
463                 new_value_str << (unsigned int) change.old_value;
464                 xml_change->add_property ("new", new_value_str.str());
465         }
466
467         /* now the rest of the note */
468         
469         if (change.property != NoteNumber) {
470                 ostringstream note_str(ios::ate);
471                 note_str << int(change.note->note());
472                 xml_change->add_property("note", note_str.str());
473         }
474         
475         if (change.property != Channel) {
476                 ostringstream channel_str(ios::ate);
477                 channel_str << int(change.note->channel());
478                 xml_change->add_property("channel", channel_str.str());
479         }
480
481         if (change.property != StartTime) {
482                 ostringstream time_str(ios::ate);
483                 time_str << int(change.note->time());
484                 xml_change->add_property("time", time_str.str());
485         }
486
487         if (change.property != Length) {
488                 ostringstream length_str(ios::ate);
489                 length_str <<(unsigned int) change.note->length();
490                 xml_change->add_property("length", length_str.str());
491         }
492
493         if (change.property != Velocity) {
494                 ostringstream velocity_str(ios::ate);
495                 velocity_str << (unsigned int) change.note->velocity();
496                 xml_change->add_property("velocity", velocity_str.str());
497         }
498
499         return *xml_change;
500 }
501
502 MidiModel::DiffCommand::NotePropertyChange
503 MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
504 {
505         XMLProperty* prop;
506         NotePropertyChange change;
507         unsigned int note;
508         unsigned int channel;
509         unsigned int time;
510         unsigned int length;
511         unsigned int velocity;
512
513         if ((prop = xml_change->property("property")) != 0) {
514                 change.property = (Property) string_2_enum (prop->value(), change.property);
515         } else {
516                 fatal << "!!!" << endmsg;
517                 /*NOTREACHED*/
518         }
519
520         if ((prop = xml_change->property ("old")) != 0) {
521                 istringstream old_str (prop->value());
522                 old_str >> change.old_value;
523         } else {
524                 fatal << "!!!" << endmsg;
525                 /*NOTREACHED*/
526         }
527
528         if ((prop = xml_change->property ("new")) != 0) {
529                 istringstream new_str (prop->value());
530                 new_str >> change.new_value;
531         } else {
532                 fatal << "!!!" << endmsg;
533                 /*NOTREACHED*/
534         }
535
536         if (change.property != NoteNumber) {
537                 if ((prop = xml_change->property("note")) != 0) {
538                         istringstream note_str(prop->value());
539                         note_str >> note;
540                 } else {
541                         warning << "note information missing note value" << endmsg;
542                         note = 127;
543                 }
544         } else {
545                 note = change.new_value;
546         }
547
548         if (change.property != Channel) {
549                 if ((prop = xml_change->property("channel")) != 0) {
550                         istringstream channel_str(prop->value());
551                         channel_str >> channel;
552                 } else {
553                         warning << "note information missing channel" << endmsg;
554                         channel = 0;
555                 }
556         } else {
557                 channel = change.new_value;
558         }
559
560         if (change.property != StartTime) {
561                 if ((prop = xml_change->property("time")) != 0) {
562                         istringstream time_str(prop->value());
563                         time_str >> time;
564                 } else {
565                         warning << "note information missing time" << endmsg;
566                         time = 0;
567                 }
568         } else {
569                 time = change.new_value;
570         }
571
572         if (change.property != Length) {
573                 if ((prop = xml_change->property("length")) != 0) {
574                         istringstream length_str(prop->value());
575                         length_str >> length;
576                 } else {
577                         warning << "note information missing length" << endmsg;
578                         length = 1;
579                 }
580         } else {
581                 length = change.new_value;
582         }
583
584         if (change.property != Velocity) {
585                 if ((prop = xml_change->property("velocity")) != 0) {
586                         istringstream velocity_str(prop->value());
587                         velocity_str >> velocity;
588                 } else {
589                         warning << "note information missing velocity" << endmsg;
590                         velocity = 127;
591                 }
592         } else {
593                 velocity = change.new_value;
594         }
595
596         /* we must point at the instance of the note that is actually in the model.
597            so go look for it ...
598         */
599
600         boost::shared_ptr<Evoral::Note<TimeType> > new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
601
602         change.note = _model->find_note (new_note);
603
604         if (!change.note) {
605                 warning << "MIDI note not found in model - programmers should investigate this" << endmsg;
606                 /* use the actual new note */
607                 change.note = new_note;
608         }
609
610         return change;
611 }
612
613 int
614 MidiModel::DiffCommand::set_state(const XMLNode& diff_command)
615 {
616         if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
617                 return 1;
618         }
619
620         _changes.clear();
621
622         XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
623         XMLNodeList notes = changed_notes->children();
624
625         transform (notes.begin(), notes.end(), back_inserter(_changes),
626                    sigc::mem_fun(*this, &DiffCommand::unmarshal_change));
627         
628         return 0;
629 }
630
631 XMLNode&
632 MidiModel::DiffCommand::get_state ()
633 {
634         XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT);
635         diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
636
637         XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
638         for_each(_changes.begin(), _changes.end(), sigc::compose(
639                          sigc::mem_fun(*changes, &XMLNode::add_child_nocopy),
640                          sigc::mem_fun(*this, &DiffCommand::marshal_change)));
641
642         return *diff_command;
643 }
644
645 /** Write the model to a MidiSource (i.e. save the model).
646  * This is different from manually using read to write to a source in that
647  * note off events are written regardless of the track mode.  This is so the
648  * user can switch a recorded track (with note durations from some instrument)
649  * to percussive, save, reload, then switch it back to sustained without
650  * destroying the original note durations.
651  */
652 bool
653 MidiModel::write_to(boost::shared_ptr<MidiSource> source)
654 {
655         read_lock();
656
657         const bool old_percussive = percussive();
658         set_percussive(false);
659
660         source->drop_model();
661         
662         for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
663                 source->append_event_unlocked_beats(*i);
664         }
665                 
666         set_percussive(old_percussive);
667         
668         read_unlock();
669         set_edited(false);
670
671         return true;
672 }
673
674 XMLNode&
675 MidiModel::get_state()
676 {
677         XMLNode *node = new XMLNode("MidiModel");
678         return *node;
679 }
680
681 boost::shared_ptr<Evoral::Note<MidiModel::TimeType> >
682 MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other) 
683 {
684         Notes::iterator i = find (notes().begin(), notes().end(), other);
685
686         if (i == notes().end()) {
687                 return boost::shared_ptr<Evoral::Note<TimeType> > ();
688         }
689         
690         return *i;
691 }