Fix alignment of automation paste.

Preserve alignment between notes and control points when doing an internal
copy/paste of both.

Relative alignment between points on multi-paste is still not preserved.  Both
behaviours here are actually useful, perhaps a modifier...
This commit is contained in:
David Robillard 2014-12-07 23:16:42 -05:00
parent 116722f182
commit 4f8714a038
3 changed files with 35 additions and 13 deletions

View file

@ -277,6 +277,9 @@ AutomationStreamView::clear ()
void void
AutomationStreamView::get_selectables (framepos_t start, framepos_t end, double botfrac, double topfrac, list<Selectable*>& results) AutomationStreamView::get_selectables (framepos_t start, framepos_t end, double botfrac, double topfrac, list<Selectable*>& results)
{ {
if (!_trackview.editor().internal_editing()) {
return; // TODO: selection of automation regions
}
for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) { for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
AutomationRegionView* arv = dynamic_cast<AutomationRegionView*> (*i); AutomationRegionView* arv = dynamic_cast<AutomationRegionView*> (*i);
assert (arv); assert (arv);

View file

@ -1127,7 +1127,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void cut_copy (Editing::CutCopyOp); void cut_copy (Editing::CutCopyOp);
bool can_cut_copy () const; bool can_cut_copy () const;
void cut_copy_points (Editing::CutCopyOp); void cut_copy_points (Editing::CutCopyOp, Evoral::MusicalTime earliest=Evoral::MusicalTime(), bool midi=false);
void cut_copy_regions (Editing::CutCopyOp, RegionSelection&); void cut_copy_regions (Editing::CutCopyOp, RegionSelection&);
void cut_copy_ranges (Editing::CutCopyOp); void cut_copy_ranges (Editing::CutCopyOp);
void cut_copy_midi (Editing::CutCopyOp); void cut_copy_midi (Editing::CutCopyOp);

View file

@ -84,6 +84,7 @@
#include "mixer_strip.h" #include "mixer_strip.h"
#include "mouse_cursors.h" #include "mouse_cursors.h"
#include "normalize_dialog.h" #include "normalize_dialog.h"
#include "note.h"
#include "paste_context.h" #include "paste_context.h"
#include "patch_change_dialog.h" #include "patch_change_dialog.h"
#include "quantize_dialog.h" #include "quantize_dialog.h"
@ -3912,7 +3913,7 @@ struct AutomationRecord {
* @param op Operation (Cut, Copy or Clear) * @param op Operation (Cut, Copy or Clear)
*/ */
void void
Editor::cut_copy_points (CutCopyOp op) Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::MusicalTime earliest, bool midi)
{ {
if (selection->points.empty ()) { if (selection->points.empty ()) {
return; return;
@ -3941,31 +3942,45 @@ Editor::cut_copy_points (CutCopyOp op)
/* This operation will involve putting things in the cut buffer, so create an empty /* This operation will involve putting things in the cut buffer, so create an empty
ControlList for each of our source lists to put the cut buffer data in. ControlList for each of our source lists to put the cut buffer data in.
*/ */
framepos_t start = std::numeric_limits<framepos_t>::max();
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) { for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor()); i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
/* Calculate earliest start position of any point in selection. */
start = std::min(start, i->second.line->session_position(i->first->begin()));
} }
/* Add all selected points to the relevant copy ControlLists */ /* Add all selected points to the relevant copy ControlLists */
framepos_t start = std::numeric_limits<framepos_t>::max();
for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) { for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
boost::shared_ptr<AutomationList> al = (*i)->line().the_list(); boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
AutomationList::const_iterator j = (*i)->model (); AutomationList::const_iterator j = (*i)->model();
lists[al].copy->fast_simple_add ((*j)->when, (*j)->value); lists[al].copy->fast_simple_add ((*j)->when, (*j)->value);
if (midi) {
/* Update earliest MIDI start time in beats */
earliest = std::min(earliest, Evoral::MusicalTime((*j)->when));
} else {
/* Update earliest session start time in frames */
start = std::min(start, (*i)->line().session_position(j));
}
} }
/* Snap start time backwards, so copy/paste is snap aligned. */ /* Snap start time backwards, so copy/paste is snap aligned. */
snap_to(start, RoundDownMaybe); if (midi) {
if (earliest == Evoral::MusicalTime::max()) {
earliest = Evoral::MusicalTime(); // Weird... don't offset
}
earliest.round_down_to_beat();
} else {
if (start == std::numeric_limits<double>::max()) {
start = 0; // Weird... don't offset
}
snap_to(start, RoundDownMaybe);
}
const double line_offset = midi ? earliest.to_double() : start;
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) { for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
/* Correct this copy list so that it is relative to the earliest /* Correct this copy list so that it is relative to the earliest
start time, so relative ordering between points is preserved start time, so relative ordering between points is preserved
when copying from several lists. */ when copying from several lists and the paste starts at the
const AutomationLine* line = i->second.line; earliest copied piece of data. */
const double line_offset = line->time_converter().from(start);
for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) { for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
(*j)->when -= line_offset; (*j)->when -= line_offset;
} }
@ -4003,9 +4018,13 @@ Editor::cut_copy_points (CutCopyOp op)
void void
Editor::cut_copy_midi (CutCopyOp op) Editor::cut_copy_midi (CutCopyOp op)
{ {
Evoral::MusicalTime earliest = Evoral::MusicalTime::max();
for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) { for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i); MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
if (mrv) { if (mrv) {
if (!mrv->selection().empty()) {
earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
}
mrv->cut_copy_clear (op); mrv->cut_copy_clear (op);
/* XXX: not ideal, as there may be more than one track involved in the selection */ /* XXX: not ideal, as there may be more than one track involved in the selection */
@ -4014,7 +4033,7 @@ Editor::cut_copy_midi (CutCopyOp op)
} }
if (!selection->points.empty()) { if (!selection->points.empty()) {
cut_copy_points (op); cut_copy_points (op, earliest, true);
if (op == Cut || op == Delete) { if (op == Cut || op == Delete) {
selection->clear_points (); selection->clear_points ();
} }