introduce the notion that note additions and property changes can cause the removal...
authorPaul Davis <paul@linuxaudiosystems.com>
Sat, 12 Jun 2010 13:55:22 +0000 (13:55 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Sat, 12 Jun 2010 13:55:22 +0000 (13:55 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@7254 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/types.h
libs/ardour/midi_model.cc
libs/ardour/session_state.cc
libs/evoral/evoral/Sequence.hpp
libs/evoral/src/Sequence.cpp

index cbd89dd1c5e73f5f98df47e6447e1c239d59f72e..b88cdf8cbdc0945a903aede8efe25dbf6fa9705b 100644 (file)
@@ -83,7 +83,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
        , _custom_device_mode(string())
        , _active_notes(0)
        , _note_group(new ArdourCanvas::Group(*parent))
-       , _delta_command(0)
        , _diff_command(0)
        , _ghost_note(0)
         , _drag_rect (0)
@@ -108,7 +107,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
        , _custom_device_mode(string())
        , _active_notes(0)
        , _note_group(new ArdourCanvas::Group(*parent))
-       , _delta_command(0)
        , _diff_command(0)
        , _ghost_note(0)
         , _drag_rect (0)
@@ -132,7 +130,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
        , _custom_device_mode(string())
        , _active_notes(0)
        , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
-       , _delta_command(0)
        , _diff_command(0)
        , _ghost_note(0)
         , _drag_rect (0)
@@ -160,7 +157,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
        , _custom_device_mode(string())
        , _active_notes(0)
        , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
-       , _delta_command(0)
        , _diff_command(0)
        , _ghost_note(0)
         , _drag_rect (0)
@@ -680,7 +676,7 @@ MidiRegionView::create_note_at(double x, double y, double length)
 
        view->update_note_range(new_note->note());
 
-       MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
+       MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
        cmd->add(new_note);
        _model->apply_command(*trackview.session(), cmd);
 
@@ -724,15 +720,6 @@ MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
        }
 }
 
-
-void
-MidiRegionView::start_delta_command(string name)
-{
-       if (!_delta_command) {
-               _delta_command = _model->new_delta_command(name);
-       }
-}
-
 void
 MidiRegionView::start_diff_command(string name)
 {
@@ -742,10 +729,10 @@ MidiRegionView::start_diff_command(string name)
 }
 
 void
-MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
+MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
 {
-       if (_delta_command) {
-               _delta_command->add(note);
+       if (_diff_command) {
+               _diff_command->add(note);
        }
        if (selected) {
                _marked_for_selection.insert(note);
@@ -756,10 +743,10 @@ MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool sele
 }
 
 void
-MidiRegionView::delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
+MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
 {
-       if (_delta_command && ev->note()) {
-               _delta_command->remove(ev->note());
+       if (_diff_command && ev->note()) {
+               _diff_command->remove(ev->note());
        }
 }
 
@@ -783,85 +770,64 @@ MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
        }
 }
 
-void
-MidiRegionView::apply_delta()
-{
-       if (!_delta_command) {
-               return;
-       }
-
-       // Mark all selected notes for selection when model reloads
-       for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
-               _marked_for_selection.insert((*i)->note());
-       }
-
-       _model->apply_command(*trackview.session(), _delta_command);
-       _delta_command = 0;
-       midi_view()->midi_track()->playlist_modified();
-
-       _marked_for_selection.clear();
-       _marked_for_velocity.clear();
-}
-
 void
 MidiRegionView::apply_diff ()
 {
+        bool add_or_remove;
+
        if (!_diff_command) {
                return;
        }
 
+        if ((add_or_remove = _diff_command->adds_or_removes())) {
+                // Mark all selected notes for selection when model reloads
+                for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+                        _marked_for_selection.insert((*i)->note());
+                }
+        }
+
        _model->apply_command(*trackview.session(), _diff_command);
        _diff_command = 0;
        midi_view()->midi_track()->playlist_modified();
 
-       _marked_for_velocity.clear();
-}
-
-void
-MidiRegionView::apply_delta_as_subcommand()
-{
-       if (!_delta_command) {
-               return;
-       }
-
-       // Mark all selected notes for selection when model reloads
-       for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
-               _marked_for_selection.insert((*i)->note());
-       }
-
-       _model->apply_command_as_subcommand(*trackview.session(), _delta_command);
-       _delta_command = 0;
-       midi_view()->midi_track()->playlist_modified();
+        
+        if (add_or_remove) {
+               _marked_for_selection.clear();
+        }
 
-       _marked_for_selection.clear();
        _marked_for_velocity.clear();
 }
 
 void
 MidiRegionView::apply_diff_as_subcommand()
 {
+        bool add_or_remove;
+
        if (!_diff_command) {
                return;
        }
 
-       // Mark all selected notes for selection when model reloads
-       for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
-               _marked_for_selection.insert((*i)->note());
-       }
+        if ((add_or_remove = _diff_command->adds_or_removes())) {
+                // Mark all selected notes for selection when model reloads
+                for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+                        _marked_for_selection.insert((*i)->note());
+                }
+        }
 
        _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
        _diff_command = 0;
        midi_view()->midi_track()->playlist_modified();
 
-       _marked_for_selection.clear();
+        if (add_or_remove) {
+                _marked_for_selection.clear();
+        }
        _marked_for_velocity.clear();
 }
 
+
 void
 MidiRegionView::abort_command()
 {
-       delete _delta_command;
-       _delta_command = 0;
        delete _diff_command;
        _diff_command = 0;
        clear_selection();
@@ -1096,7 +1062,7 @@ MidiRegionView::~MidiRegionView ()
        _selection.clear();
        clear_events();
        delete _note_group;
-       delete _delta_command;
+       delete _diff_command;
 }
 
 void
@@ -1457,9 +1423,9 @@ MidiRegionView::add_note (uint8_t channel, uint8_t number, uint8_t velocity,
 {
        boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
 
-       start_delta_command (_("step add"));
-       delta_add_note (new_note, true, false);
-       apply_delta();
+       start_diff_command (_("step add"));
+       diff_add_note (new_note, true, false);
+       apply_diff();
 
        /* potentially extend region to hold new note */
 
@@ -1627,25 +1593,25 @@ MidiRegionView::delete_selection()
                return;
        }
 
-       start_delta_command (_("delete selection"));
+       start_diff_command (_("delete selection"));
 
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                if ((*i)->selected()) {
-                       _delta_command->remove((*i)->note());
+                       _diff_command->remove((*i)->note());
                }
        }
 
        _selection.clear();
 
-       apply_delta ();
+       apply_diff ();
 }
 
 void
 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
 {
-       start_delta_command (_("delete note"));
-       _delta_command->remove (n);
-       apply_delta ();
+       start_diff_command (_("delete note"));
+       _diff_command->remove (n);
+       apply_diff ();
 
        trackview.editor().hide_verbose_canvas_cursor ();
 }
@@ -2622,7 +2588,7 @@ MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
 
         if (op != Copy) {
 
-                start_delta_command();
+                start_diff_command();
                 
                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                         switch (op) {
@@ -2630,12 +2596,12 @@ MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
                                 break;
                         case Cut:
                         case Clear:
-                                delta_remove_note (*i);
+                                diff_remove_note (*i);
                                 break;
                         }
                 }
                 
-                apply_delta();
+                apply_diff();
         }
 }
 
@@ -2662,7 +2628,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
                return;
        }
 
-       start_delta_command (_("paste"));
+       start_diff_command (_("paste"));
 
        Evoral::MusicalTime beat_delta;
        Evoral::MusicalTime paste_pos_beats;
@@ -2678,8 +2644,6 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
 
        for (int n = 0; n < (int) times; ++n) {
 
-                cerr << "Pasting " << mcb.notes().size() << " for the " << n+1 << "th time\n";
-                
                for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
 
                        boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
@@ -2687,7 +2651,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
 
                        /* make all newly added notes selected */
 
-                       delta_add_note (copied_note, true);
+                       diff_add_note (copied_note, true);
                        end_point = copied_note->end_time();
                }
 
@@ -2701,8 +2665,6 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
 
        if (end_frame > region_end) {
 
-                cerr << "region end is now " << end_frame << " to extend from " << region_end << endl;
-
                trackview.session()->begin_reversible_command (_("paste"));
 
                 _region->clear_history ();
@@ -2710,8 +2672,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
                trackview.session()->add_command (new StatefulDiffCommand (_region));
        }
 
-        cerr << "region end finally at " << _region->position() + _region->length() - 1;
-       apply_delta ();
+       apply_diff ();
 }
 
 struct EventNoteTimeEarlyFirstComparator {
index 1f2d53980ecfdf80f629ef41ea2855edda4e21e5..6c5dff9ff2b5fefe24738e7d7f1275094c79b170 100644 (file)
@@ -173,17 +173,13 @@ class MidiRegionView : public RegionView
 
        void display_model(boost::shared_ptr<ARDOUR::MidiModel> model);
 
-       void start_delta_command(std::string name = "midi edit");
-       void delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity=false);
-       void delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev);
-
        void start_diff_command(std::string name = "midi edit");
        void diff_add_change(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::DiffCommand::Property, uint8_t val);
        void diff_add_change(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::DiffCommand::Property, Evoral::MusicalTime val);
+       void diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity=false);
+       void diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev);
 
-       void apply_delta();
        void apply_diff();
-       void apply_delta_as_subcommand();
        void apply_diff_as_subcommand();
        void abort_command();
 
@@ -355,7 +351,6 @@ class MidiRegionView : public RegionView
        SysExes                              _sys_exes;
        ArdourCanvas::CanvasNote**           _active_notes;
        ArdourCanvas::Group*                 _note_group;
-       ARDOUR::MidiModel::DeltaCommand*     _delta_command;
        ARDOUR::MidiModel::DiffCommand*      _diff_command;
        ArdourCanvas::CanvasNote*            _ghost_note;
        double                               _last_ghost_x;
