new tempo mapping drag objects (API and implementation)
This commit is contained in:
parent
6d5273e514
commit
77cefd7721
@ -2523,6 +2523,8 @@ private:
|
|||||||
|
|
||||||
void remove_gap_marker_callback (Temporal::timepos_t at, Temporal::timecnt_t distance);
|
void remove_gap_marker_callback (Temporal::timepos_t at, Temporal::timecnt_t distance);
|
||||||
|
|
||||||
|
void choose_mapping_drag (ArdourCanvas::Item*, GdkEvent*);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Temporal::TimeDomain drag_time_domain (T* thing_with_time_domain) {
|
Temporal::TimeDomain drag_time_domain (T* thing_with_time_domain) {
|
||||||
return thing_with_time_domain ? thing_with_time_domain->time_domain() : Temporal::AudioTime;
|
return thing_with_time_domain ? thing_with_time_domain->time_domain() : Temporal::AudioTime;
|
||||||
@ -2538,7 +2540,9 @@ private:
|
|||||||
friend class RegionDrag;
|
friend class RegionDrag;
|
||||||
friend class RegionMoveDrag;
|
friend class RegionMoveDrag;
|
||||||
friend class TrimDrag;
|
friend class TrimDrag;
|
||||||
friend class MappingDrag;
|
friend class MappingTwistDrag;
|
||||||
|
friend class MappingLinearDrag;
|
||||||
|
friend class MappingStretchDrag;
|
||||||
friend class MeterMarkerDrag;
|
friend class MeterMarkerDrag;
|
||||||
friend class BBTMarkerDrag;
|
friend class BBTMarkerDrag;
|
||||||
friend class TempoMarkerDrag;
|
friend class TempoMarkerDrag;
|
||||||
|
@ -261,6 +261,7 @@ Editor::initialize_canvas ()
|
|||||||
|
|
||||||
tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), tempo_bar, TempoBarItem, "tempo bar"));
|
tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), tempo_bar, TempoBarItem, "tempo bar"));
|
||||||
mapping_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), mapping_bar, MappingBarItem, "mapping bar"));
|
mapping_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), mapping_bar, MappingBarItem, "mapping bar"));
|
||||||
|
mapping_cursor->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), mapping_cursor, MappingCursorItem, "mapping cursor"));
|
||||||
meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), meter_bar, MeterBarItem, "meter bar"));
|
meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), meter_bar, MeterBarItem, "meter bar"));
|
||||||
marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), marker_bar, MarkerBarItem, "marker bar"));
|
marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), marker_bar, MarkerBarItem, "marker bar"));
|
||||||
cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), cd_marker_bar, CdMarkerBarItem, "cd marker bar"));
|
cd_marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_bar_event), cd_marker_bar, CdMarkerBarItem, "cd marker bar"));
|
||||||
|
@ -3331,7 +3331,6 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
|
TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
|
||||||
{
|
{
|
||||||
@ -3475,32 +3474,34 @@ BBTMarkerDrag::aborted (bool moved)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MappingDrag::MappingDrag (Editor* e, ArdourCanvas::Item* i)
|
/******************************************************************************/
|
||||||
|
|
||||||
|
MappingLinearDrag::MappingLinearDrag (Editor* e, ArdourCanvas::Item* i, Temporal::TempoMap::WritableSharedPtr& wmap)
|
||||||
: Drag (e, i, Temporal::BeatTime)
|
: Drag (e, i, Temporal::BeatTime)
|
||||||
, _tempo (0)
|
, _tempo (0)
|
||||||
|
, _grab_bpm (0)
|
||||||
|
, map (wmap)
|
||||||
, _before_state (0)
|
, _before_state (0)
|
||||||
, _drag_valid (true)
|
, _drag_valid (true)
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::Drags, "New MappingDrag\n");
|
DEBUG_TRACE (DEBUG::Drags, "New MappingLinearDrag\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MappingDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
MappingLinearDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
||||||
{
|
{
|
||||||
map = _editor->begin_tempo_mapping ();
|
|
||||||
|
|
||||||
Drag::start_grab (event, cursor);
|
Drag::start_grab (event, cursor);
|
||||||
|
|
||||||
_tempo = const_cast<TempoPoint*> (&map->metric_at (raw_grab_time().beats()).tempo());
|
_tempo = const_cast<TempoPoint*> (&map->metric_at (raw_grab_time().beats()).tempo());
|
||||||
|
_grab_bpm = _tempo->note_types_per_minute();
|
||||||
|
|
||||||
if (adjusted_current_time (event, false) <= _tempo->time()) {
|
if (adjusted_current_time (event, false) <= _tempo->time()) {
|
||||||
|
std::cerr << "too early for " << *_tempo << std::endl;
|
||||||
_drag_valid = false;
|
_drag_valid = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_editor->tempo_curve_selected (_tempo, true);
|
|
||||||
|
|
||||||
ostringstream sstr;
|
ostringstream sstr;
|
||||||
if (_tempo->continuing()) {
|
if (_tempo->continuing()) {
|
||||||
TempoPoint const * prev = map->previous_tempo (*_tempo);
|
TempoPoint const * prev = map->previous_tempo (*_tempo);
|
||||||
@ -3514,12 +3515,12 @@ MappingDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MappingDrag::setup_pointer_offset ()
|
MappingLinearDrag::setup_pointer_offset ()
|
||||||
{
|
{
|
||||||
/* get current state */
|
/* get current state */
|
||||||
_before_state = &map->get_state();
|
_before_state = &map->get_state();
|
||||||
|
|
||||||
_grab_qn = max (Beats(), raw_grab_time().beats());
|
Beats grab_qn = max (Beats(), raw_grab_time().beats());
|
||||||
|
|
||||||
uint32_t divisions = _editor->get_grid_beat_divisions (_editor->grid_type());
|
uint32_t divisions = _editor->get_grid_beat_divisions (_editor->grid_type());
|
||||||
|
|
||||||
@ -3527,52 +3528,33 @@ MappingDrag::setup_pointer_offset ()
|
|||||||
divisions = 4;
|
divisions = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
_grab_qn = _grab_qn.round_to_subdivision (divisions, Temporal::RoundDownAlways);
|
grab_qn = grab_qn.round_to_subdivision (divisions, Temporal::RoundDownAlways);
|
||||||
_pointer_offset = timepos_t (_grab_qn).distance (raw_grab_time());
|
_pointer_offset = timepos_t (grab_qn).distance (raw_grab_time());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MappingDrag::motion (GdkEvent* event, bool first_move)
|
MappingLinearDrag::motion (GdkEvent* event, bool first_move)
|
||||||
{
|
{
|
||||||
if (!_drag_valid) {
|
if (!_drag_valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first_move) {
|
if (first_move) {
|
||||||
_editor->begin_reversible_command (_("stretch tempo"));
|
_editor->begin_reversible_command (_("map tempo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
timepos_t pf;
|
double new_bpm = std::max (1.5, _grab_bpm - ((current_pointer_x() - grab_x()) / 5.0));
|
||||||
|
stringstream strs;
|
||||||
if (_editor->grid_musical()) {
|
Temporal::Tempo new_tempo (new_bpm, _tempo->note_type());
|
||||||
pf = adjusted_current_time (event, false);
|
map->change_tempo (*_tempo, new_tempo);
|
||||||
} else {
|
_editor->mid_tempo_change (Editor::MappingChanged);
|
||||||
pf = adjusted_current_time (event);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ArdourKeyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
|
|
||||||
/* adjust previous tempo to match pointer sample */
|
|
||||||
map->stretch_tempo (_tempo, timepos_t (_grab_qn).samples(), pf.samples(), _grab_qn, pf.beats());
|
|
||||||
_editor->mid_tempo_change (Editor::BBTChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
ostringstream sstr;
|
|
||||||
if (_tempo->continuing()) {
|
|
||||||
TempoPoint const * prev = map->previous_tempo (*_tempo);
|
|
||||||
if (prev) {
|
|
||||||
_editor->tempo_curve_selected (prev, true);
|
|
||||||
sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
|
|
||||||
show_verbose_cursor_text (sstr.str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MappingDrag::finished (GdkEvent* event, bool movement_occurred)
|
MappingLinearDrag::finished (GdkEvent* event, bool movement_occurred)
|
||||||
{
|
{
|
||||||
if (!_drag_valid) {
|
if (!_drag_valid) {
|
||||||
_editor->abort_tempo_map_edit ();
|
_editor->abort_tempo_mapping ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3580,21 +3562,12 @@ MappingDrag::finished (GdkEvent* event, bool movement_occurred)
|
|||||||
|
|
||||||
/* click, no drag */
|
/* click, no drag */
|
||||||
|
|
||||||
_editor->abort_tempo_map_edit ();
|
_editor->abort_tempo_mapping ();
|
||||||
|
_editor->session()->request_locate (grab_sample(), false, _was_rolling ? MustRoll : RollIfAppropriate);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
_editor->tempo_curve_selected (_tempo, false);
|
|
||||||
|
|
||||||
if (_tempo->continuing()) {
|
|
||||||
|
|
||||||
TempoPoint const * prev_tempo = map->previous_tempo (*_tempo);
|
|
||||||
|
|
||||||
if (prev_tempo) {
|
|
||||||
_editor->tempo_curve_selected (prev_tempo, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XMLNode &after = map->get_state();
|
XMLNode &after = map->get_state();
|
||||||
@ -3612,7 +3585,253 @@ MappingDrag::finished (GdkEvent* event, bool movement_occurred)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MappingDrag::aborted (bool moved)
|
MappingLinearDrag::aborted (bool moved)
|
||||||
|
{
|
||||||
|
_editor->abort_tempo_mapping ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
MappingStretchDrag::MappingStretchDrag (Editor* e, ArdourCanvas::Item* i, Temporal::TempoMap::WritableSharedPtr& wmap)
|
||||||
|
: Drag (e, i, Temporal::BeatTime)
|
||||||
|
, _tempo (0)
|
||||||
|
, map (wmap)
|
||||||
|
, _before_state (0)
|
||||||
|
, _drag_valid (true)
|
||||||
|
{
|
||||||
|
DEBUG_TRACE (DEBUG::Drags, "New MappingStretchDrag\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingStretchDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
||||||
|
{
|
||||||
|
Drag::start_grab (event, cursor);
|
||||||
|
|
||||||
|
_tempo = const_cast<TempoPoint*> (&map->metric_at (raw_grab_time().beats()).tempo());
|
||||||
|
|
||||||
|
if (adjusted_current_time (event, false) <= _tempo->time()) {
|
||||||
|
std::cerr << "too early for " << *_tempo << std::endl;
|
||||||
|
_drag_valid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ostringstream sstr;
|
||||||
|
if (_tempo->continuing()) {
|
||||||
|
TempoPoint const * prev = map->previous_tempo (*_tempo);
|
||||||
|
if (prev) {
|
||||||
|
sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
|
||||||
|
show_verbose_cursor_text (sstr.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingStretchDrag::setup_pointer_offset ()
|
||||||
|
{
|
||||||
|
/* get current state */
|
||||||
|
_before_state = &map->get_state();
|
||||||
|
|
||||||
|
_grab_qn = max (Beats(), raw_grab_time().beats());
|
||||||
|
|
||||||
|
uint32_t divisions = _editor->get_grid_beat_divisions (_editor->grid_type());
|
||||||
|
|
||||||
|
if (divisions == 0) {
|
||||||
|
divisions = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
_grab_qn = _grab_qn.round_to_subdivision (divisions, Temporal::RoundDownAlways);
|
||||||
|
_pointer_offset = timepos_t (_grab_qn).distance (raw_grab_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingStretchDrag::motion (GdkEvent* event, bool first_move)
|
||||||
|
{
|
||||||
|
if (!_drag_valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first_move) {
|
||||||
|
_editor->begin_reversible_command (_("map tempo w/stretch"));
|
||||||
|
}
|
||||||
|
|
||||||
|
timepos_t pf;
|
||||||
|
|
||||||
|
if (_editor->grid_musical()) {
|
||||||
|
pf = adjusted_current_time (event, false);
|
||||||
|
} else {
|
||||||
|
pf = adjusted_current_time (event);
|
||||||
|
}
|
||||||
|
|
||||||
|
map->stretch_tempo (_tempo, timepos_t (_grab_qn).samples(), pf.samples(), _grab_qn, pf.beats());
|
||||||
|
_editor->mapping_cursor->set_position (Duple (_editor->time_to_pixel_unrounded (pf), _editor->mapping_cursor->position().y));
|
||||||
|
_editor->mid_tempo_change (Editor::MappingChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingStretchDrag::finished (GdkEvent* event, bool movement_occurred)
|
||||||
|
{
|
||||||
|
if (!movement_occurred) {
|
||||||
|
|
||||||
|
/* click, no drag */
|
||||||
|
|
||||||
|
_editor->abort_tempo_mapping ();
|
||||||
|
_editor->session()->request_locate (grab_sample(), false, _was_rolling ? MustRoll : RollIfAppropriate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_drag_valid) {
|
||||||
|
_editor->abort_tempo_mapping ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLNode &after = map->get_state();
|
||||||
|
|
||||||
|
_editor->session()->add_command (new Temporal::TempoCommand (_("move BBT point"), _before_state, &after));
|
||||||
|
_editor->commit_reversible_command ();
|
||||||
|
|
||||||
|
/* 2nd argument means "update tempo map display after the new map is
|
||||||
|
* installed. We need to do this because the code above has not
|
||||||
|
* actually changed anything about how tempo is displayed, it simply
|
||||||
|
* modified the map.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_editor->commit_tempo_mapping (map);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingStretchDrag::aborted (bool moved)
|
||||||
|
{
|
||||||
|
_editor->abort_tempo_mapping ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
MappingTwistDrag::MappingTwistDrag (Editor* e, ArdourCanvas::Item* i, Temporal::TempoMap::WritableSharedPtr& wmap,
|
||||||
|
TempoPoint& prv,
|
||||||
|
TempoPoint& fcus,
|
||||||
|
TempoPoint& nxt)
|
||||||
|
: Drag (e, i, Temporal::BeatTime)
|
||||||
|
, prev (prv)
|
||||||
|
, focus (fcus)
|
||||||
|
, next (nxt)
|
||||||
|
, map (wmap)
|
||||||
|
, direction (0.)
|
||||||
|
, delta (0.)
|
||||||
|
, _before_state (0)
|
||||||
|
, _drag_valid (true)
|
||||||
|
{
|
||||||
|
DEBUG_TRACE (DEBUG::Drags, "New MappingTwistDrag\n");
|
||||||
|
initial_npm = focus.note_types_per_minute ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
||||||
|
{
|
||||||
|
Drag::start_grab (event, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingTwistDrag::setup_pointer_offset ()
|
||||||
|
{
|
||||||
|
/* get current state */
|
||||||
|
_before_state = &map->get_state();
|
||||||
|
|
||||||
|
Beats grab_qn = max (Beats(), raw_grab_time().beats());
|
||||||
|
|
||||||
|
uint32_t divisions = _editor->get_grid_beat_divisions (_editor->grid_type());
|
||||||
|
|
||||||
|
if (divisions == 0) {
|
||||||
|
divisions = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
grab_qn = grab_qn.round_to_subdivision (divisions, Temporal::RoundDownAlways);
|
||||||
|
_pointer_offset = timepos_t (grab_qn).distance (raw_grab_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingTwistDrag::motion (GdkEvent* event, bool first_move)
|
||||||
|
{
|
||||||
|
if (first_move) {
|
||||||
|
_editor->begin_reversible_command (_("map tempo w/twist"));
|
||||||
|
}
|
||||||
|
|
||||||
|
samplepos_t mouse_pos;
|
||||||
|
|
||||||
|
if (_editor->grid_musical()) {
|
||||||
|
mouse_pos = adjusted_current_time (event, false).samples();
|
||||||
|
} else {
|
||||||
|
mouse_pos = adjusted_current_time (event).samples();
|
||||||
|
}
|
||||||
|
|
||||||
|
double pixels_moved = _drags->current_pointer_x() - last_pointer_x();
|
||||||
|
|
||||||
|
if (_drags->current_pointer_x() < last_pointer_x()) {
|
||||||
|
if (direction < 0.) {
|
||||||
|
direction = 1.;
|
||||||
|
initial_npm += delta;
|
||||||
|
delta = 0.;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (direction >= 0.) {
|
||||||
|
direction = -1.;
|
||||||
|
initial_npm += delta;
|
||||||
|
delta = 0.;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_drags->current_pointer_time() >= timepos_t::from_superclock (next.sclock())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_drags->current_pointer_time() <= timepos_t::from_superclock (prev.sclock())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX needs to scale somehow with zoom level */
|
||||||
|
|
||||||
|
delta += 0.75 * (last_pointer_x() - _drags->current_pointer_x());
|
||||||
|
|
||||||
|
map->twist_tempi (prev, focus, next, initial_npm + delta);
|
||||||
|
|
||||||
|
// _editor->mapping_cursor->set_position (Duple (_editor->sample_to_pixel_unrounded (mouse_pos), _editor->mapping_cursor->position().y));
|
||||||
|
_editor->mid_tempo_change (Editor::MappingChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingTwistDrag::finished (GdkEvent* event, bool movement_occurred)
|
||||||
|
{
|
||||||
|
if (!movement_occurred) {
|
||||||
|
|
||||||
|
/* click, no drag */
|
||||||
|
|
||||||
|
_editor->abort_tempo_mapping ();
|
||||||
|
_editor->session()->request_locate (grab_sample(), false, _was_rolling ? MustRoll : RollIfAppropriate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_drag_valid) {
|
||||||
|
_editor->abort_tempo_mapping ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLNode &after = map->get_state();
|
||||||
|
|
||||||
|
_editor->session()->add_command (new Temporal::TempoCommand (_("move BBT point"), _before_state, &after));
|
||||||
|
_editor->commit_reversible_command ();
|
||||||
|
|
||||||
|
/* 2nd argument means "update tempo map display after the new map is
|
||||||
|
* installed. We need to do this because the code above has not
|
||||||
|
* actually changed anything about how tempo is displayed, it simply
|
||||||
|
* modified the map.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_editor->commit_tempo_mapping (map);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MappingTwistDrag::aborted (bool moved)
|
||||||
{
|
{
|
||||||
_editor->abort_tempo_mapping ();
|
_editor->abort_tempo_mapping ();
|
||||||
}
|
}
|
||||||
@ -3634,8 +3853,6 @@ TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|||||||
{
|
{
|
||||||
Drag::start_grab (event, cursor);
|
Drag::start_grab (event, cursor);
|
||||||
|
|
||||||
map = _editor->begin_tempo_mapping ();
|
|
||||||
|
|
||||||
/* Get the tempo point that starts this section */
|
/* Get the tempo point that starts this section */
|
||||||
|
|
||||||
_tempo = const_cast<TempoPoint*> (&map->tempo_at (raw_grab_time()));
|
_tempo = const_cast<TempoPoint*> (&map->tempo_at (raw_grab_time()));
|
||||||
@ -3685,7 +3902,7 @@ TempoTwistDrag::motion (GdkEvent* event, bool first_move)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* adjust this and the next tempi to match pointer sample */
|
/* adjust this and the next tempi to match pointer sample */
|
||||||
map->twist_tempi (_tempo, adjusted_time (grab_time(), 0, false).samples(), mouse_pos);
|
// map->twist_tempi (_tempo, adjusted_time (grab_time(), 0, false).samples(), mouse_pos);
|
||||||
|
|
||||||
ostringstream sstr;
|
ostringstream sstr;
|
||||||
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
|
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
|
||||||
|
@ -913,12 +913,10 @@ private:
|
|||||||
XMLNode* _before_state;
|
XMLNode* _before_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MappingLinearDrag : public Drag
|
||||||
/** BBT Ruler drag */
|
|
||||||
class MappingDrag : public Drag
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MappingDrag (Editor *, ArdourCanvas::Item *);
|
MappingLinearDrag (Editor *, ArdourCanvas::Item *, Temporal::TempoMap::WritableSharedPtr&);
|
||||||
|
|
||||||
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
|
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
|
||||||
void motion (GdkEvent *, bool);
|
void motion (GdkEvent *, bool);
|
||||||
@ -936,14 +934,82 @@ public:
|
|||||||
void setup_pointer_offset ();
|
void setup_pointer_offset ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Temporal::Beats _grab_qn;
|
|
||||||
Temporal::TempoPoint* _tempo;
|
Temporal::TempoPoint* _tempo;
|
||||||
|
double _grab_bpm;
|
||||||
Temporal::TempoMap::WritableSharedPtr map;
|
Temporal::TempoMap::WritableSharedPtr map;
|
||||||
|
|
||||||
XMLNode* _before_state;
|
XMLNode* _before_state;
|
||||||
bool _drag_valid;
|
bool _drag_valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MappingStretchDrag : public Drag
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MappingStretchDrag (Editor *, ArdourCanvas::Item *, Temporal::TempoMap::WritableSharedPtr&);
|
||||||
|
|
||||||
|
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
|
||||||
|
void motion (GdkEvent *, bool);
|
||||||
|
void finished (GdkEvent *, bool);
|
||||||
|
void aborted (bool);
|
||||||
|
|
||||||
|
bool allow_vertical_autoscroll () const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool y_movement_matters () const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_pointer_offset ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Temporal::TempoPoint* _tempo;
|
||||||
|
Temporal::TempoMap::WritableSharedPtr map;
|
||||||
|
Temporal::Beats _grab_qn;
|
||||||
|
|
||||||
|
XMLNode* _before_state;
|
||||||
|
bool _drag_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MappingTwistDrag : public Drag
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MappingTwistDrag (Editor *, ArdourCanvas::Item *, Temporal::TempoMap::WritableSharedPtr&,
|
||||||
|
Temporal::TempoPoint& prev,
|
||||||
|
Temporal::TempoPoint& focus,
|
||||||
|
Temporal::TempoPoint& next);
|
||||||
|
|
||||||
|
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
|
||||||
|
void motion (GdkEvent *, bool);
|
||||||
|
void finished (GdkEvent *, bool);
|
||||||
|
void aborted (bool);
|
||||||
|
|
||||||
|
bool allow_vertical_autoscroll () const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool y_movement_matters () const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_pointer_offset ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Temporal::TempoPoint& prev;
|
||||||
|
Temporal::TempoPoint& focus;
|
||||||
|
Temporal::TempoPoint& next;
|
||||||
|
double _grab_bpm;
|
||||||
|
Temporal::TempoMap::WritableSharedPtr map;
|
||||||
|
|
||||||
|
double direction;
|
||||||
|
double delta;
|
||||||
|
double initial_npm;
|
||||||
|
|
||||||
|
XMLNode* _before_state;
|
||||||
|
bool _drag_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/** tempo curve twist drag */
|
/** tempo curve twist drag */
|
||||||
class TempoTwistDrag : public Drag
|
class TempoTwistDrag : public Drag
|
||||||
{
|
{
|
||||||
|
@ -817,14 +817,8 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MappingBarItem:
|
case MappingBarItem:
|
||||||
if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)
|
case MappingCursorItem:
|
||||||
&& !ArdourKeyboard::indicates_constraint (event->button.state)) {
|
choose_mapping_drag (item, event);
|
||||||
_drags->set (new CursorDrag (this, *_playhead_cursor, false), event);
|
|
||||||
} else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
|
|
||||||
_drags->set (new TempoTwistDrag (this, item), event);
|
|
||||||
} else if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
|
|
||||||
_drags->set (new MappingDrag (this, item), event);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case TempoBarItem:
|
case TempoBarItem:
|
||||||
@ -1698,6 +1692,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
|||||||
case CdMarkerBarItem:
|
case CdMarkerBarItem:
|
||||||
case TempoBarItem:
|
case TempoBarItem:
|
||||||
case MappingBarItem:
|
case MappingBarItem:
|
||||||
|
case MappingCursorItem:
|
||||||
case TempoCurveItem:
|
case TempoCurveItem:
|
||||||
case MeterBarItem:
|
case MeterBarItem:
|
||||||
case VideoBarItem:
|
case VideoBarItem:
|
||||||
@ -1823,7 +1818,9 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
|||||||
mouse_add_new_marker (where, Location::IsCueMarker);
|
mouse_add_new_marker (where, Location::IsCueMarker);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MappingBarItem:
|
case MappingBarItem:
|
||||||
|
case MappingCursorItem:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case TempoBarItem:
|
case TempoBarItem:
|
||||||
@ -1987,8 +1984,13 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
|
|||||||
choose_canvas_cursor_on_entry (item_type);
|
choose_canvas_cursor_on_entry (item_type);
|
||||||
|
|
||||||
switch (item_type) {
|
switch (item_type) {
|
||||||
|
case MappingCursorItem:
|
||||||
|
/* nothing to do ??? */
|
||||||
|
break;
|
||||||
|
|
||||||
case MappingBarItem:
|
case MappingBarItem:
|
||||||
mapping_cursor->show ();
|
mapping_cursor->show ();
|
||||||
|
mapping_cursor->raise_to_top ();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ControlPointItem:
|
case ControlPointItem:
|
||||||
@ -2136,6 +2138,10 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (item_type) {
|
switch (item_type) {
|
||||||
|
case MappingCursorItem:
|
||||||
|
/* ignore */
|
||||||
|
break;
|
||||||
|
|
||||||
case MappingBarItem:
|
case MappingBarItem:
|
||||||
mapping_cursor->hide ();
|
mapping_cursor->hide ();
|
||||||
break;
|
break;
|
||||||
@ -2324,9 +2330,11 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, bool from_aut
|
|||||||
//drags change the snapped_cursor location, because we are snapping the thing being dragged, not the actual mouse cursor
|
//drags change the snapped_cursor location, because we are snapping the thing being dragged, not the actual mouse cursor
|
||||||
return _drags->motion_handler (event, from_autoscroll);
|
return _drags->motion_handler (event, from_autoscroll);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
bool ignored;
|
bool ignored;
|
||||||
bool peaks_visible = false;
|
bool peaks_visible = false;
|
||||||
samplepos_t where;
|
samplepos_t where;
|
||||||
|
|
||||||
if (mouse_sample (where, ignored)) {
|
if (mouse_sample (where, ignored)) {
|
||||||
|
|
||||||
/* display peaks */
|
/* display peaks */
|
||||||
@ -2338,14 +2346,48 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, bool from_aut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the snapped_cursor shows where an operation (like Split) is going to occur */
|
|
||||||
timepos_t t (where);
|
timepos_t t (where);
|
||||||
snap_to_with_modifier (t, event);
|
bool move_snapped_cursor = true;
|
||||||
set_snapped_cursor_position (t);
|
|
||||||
|
|
||||||
if (item == mapping_bar) {
|
if (item == mapping_bar || item == mapping_cursor) {
|
||||||
double const new_pos = sample_to_pixel_unrounded (where);
|
|
||||||
mapping_cursor->set_center (ArdourCanvas::Duple (new_pos, mapping_cursor->center().y));
|
/* Snap to the nearest beat, and figure out how
|
||||||
|
* many pixels from the pointer cursor that is.
|
||||||
|
*/
|
||||||
|
|
||||||
|
timepos_t snapped = _snap_to_bbt (t, RoundNearest, SnapToGrid_Unscaled, GridTypeBeat);
|
||||||
|
const double unsnapped_pos = time_to_pixel_unrounded (t);
|
||||||
|
const double snapped_pos = time_to_pixel_unrounded (snapped);
|
||||||
|
|
||||||
|
if (std::abs (snapped_pos - unsnapped_pos) < 10 * UIConfiguration::instance().get_ui_scale()) {
|
||||||
|
|
||||||
|
/* Close to a beat, so snap the mapping
|
||||||
|
* cursor *and* the snapped cursor to
|
||||||
|
* the beat.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mapping_cursor->show ();
|
||||||
|
mapping_cursor->raise_to_top ();
|
||||||
|
|
||||||
|
mapping_cursor->set_position (ArdourCanvas::Duple (snapped_pos, mapping_cursor->position().y));
|
||||||
|
set_snapped_cursor_position (snapped);
|
||||||
|
|
||||||
|
move_snapped_cursor = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* Not close to a beat, hide the
|
||||||
|
* mapping cursor, then move the
|
||||||
|
* snapped cursor as normal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mapping_cursor->hide ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (move_snapped_cursor) {
|
||||||
|
snap_to_with_modifier (t, event);
|
||||||
|
set_snapped_cursor_position (t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2901,3 +2943,88 @@ Editor::get_pointer_position (double& x, double& y) const
|
|||||||
_track_canvas->get_pointer (px, py);
|
_track_canvas->get_pointer (px, py);
|
||||||
_track_canvas->window_to_canvas (px, py, x, y);
|
_track_canvas->window_to_canvas (px, py, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Editor::choose_mapping_drag (ArdourCanvas::Item* item, GdkEvent* event)
|
||||||
|
{
|
||||||
|
if (item != mapping_cursor && item != mapping_bar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Temporal::TempoMap::WritableSharedPtr map = begin_tempo_mapping ();
|
||||||
|
|
||||||
|
if (item == mapping_bar) {
|
||||||
|
/* Drag on the bar, not the cursor: just adjust tempo up or
|
||||||
|
* down.
|
||||||
|
*/
|
||||||
|
_drags->set (new MappingLinearDrag (this, item, map), event);
|
||||||
|
std::cerr << ":Linear\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decide between a tempo twist drag, which we do if the
|
||||||
|
* pointer is between two tempo markers, and a tempo stretch
|
||||||
|
* drag, which we do if the pointer is after the last tempo
|
||||||
|
* marker before the end of the map or a BBT Marker.
|
||||||
|
*/
|
||||||
|
|
||||||
|
timepos_t pointer_time (canvas_event_sample (event, nullptr, nullptr));
|
||||||
|
Temporal::TempoPoint& tempo = const_cast<Temporal::TempoPoint&>(map->tempo_at (pointer_time));
|
||||||
|
|
||||||
|
TempoPoint* after = const_cast<TempoPoint*> (map->next_tempo (tempo));
|
||||||
|
|
||||||
|
if (!after || dynamic_cast<MusicTimePoint*>(after)) {
|
||||||
|
/* This is the final tempo, or the next one is a BBT marker.
|
||||||
|
* No twisting, just stretch this one.
|
||||||
|
*/
|
||||||
|
std::cerr << "stretch!\n";
|
||||||
|
_drags->set (new MappingStretchDrag (this, item, map), event);
|
||||||
|
std::cerr << ":Stretch\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Pointer time: " << pointer_time << std::endl;
|
||||||
|
|
||||||
|
BBT_Argument bbt = map->bbt_at (pointer_time);
|
||||||
|
std::cerr << " bbt " << bbt << std::endl;
|
||||||
|
bbt = BBT_Argument (bbt.reference(), bbt.round_to_beat ());
|
||||||
|
std::cerr << "rounded to " << bbt << " vs. " << tempo.bbt() << std::endl;
|
||||||
|
|
||||||
|
TempoPoint* before;
|
||||||
|
TempoPoint* focus;
|
||||||
|
|
||||||
|
if (tempo.bbt() < bbt) {
|
||||||
|
|
||||||
|
/* Add a new tempo marker at the nearest beat point
|
||||||
|
(essentially the snapped grab point for the drag), so that
|
||||||
|
it becomes the middle one of three used by the twist tempo
|
||||||
|
operation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
before = const_cast<TempoPoint*> (&tempo);
|
||||||
|
Tempo copied_no_ramp (map->tempo_at (bbt));
|
||||||
|
TempoPoint& added = const_cast<TempoPoint&> (map->set_tempo (copied_no_ramp, bbt));
|
||||||
|
focus = &added;
|
||||||
|
std::cerr << "Focus will be " << *focus << std::endl;
|
||||||
|
reset_tempo_marks ();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
before = const_cast<TempoPoint*> (map->previous_tempo (tempo));
|
||||||
|
|
||||||
|
if (!before) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus = &tempo;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Prev: " << *before << std::endl;
|
||||||
|
std::cerr << "Focus: " << *focus << std::endl;
|
||||||
|
std::cerr << "Next: " << *after << std::endl;
|
||||||
|
|
||||||
|
std::cerr << "focus says next at " << focus->superclock_at (after->beats()) << " vs. " << after->sclock() << std::endl;
|
||||||
|
|
||||||
|
_drags->set (new MappingTwistDrag (this, item, map, *before, *focus, *after), event);
|
||||||
|
std::cerr << ":Twist\n";
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user