diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index cbd89dd1c5..b88cdf8cbd 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -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_ptrupdate_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 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 note, bool selected, bool show_velocity) +MidiRegionView::diff_add_note(const boost::shared_ptr 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 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(); -} + + if (add_or_remove) { + _marked_for_selection.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(); - - _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 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 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 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 { diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 1f2d53980e..6c5dff9ff2 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -173,17 +173,13 @@ class MidiRegionView : public RegionView void display_model(boost::shared_ptr model); - void start_delta_command(std::string name = "midi edit"); - void delta_add_note(const boost::shared_ptr 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 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; diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index b3a4d746a1..ac578dc5a9 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -49,53 +49,13 @@ class MidiSource; */ class MidiModel : public AutomatableSequence { 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 m, const std::string& name); - DeltaCommand (boost::shared_ptr 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 > note); - void remove(const boost::shared_ptr< Evoral::Note > note); - - private: - XMLNode &marshal_note(const boost::shared_ptr< Evoral::Note > note); - boost::shared_ptr< Evoral::Note > unmarshal_note(XMLNode *xml_note); - - boost::shared_ptr _model; - const std::string _name; - - typedef std::list< boost::shared_ptr< Evoral::Note > > 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 > note, - Property prop, uint8_t new_value); - void change (const boost::shared_ptr > note, - Property prop, TimeType new_time); + void add(const NotePtr note); + void remove(const NotePtr note); - private: + 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: boost::shared_ptr _model; const std::string _name; struct NoteChange { DiffCommand::Property property; - boost::shared_ptr< Evoral::Note > note; + NotePtr note; union { uint8_t old_value; TimeType old_time; @@ -142,11 +107,19 @@ public: typedef std::list ChangeList; ChangeList _changes; + typedef std::list< boost::shared_ptr< Evoral::Note > > NoteList; + NoteList _added_notes; + NoteList _removed_notes; + + std::set 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 > find_note (boost::shared_ptr >); + boost::shared_ptr > find_note (NotePtr); + + InsertMergePolicy insert_merge_policy () const; + void set_insert_merge_policy (InsertMergePolicy); + +protected: + int resolve_overlaps_unlocked (const NotePtr, std::set* removed = 0); private: - struct WriteLockImpl : public AutomatableSequence::WriteLockImpl { + struct WriteLockImpl : public AutomatableSequence::WriteLockImpl { WriteLockImpl(Glib::Mutex::Lock* source_lock, Glib::RWLock& s, Glib::Mutex& c) - : AutomatableSequence::WriteLockImpl(s, c) + : AutomatableSequence::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 */ diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index e55840cb20..0e64e6acb1 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -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. */ diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index ebb440ef8f..c4c989d362 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -19,7 +19,7 @@ */ #define __STDC_LIMIT_MACROS 1 - +#include #include #include #include @@ -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 m, const std::string& name) +MidiModel::DiffCommand::DiffCommand(boost::shared_ptr m, const std::string& name) : Command(name) , _model(m) , _name(name) @@ -109,7 +101,7 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, const std: assert(_model); } -MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, const XMLNode& node) +MidiModel::DiffCommand::DiffCommand(boost::shared_ptr m, const XMLNode& node) : _model(m) { assert(_model); @@ -117,63 +109,242 @@ MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, const XMLN } void -MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note > 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 > note) +MidiModel::DiffCommand::remove(const NotePtr note) { _added_notes.remove(note); _removed_notes.push_back(note); } void -MidiModel::DeltaCommand::operator()() +MidiModel::DiffCommand::change(const NotePtr note, Property prop, + uint8_t new_value) { - // 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 + NoteChange change; - MidiModel::WriteLock lock(_model->edit_lock()); + switch (prop) { + case NoteNumber: + if (new_value == note->note()) { + return; + } + change.old_value = note->note(); + break; + case Velocity: + if (new_value == note->velocity()) { + return; + } + change.old_value = note->velocity(); + break; + case Channel: + if (new_value == note->channel()) { + return; + } + change.old_value = note->channel(); + break; - for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) { - _model->add_note_unlocked(*i); + + case StartTime: + fatal << "MidiModel::DiffCommand::change() with integer argument called for start time" << endmsg; + /*NOTREACHED*/ + break; + case Length: + fatal << "MidiModel::DiffCommand::change() with integer argument called for length" << endmsg; + /*NOTREACHED*/ + break; } - for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) { - _model->remove_note_unlocked(*i); + change.note = note; + change.property = prop; + change.new_value = new_value; + + _changes.push_back (change); +} + +void +MidiModel::DiffCommand::change(const NotePtr note, Property prop, + TimeType new_time) +{ + NoteChange change; + + switch (prop) { + case NoteNumber: + case Channel: + case Velocity: + fatal << "MidiModel::DiffCommand::change() with time argument called for note, channel or velocity" << endmsg; + break; + + case StartTime: + if (Evoral::musical_time_equal (note->time(), new_time)) { + return; + } + change.old_time = note->time(); + break; + case Length: + if (Evoral::musical_time_equal (note->length(), new_time)) { + return; + } + change.old_time = note->length(); + break; } - lock.reset(); + change.note = note; + change.property = prop; + change.new_time = new_time; + + _changes.push_back (change); +} + +void +MidiModel::DiffCommand::operator()() +{ + { + 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); + } + + /* notes we modify in a way that requires remove-then-add to maintain ordering */ + set temporary_removals; + + for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) { + Property prop = i->property; + switch (prop) { + case NoteNumber: + if (temporary_removals.find (i->note) == temporary_removals.end()) { + _model->remove_note_unlocked (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 (temporary_removals.find (i->note) == temporary_removals.end()) { + _model->remove_note_unlocked (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 (temporary_removals.find (i->note) == temporary_removals.end()) { + _model->remove_note_unlocked (i->note); + temporary_removals.insert (i->note); + } + i->note->set_channel (i->new_value); + break; + } + } + + for (set::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::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) { + cerr << "\t" << *i << ' ' << **i << endl; + } + } + } + _model->ContentsChanged(); /* EMIT SIGNAL */ } void -MidiModel::DeltaCommand::undo() +MidiModel::DiffCommand::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); + } - MidiModel::WriteLock lock(_model->edit_lock());; + /* notes we modify in a way that requires remove-then-add to maintain ordering */ + set temporary_removals; - for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) { - _model->remove_note_unlocked(*i); - } + for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) { + Property prop = i->property; + switch (prop) { + case NoteNumber: + if (temporary_removals.find (i->note) == temporary_removals.end()) { + _model->remove_note_unlocked (i->note); + temporary_removals.insert (i->note); + } + i->note->set_note (i->old_value); + break; + case Velocity: + i->note->set_velocity (i->old_value); + break; + case StartTime: + if (temporary_removals.find (i->note) == temporary_removals.end()) { + _model->remove_note_unlocked (i->note); + temporary_removals.insert (i->note); + } + i->note->set_time (i->old_time); + break; + case Length: + i->note->set_length (i->old_time); + break; + case Channel: + if (temporary_removals.find (i->note) == temporary_removals.end()) { + _model->remove_note_unlocked (i->note); + temporary_removals.insert (i->note); + } + i->note->set_channel (i->old_value); + break; + } + } - for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) { - _model->add_note_unlocked(*i); - } + for (set::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::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) { + _model->add_note_unlocked (*i); + } + } - lock.reset(); _model->ContentsChanged(); /* EMIT SIGNAL */ } XMLNode& -MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note > note) +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()); @@ -183,11 +354,11 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note