index b3a4d746a195613e03fcad0756894bc354f71df9..ac578dc5a958ef19a58c9b7fde7654a0a4e09df3 100644 (file)
@@ -49,53 +49,13 @@ class MidiSource;
  */
 class MidiModel : public AutomatableSequence<Evoral::MusicalTime> {
 public:
-       typedef double TimeType;
+       typedef Evoral::MusicalTime TimeType;
 
        MidiModel(MidiSource* s);
 
        NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
        void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
 
-       /** Add/Remove notes.
-        * Technically all note operations can be implemented as one of these, but
-        * a custom command can be more efficient.
-        */
-       class DeltaCommand : public Command {
-       public:
-               DeltaCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
-               DeltaCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node);
-
-               const std::string& name() const { return _name; }
-
-               void operator()();
-               void undo();
-
-               int set_state (const XMLNode&, int version);
-               XMLNode& get_state ();
-
-               void add(const boost::shared_ptr< Evoral::Note<TimeType> > note);
-               void remove(const boost::shared_ptr< Evoral::Note<TimeType> > note);
-
-       private:
-               XMLNode &marshal_note(const boost::shared_ptr< Evoral::Note<TimeType> > note);
-               boost::shared_ptr< Evoral::Note<TimeType> > unmarshal_note(XMLNode *xml_note);
-
-               boost::shared_ptr<MidiModel> _model;
-               const std::string            _name;
-
-               typedef std::list< boost::shared_ptr< Evoral::Note<TimeType> > > NoteList;
-
-               NoteList _added_notes;
-               NoteList _removed_notes;
-       };
-
-
-       /** Change note properties.
-        * More efficient than DeltaCommand and has the important property that
-        * it leaves the objects in the MidiModel (Notes) the same, thus
-        * enabling selection and other state to persist across command
-        * do/undo/redo.
-        */
        class DiffCommand : public Command {
        public:
                enum Property {
@@ -117,18 +77,23 @@ public:
                int set_state (const XMLNode&, int version);
                XMLNode& get_state ();
 
-               void change (const boost::shared_ptr<Evoral::Note<TimeType> > note,
-                               Property prop, uint8_t new_value);
-               void change (const boost::shared_ptr<Evoral::Note<TimeType> > note,
-                               Property prop, TimeType new_time);
+               void add(const NotePtr note);
+               void remove(const NotePtr note);
+
+               void change (const NotePtr note, Property prop, uint8_t new_value);
+               void change (const NotePtr note, Property prop, TimeType new_time);
+
+                bool adds_or_removes() const { 
+                        return !_added_notes.empty() || !_removed_notes.empty();
+                }
 
-       private:
+          private:
                boost::shared_ptr<MidiModel> _model;
                const std::string            _name;
 
                struct NoteChange {
                        DiffCommand::Property property;
-                       boost::shared_ptr< Evoral::Note<TimeType> > note;
+                       NotePtr note;
                        union {
                                uint8_t  old_value;
                                TimeType old_time;
@@ -142,11 +107,19 @@ public:
                typedef std::list<NoteChange> ChangeList;
                ChangeList _changes;
 
+               typedef std::list< boost::shared_ptr< Evoral::Note<TimeType> > > NoteList;
+               NoteList _added_notes;
+               NoteList _removed_notes;
+
+                std::set<NotePtr> side_effect_removals;
+
                XMLNode &marshal_change(const NoteChange&);
                NoteChange unmarshal_change(XMLNode *xml_note);
+
+               XMLNode &marshal_note(const NotePtr note);
+               NotePtr unmarshal_note(XMLNode *xml_note);
        };
 
-       MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
        MidiModel::DiffCommand*  new_diff_command(const std::string name="midi edit");
        void                     apply_command(Session& session, Command* cmd);
        void                     apply_command_as_subcommand(Session& session, Command* cmd);
@@ -165,12 +138,18 @@ public:
        const MidiSource* midi_source() const { return _midi_source; }
        void set_midi_source(MidiSource* source) { _midi_source = source; }
 
-       boost::shared_ptr<Evoral::Note<TimeType> > find_note (boost::shared_ptr<Evoral::Note<TimeType> >);
+       boost::shared_ptr<Evoral::Note<TimeType> > find_note (NotePtr);
+
+        InsertMergePolicy insert_merge_policy () const;
+        void set_insert_merge_policy (InsertMergePolicy);
+        
+protected:
+        int resolve_overlaps_unlocked (const NotePtr, std::set<NotePtr>* removed = 0);
 
 private:
-       struct WriteLockImpl : public AutomatableSequence<Evoral::MusicalTime>::WriteLockImpl {
+       struct WriteLockImpl : public AutomatableSequence<TimeType>::WriteLockImpl {
                WriteLockImpl(Glib::Mutex::Lock* source_lock, Glib::RWLock& s, Glib::Mutex& c)
-                       : AutomatableSequence<Evoral::MusicalTime>::WriteLockImpl(s, c)
+                       : AutomatableSequence<TimeType>::WriteLockImpl(s, c)
                        , source_lock(source_lock)
                {}
                ~WriteLockImpl() {
@@ -188,6 +167,7 @@ private:
 
        // We cannot use a boost::shared_ptr here to avoid a retain cycle
        MidiSource* _midi_source;
+        InsertMergePolicy _insert_merge_policy;
 };
 
 } /* namespace ARDOUR */
index e55840cb2027a16d4a0a21f7068709aa51eae075..0e64e6acb16a1b23f686a7e4f30b7b6b5f6b75a3 100644 (file)
@@ -86,6 +86,19 @@ namespace ARDOUR {
         ARDOUR::OverlapType coverage (framepos_t sa, framepos_t ea,
                                       framepos_t sb, framepos_t eb);
 
+        /* policies for inserting/pasting material where overlaps
+           might be an issue.
+        */
+
+        enum InsertMergePolicy {
+                InsertMergeReject,  // no overlaps allowed
+                InsertMergeRelax,   // we just don't care about overlaps
+                InsertMergeReplace, // replace old with new
+                InsertMergeTruncateExisting, // shorten existing to avoid overlap
+                InsertMergeTruncateAddition, // shorten new to avoid overlap
+                InsertMergeExtend   // extend new (or old) to the range of old+new
+        };
+
        /** See parameter.h
         * XXX: I don't think/hope these hex values matter anymore.
         */
index ebb440ef8f10eb6f22c39aa8cfd1aa140b91687e..c4c989d36297102c211754bc2e864f3f9eae34bd 100644 (file)
@@ -19,7 +19,7 @@
 */
 
 #define __STDC_LIMIT_MACROS 1
-
+#include <set>
 #include <iostream>
 #include <algorithm>
 #include <stdexcept>
@@ -45,19 +45,6 @@ MidiModel::MidiModel(MidiSource* s)
 {
 }
 
-/** Start a new Delta command.
- *
- * This has no side-effects on the model or Session, the returned command
- * can be held on to for as long as the caller wishes, or discarded without
- * formality, until apply_command is called and ownership is taken.
- */
-MidiModel::DeltaCommand*
-MidiModel::new_delta_command(const string name)
-{
-       DeltaCommand* cmd = new DeltaCommand(_midi_source->model(), name);
-       return cmd;
-}
-
 /** Start a new Diff command.
  *
  * This has no side-effects on the model or Session, the returned command
@@ -98,10 +85,15 @@ MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
        set_edited(true);
 }
 
+/************** DIFF COMMAND ********************/
 
-// DeltaCommand
+#define DIFF_COMMAND_ELEMENT "DiffCommand"
+#define DIFF_NOTES_ELEMENT "ChangedNotes"
+#define ADDED_NOTES_ELEMENT "AddedNotes"
+#define REMOVED_NOTES_ELEMENT "RemovedNotes"
+#define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
 
-MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
+MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
        : Command(name)
        , _model(m)
        , _name(name)
@@ -109,7 +101,7 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const std:
        assert(_model);
 }
 
-MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
+MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
        : _model(m)
 {
        assert(_model);
@@ -117,214 +109,21 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const XMLN
 }
 
 void
-MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note<TimeType> > note)
+MidiModel::DiffCommand::add(const NotePtr note)
 {
        _removed_notes.remove(note);
        _added_notes.push_back(note);
 }
 
 void
-MidiModel::DeltaCommand::remove(const boost::shared_ptr< Evoral::Note<TimeType> > note)
+MidiModel::DiffCommand::remove(const NotePtr note)
 {
        _added_notes.remove(note);
        _removed_notes.push_back(note);
 }
 
 void
-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
-
-       MidiModel::WriteLock lock(_model->edit_lock());
-
-       for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
-               _model->add_note_unlocked(*i);
-       }
-
-       for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
-               _model->remove_note_unlocked(*i);
-       }
-
-       lock.reset();
-       _model->ContentsChanged(); /* EMIT SIGNAL */
-}
-
-void
-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
-
-       MidiModel::WriteLock lock(_model->edit_lock());;
-
-       for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
-               _model->remove_note_unlocked(*i);
-       }
-
-       for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
-               _model->add_note_unlocked(*i);
-       }
-
-       lock.reset();
-       _model->ContentsChanged(); /* EMIT SIGNAL */
-}
-
-XMLNode&
-MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note<TimeType> > note)
-{
-       XMLNode* xml_note = new XMLNode("note");
-       ostringstream note_str(ios::ate);
-       note_str << int(note->note());
-       xml_note->add_property("note", note_str.str());
-
-       ostringstream channel_str(ios::ate);
-       channel_str << int(note->channel());
-       xml_note->add_property("channel", channel_str.str());
-
-       ostringstream time_str(ios::ate);
-       time_str << int(note->time());
-       xml_note->add_property("time", time_str.str());
-
-       ostringstream length_str(ios::ate);
-       length_str <<(unsigned int) note->length();
-       xml_note->add_property("length", length_str.str());
-
-       ostringstream velocity_str(ios::ate);
-       velocity_str << (unsigned int) note->velocity();
-       xml_note->add_property("velocity", velocity_str.str());
-
-       return *xml_note;
-}
-
-boost::shared_ptr< Evoral::Note<MidiModel::TimeType> >
-MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
-{
-       unsigned int note;
-       XMLProperty* prop;
-       unsigned int channel;
-       unsigned int time;
-       unsigned int length;
-       unsigned int velocity;
-
-       if ((prop = xml_note->property("note")) != 0) {
-               istringstream note_str(prop->value());
-               note_str >> note;
-       } else {
-               warning << "note information missing note value" << endmsg;
-               note = 127;
-       }
-
-       if ((prop = xml_note->property("channel")) != 0) {
-               istringstream channel_str(prop->value());
-               channel_str >> channel;
-       } else {
-               warning << "note information missing channel" << endmsg;
-               channel = 0;
-       }
-
-       if ((prop = xml_note->property("time")) != 0) {
-               istringstream time_str(prop->value());
-               time_str >> time;
-       } else {
-               warning << "note information missing time" << endmsg;
-               time = 0;
-       }
-
-       if ((prop = xml_note->property("length")) != 0) {
-               istringstream length_str(prop->value());
-               length_str >> length;
-       } else {
-               warning << "note information missing length" << endmsg;
-               length = 1;
-       }
-
-       if ((prop = xml_note->property("velocity")) != 0) {
-               istringstream velocity_str(prop->value());
-               velocity_str >> velocity;
-       } else {
-               warning << "note information missing velocity" << endmsg;
-               velocity = 127;
-       }
-
-       boost::shared_ptr< Evoral::Note<TimeType> > note_ptr(new Evoral::Note<TimeType>(
-                       channel, time, length, note, velocity));
-       return note_ptr;
-}
-
-#define ADDED_NOTES_ELEMENT "AddedNotes"
-#define REMOVED_NOTES_ELEMENT "RemovedNotes"
-#define DELTA_COMMAND_ELEMENT "DeltaCommand"
-
-int
-MidiModel::DeltaCommand::set_state (const XMLNode& delta_command, int /*version*/)
-{
-       if (delta_command.name() != string(DELTA_COMMAND_ELEMENT)) {
-               return 1;
-       }
-
-       _added_notes.clear();
-       XMLNode* added_notes = delta_command.child(ADDED_NOTES_ELEMENT);
-       if (added_notes) {
-               XMLNodeList notes = added_notes->children();
-               transform(notes.begin(), notes.end(), back_inserter(_added_notes),
-                         boost::bind (&DeltaCommand::unmarshal_note, this, _1));
-       }
-
-       _removed_notes.clear();
-       XMLNode* removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT);
-       if (removed_notes) {
-               XMLNodeList notes = removed_notes->children();
-               transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
-                         boost::bind (&DeltaCommand::unmarshal_note, this, _1));
-       }
-
-       return 0;
-}
-
-XMLNode&
-MidiModel::DeltaCommand::get_state()
-{
-       XMLNode* delta_command = new XMLNode(DELTA_COMMAND_ELEMENT);
-       delta_command->add_property("midi-source", _model->midi_source()->id().to_s());
-
-       XMLNode* added_notes = delta_command->add_child(ADDED_NOTES_ELEMENT);
-       for_each(_added_notes.begin(), _added_notes.end(), 
-                boost::bind(
-                        boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
-                        boost::bind (&DeltaCommand::marshal_note, this, _1)));
-
-       XMLNode* removed_notes = delta_command->add_child(REMOVED_NOTES_ELEMENT);
-       for_each(_removed_notes.begin(), _removed_notes.end(), 
-                boost::bind (
-                        boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
-                        boost::bind (&DeltaCommand::marshal_note, this, _1)));
-
-       return *delta_command;
-}
-
-/************** DIFF COMMAND ********************/
-
-#define DIFF_NOTES_ELEMENT "ChangedNotes"
-#define DIFF_COMMAND_ELEMENT "DiffCommand"
-
-MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
-       : Command(name)
-       , _model(m)
-       , _name(name)
-{
-       assert(_model);
-}
-
-MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node)
-       : _model(m)
-{
-       assert(_model);
-       set_state(node, Stateful::loading_state_version);
-}
-
-void
-MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop,
+MidiModel::DiffCommand::change(const NotePtr note, Property prop,
                               uint8_t new_value)
 {
        NoteChange change;
@@ -368,7 +167,7 @@ MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> >
 }
 
 void
-MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop,
+MidiModel::DiffCommand::change(const NotePtr note, Property prop,
                               TimeType new_time)
 {
        NoteChange change;
@@ -407,44 +206,64 @@ MidiModel::DiffCommand::operator()()
         {
                 MidiModel::WriteLock lock(_model->edit_lock());
                 
-                set<boost::shared_ptr<Evoral::Note<TimeType> > > removed_notes;
+                for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
+                        _model->add_note_unlocked(*i);
+                }
+                
+                for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
+                        _model->remove_note_unlocked(*i);
+                }
+
+                /* notes we modify in a way that requires remove-then-add to maintain ordering */
+                set<NotePtr> temporary_removals;
 
                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
                         Property prop = i->property;
                         switch (prop) {
                         case NoteNumber:
-                                if (removed_notes.find (i->note) == removed_notes.end()) {
+                                if (temporary_removals.find (i->note) == temporary_removals.end()) {
                                         _model->remove_note_unlocked (i->note);
-                                        removed_notes.insert (i->note);
+                                        temporary_removals.insert (i->note);
                                 }
                                 i->note->set_note (i->new_value);
                                 break;
+
                         case Velocity:
                                 i->note->set_velocity (i->new_value);
                                 break;
+
                         case StartTime:
-                                if (removed_notes.find (i->note) == removed_notes.end()) {
+                                if (temporary_removals.find (i->note) == temporary_removals.end()) {
                                         _model->remove_note_unlocked (i->note);
-                                        removed_notes.insert (i->note);
+                                        temporary_removals.insert (i->note);
 
                                 }
                                 i->note->set_time (i->new_time);
                                 break;
+
                         case Length:
                                 i->note->set_length (i->new_time);
                                 break;
+
                         case Channel:
-                                if (removed_notes.find (i->note) == removed_notes.end()) {
+                                if (temporary_removals.find (i->note) == temporary_removals.end()) {
                                         _model->remove_note_unlocked (i->note);
-                                        removed_notes.insert (i->note);
+                                        temporary_removals.insert (i->note);
                                 }
                                 i->note->set_channel (i->new_value);
                                 break;
                         }
                 }
 
-                for (set<boost::shared_ptr<Evoral::Note<TimeType> > >::iterator i = removed_notes.begin(); i != removed_notes.end(); ++i) {
-                        _model->add_note_unlocked (*i);
+                for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
+                        _model->add_note_unlocked (*i, &side_effect_removals);
+                }
+
+                if (!side_effect_removals.empty()) {
+                        cerr << "SER: \n";
+                        for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
+                                cerr << "\t" << *i << ' ' << **i << endl;
+                        }
                 }
         }
 
@@ -457,15 +276,24 @@ MidiModel::DiffCommand::undo()
         {
                 MidiModel::WriteLock lock(_model->edit_lock());
                 
-                set<boost::shared_ptr<Evoral::Note<TimeType> > > removed_notes;
+                for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
+                        _model->remove_note_unlocked(*i);
+                }
+                
+                for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
+                        _model->add_note_unlocked(*i);
+                }
+
+                /* notes we modify in a way that requires remove-then-add to maintain ordering */
+                set<NotePtr> temporary_removals;
 
                 for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
                         Property prop = i->property;
                         switch (prop) {
                         case NoteNumber:
-                                if (removed_notes.find (i->note) == removed_notes.end()) {
+                                if (temporary_removals.find (i->note) == temporary_removals.end()) {
                                         _model->remove_note_unlocked (i->note);
-                                        removed_notes.insert (i->note);
+                                        temporary_removals.insert (i->note);
                                 }
                                 i->note->set_note (i->old_value);
                                 break;
@@ -473,9 +301,9 @@ MidiModel::DiffCommand::undo()
                                 i->note->set_velocity (i->old_value);
                                 break;
                         case StartTime:
-                                if (removed_notes.find (i->note) == removed_notes.end()) {
+                                if (temporary_removals.find (i->note) == temporary_removals.end()) {
                                         _model->remove_note_unlocked (i->note);
-                                        removed_notes.insert (i->note);
+                                        temporary_removals.insert (i->note);
                                 }
                                 i->note->set_time (i->old_time);
                                 break;
@@ -483,16 +311,26 @@ MidiModel::DiffCommand::undo()
                                 i->note->set_length (i->old_time);
                                 break;
                         case Channel:
-                                if (removed_notes.find (i->note) == removed_notes.end()) {
+                                if (temporary_removals.find (i->note) == temporary_removals.end()) {
                                         _model->remove_note_unlocked (i->note);
-                                        removed_notes.insert (i->note);
+                                        temporary_removals.insert (i->note);
                                 }
                                 i->note->set_channel (i->old_value);
                                 break;
                         }
                 }
 
-                for (set<boost::shared_ptr<Evoral::Note<TimeType> > >::iterator i = removed_notes.begin(); i != removed_notes.end(); ++i) {
+                for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
+                        _model->add_note_unlocked (*i);
+                }
+
+                /* finally add back notes that were removed by the "do". we don't care
+                   about side effects here since the model should be back to its original
+                   state once this is done.
+                 */
+
+                cerr << "This undo has " << side_effect_removals.size() << " SER's\n";
+                for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
                         _model->add_note_unlocked (*i);
                 }
         }
@@ -500,6 +338,91 @@ MidiModel::DiffCommand::undo()
        _model->ContentsChanged(); /* EMIT SIGNAL */
 }
 
