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