diff options
-rw-r--r-- | gtk2_ardour/ardour3_ui_dark.rc.in | 2 | ||||
-rw-r--r-- | gtk2_ardour/midi_region_view.cc | 150 | ||||
-rw-r--r-- | gtk2_ardour/midi_region_view.h | 14 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_model.h | 54 | ||||
-rw-r--r-- | libs/ardour/enums.cc | 9 | ||||
-rw-r--r-- | libs/ardour/midi_model.cc | 342 | ||||
-rw-r--r-- | libs/ardour/session_state.cc | 10 | ||||
-rw-r--r-- | libs/evoral/evoral/Note.hpp | 1 | ||||
-rw-r--r-- | libs/evoral/src/Note.cpp | 19 |
9 files changed, 536 insertions, 65 deletions
diff --git a/gtk2_ardour/ardour3_ui_dark.rc.in b/gtk2_ardour/ardour3_ui_dark.rc.in index 8046d4d075..860f7f4a01 100644 --- a/gtk2_ardour/ardour3_ui_dark.rc.in +++ b/gtk2_ardour/ardour3_ui_dark.rc.in @@ -1636,5 +1636,5 @@ widget "*RegionListWholeFile" style:highest "treeview_parent_node" widget "*EditorHScrollbar" style:highest "editor_hscrollbar" widget "*OddPortGroups" style:highest "odd_port_groups" widget "*EvenPortGroups" style:highest "even_port_groups" -Widget "*MidiListView" style:highest "white_tree_view" +widget "*MidiListView" style:highest "white_tree_view" Widget "*MidiListView*" style:highest "white_tree_view" diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 5e494605b8..d0ec389262 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -261,7 +261,6 @@ MidiRegionView::canvas_event(GdkEvent* ev) } else if (ev->key.keyval == GDK_Delete) { delete_selection(); - apply_command(); return true; } else if (ev->key.keyval == GDK_Tab) { @@ -309,8 +308,8 @@ MidiRegionView::canvas_event(GdkEvent* ev) } else if (ev->key.keyval == GDK_Control_L) { return true; - } + } return false; case GDK_KEY_RELEASE: @@ -584,7 +583,15 @@ MidiRegionView::start_delta_command(string name) } void -MidiRegionView::command_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity) +MidiRegionView::start_diff_command(string name) +{ + if (!_diff_command) { + _diff_command = _model->new_diff_command(name); + } +} + +void +MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity) { if (_delta_command) { _delta_command->add(note); @@ -598,15 +605,25 @@ MidiRegionView::command_add_note(const boost::shared_ptr<NoteType> note, bool se } void -MidiRegionView::command_remove_note(ArdourCanvas::CanvasNoteEvent* ev) +MidiRegionView::delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev) { if (_delta_command && ev->note()) { _delta_command->remove(ev->note()); } } + +void +MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev, + MidiModel::DiffCommand::Property property, + uint8_t val) +{ + if (_diff_command) { + _diff_command->change (ev->note(), property, val); + } +} void -MidiRegionView::apply_command() +MidiRegionView::apply_delta() { if (!_delta_command) { return; @@ -626,7 +643,27 @@ MidiRegionView::apply_command() } void -MidiRegionView::apply_command_as_subcommand() +MidiRegionView::apply_diff () +{ + 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()); + } + + _model->apply_command(trackview.session(), _diff_command); + _diff_command = NULL; + midi_view()->midi_track()->diskstream()->playlist_modified(); + + _marked_for_selection.clear(); + _marked_for_velocity.clear(); +} + +void +MidiRegionView::apply_delta_as_subcommand() { if (!_delta_command) { return; @@ -646,14 +683,35 @@ MidiRegionView::apply_command_as_subcommand() } void +MidiRegionView::apply_diff_as_subcommand() +{ + 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()); + } + + _model->apply_command_as_subcommand(trackview.session(), _diff_command); + _diff_command = NULL; + midi_view()->midi_track()->diskstream()->playlist_modified(); + + _marked_for_selection.clear(); + _marked_for_velocity.clear(); +} + +void MidiRegionView::abort_command() { delete _delta_command; - _delta_command = NULL; + _delta_command = 0; + delete _diff_command; + _diff_command = 0; clear_selection(); } - void MidiRegionView::redisplay_model() { @@ -1277,7 +1335,7 @@ MidiRegionView::delete_selection() _selection.clear(); - apply_command (); + apply_delta (); } void @@ -1558,14 +1616,14 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) copy->set_note(new_pitch); - command_remove_note(*i); + delta_remove_note(*i); cerr << "Adding note: " << *copy << endl; - command_add_note(copy, (*i)->selected()); + delta_add_note(copy, (*i)->selected()); i = next; } - apply_command(); + apply_delta(); // care about notes being moved beyond the upper/lower bounds on the canvas if (lowest_note_in_selection < midi_stream_view()->lowest_note() || @@ -1720,15 +1778,15 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo // resize beginning of note if (note_end == CanvasNote::NOTE_ON && current_frame < copy->end_time()) { - command_remove_note(canvas_note); + delta_remove_note(canvas_note); copy->on_event().time() = current_frame; - command_add_note(copy, _selection.find(canvas_note) != _selection.end()); + delta_add_note(copy, _selection.find(canvas_note) != _selection.end()); } // resize end of note if (note_end == CanvasNote::NOTE_OFF && current_frame > copy->time()) { - command_remove_note(canvas_note); + delta_remove_note(canvas_note); copy->off_event().time() = current_frame; - command_add_note(copy, _selection.find(canvas_note) != _selection.end()); + delta_add_note(copy, _selection.find(canvas_note) != _selection.end()); } delete resize_rect; @@ -1736,7 +1794,7 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo } _resize_data.clear(); - apply_command(); + apply_delta(); } void @@ -1752,25 +1810,23 @@ MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bo copy->set_velocity(velocity); } - command_remove_note(event); - command_add_note(copy, event->selected(), true); + delta_remove_note(event); + delta_add_note(copy, event->selected(), true); } void MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative) { - const boost::shared_ptr<NoteType> copy(new NoteType(*(event->note().get()))); + uint8_t new_note; if (relative) { - uint8_t new_note = copy->note() + note; - clamp_to_0_127(new_note); - copy->set_note(new_note); + new_note = event->note()->note() + note; } else { - copy->set_note(note); + new_note = note; } - command_remove_note(event); - command_add_note(copy, event->selected(), false); + clamp_to_0_127 (new_note); + diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note); } void @@ -1825,8 +1881,8 @@ MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_del copy->set_length (copy->length() + end_delta); } - command_remove_note(event); - command_add_note(copy, event->selected(), false); + delta_remove_note(event); + delta_add_note(copy, event->selected(), false); } void @@ -1848,8 +1904,8 @@ MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime de copy->set_time (delta); } - command_remove_note(event); - command_add_note(copy, event->selected(), false); + delta_remove_note(event); + delta_add_note(copy, event->selected(), false); } void @@ -1888,7 +1944,7 @@ MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush) i = next; } - apply_command(); + apply_delta(); } @@ -1925,7 +1981,7 @@ MidiRegionView::transpose (bool up, bool fine, bool allow_smush) } } - start_delta_command (_("transpose")); + start_diff_command (_("transpose")); for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { Selection::iterator next = i; @@ -1934,7 +1990,7 @@ MidiRegionView::transpose (bool up, bool fine, bool allow_smush) i = next; } - apply_command (); + apply_diff (); } void @@ -1971,7 +2027,7 @@ MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool e i = next; } - apply_command (); + apply_delta (); } @@ -2032,7 +2088,7 @@ MidiRegionView::nudge_notes (bool forward) i = next; } - apply_command (); + apply_delta (); } @@ -2049,13 +2105,13 @@ MidiRegionView::change_channel(uint8_t channel) copy->set_channel(channel); - command_remove_note(event); - command_add_note(copy, event->selected()); + delta_remove_note(event); + delta_add_note(copy, event->selected()); i = next; } - apply_command(); + apply_delta(); } @@ -2152,14 +2208,14 @@ MidiRegionView::cut_copy_clear (Editing::CutCopyOp op) case Copy: break; case Cut: - command_remove_note (*i); + delta_remove_note (*i); break; case Clear: break; } } - apply_command(); + apply_delta(); } MidiCutBuffer* @@ -2212,7 +2268,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb) /* make all newly added notes selected */ - command_add_note (copied_note, true); + delta_add_note (copied_note, true); end_point = copied_note->end_time(); } @@ -2233,7 +2289,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb) trackview.session().add_command (new MementoCommand<Region>(*_region, &before, &_region->get_state())); } - apply_command (); + apply_delta (); } void @@ -2243,8 +2299,8 @@ 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")); - command_add_note (new_note, true, false); - apply_command (); + delta_add_note (new_note, true, false); + apply_delta(); /* potentially extend region to hold new note */ @@ -2333,7 +2389,7 @@ MidiRegionView::replace_selected (NoteList& replacements) tmp = i; ++tmp; - command_remove_note (*i); + delta_remove_note (*i); remove_from_selection (*i); i = tmp; @@ -2342,9 +2398,9 @@ MidiRegionView::replace_selected (NoteList& replacements) _selection.clear (); for (NoteList::iterator i = replacements.begin(); i != replacements.end(); ++i) { - command_add_note (*i, true, false); + delta_add_note (*i, true, false); } - apply_command_as_subcommand (); + apply_delta_as_subcommand (); } diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index c32fd2cb69..611ba4c142 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -169,11 +169,16 @@ class MidiRegionView : public RegionView void display_model(boost::shared_ptr<ARDOUR::MidiModel> model); void start_delta_command(std::string name = "midi edit"); - void command_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity=false); - void command_remove_note(ArdourCanvas::CanvasNoteEvent* ev); + void delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity=false); + void delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev); - void apply_command(); - void apply_command_as_subcommand(); + void start_diff_command(std::string name = "midi edit"); + void diff_add_change(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::DiffCommand::Property, uint8_t val); + + void apply_delta(); + void apply_diff(); + void apply_delta_as_subcommand(); + void apply_diff_as_subcommand(); void abort_command(); void note_entered(ArdourCanvas::CanvasNoteEvent* ev); @@ -354,6 +359,7 @@ class MidiRegionView : public RegionView ArdourCanvas::CanvasNote** _active_notes; ArdourCanvas::Group* _note_group; ARDOUR::MidiModel::DeltaCommand* _delta_command; + ARDOUR::MidiModel::DiffCommand* _diff_command; MouseState _mouse_state; int _pressed_button; diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 967372fa9a..46d4cf794c 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -89,10 +89,62 @@ public: 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 { + NoteNumber, + Velocity, + StartTime, + Length, + Channel + }; + + DiffCommand (boost::shared_ptr<MidiModel> m, const std::string& name); + DiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node); + + const std::string& name() const { return _name; } + + void operator()(); + void undo(); + + int set_state (const XMLNode&); + XMLNode& get_state (); + + void change (const boost::shared_ptr<Evoral::Note<TimeType> > note, + Property prop, uint8_t new_value); + + private: + boost::shared_ptr<MidiModel> _model; + const std::string _name; + + struct NotePropertyChange { + DiffCommand::Property property; + boost::shared_ptr<Evoral::Note<TimeType> > note; + uint8_t old_value; + uint8_t new_value; + }; + + typedef std::list<NotePropertyChange> ChangeList; + ChangeList _changes; + + XMLNode &marshal_change(const NotePropertyChange&); + NotePropertyChange unmarshal_change(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); + + bool write_to(boost::shared_ptr<MidiSource> source); // MidiModel doesn't use the normal AutomationList serialisation code @@ -104,6 +156,8 @@ 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> >); private: friend class DeltaCommand; diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index efe72ddb4b..92810534ee 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -28,6 +28,7 @@ #include "ardour/export_profile_manager.h" #include "ardour/io.h" #include "ardour/location.h" +#include "ardour/midi_model.h" #include "ardour/midi_track.h" #include "ardour/mute_master.h" #include "ardour/panner.h" @@ -109,6 +110,7 @@ setup_enum_writer () Delivery::Role _Delivery_Role; IO::Direction _IO_Direction; MuteMaster::MutePoint _MuteMaster_MutePoint; + MidiModel::DiffCommand::Property _MidiModel_DiffCommand_Property; #define REGISTER(e) enum_writer->register_distinct (typeid(e).name(), i, s); i.clear(); s.clear() #define REGISTER_BITS(e) enum_writer->register_bits (typeid(e).name(), i, s); i.clear(); s.clear() @@ -522,4 +524,11 @@ setup_enum_writer () REGISTER_CLASS_ENUM (IO, Input); REGISTER_CLASS_ENUM (IO, Output); REGISTER (_IO_Direction); + + REGISTER_CLASS_ENUM (MidiModel::DiffCommand, NoteNumber); + REGISTER_CLASS_ENUM (MidiModel::DiffCommand, Channel); + REGISTER_CLASS_ENUM (MidiModel::DiffCommand, Velocity); + REGISTER_CLASS_ENUM (MidiModel::DiffCommand, StartTime); + REGISTER_CLASS_ENUM (MidiModel::DiffCommand, Length); + REGISTER (_MidiModel_DiffCommand_Property); } diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index da524307f6..60c8829b61 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -43,7 +43,7 @@ MidiModel::MidiModel(MidiSource* s, size_t size) { } -/** Start a new command. +/** 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 @@ -56,6 +56,19 @@ MidiModel::new_delta_command(const string name) return cmd; } +/** Start a new Diff 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::DiffCommand* +MidiModel::new_diff_command(const string name) +{ + DiffCommand* cmd = new DiffCommand(_midi_source->model(), name); + return cmd; +} + /** Apply a command. * * Ownership of cmd is taken, it must not be deleted by the caller. @@ -225,7 +238,7 @@ MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note) length_str >> length; } else { warning << "note information missing length" << endmsg; - note = 1; + length = 1; } if ((prop = xml_note->property("velocity")) != 0) { @@ -286,6 +299,320 @@ MidiModel::DeltaCommand::get_state() return *delta_command; } +/************** DIFF COMMAND ********************/ + +#define DIFF_NOTES_ELEMENT "changed_notes" +#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); +} + +void +MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> > note, Property prop, + uint8_t new_value) +{ + NotePropertyChange change; + + change.note = note; + change.property = prop; + change.new_value = new_value; + + switch (prop) { + case NoteNumber: + change.old_value = note->note(); + break; + case Velocity: + change.old_value = note->velocity(); + break; + case StartTime: + change.old_value = note->time(); + break; + case Length: + change.old_value = note->length(); + break; + case Channel: + change.old_value = note->channel(); + break; + } + + _changes.push_back (change); +} + +void +MidiModel::DiffCommand::operator()() +{ + Glib::Mutex::Lock lm (_model->_midi_source->mutex()); + _model->_midi_source->invalidate(); // release model read lock + _model->write_lock(); + + for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) { + Property prop = i->property; + switch (prop) { + case NoteNumber: + i->note->set_note (i->new_value); + break; + case Velocity: + i->note->set_velocity (i->new_value); + break; + case StartTime: + i->note->set_time (i->new_value); + break; + case Length: + i->note->set_length (i->new_value); + break; + case Channel: + i->note->set_channel (i->new_value); + break; + } + } + + _model->write_unlock(); + _model->ContentsChanged(); /* EMIT SIGNAL */ +} + +void +MidiModel::DiffCommand::undo() +{ + Glib::Mutex::Lock lm (_model->_midi_source->mutex()); + _model->_midi_source->invalidate(); // release model read lock + _model->write_lock(); + + for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) { + Property prop = i->property; + switch (prop) { + case NoteNumber: + i->note->set_note (i->old_value); + break; + case Velocity: + i->note->set_velocity (i->old_value); + break; + case StartTime: + i->note->set_time (i->old_value); + break; + case Length: + i->note->set_length (i->old_value); + break; + case Channel: + i->note->set_channel (i->old_value); + break; + } + } + + _model->write_unlock(); + _model->ContentsChanged(); /* EMIT SIGNAL */ +} + +XMLNode& +MidiModel::DiffCommand::marshal_change(const NotePropertyChange& change) +{ + XMLNode* xml_change = new XMLNode("change"); + + /* first, the change itself */ + + xml_change->add_property ("property", enum_2_string (change.property)); + + { + ostringstream old_value_str (ios::ate); + old_value_str << (unsigned int) change.old_value; + xml_change->add_property ("old", old_value_str.str()); + } + + { + ostringstream new_value_str (ios::ate); + new_value_str << (unsigned int) change.old_value; + xml_change->add_property ("new", new_value_str.str()); + } + + /* now the rest of the note */ + + if (change.property != NoteNumber) { + ostringstream note_str(ios::ate); + note_str << int(change.note->note()); + xml_change->add_property("note", note_str.str()); + } + + if (change.property != Channel) { + ostringstream channel_str(ios::ate); + channel_str << int(change.note->channel()); + xml_change->add_property("channel", channel_str.str()); + } + + if (change.property != StartTime) { + ostringstream time_str(ios::ate); + time_str << int(change.note->time()); + xml_change->add_property("time", time_str.str()); + } + + if (change.property != Length) { + ostringstream length_str(ios::ate); + length_str <<(unsigned int) change.note->length(); + xml_change->add_property("length", length_str.str()); + } + + if (change.property != Velocity) { + ostringstream velocity_str(ios::ate); + velocity_str << (unsigned int) change.note->velocity(); + xml_change->add_property("velocity", velocity_str.str()); + } + + return *xml_change; +} + +MidiModel::DiffCommand::NotePropertyChange +MidiModel::DiffCommand::unmarshal_change(XMLNode *xml_change) +{ + XMLProperty* prop; + NotePropertyChange change; + unsigned int note; + unsigned int channel; + unsigned int time; + unsigned int length; + unsigned int velocity; + + if ((prop = xml_change->property("property")) != 0) { + change.property = (Property) string_2_enum (prop->value(), change.property); + } else { + fatal << "!!!" << endmsg; + /*NOTREACHED*/ + } + + if ((prop = xml_change->property ("old")) != 0) { + istringstream old_str (prop->value()); + old_str >> change.old_value; + } else { + fatal << "!!!" << endmsg; + /*NOTREACHED*/ + } + + if ((prop = xml_change->property ("new")) == 0) { + istringstream new_str (prop->value()); + new_str >> change.new_value; + } else { + fatal << "!!!" << endmsg; + /*NOTREACHED*/ + } + + if (change.property != NoteNumber) { + if ((prop = xml_change->property("note")) != 0) { + istringstream note_str(prop->value()); + note_str >> note; + } else { + warning << "note information missing note value" << endmsg; + note = 127; + } + } else { + note = change.new_value; + } + + if (change.property != Channel) { + if ((prop = xml_change->property("channel")) != 0) { + istringstream channel_str(prop->value()); + channel_str >> channel; + } else { + warning << "note information missing channel" << endmsg; + channel = 0; + } + } else { + channel = change.new_value; + } + + if (change.property != StartTime) { + if ((prop = xml_change->property("time")) != 0) { + istringstream time_str(prop->value()); + time_str >> time; + } else { + warning << "note information missing time" << endmsg; + time = 0; + } + } else { + time = change.new_value; + } + + if (change.property != Length) { + if ((prop = xml_change->property("length")) != 0) { + istringstream length_str(prop->value()); + length_str >> length; + } else { + warning << "note information missing length" << endmsg; + length = 1; + } + } else { + length = change.new_value; + } + + if (change.property != Velocity) { + if ((prop = xml_change->property("velocity")) != 0) { + istringstream velocity_str(prop->value()); + velocity_str >> velocity; + } else { + warning << "note information missing velocity" << endmsg; + velocity = 127; + } + } else { + velocity = change.new_value; + } + + /* we must point at the instance of the note that is actually in the model. + so go look for it ... + */ + + boost::shared_ptr<Evoral::Note<TimeType> > new_note (new Evoral::Note<TimeType> (channel, time, length, note, velocity)); + + change.note = _model->find_note (new_note); + + if (!change.note) { + warning << "MIDI note not found in model - programmers should investigate this" << endmsg; + /* use the actual new note */ + change.note = new_note; + } + + return change; +} + +int +MidiModel::DiffCommand::set_state(const XMLNode& diff_command) +{ + if (diff_command.name() != string(DIFF_COMMAND_ELEMENT)) { + return 1; + } + + _changes.clear(); + + XMLNode* changed_notes = diff_command.child(DIFF_NOTES_ELEMENT); + XMLNodeList notes = changed_notes->children(); + + transform (notes.begin(), notes.end(), back_inserter(_changes), + sigc::mem_fun(*this, &DiffCommand::unmarshal_change)); + + return 0; +} + +XMLNode& +MidiModel::DiffCommand::get_state () +{ + XMLNode* diff_command = new XMLNode(DIFF_COMMAND_ELEMENT); + diff_command->add_property("midi-source", _model->midi_source()->id().to_s()); + + XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT); + for_each(_changes.begin(), _changes.end(), sigc::compose( + sigc::mem_fun(*changes, &XMLNode::add_child_nocopy), + sigc::mem_fun(*this, &DiffCommand::marshal_change))); + + return *diff_command; +} + /** Write the model to a MidiSource (i.e. save the model). * This is different from manually using read to write to a source in that * note off events are written regardless of the track mode. This is so the @@ -322,3 +649,14 @@ MidiModel::get_state() return *node; } +boost::shared_ptr<Evoral::Note<MidiModel::TimeType> > +MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other) +{ + Notes::iterator i = find (notes().begin(), notes().end(), other); + + if (i == notes().end()) { + return boost::shared_ptr<Evoral::Note<TimeType> > (); + } + + return *i; +} diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index c782882c0f..32a4a50cb4 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -2932,6 +2932,16 @@ Session::restore_history (string snapshot_name) } else { error << "FIXME: 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 = + boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id)); + if(midi_source) { + ut->add_command(new MidiModel::DiffCommand(midi_source->model(), *n)); + } else { + error << "FIXME: Failed to downcast MidiSource for DeltaCommand" << endmsg; + } + } else { error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg; } diff --git a/libs/evoral/evoral/Note.hpp b/libs/evoral/evoral/Note.hpp index b891c2096a..e92094ae78 100644 --- a/libs/evoral/evoral/Note.hpp +++ b/libs/evoral/evoral/Note.hpp @@ -72,7 +72,6 @@ private: MIDIEvent<Time> _off_event; }; - } // namespace Evoral template<typename Time> diff --git a/libs/evoral/src/Note.cpp b/libs/evoral/src/Note.cpp index cc3d4fee62..9440cde273 100644 --- a/libs/evoral/src/Note.cpp +++ b/libs/evoral/src/Note.cpp @@ -78,21 +78,20 @@ Note<Time>::~Note() { } - template<typename Time> const Note<Time>& -Note<Time>::operator=(const Note<Time>& copy) +Note<Time>::operator=(const Note<Time>& other) { - _on_event = copy._on_event; - _off_event = copy._off_event; + _on_event = other._on_event; + _off_event = other._off_event; - assert(time() == copy.time()); - assert(end_time() == copy.end_time()); - assert(note() == copy.note()); - assert(velocity() == copy.velocity()); - assert(length() == copy.length()); + assert(time() == other.time()); + assert(end_time() == other.end_time()); + assert(note() == other.note()); + assert(velocity() == other.velocity()); + assert(length() == other.length()); assert(_on_event.channel() == _off_event.channel()); - assert(channel() == copy.channel()); + assert(channel() == other.channel()); return *this; } |