+XMLNode&
+MidiModel::DiffCommand::marshal_note(const NotePtr note)
+{
+       XMLNode* xml_note = new XMLNode("note");
+
+        cerr << "Marshalling note: " << *note << endl;
+
+       ostringstream note_str(ios::ate);
+       note_str << int(note->note());
+       xml_note->add_property("note", note_str.str());
+
+       ostringstream channel_str(ios::ate);
+       channel_str << int(note->channel());
+       xml_note->add_property("channel", channel_str.str());
+
+       ostringstream time_str(ios::ate);
+       time_str << note->time();
+       xml_note->add_property("time", time_str.str());
+        
+       ostringstream length_str(ios::ate);
+       length_str << note->length();
+       xml_note->add_property("length", length_str.str());
+
+       ostringstream velocity_str(ios::ate);
+       velocity_str << (unsigned int) note->velocity();
+       xml_note->add_property("velocity", velocity_str.str());
+
+       return *xml_note;
+}
+
+Evoral::Sequence<MidiModel::TimeType>::NotePtr
+MidiModel::DiffCommand::unmarshal_note(XMLNode *xml_note)
+{
+       unsigned int note;
+       XMLProperty* prop;
+       unsigned int channel;
+       unsigned int time;
+       unsigned int length;
+       unsigned int velocity;
+
+       if ((prop = xml_note->property("note")) != 0) {
+               istringstream note_str(prop->value());
+               note_str >> note;
+       } else {
+               warning << "note information missing note value" << endmsg;
+               note = 127;
+       }
+
+       if ((prop = xml_note->property("channel")) != 0) {
+               istringstream channel_str(prop->value());
+               channel_str >> channel;
+       } else {
+               warning << "note information missing channel" << endmsg;
+               channel = 0;
+       }
+
+       if ((prop = xml_note->property("time")) != 0) {
+               istringstream time_str(prop->value());
+               time_str >> time;
+       } else {
+               warning << "note information missing time" << endmsg;
+               time = 0;
+       }
+
+       if ((prop = xml_note->property("length")) != 0) {
+               istringstream length_str(prop->value());
+               length_str >> length;
+       } else {
+               warning << "note information missing length" << endmsg;
+               length = 1;
+       }
+
+       if ((prop = xml_note->property("velocity")) != 0) {
+               istringstream velocity_str(prop->value());
+               velocity_str >> velocity;
+       } else {
+               warning << "note information missing velocity" << endmsg;
+               velocity = 127;
+       }
+
+       NotePtr note_ptr(new Evoral::Note<TimeType>(channel, time, length, note, velocity));
+
+       return note_ptr;
+}
+
 XMLNode&
 MidiModel::DiffCommand::marshal_change(const NoteChange& change)
 {
@@ -571,6 +494,8 @@ MidiModel::DiffCommand::marshal_change(const NoteChange& change)
                xml_change->add_property("velocity", velocity_str.str());
        }
 
+        /* and now notes that were remove as a side-effect */
+
        return *xml_change;
 }
 
@@ -684,7 +609,7 @@ MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change)
           so go look for it ...
        */
 
-       boost::shared_ptr<Evoral::Note<TimeType> > new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
+       NotePtr new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity));
 
        change.note = _model->find_note (new_note);
 
@@ -704,6 +629,30 @@ MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
                return 1;
        }
 
+        /* additions */
+
+       _added_notes.clear();
+       XMLNode* added_notes = diff_command.child(ADDED_NOTES_ELEMENT);
+       if (added_notes) {
+               XMLNodeList notes = added_notes->children();
+               transform(notes.begin(), notes.end(), back_inserter(_added_notes),
+                         boost::bind (&DiffCommand::unmarshal_note, this, _1));
+       }
+
+
+        /* removals */
+
+       _removed_notes.clear();
+       XMLNode* removed_notes = diff_command.child(REMOVED_NOTES_ELEMENT);
+       if (removed_notes) {
+               XMLNodeList notes = removed_notes->children();
+               transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
+                         boost::bind (&DiffCommand::unmarshal_note, this, _1));
+       }
+
+
+        /* changes */
+
        _changes.clear();
 
        XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT);
@@ -715,6 +664,20 @@ MidiModel::DiffCommand::set_state(const XMLNode& diff_command, int /*version*/)
 
        }
 
+        /* side effect removals caused by changes */
+        
+       side_effect_removals.clear();
+
+       XMLNode* side_effect_notes = diff_command.child(SIDE_EFFECT_REMOVALS_ELEMENT);
+
+       if (side_effect_notes) {
+               XMLNodeList notes = side_effect_notes->children();
+                cerr << "Reconstruct DiffCommand with " << notes.size() << " SER's\n";
+                for (XMLNodeList::iterator n = notes.begin(); n != notes.end(); ++n) {
+                        side_effect_removals.insert (unmarshal_note (*n));
+                }
+       }
+
        return 0;
 }
 
@@ -730,6 +693,29 @@ MidiModel::DiffCommand::get_state ()
                         boost::bind (&XMLNode::add_child_nocopy, changes, _1),
                         boost::bind (&DiffCommand::marshal_change, this, _1)));
 
+       XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
+       for_each(_added_notes.begin(), _added_notes.end(), 
+                boost::bind(
+                        boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
+                        boost::bind (&DiffCommand::marshal_note, this, _1)));
+
+       XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
+       for_each(_removed_notes.begin(), _removed_notes.end(), 
+                boost::bind (
+                        boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
+                        boost::bind (&DiffCommand::marshal_note, this, _1)));
+
+        /* if this command had side-effects, store that state too 
+         */
+
+        if (!side_effect_removals.empty()) {
+                XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
+                for_each(side_effect_removals.begin(), side_effect_removals.end(), 
+                         boost::bind (
+                                 boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
+                                 boost::bind (&DiffCommand::marshal_note, this, _1)));
+        }
+
        return *diff_command;
 }
 
@@ -851,8 +837,8 @@ MidiModel::get_state()
        return *node;
 }
 
-boost::shared_ptr<Evoral::Note<MidiModel::TimeType> >
-MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other)
+Evoral::Sequence<MidiModel::TimeType>::NotePtr
+MidiModel::find_note (NotePtr other)
 {
        Notes::iterator l = notes().lower_bound(other);
 
@@ -869,7 +855,7 @@ MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other)
                }
        }
 
