mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-11 17:16:38 +01:00
make note overlap resolution store side effects in a DiffCommand, and add its changes to the DiffCommand being executed, so as to retain "internal" note property changes across undo
git-svn-id: svn://localhost/ardour2/branches/3.0@7256 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
f1fc47b077
commit
c158c44fab
4 changed files with 78 additions and 24 deletions
|
|
@ -77,8 +77,9 @@ public:
|
||||||
int set_state (const XMLNode&, int version);
|
int set_state (const XMLNode&, int version);
|
||||||
XMLNode& get_state ();
|
XMLNode& get_state ();
|
||||||
|
|
||||||
void add(const NotePtr note);
|
void add (const NotePtr note);
|
||||||
void remove(const NotePtr note);
|
void remove (const NotePtr note);
|
||||||
|
void side_effect_remove (const NotePtr note);
|
||||||
|
|
||||||
void change (const NotePtr note, Property prop, uint8_t new_value);
|
void change (const NotePtr note, Property prop, uint8_t new_value);
|
||||||
void change (const NotePtr note, Property prop, TimeType new_time);
|
void change (const NotePtr note, Property prop, TimeType new_time);
|
||||||
|
|
@ -87,6 +88,9 @@ public:
|
||||||
return !_added_notes.empty() || !_removed_notes.empty();
|
return !_added_notes.empty() || !_removed_notes.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DiffCommand& operator+= (const DiffCommand& other);
|
||||||
|
boost::shared_ptr<MidiModel> model() const { return _model; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::shared_ptr<MidiModel> _model;
|
boost::shared_ptr<MidiModel> _model;
|
||||||
const std::string _name;
|
const std::string _name;
|
||||||
|
|
@ -144,7 +148,7 @@ public:
|
||||||
void set_insert_merge_policy (InsertMergePolicy);
|
void set_insert_merge_policy (InsertMergePolicy);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int resolve_overlaps_unlocked (const NotePtr, std::set<NotePtr>* removed = 0);
|
int resolve_overlaps_unlocked (const NotePtr, void* arg = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct WriteLockImpl : public AutomatableSequence<TimeType>::WriteLockImpl {
|
struct WriteLockImpl : public AutomatableSequence<TimeType>::WriteLockImpl {
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,12 @@ MidiModel::DiffCommand::remove(const NotePtr note)
|
||||||
_removed_notes.push_back(note);
|
_removed_notes.push_back(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MidiModel::DiffCommand::side_effect_remove(const NotePtr note)
|
||||||
|
{
|
||||||
|
side_effect_removals.insert (note);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiModel::DiffCommand::change(const NotePtr note, Property prop,
|
MidiModel::DiffCommand::change(const NotePtr note, Property prop,
|
||||||
uint8_t new_value)
|
uint8_t new_value)
|
||||||
|
|
@ -200,12 +206,31 @@ MidiModel::DiffCommand::change(const NotePtr note, Property prop,
|
||||||
_changes.push_back (change);
|
_changes.push_back (change);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MidiModel::DiffCommand&
|
||||||
|
MidiModel::DiffCommand::operator+= (const DiffCommand& other)
|
||||||
|
{
|
||||||
|
if (this == &other) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_model != other._model) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_added_notes.insert (_added_notes.end(), other._added_notes.begin(), other._added_notes.end());
|
||||||
|
_removed_notes.insert (_removed_notes.end(), other._removed_notes.begin(), other._removed_notes.end());
|
||||||
|
side_effect_removals.insert (other.side_effect_removals.begin(), other.side_effect_removals.end());
|
||||||
|
_changes.insert (_changes.end(), other._changes.begin(), other._changes.end());
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiModel::DiffCommand::operator()()
|
MidiModel::DiffCommand::operator()()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
MidiModel::WriteLock lock(_model->edit_lock());
|
MidiModel::WriteLock lock(_model->edit_lock());
|
||||||
|
|
||||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
||||||
_model->add_note_unlocked(*i);
|
_model->add_note_unlocked(*i);
|
||||||
}
|
}
|
||||||
|
|
@ -228,10 +253,6 @@ MidiModel::DiffCommand::operator()()
|
||||||
i->note->set_note (i->new_value);
|
i->note->set_note (i->new_value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Velocity:
|
|
||||||
i->note->set_velocity (i->new_value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case StartTime:
|
case StartTime:
|
||||||
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
||||||
_model->remove_note_unlocked (i->note);
|
_model->remove_note_unlocked (i->note);
|
||||||
|
|
@ -241,10 +262,6 @@ MidiModel::DiffCommand::operator()()
|
||||||
i->note->set_time (i->new_time);
|
i->note->set_time (i->new_time);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Length:
|
|
||||||
i->note->set_length (i->new_time);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Channel:
|
case Channel:
|
||||||
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
if (temporary_removals.find (i->note) == temporary_removals.end()) {
|
||||||
_model->remove_note_unlocked (i->note);
|
_model->remove_note_unlocked (i->note);
|
||||||
|
|
@ -252,11 +269,26 @@ MidiModel::DiffCommand::operator()()
|
||||||
}
|
}
|
||||||
i->note->set_channel (i->new_value);
|
i->note->set_channel (i->new_value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* no remove-then-add required for these properties, since we do not index them
|
||||||
|
*/
|
||||||
|
|
||||||
|
case Velocity:
|
||||||
|
i->note->set_velocity (i->new_value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Length:
|
||||||
|
i->note->set_length (i->new_time);
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
|
for (set<NotePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
|
||||||
_model->add_note_unlocked (*i, &side_effect_removals);
|
DiffCommand side_effects (model(), "side effects");
|
||||||
|
_model->add_note_unlocked (*i, &side_effects);
|
||||||
|
*this += side_effects;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!side_effect_removals.empty()) {
|
if (!side_effect_removals.empty()) {
|
||||||
|
|
@ -329,7 +361,7 @@ MidiModel::DiffCommand::undo()
|
||||||
state once this is done.
|
state once this is done.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
cerr << "This undo has " << side_effect_removals.size() << " SER's\n";
|
cerr << "This undo has " << side_effect_removals.size() << " SER's to be re-added\n";
|
||||||
for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
|
for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
|
||||||
_model->add_note_unlocked (*i);
|
_model->add_note_unlocked (*i);
|
||||||
}
|
}
|
||||||
|
|
@ -880,15 +912,16 @@ MidiModel::write_lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
MidiModel::resolve_overlaps_unlocked (const NotePtr note, set<NotePtr>* removed)
|
MidiModel::resolve_overlaps_unlocked (const NotePtr note, void* arg)
|
||||||
|
|
||||||
{
|
{
|
||||||
using namespace Evoral;
|
using namespace Evoral;
|
||||||
|
|
||||||
if (_writing || insert_merge_policy() == InsertMergeRelax) {
|
if (_writing || insert_merge_policy() == InsertMergeRelax) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DiffCommand* cmd = static_cast<DiffCommand*>(arg);
|
||||||
|
|
||||||
TimeType sa = note->time();
|
TimeType sa = note->time();
|
||||||
TimeType ea = note->end_time();
|
TimeType ea = note->end_time();
|
||||||
|
|
||||||
|
|
@ -926,19 +959,28 @@ MidiModel::resolve_overlaps_unlocked (const NotePtr note, set<NotePtr>* removed)
|
||||||
|
|
||||||
switch (overlap) {
|
switch (overlap) {
|
||||||
case OverlapStart:
|
case OverlapStart:
|
||||||
|
cerr << "OverlapStart\n";
|
||||||
/* existing note covers start of new note */
|
/* existing note covers start of new note */
|
||||||
switch (insert_merge_policy()) {
|
switch (insert_merge_policy()) {
|
||||||
case InsertMergeReplace:
|
case InsertMergeReplace:
|
||||||
to_be_deleted.insert (*i);
|
to_be_deleted.insert (*i);
|
||||||
break;
|
break;
|
||||||
case InsertMergeTruncateExisting:
|
case InsertMergeTruncateExisting:
|
||||||
|
if (cmd) {
|
||||||
|
cmd->change (*i, DiffCommand::Length, (note->time() - (*i)->time()));
|
||||||
|
}
|
||||||
(*i)->set_length (note->time() - (*i)->time());
|
(*i)->set_length (note->time() - (*i)->time());
|
||||||
break;
|
break;
|
||||||
case InsertMergeTruncateAddition:
|
case InsertMergeTruncateAddition:
|
||||||
set_note_time = true;
|
set_note_time = true;
|
||||||
|
set_note_length = true;
|
||||||
note_time = (*i)->time() + (*i)->length();
|
note_time = (*i)->time() + (*i)->length();
|
||||||
|
note_length = min (note_length, (*i)->length() - ((*i)->end_time() - note->time()));
|
||||||
break;
|
break;
|
||||||
case InsertMergeExtend:
|
case InsertMergeExtend:
|
||||||
|
if (cmd) {
|
||||||
|
cmd->change ((*i), DiffCommand::Length, note->end_time() - (*i)->time());
|
||||||
|
}
|
||||||
(*i)->set_length (note->end_time() - (*i)->time());
|
(*i)->set_length (note->end_time() - (*i)->time());
|
||||||
return -1; /* do not add the new note */
|
return -1; /* do not add the new note */
|
||||||
break;
|
break;
|
||||||
|
|
@ -950,6 +992,7 @@ MidiModel::resolve_overlaps_unlocked (const NotePtr note, set<NotePtr>* removed)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OverlapEnd:
|
case OverlapEnd:
|
||||||
|
cerr << "OverlapEnd\n";
|
||||||
/* existing note covers end of new note */
|
/* existing note covers end of new note */
|
||||||
switch (insert_merge_policy()) {
|
switch (insert_merge_policy()) {
|
||||||
case InsertMergeReplace:
|
case InsertMergeReplace:
|
||||||
|
|
@ -985,6 +1028,7 @@ MidiModel::resolve_overlaps_unlocked (const NotePtr note, set<NotePtr>* removed)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OverlapExternal:
|
case OverlapExternal:
|
||||||
|
cerr << "OverlapExt\n";
|
||||||
/* existing note overlaps all the new note */
|
/* existing note overlaps all the new note */
|
||||||
switch (insert_merge_policy()) {
|
switch (insert_merge_policy()) {
|
||||||
case InsertMergeReplace:
|
case InsertMergeReplace:
|
||||||
|
|
@ -1003,6 +1047,7 @@ MidiModel::resolve_overlaps_unlocked (const NotePtr note, set<NotePtr>* removed)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OverlapInternal:
|
case OverlapInternal:
|
||||||
|
cerr << "OverlapInt\n";
|
||||||
/* new note fully overlaps an existing note */
|
/* new note fully overlaps an existing note */
|
||||||
switch (insert_merge_policy()) {
|
switch (insert_merge_policy()) {
|
||||||
case InsertMergeReplace:
|
case InsertMergeReplace:
|
||||||
|
|
@ -1029,16 +1074,22 @@ MidiModel::resolve_overlaps_unlocked (const NotePtr note, set<NotePtr>* removed)
|
||||||
for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
|
for (set<NotePtr>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
|
||||||
remove_note_unlocked (*i);
|
remove_note_unlocked (*i);
|
||||||
|
|
||||||
if (removed) {
|
if (cmd) {
|
||||||
removed->insert (*i);
|
cmd->side_effect_remove (*i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_note_time) {
|
if (set_note_time) {
|
||||||
|
if (cmd) {
|
||||||
|
cmd->change (note, DiffCommand::StartTime, note_time);
|
||||||
|
}
|
||||||
note->set_time (note_time);
|
note->set_time (note_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_note_length) {
|
if (set_note_length) {
|
||||||
|
if (cmd) {
|
||||||
|
cmd->change (note, DiffCommand::Length, note_length);
|
||||||
|
}
|
||||||
note->set_length (note_length);
|
note->set_length (note_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ public:
|
||||||
const NotePtr& ignore_this_note) const;
|
const NotePtr& ignore_this_note) const;
|
||||||
bool contains (const NotePtr& ev) const;
|
bool contains (const NotePtr& ev) const;
|
||||||
|
|
||||||
bool add_note_unlocked (const NotePtr note, std::set<NotePtr>* removed = 0);
|
bool add_note_unlocked (const NotePtr note, void* arg = 0);
|
||||||
void remove_note_unlocked(const constNotePtr note);
|
void remove_note_unlocked(const constNotePtr note);
|
||||||
|
|
||||||
uint8_t lowest_note() const { return _lowest_note; }
|
uint8_t lowest_note() const { return _lowest_note; }
|
||||||
|
|
@ -250,7 +250,7 @@ protected:
|
||||||
mutable Glib::RWLock _lock;
|
mutable Glib::RWLock _lock;
|
||||||
bool _writing;
|
bool _writing;
|
||||||
|
|
||||||
virtual int resolve_overlaps_unlocked (const NotePtr, std::set<NotePtr>* removed = 0) {
|
virtual int resolve_overlaps_unlocked (const NotePtr, void* arg = 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -580,15 +580,14 @@ Sequence<Time>::end_write (bool delete_stuck)
|
||||||
|
|
||||||
template<typename Time>
|
template<typename Time>
|
||||||
bool
|
bool
|
||||||
Sequence<Time>::add_note_unlocked(const NotePtr note,
|
Sequence<Time>::add_note_unlocked(const NotePtr note, void* arg)
|
||||||
set<NotePtr >* removed)
|
|
||||||
{
|
{
|
||||||
/* This is the core method to add notes to a Sequence
|
/* This is the core method to add notes to a Sequence
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 add note %2 @ %3\n", this, (int)note->note(), note->time()));
|
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 add note %2 @ %3\n", this, (int)note->note(), note->time()));
|
||||||
|
|
||||||
if (resolve_overlaps_unlocked (note, removed)) {
|
if (resolve_overlaps_unlocked (note, arg)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue