debug flag for session destruction and waf option for boost SP debug
[ardour.git] / libs / ardour / midi_model.cc
index 95d6d6a92401a412c5ac3d545271005ac6e692af..47bc14852c1cf1828521f98fbc57415bebff3938 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "ardour/midi_model.h"
 #include "ardour/midi_source.h"
+#include "ardour/smf_source.h"
 #include "ardour/types.h"
 #include "ardour/session.h"
 
@@ -111,7 +112,7 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const XMLN
        : _model(m)
 {
        assert(_model);
-       set_state(node);
+       set_state(node, Stateful::loading_state_version);
 }
 
 void
@@ -134,9 +135,7 @@ MidiModel::DeltaCommand::operator()()
        // This could be made much faster by using a priority_queue for added and
        // removed notes (or sort here), and doing a single iteration over _model
 
-       Glib::Mutex::Lock lm (_model->_midi_source->mutex());
-       _model->_midi_source->invalidate(); // release model read lock
-       _model->write_lock();
+       MidiModel::WriteLock lock(_model->edit_lock());
 
        for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
                _model->add_note_unlocked(*i);
@@ -145,8 +144,8 @@ MidiModel::DeltaCommand::operator()()
        for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
                _model->remove_note_unlocked(*i);
        }
-       
-       _model->write_unlock();
+
+       lock.reset();
        _model->ContentsChanged(); /* EMIT SIGNAL */
 }
 
@@ -155,10 +154,8 @@ MidiModel::DeltaCommand::undo()
 {
        // This could be made much faster by using a priority_queue for added and
        // removed notes (or sort here), and doing a single iteration over _model
-       
-       Glib::Mutex::Lock lm (_model->_midi_source->mutex());
-       _model->_midi_source->invalidate(); // release model read lock
-       _model->write_lock();
+
+       MidiModel::WriteLock lock(_model->edit_lock());;
 
        for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
                _model->remove_note_unlocked(*i);
@@ -168,7 +165,7 @@ MidiModel::DeltaCommand::undo()
                _model->add_note_unlocked(*i);
        }
 
-       _model->write_unlock();
+       lock.reset();
        _model->ContentsChanged(); /* EMIT SIGNAL */
 }
 
@@ -259,7 +256,7 @@ MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
 #define DELTA_COMMAND_ELEMENT "DeltaCommand"
 
 int
-MidiModel::DeltaCommand::set_state(const XMLNode& delta_command)
+MidiModel::DeltaCommand::set_state (const XMLNode& delta_command, int /*version*/)
 {
        if (delta_command.name() != string(DELTA_COMMAND_ELEMENT)) {
                return 1;
@@ -267,15 +264,19 @@ MidiModel::DeltaCommand::set_state(const XMLNode& delta_command)
 
        _added_notes.clear();
        XMLNode* added_notes = delta_command.child(ADDED_NOTES_ELEMENT);
-       XMLNodeList notes = added_notes->children();
-       transform(notes.begin(), notes.end(), back_inserter(_added_notes),
-                       sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
+       if (added_notes) {
+               XMLNodeList notes = added_notes->children();
+               transform(notes.begin(), notes.end(), back_inserter(_added_notes),
+                         sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
+       }
 
        _removed_notes.clear();
        XMLNode* removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT);
-       notes = removed_notes->children();
-       transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
-                       sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
+       if (removed_notes) {
+               XMLNodeList notes = removed_notes->children();
+               transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
+                         sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
+       }
 
        return 0;
 }
@@ -316,7 +317,7 @@ MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNod
        : _model(m)
 {
        assert(_model);
-       set_state(node);
+       set_state(node, Stateful::loading_state_version);
 }
 
 void
@@ -382,9 +383,7 @@ MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> >
 void
 MidiModel::DiffCommand::operator()()
 {
-       Glib::Mutex::Lock lm (_model->_midi_source->mutex());
-       _model->_midi_source->invalidate(); // release model read lock
-       _model->write_lock();
+       MidiModel::WriteLock lock(_model->edit_lock());
 
        for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
                Property prop = i->property;
@@ -406,17 +405,15 @@ MidiModel::DiffCommand::operator()()
                        break;
                }
        }
-       
-       _model->write_unlock();
+
+       lock.reset();
        _model->ContentsChanged(); /* EMIT SIGNAL */
 }
 
 void
 MidiModel::DiffCommand::undo()
 {
-       Glib::Mutex::Lock lm (_model->_midi_source->mutex());
-       _model->_midi_source->invalidate(); // release model read lock
-       _model->write_lock();
+       MidiModel::WriteLock lock(_model->edit_lock());
 
        for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
                Property prop = i->property;
@@ -439,7 +436,7 @@ MidiModel::DiffCommand::undo()
                }
        }
 