-       return boost::shared_ptr<Evoral::Note<TimeType> >();
+       return NotePtr();
 }
 
 /** Lock and invalidate the source.
@@ -892,3 +878,198 @@ MidiModel::write_lock()
        assert(!_midi_source->mutex().trylock());
        return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
 }
+
+int
+MidiModel::resolve_overlaps_unlocked (const NotePtr note, set<NotePtr>* removed)
+
+{
+        using namespace Evoral;
+
+        if (_writing || insert_merge_policy() == InsertMergeRelax) {
+                return 0;
+        }
+
+        TimeType sa = note->time();
+        TimeType ea  = note->end_time();
+        
+        const Pitches& p (pitches (note->channel()));
+        NotePtr search_note(new Note<TimeType>(0, 0, 0, note->note()));
+        set<NotePtr> to_be_deleted;
+        bool set_note_length = false;
+        bool set_note_time = false;
+        TimeType note_time = note->time();
+        TimeType note_length = note->length();
+
+        for (Pitches::const_iterator i = p.lower_bound (search_note); 
+             i != p.end() && (*i)->note() == note->note(); ++i) {
+                
+                TimeType sb = (*i)->time();
+                TimeType eb = (*i)->end_time();
+                OverlapType overlap = OverlapNone;
+                
+                if ((sb > sa) && (eb <= ea)) {
+                        overlap = OverlapInternal;
+                } else if ((eb >= sa) && (eb <= ea)) {
+                        overlap = OverlapStart;
+                } else if ((sb > sa) && (sb <= ea)) {
+                        overlap = OverlapEnd;
+                } else if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
+                        overlap = OverlapExternal;
+                } else {
+                        /* no overlap */
+                        continue;
+                }
+
+                if (insert_merge_policy() == InsertMergeReject) {
+                        return -1;
+                }
+
+                switch (overlap) {
+                case OverlapStart:
+                        /* existing note covers start of new note */
+                        switch (insert_merge_policy()) {
+                        case InsertMergeReplace:
+                                to_be_deleted.insert (*i);
+                                break;
+                        case InsertMergeTruncateExisting:
+                                (*i)->set_length (note->time() - (*i)->time());
+                                break;
+                        case InsertMergeTruncateAddition:
+                                set_note_time = true;
+                                note_time = (*i)->time() + (*i)->length();
+                                break;
+                        case InsertMergeExtend:
+                                (*i)->set_length (note->end_time() - (*i)->time());
+                                return -1; /* do not add the new note */
+                                break;
+                        default:
+                                /*NOTREACHED*/
+                                /* stupid gcc */
+                                break;
+                        }
+                        break;
+
+                case OverlapEnd:
+                        /* existing note covers end of new note */
+                        switch (insert_merge_policy()) {
+                        case InsertMergeReplace:
+                                to_be_deleted.insert (*i);
+                                break;
+
+                        case InsertMergeTruncateExisting:
+                                /* resetting the start time of the existing note
+                                   is a problem because of time ordering.
+                                */
+                                break;
+
+                        case InsertMergeTruncateAddition:
+                                set_note_length = true;
+                                note_length = min (note_length, ((*i)->time() - note->time()));
+                                break;
+                                
+                        case InsertMergeExtend:
+                                /* we can't reset the time of the existing note because
+                                   that will corrupt time ordering. So remove the
+                                   existing note and change the position/length
+                                   of the new note (which has not been added yet)
+                                */
+                                to_be_deleted.insert (*i);
+                                set_note_length = true;
+                                note_length = min (note_length, (*i)->end_time() - note->time());
+                                break;
+                        default:
+                                /*NOTREACHED*/
+                                /* stupid gcc */
+                                break;
+                        }
+                        break;
+
+                case OverlapExternal:
+                        /* existing note overlaps all the new note */
+                        switch (insert_merge_policy()) {
+                        case InsertMergeReplace:
+                                to_be_deleted.insert (*i);
+                                break;
+                        case InsertMergeTruncateExisting:
+                        case InsertMergeTruncateAddition:
+                        case InsertMergeExtend:
+                                /* cannot add in this case */
+                                return -1;
+                        default:
+                                /*NOTREACHED*/
+                                /* stupid gcc */
+                                break;
+                        }
+                        break;
+
+                case OverlapInternal:
+                        /* new note fully overlaps an existing note */
+                        switch (insert_merge_policy()) {
+                        case InsertMergeReplace:
+                        case InsertMergeTruncateExisting:
+                        case InsertMergeTruncateAddition:
+                        case InsertMergeExtend:
+                                /* delete the existing note, the new one will cover it */
+                                to_be_deleted.insert (*i);
+                                break;
+                        default:
+                                /*NOTREACHED*/
+                                /* stupid gcc */
+                                break;
+                        }
+                        break;
+
+                default:
+                        /*NOTREACHED*/
+                        /* stupid gcc */
+                        break;
+                }
+        }
+
+        for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
+                remove_note_unlocked (*i);
+
+                if (removed) {
+                        removed->insert (*i);
+                }
+        }
+
+        if (set_note_time) {
+                note->set_time (note_time);
+        }
+
+        if (set_note_length) {
+                note->set_length (note_length);
+        }
+
+        return 0;
+}
+
+InsertMergePolicy
+MidiModel::insert_merge_policy () const 
+{
+        char* c = getenv ("AMP");
+
+        if (!c || c[0] == 0) {
+                return InsertMergeReject;
+        }
+
+        switch (c[0]) {
+        case 'x':
+                return InsertMergeRelax;
+        case 'p':
+                return InsertMergeReplace;
+        case 't':
+                return InsertMergeTruncateExisting;
+        case 'a':
+                return InsertMergeTruncateAddition;
+        case 'e':
+        default:
+                return InsertMergeExtend;
+        }
+}
+                        
+
+
+
+
index 92be2da9abaf9b2e6ef03cee20b28adcd3f32b31..b8409bfae97886582e39b7cc1f5b4440e529b4cc 100644 (file)
@@ -3080,16 +3080,6 @@ Session::restore_history (string snapshot_name)
                                        ut->add_command(c);
                                }
 
-                       } else if (n->name() == "DeltaCommand") {
-                               PBD::ID  id(n->property("midi-source")->value());
-                               boost::shared_ptr<MidiSource> midi_source =
-                                       boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
-                               if (midi_source) {
-                                       ut->add_command(new MidiModel::DeltaCommand(midi_source->model(), *n));
-                               } else {
-                                       error << _("Failed to downcast MidiSource for DeltaCommand") << endmsg;
-                               }
-
                        } else if (n->name() == "DiffCommand") {
                                PBD::ID  id(n->property("midi-source")->value());
                                boost::shared_ptr<MidiSource> midi_source =
@@ -3097,7 +3087,7 @@ Session::restore_history (string snapshot_name)
                                if (midi_source) {
                                        ut->add_command(new MidiModel::DiffCommand(midi_source->model(), *n));
                                } else {
-                                       error << _("Failed to downcast MidiSource for DeltaCommand") << endmsg;
+                                       error << _("Failed to downcast MidiSource for DiffCommand") << endmsg;
                                }
 
                        } else if (n->name() == "StatefulDiffCommand") {
index 9e1083b3fd312182e931fd36b463756deff91310..e3b0a3636e2e0a5be71b1013bba4d415b8bfab05 100644 (file)
@@ -22,6 +22,7 @@
 #include <vector>
 #include <queue>
 #include <set>
+#include <list>
 #include <utility>
 #include <boost/shared_ptr.hpp>
 #include <glibmm/thread.h>
@@ -78,6 +79,9 @@ protected:
        };
 
 public:
+        typedef typename boost::shared_ptr<Evoral::Note<Time> >  NotePtr;
+        typedef typename boost::shared_ptr<const Evoral::Note<Time> >  constNotePtr;
+
        typedef boost::shared_ptr<Glib::RWLock::ReaderLock> ReadLock;
        typedef boost::shared_ptr<WriteLockImpl>            WriteLock;
 
@@ -133,7 +137,7 @@ public:
                }
        };
 
-       typedef std::multiset<boost::shared_ptr< Note<Time> >, EarlierNoteComparator> Notes;
+       typedef std::multiset<NotePtr, EarlierNoteComparator> Notes;
        inline       Notes& notes()       { return _notes; }
        inline const Notes& notes() const { return _notes; }
 
@@ -173,10 +177,7 @@ public:
        inline const SysExes& sysexes() const { return _sysexes; }
 
 private:
-       typedef std::priority_queue< boost::shared_ptr< Note<Time> >,
-                                    std::deque< boost::shared_ptr< Note<Time> > >,
-                                    LaterNoteEndComparator >
-               ActiveNotes;
+       typedef std::priority_queue<NotePtr, std::deque<NotePtr>, LaterNoteEndComparator> ActiveNotes;
 public:
 
        /** Read iterator */
@@ -231,12 +232,12 @@ public:
        bool edited() const      { return _edited; }
        void set_edited(bool yn) { _edited = yn; }
 
-        bool overlaps (const boost::shared_ptr< Note<Time> >& ev, 
-                       const boost::shared_ptr< Note<Time> >& ignore_this_note) const;
-        bool contains (const boost::shared_ptr< Note<Time> >& ev) const;
+        bool overlaps (const NotePtr& ev, 
+                       const NotePtr& ignore_this_note) const;
+        bool contains (const NotePtr& ev) const;
 
