Undo for sys-ex movements in time.
git-svn-id: svn://localhost/ardour2/branches/3.0@8232 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user