-       _model->write_unlock();
+       lock.reset();
        _model->ContentsChanged(); /* EMIT SIGNAL */
 }
 
@@ -447,52 +444,70 @@ XMLNode&
 MidiModel::DiffCommand::marshal_change(const NotePropertyChange& change)
 {
        XMLNode* xml_change = new XMLNode("change");
-       
+
        /* first, the change itself */
 
        xml_change->add_property ("property", enum_2_string (change.property));
 
        {
                ostringstream old_value_str (ios::ate);
-               old_value_str << (unsigned int) change.old_value;
+               if (change.property == StartTime || change.property == Length) {
+                       old_value_str << change.old_time;
+               } else {
+                       old_value_str << (unsigned int) change.old_value;
+               }
                xml_change->add_property ("old", old_value_str.str());
        }
 
        {
                ostringstream new_value_str (ios::ate);
-               new_value_str << (unsigned int) change.old_value;
+               if (change.property == StartTime || change.property == Length) {
+                       new_value_str << change.new_time;
+               } else {
+                       new_value_str << (unsigned int) change.new_value;
+               }
                xml_change->add_property ("new", new_value_str.str());
        }
 
        /* now the rest of the note */
-       
+
+       const SMFSource* smf = dynamic_cast<const SMFSource*> (_model->midi_source());
+
        if (change.property != NoteNumber) {
-               ostringstream note_str(ios::ate);
+               ostringstream note_str;
                note_str << int(change.note->note());
                xml_change->add_property("note", note_str.str());
        }
-       
+
        if (change.property != Channel) {
-               ostringstream channel_str(ios::ate);
+               ostringstream channel_str;
                channel_str << int(change.note->channel());
                xml_change->add_property("channel", channel_str.str());
        }
 
        if (change.property != StartTime) {
-               ostringstream time_str(ios::ate);
-               time_str << int(change.note->time());
+               ostringstream time_str;
+               if (smf) {
+                       time_str << smf->round_to_file_precision (change.note->time());
+               } else {
+                       time_str << change.note->time();
+               }
                xml_change->add_property("time", time_str.str());
        }
 
        if (change.property != Length) {
-               ostringstream length_str(ios::ate);
-               length_str <<(unsigned int) change.note->length();
-               xml_change->add_property("length", length_str.str());
+               ostringstream length_str;
+               if (smf) {
+                       length_str << smf->round_to_file_precision (change.note->length());
+               } else {
+                       length_str << change.note->length();
+               }
+               xml_change->add_property ("length", length_str.str());
        }
 
        if (change.property != Velocity) {
-               ostringstream velocity_str(ios::ate);
-               velocity_str << (unsigned int) change.note->velocity();
+               ostringstream velocity_str;
+               velocity_str << int (change.note->velocity());
                xml_change->add_property("velocity", velocity_str.str());
        }
 
@@ -506,9 +521,9 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
        NotePropertyChange change;
        unsigned int note;
        unsigned int channel;
-       unsigned int time;
-       unsigned int length;
        unsigned int velocity;
+       Evoral::MusicalTime time;
+       Evoral::MusicalTime length;
 
        if ((prop = xml_change->property("property")) != 0) {
                change.property = (Property) string_2_enum (prop->value(), change.property);
@@ -519,7 +534,13 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
 
        if ((prop = xml_change->property ("old")) != 0) {
                istringstream old_str (prop->value());
-               old_str >> change.old_value;
+               if (change.property == StartTime || change.property == Length) {
+                       old_str >> change.old_time;
+               } else {
+                       int integer_value_so_that_istream_does_the_right_thing;
+                       old_str >> integer_value_so_that_istream_does_the_right_thing;
+                       change.old_value = integer_value_so_that_istream_does_the_right_thing;
+               }
        } else {
                fatal << "!!!" << endmsg;
                /*NOTREACHED*/
@@ -527,7 +548,13 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
 
        if ((prop = xml_change->property ("new")) != 0) {
                istringstream new_str (prop->value());
-               new_str >> change.new_value;
+               if (change.property == StartTime || change.property == Length) {
+                       new_str >> change.new_time;
+               } else {
+                       int integer_value_so_that_istream_does_the_right_thing;
+                       new_str >> integer_value_so_that_istream_does_the_right_thing;
+                       change.new_value = integer_value_so_that_istream_does_the_right_thing;
+               }
        } else {
                fatal << "!!!" << endmsg;
                /*NOTREACHED*/
@@ -566,7 +593,7 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
                        time = 0;
                }
        } else {
-               time = change.new_value;
+               time = change.new_time;
        }
 
        if (change.property != Length) {
@@ -578,7 +605,7 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
                        length = 1;
                }
        } else {
-               length = change.new_value;
+               length = change.new_time;
        }
 
        if (change.property != Velocity) {
@@ -602,7 +629,7 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
        change.note = _model->find_note (new_note);
 
        if (!change.note) {
-               warning << "MIDI note not found in model - programmers should investigate this" << endmsg;
+               warning << "MIDI note " << *new_note << " not found in model - programmers should investigate this" << endmsg;
                /* use the actual new note */
                change.note = new_note;
        }
@@ -611,7 +638,7 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
 }
 
 int
-MidiModel::DiffCommand::set_state(const XMLNode& diff_command)
+MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
 {
        if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) {
                return 1;
@@ -620,11 +647,14 @@ MidiModel::DiffCommand::set_state(const XMLNode& diff_command)
        _changes.clear();
 
        XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
-       XMLNodeList notes = changed_notes->children();
 
-       transform (notes.begin(), notes.end(), back_inserter(_changes),
-                  sigc::mem_fun(*this, &DiffCommand::unmarshal_change));
-       
+       if (changed_notes) {
+               XMLNodeList notes = changed_notes->children();
+
+               transform (notes.begin(), notes.end(), back_inserter(_changes),
+                          sigc::mem_fun(*this, &DiffCommand::unmarshal_change));
+       }
+
        return 0;
 }
 
@@ -652,20 +682,21 @@ MidiModel::DiffCommand::get_state ()
 bool
 MidiModel::write_to(boost::shared_ptr<MidiSource> source)
 {
-       read_lock();
+       ReadLock lock(read_lock());
 
        const bool old_percussive = percussive();
        set_percussive(false);
 
        source->drop_model();
-       
+       source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
+
        for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
                source->append_event_unlocked_beats(*i);
        }
-               
+
        set_percussive(old_percussive);
-       
-       read_unlock();
+       source->mark_streaming_write_completed();
+
        set_edited(false);
 
        return true;
@@ -679,13 +710,38 @@ MidiModel::get_state()
 }
 
 boost::shared_ptr<Evoral::Note<MidiModel::TimeType> >
-MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other) 
+MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other)
 {
-       Notes::iterator i = find (notes().begin(), notes().end(), other);
+       Notes::iterator l = notes().lower_bound(other);
 
-       if (i == notes().end()) {
-               return boost::shared_ptr<Evoral::Note<TimeType> > ();
+       if (l != notes().end()) {
+               for (; (*l)->time() == other->time(); ++l) {
+                       if (*l == other) {
+                               return *l;
+                       }
+               }
        }
-       
-       return *i;
+
+       return boost::shared_ptr<Evoral::Note<TimeType> >();
+}
+
+/** Lock and invalidate the source.
+ * This should be used by commands and editing things
+ */
+MidiModel::WriteLock
+MidiModel::edit_lock()
+{
+       Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
+       _midi_source->invalidate(); // Release cached iterator's read lock on model
+       return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
+}
+
+/** Lock just the model, the source lock must already be held.
+ * This should only be called from libardour/evoral places
+ */
+MidiModel::WriteLock
+MidiModel::write_lock()
+{
+       assert(!_midi_source->mutex().trylock());
+       return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
 }