-       bool add_note_unlocked(const boost::shared_ptr< Note<Time> > note);
-       void remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note);
+        bool add_note_unlocked (const NotePtr note, std::set<NotePtr>* removed = 0);
+       void remove_note_unlocked(const constNotePtr note);
 
        uint8_t lowest_note()  const { return _lowest_note; }
        uint8_t highest_note() const { return _highest_note; }
@@ -247,20 +248,24 @@ protected:
         bool                   _overlapping_pitches_accepted;
         OverlapPitchResolution _overlap_pitch_resolution;
        mutable Glib::RWLock   _lock;
+       bool                   _writing;
 
-private:
-       friend class const_iterator;
+        virtual int resolve_overlaps_unlocked (const NotePtr, std::set<NotePtr>* removed = 0) {
+                return 0;
+        }
 
-       typedef std::multiset<boost::shared_ptr< Note<Time> >, NoteNumberComparator>  Pitches;
+       typedef std::multiset<NotePtr, NoteNumberComparator>  Pitches;
        inline       Pitches& pitches(uint8_t chan)       { return _pitches[chan&0xf]; }
         inline const Pitches& pitches(uint8_t chan) const { return _pitches[chan&0xf]; }
 
-        bool overlaps_unlocked (const boost::shared_ptr< Note<Time> >& ev, 
-                                const boost::shared_ptr< Note<Time> >& ignore_this_note) const;
-        bool contains_unlocked (const boost::shared_ptr< Note<Time> >& ev) const;
+private:
+       friend class const_iterator;
+
+        bool overlaps_unlocked (const NotePtr& ev, const NotePtr& ignore_this_note) const;
+        bool contains_unlocked (const NotePtr& ev) const;
 
-        void append_note_on_unlocked (boost::shared_ptr< Note<Time> >);
-        void append_note_off_unlocked(boost::shared_ptr< Note<Time> >);
+        void append_note_on_unlocked (NotePtr);
+        void append_note_off_unlocked(NotePtr);
        void append_control_unlocked(const Parameter& param, Time time, double value);
        void append_sysex_unlocked(const MIDIEvent<Time>& ev);
 
@@ -273,9 +278,8 @@ private:
         Pitches _pitches[16]; // notes indexed by channel+pitch
        SysExes _sysexes;
 
-       typedef std::multiset<boost::shared_ptr< Note<Time> >, EarlierNoteComparator> WriteNotes;
+       typedef std::multiset<NotePtr, EarlierNoteComparator> WriteNotes;
        WriteNotes _write_notes[16];
-       bool       _writing;
 
        typedef std::vector< boost::shared_ptr<const ControlList> > ControlLists;
        ControlLists _dirty_controls;
index d11ec7c4fdc7963a837c996e7cf5dddf826660db..dba79a556aadf7aaa7e031a0f4c4bff01507bf29 100644 (file)
@@ -383,8 +383,8 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
        : _edited(false)
         , _overlapping_pitches_accepted (true)
         , _overlap_pitch_resolution (FirstOnFirstOff)
-       , _type_map(type_map)
        , _writing(false)
+       , _type_map(type_map)
        , _end_iter(*this, DBL_MAX)
        , _percussive(false)
        , _lowest_note(127)
@@ -401,15 +401,15 @@ Sequence<Time>::Sequence(const Sequence<Time>& other)
         , _edited(false)
         , _overlapping_pitches_accepted (other._overlapping_pitches_accepted)
         , _overlap_pitch_resolution (other._overlap_pitch_resolution)
-       , _type_map(other._type_map)
        , _writing(false)
+       , _type_map(other._type_map)
        , _end_iter(*this, DBL_MAX)
        , _percussive(other._percussive)
        , _lowest_note(other._lowest_note)
        , _highest_note(other._highest_note)
 {
         for (typename Notes::const_iterator i = other._notes.begin(); i != other._notes.end(); ++i) {
-                boost::shared_ptr<Note<Time> > n (new Note<Time> (**i));
+                NotePtr n (new Note<Time> (**i));
                 _notes.insert (n);
         }
 
@@ -580,14 +580,15 @@ Sequence<Time>::end_write (bool delete_stuck)
 
 template<typename Time>
 bool
-Sequence<Time>::add_note_unlocked(const boost::shared_ptr< Note<Time> > note)
+Sequence<Time>::add_note_unlocked(const NotePtr note,
+                                  set<NotePtr >* removed)
 {
         /* This is the core method to add notes to a Sequence 
          */
 
        DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 add note %2 @ %3\n", this, (int)note->note(), note->time()));
 
-        if (!_overlapping_pitches_accepted && overlaps_unlocked (note, boost::shared_ptr<Note<Time> >())) {
+        if (resolve_overlaps_unlocked (note, removed)) {
                 return false;
        }
 
@@ -606,7 +607,7 @@ Sequence<Time>::add_note_unlocked(const boost::shared_ptr< Note<Time> > note)
 
 template<typename Time>
 void
-Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> > note)
+Sequence<Time>::remove_note_unlocked(const constNotePtr note)
 {
         bool erased = false;
 
@@ -641,7 +642,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
 
         Pitches& p (pitches (note->channel()));
         
-        boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, 0, 0, note->note(), 0));
+        NotePtr search_note(new Note<Time>(0, 0, 0, note->note(), 0));
 
         for (typename Pitches::iterator i = p.lower_bound (search_note); 
              i != p.end() && (*i)->note() == note->note(); ++i) {
@@ -650,74 +651,74 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
                         p.erase (i);
                 }
         }
+        
+        if (!erased) {
+                cerr << "Unable to find note to erase" << endl;
+        }
+}
 
