summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorCarl Hetherington <carl@carlh.net>2010-12-09 21:36:31 +0000
committerCarl Hetherington <carl@carlh.net>2010-12-09 21:36:31 +0000
commit555c7ac094d86a02b7bef85f9691cd507e19391e (patch)
tree5a4591922d749b6a00911455dabdf2bb3e661412 /libs
parentad916f6241011266fea05877685376a06ec586bf (diff)
Undo for sys-ex movements in time.
git-svn-id: svn://localhost/ardour2/branches/3.0@8232 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/midi_model.h33
-rw-r--r--libs/ardour/enums.cc4
-rw-r--r--libs/ardour/midi_model.cc230
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<MidiModel> 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<Evoral::Event<TimeType> >, TimeType);
+
+ private:
+ struct Change {
+ boost::shared_ptr<Evoral::Event<TimeType> > sysex;
+ SysexDiffCommand::Property property;
+ TimeType old_time;
+ TimeType new_time;
+ };
+
+ typedef std::list<Change> 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<Evoral::Note<TimeType> > find_note (NotePtr);
boost::shared_ptr<Evoral::Note<TimeType> > find_note (gint note_id);
+ boost::shared_ptr<Evoral::Event<TimeType> > 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<MidiSource> 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<MidiModel> m, const std::string& name)
: Command (name)
@@ -679,6 +692,177 @@ MidiModel::NoteDiffCommand::get_state ()
return *diff_command;
}
+MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
+ : DiffCommand (m, "")
+{
+ assert (_model);
+ set_state (node, Stateful::loading_state_version);
+}
+
+void
+MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > 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<Evoral::Event<MidiModel::TimeType> >
+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<Evoral::Event<TimeType> > ();
+}
+
/** 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<MidiSource> 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);
+ }
}