From 555c7ac094d86a02b7bef85f9691cd507e19391e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 9 Dec 2010 21:36:31 +0000 Subject: [PATCH] Undo for sys-ex movements in time. git-svn-id: svn://localhost/ardour2/branches/3.0@8232 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/midi_model.h | 33 +++++ libs/ardour/enums.cc | 4 + libs/ardour/midi_model.cc | 230 ++++++++++++++++++++++++++++++-- 3 files changed, 257 insertions(+), 10 deletions(-) diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 69dc452b62..532d8c0e94 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -140,6 +140,38 @@ public: NotePtr unmarshal_note(XMLNode *xml_note); }; + /* Currently this class only supports changes of sys-ex time, but could be expanded */ + class SysexDiffCommand : public DiffCommand { + public: + SysexDiffCommand (boost::shared_ptr m, const XMLNode& node); + + enum Property { + Time, + }; + + int set_state (const XMLNode&, int version); + XMLNode & get_state (); + + void operator() (); + void undo (); + + void change (boost::shared_ptr >, TimeType); + + private: + struct Change { + boost::shared_ptr > sysex; + SysexDiffCommand::Property property; + TimeType old_time; + TimeType new_time; + }; + + typedef std::list ChangeList; + ChangeList _changes; + + XMLNode & marshal_change (const Change &); + Change unmarshal_change (XMLNode *); + }; + MidiModel::NoteDiffCommand* new_note_diff_command (const std::string name="midi edit"); void apply_command (Session& session, Command* cmd); void apply_command_as_subcommand (Session& session, Command* cmd); @@ -161,6 +193,7 @@ public: boost::shared_ptr > find_note (NotePtr); boost::shared_ptr > find_note (gint note_id); + boost::shared_ptr > find_sysex (gint); InsertMergePolicy insert_merge_policy () const; void set_insert_merge_policy (InsertMergePolicy); diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 6f3dff17a9..380fb2160c 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -113,6 +113,7 @@ setup_enum_writer () IO::Direction _IO_Direction; MuteMaster::MutePoint _MuteMaster_MutePoint; MidiModel::NoteDiffCommand::Property _MidiModel_NoteDiffCommand_Property; + MidiModel::SysExDiffCommand::Property _MidiModel_SysExDiffCommand_Property; WaveformScale _WaveformScale; WaveformShape _WaveformShape; QuantizeType _QuantizeType; @@ -532,6 +533,9 @@ setup_enum_writer () REGISTER_CLASS_ENUM (MidiModel::NoteDiffCommand, Length); REGISTER (_MidiModel_NoteDiffCommand_Property); + REGISTER_CLASS_ENUM (MidiModel::SysExDiffCommand, Time); + REGISTER (_MidiModel_SysExDiffCommand_Property); + REGISTER_ENUM(Linear); REGISTER_ENUM(Logarithmic); REGISTER(_WaveformScale); diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index e3d31df58d..b93e74ef93 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -60,6 +60,17 @@ MidiModel::new_note_diff_command (const string name) return new NoteDiffCommand (ms->model(), name); } +/** Start a new SysExDiff command */ +MidiModel::SysExDiffCommand* +MidiModel::new_sysex_diff_command (const string name) +{ + boost::shared_ptr ms = _midi_source.lock (); + assert (ms); + + return new SysExDiffCommand (ms->model(), name); +} + + /** Apply a command. * * Ownership of cmd is taken, it must not be deleted by the caller. @@ -94,6 +105,8 @@ MidiModel::apply_command_as_subcommand(Session& session, Command* cmd) #define ADDED_NOTES_ELEMENT "AddedNotes" #define REMOVED_NOTES_ELEMENT "RemovedNotes" #define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals" +#define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand" +#define DIFF_SYSEXES_ELEMENT "ChangedSysExes" MidiModel::DiffCommand::DiffCommand(boost::shared_ptr m, const std::string& name) : Command (name) @@ -679,6 +692,177 @@ MidiModel::NoteDiffCommand::get_state () return *diff_command; } +MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr m, const XMLNode& node) + : DiffCommand (m, "") +{ + assert (_model); + set_state (node, Stateful::loading_state_version); +} + +void +MidiModel::SysExDiffCommand::change (boost::shared_ptr > s, TimeType new_time) +{ + Change change; + + change.sysex = s; + change.property = Time; + change.old_time = s->time (); + change.new_time = new_time; + + _changes.push_back (change); +} + +void +MidiModel::SysExDiffCommand::operator() () +{ + { + MidiModel::WriteLock lock (_model->edit_lock ()); + + for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) { + switch (i->property) { + case Time: + i->sysex->set_time (i->new_time); + } + } + } + + _model->ContentsChanged (); /* EMIT SIGNAL */ +} + +void +MidiModel::SysExDiffCommand::undo () +{ + { + MidiModel::WriteLock lock (_model->edit_lock ()); + + for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) { + switch (i->property) { + case Time: + i->sysex->set_time (i->old_time); + break; + } + } + + } + + _model->ContentsChanged(); /* EMIT SIGNAL */ +} + +XMLNode& +MidiModel::SysExDiffCommand::marshal_change (const Change& 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 << change.old_time; + xml_change->add_property ("old", old_value_str.str()); + } + + { + ostringstream new_value_str (ios::ate); + new_value_str << change.new_time; + xml_change->add_property ("new", new_value_str.str()); + } + + ostringstream id_str; + id_str << change.sysex->id(); + xml_change->add_property ("id", id_str.str()); + + return *xml_change; +} + +MidiModel::SysExDiffCommand::Change +MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change) +{ + XMLProperty* prop; + Change change; + + 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 ("id")) == 0) { + error << _("No SysExID found for sys-ex property change - ignored") << endmsg; + return change; + } + + gint sysex_id = atoi (prop->value().c_str()); + + if ((prop = xml_change->property ("old")) != 0) { + istringstream old_str (prop->value()); + old_str >> change.old_time; + } else { + fatal << "!!!" << endmsg; + /*NOTREACHED*/ + } + + if ((prop = xml_change->property ("new")) != 0) { + istringstream new_str (prop->value()); + new_str >> change.new_time; + } else { + fatal << "!!!" << endmsg; + /*NOTREACHED*/ + } + + /* we must point at the instance of the sysex that is actually in the model. + so go look for it ... + */ + + change.sysex = _model->find_sysex (sysex_id); + + if (!change.sysex) { + warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg; + return change; + } + + return change; +} + +int +MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/) +{ + if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) { + return 1; + } + + /* changes */ + + _changes.clear(); + + XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT); + + if (changed_sysexes) { + XMLNodeList sysexes = changed_sysexes->children(); + transform (sysexes.begin(), sysexes.end(), back_inserter (_changes), + boost::bind (&SysExDiffCommand::unmarshal_change, this, _1)); + + } + + return 0; +} + +XMLNode& +MidiModel::SysExDiffCommand::get_state () +{ + XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT); + diff_command->add_property ("midi-source", _model->midi_source()->id().to_s()); + + XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT); + for_each (_changes.begin(), _changes.end(), + boost::bind ( + boost::bind (&XMLNode::add_child_nocopy, changes, _1), + boost::bind (&SysExDiffCommand::marshal_change, this, _1))); + + return *diff_command; +} /** Write all of the model to a MidiSource (i.e. save the model). * This is different from manually using read to write to a source in that @@ -874,6 +1058,22 @@ MidiModel::find_note (gint note_id) return NotePtr(); } +boost::shared_ptr > +MidiModel::find_sysex (gint sysex_id) +{ + /* used only for looking up notes when reloading history from disk, + so we don't care about performance *too* much. + */ + + for (SysExes::iterator l = sysexes().begin(); l != sysexes().end(); ++l) { + if ((*l)->id() == sysex_id) { + return *l; + } + } + + return boost::shared_ptr > (); +} + /** Lock and invalidate the source. * This should be used by commands and editing things */ @@ -1201,18 +1401,20 @@ MidiModel::midi_source () void MidiModel::insert_silence_at_start (TimeType t) { - /* Notes */ - - NoteDiffCommand* c = new_note_diff_command ("insert silence"); - - for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) { - c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t); - } - boost::shared_ptr s = _midi_source.lock (); assert (s); - apply_command_as_subcommand (s->session(), c); + /* Notes */ + + if (!notes().empty ()) { + NoteDiffCommand* c = new_note_diff_command ("insert silence"); + + for (Notes::const_iterator i = notes().begin(); i != notes().end(); ++i) { + c->change (*i, NoteDiffCommand::StartTime, (*i)->time() + t); + } + + apply_command_as_subcommand (s->session(), c); + } /* Controllers */ @@ -1226,5 +1428,13 @@ MidiModel::insert_silence_at_start (TimeType t) /* Sys-ex */ - /* XXX */ + if (!sysexes().empty()) { + SysExDiffCommand* c = new_sysex_diff_command ("insert silence"); + + for (SysExes::iterator i = sysexes().begin(); i != sysexes().end(); ++i) { + c->change (*i, (*i)->time() + t); + } + + apply_command_as_subcommand (s->session(), c); + } } -- 2.30.2