-         if (!erased) {
-                 cerr << "Unable to find note to erase" << endl;
-         }
- }
-
- /** Append \a ev to model.  NOT realtime safe.
-  *
-  * The timestamp of event is expected to be relative to
-  * the start of this model (t=0) and MUST be monotonically increasing
-  * and MUST be >= the latest event currently in the model.
-  */
- template<typename Time>
- void
- Sequence<Time>::append(const Event<Time>& event)
- {
-         WriteLock lock(write_lock());
-         _edited = true;
+/** Append \a ev to model.  NOT realtime safe.
+ *
+ * The timestamp of event is expected to be relative to
+ * the start of this model (t=0) and MUST be monotonically increasing
+ * and MUST be >= the latest event currently in the model.
+ */
+template<typename Time>
+void
+Sequence<Time>::append(const Event<Time>& event)
+{
+        WriteLock lock(write_lock());
+        _edited = true;
 
-         const MIDIEvent<Time>& ev = (const MIDIEvent<Time>&)event;
+        const MIDIEvent<Time>& ev = (const MIDIEvent<Time>&)event;
 
-         assert(_notes.empty() || ev.time() >= (*_notes.rbegin())->time());
-         assert(_writing);
+        assert(_notes.empty() || ev.time() >= (*_notes.rbegin())->time());
+        assert(_writing);
 
-         if (!midi_event_is_valid(ev.buffer(), ev.size())) {
-                 cerr << "WARNING: Sequence ignoring illegal MIDI event" << endl;
-                 return;
-         }
+        if (!midi_event_is_valid(ev.buffer(), ev.size())) {
+                cerr << "WARNING: Sequence ignoring illegal MIDI event" << endl;
+                return;
+        }
 
-         if (ev.is_note_on()) {
-                 boost::shared_ptr< Note<Time> > note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
-                 append_note_on_unlocked (note);
-         } else if (ev.is_note_off()) {
-                 boost::shared_ptr< Note<Time> > note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
-                 append_note_off_unlocked (note);
-         } else if (ev.is_sysex()) {
-                 append_sysex_unlocked(ev);
-         } else if (!_type_map.type_is_midi(ev.event_type())) {
-                 printf("WARNING: Sequence: Unknown event type %X: ", ev.event_type());
-                 for (size_t i=0; i < ev.size(); ++i) {
-                         printf("%X ", ev.buffer()[i]);
-                 }
-                 printf("\n");
-         } else if (ev.is_cc()) {
-                 append_control_unlocked(
-                                 Evoral::MIDI::ContinuousController(ev.event_type(), ev.channel(), ev.cc_number()),
-                                 ev.time(), ev.cc_value());
-         } else if (ev.is_pgm_change()) {
-                 append_control_unlocked(
-                                 Evoral::MIDI::ProgramChange(ev.event_type(), ev.channel()),
-                                 ev.time(), ev.pgm_number());
-         } else if (ev.is_pitch_bender()) {
-                 append_control_unlocked(
-                                 Evoral::MIDI::PitchBender(ev.event_type(), ev.channel()),
-                                 ev.time(), double(  (0x7F & ev.pitch_bender_msb()) << 7
-                                         | (0x7F & ev.pitch_bender_lsb()) ));
-         } else if (ev.is_channel_pressure()) {
-                 append_control_unlocked(
-                                 Evoral::MIDI::ChannelPressure(ev.event_type(), ev.channel()),
-                                 ev.time(), ev.channel_pressure());
-         } else {
-                 printf("WARNING: Sequence: Unknown MIDI event type %X\n", ev.type());
-         }
- }
+        if (ev.is_note_on()) {
+                NotePtr note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
+                append_note_on_unlocked (note);
+        } else if (ev.is_note_off()) {
+                NotePtr note(new Note<Time>(ev.channel(), ev.time(), 0, ev.note(), ev.velocity()));
+                append_note_off_unlocked (note);
+        } else if (ev.is_sysex()) {
+                append_sysex_unlocked(ev);
+        } else if (!_type_map.type_is_midi(ev.event_type())) {
+                printf("WARNING: Sequence: Unknown event type %X: ", ev.event_type());
+                for (size_t i=0; i < ev.size(); ++i) {
+                        printf("%X ", ev.buffer()[i]);
+                }
+                printf("\n");
+        } else if (ev.is_cc()) {
+                append_control_unlocked(
+                        Evoral::MIDI::ContinuousController(ev.event_type(), ev.channel(), ev.cc_number()),
+                        ev.time(), ev.cc_value());
+        } else if (ev.is_pgm_change()) {
+                append_control_unlocked(
+                        Evoral::MIDI::ProgramChange(ev.event_type(), ev.channel()),
+                        ev.time(), ev.pgm_number());
+        } else if (ev.is_pitch_bender()) {
+                append_control_unlocked(
+                        Evoral::MIDI::PitchBender(ev.event_type(), ev.channel()),
+                        ev.time(), double(  (0x7F & ev.pitch_bender_msb()) << 7
+                                            | (0x7F & ev.pitch_bender_lsb()) ));
+        } else if (ev.is_channel_pressure()) {
+                append_control_unlocked(
+                        Evoral::MIDI::ChannelPressure(ev.event_type(), ev.channel()),
+                        ev.time(), ev.channel_pressure());
+        } else {
+                printf("WARNING: Sequence: Unknown MIDI event type %X\n", ev.type());
+        }
+}
 
- template<typename Time>
+template<typename Time>
  void
- Sequence<Time>::append_note_on_unlocked (boost::shared_ptr< Note<Time> > note)
+ Sequence<Time>::append_note_on_unlocked (NotePtr note)
  {
          DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 on @ %4 v=%5\n", this, 
                                                        (int) note->channel(), (int) note->note(), 
@@ -744,7 +745,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
 
  template<typename Time>
  void
- Sequence<Time>::append_note_off_unlocked (boost::shared_ptr< Note<Time> > note)
+ Sequence<Time>::append_note_off_unlocked (NotePtr note)
  {
          DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 c=%2 note %3 on @ %4 v=%5\n",
                                                        this, (int)note->channel(), 
@@ -770,7 +771,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
          /* XXX use _overlap_pitch_resolution to determine FIFO/LIFO ... */
 
          for (typename WriteNotes::iterator n = _write_notes[note->channel()].begin(); n != _write_notes[note->channel()].end(); ++n) {
-                 boost::shared_ptr< Note<Time> > nn = *n;
+                 NotePtr nn = *n;
                  if (note->note() == nn->note() && nn->channel() == note->channel()) {
                          assert(note->time() >= nn->time());
 
@@ -817,17 +818,17 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
 
  template<typename Time>
  bool
- Sequence<Time>::contains (const boost::shared_ptr< Note<Time> >& note) const
+ Sequence<Time>::contains (const NotePtr& note) const
  {
          return contains_unlocked (note);
  }
 
  template<typename Time>
  bool
- Sequence<Time>::contains_unlocked (const boost::shared_ptr< Note<Time> >& note) const
+ Sequence<Time>::contains_unlocked (const NotePtr& note) const
  {
          const Pitches& p (pitches (note->channel()));
-         boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, 0, 0, note->note()));
+         NotePtr search_note(new Note<Time>(0, 0, 0, note->note()));
 
          for (typename Pitches::const_iterator i = p.lower_bound (search_note); 
               i != p.end() && (*i)->note() == note->note(); ++i) {
@@ -843,7 +844,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
 
  template<typename Time>
  bool
- Sequence<Time>::overlaps (const boost::shared_ptr< Note<Time> >& note, const boost::shared_ptr<Note<Time> >& without) const
+ Sequence<Time>::overlaps (const NotePtr& note, const NotePtr& without) const
  {
          ReadLock lock (read_lock());
          return overlaps_unlocked (note, without);
@@ -851,13 +852,13 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
 
  template<typename Time>
  bool
- Sequence<Time>::overlaps_unlocked (const boost::shared_ptr< Note<Time> >& note, const boost::shared_ptr<Note<Time> >& without) const
+ Sequence<Time>::overlaps_unlocked (const NotePtr& note, const NotePtr& without) const
  {
          Time sa = note->time();
          Time ea  = note->end_time();
          
          const Pitches& p (pitches (note->channel()));
-         boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, 0, 0, note->note()));
+         NotePtr search_note(new Note<Time>(0, 0, 0, note->note()));
 
          for (typename Pitches::const_iterator i = p.lower_bound (search_note); 
               i != p.end() && (*i)->note() == note->note(); ++i) {
@@ -892,7 +893,7 @@ Sequence<Time>::remove_note_unlocked(const boost::shared_ptr< const Note<Time> >
  typename Sequence<Time>::Notes::const_iterator
  Sequence<Time>::note_lower_bound (Time t) const
  {
-         boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, t, 0, 0, 0));
+         NotePtr search_note(new Note<Time>(0, t, 0, 0, 0));
          typename Sequence<Time>::Notes::const_iterator i = _notes.lower_bound(search_note);
          assert(i == _notes.end() || (*i)->time() >= t);
          return i;
@@ -932,7 +933,7 @@ Sequence<Time>::get_notes_by_pitch (Notes& n, NoteOperator op, uint8_t val, int
                 }
 
                 const Pitches& p (pitches (c));
-                boost::shared_ptr< Note<Time> > search_note(new Note<Time>(0, 0, 0, val, 0));
+                NotePtr search_note(new Note<Time>(0, 0, 0, val, 0));
                 typename Pitches::const_iterator i;
                 switch (op) {
                 case PitchEqual: