tempo mapping: various tweaks and improvements to workflow/Ux

This commit is contained in:
Paul Davis 2023-04-13 17:47:34 -06:00
parent f29557348e
commit c10b265333
3 changed files with 61 additions and 53 deletions

View file

@ -3476,12 +3476,13 @@ BBTMarkerDrag::aborted (bool moved)
/******************************************************************************/ /******************************************************************************/
MappingLinearDrag::MappingLinearDrag (Editor* e, ArdourCanvas::Item* i, Temporal::TempoMap::WritableSharedPtr& wmap) MappingLinearDrag::MappingLinearDrag (Editor* e, ArdourCanvas::Item* i, Temporal::TempoMap::WritableSharedPtr& wmap, TempoPoint& tp, TempoPoint& ap, XMLNode& before)
: Drag (e, i, Temporal::BeatTime) : Drag (e, i, Temporal::BeatTime)
, _tempo (0) , _tempo (tp)
, _after (ap)
, _grab_bpm (0) , _grab_bpm (0)
, map (wmap) , map (wmap)
, _before_state (0) , _before_state (&before)
, _drag_valid (true) , _drag_valid (true)
{ {
DEBUG_TRACE (DEBUG::Drags, "New MappingLinearDrag\n"); DEBUG_TRACE (DEBUG::Drags, "New MappingLinearDrag\n");
@ -3493,33 +3494,23 @@ MappingLinearDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{ {
Drag::start_grab (event, cursor); Drag::start_grab (event, cursor);
_tempo = const_cast<TempoPoint*> (&map->metric_at (raw_grab_time().beats()).tempo()); _grab_bpm = _tempo.note_types_per_minute();
_grab_bpm = _tempo->note_types_per_minute();
if (adjusted_current_time (event, false) <= _tempo->time()) {
std::cerr << "too early for " << *_tempo << std::endl;
_drag_valid = false;
return;
}
ostringstream sstr; ostringstream sstr;
if (_tempo->continuing()) { if (_tempo.continuing()) {
TempoPoint const * prev = map->previous_tempo (*_tempo); TempoPoint const * prev = map->previous_tempo (_tempo);
if (prev) { if (prev) {
sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n"; sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
} }
} }
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute(); sstr << "start: " << fixed << setprecision(3) << _tempo.note_types_per_minute();
show_verbose_cursor_text (sstr.str()); show_verbose_cursor_text (sstr.str());
} }
void void
MappingLinearDrag::setup_pointer_offset () MappingLinearDrag::setup_pointer_offset ()
{ {
/* get current state */
_before_state = &map->get_state();
Beats 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());
@ -3539,14 +3530,26 @@ MappingLinearDrag::motion (GdkEvent* event, bool first_move)
return; return;
} }
if (first_move) { const double pixel_distance = current_pointer_x() - grab_x();
_editor->begin_reversible_command (_("map tempo")); const double spp = _editor->get_current_zoom();
const double scaling_factor = 0.4 * (spp / 1000.);
const double delta = scaling_factor * pixel_distance;
double new_bpm = std::min (300., std::max (3., _grab_bpm - delta));
Temporal::Tempo new_tempo (new_bpm, _tempo.note_type());
/* Change both the previous tempo and the one under the pointer */
map->change_tempo (_tempo, new_tempo);
/* if the user drags the last tempo, then _tempo and _focus are the
* same object.
*/
if (_after.sclock() != _tempo.sclock()) {
map->change_tempo (_after, new_tempo);
} }
double new_bpm = std::max (1.5, _grab_bpm - ((current_pointer_x() - grab_x()) / 5.0));
stringstream strs;
Temporal::Tempo new_tempo (new_bpm, _tempo->note_type());
map->change_tempo (*_tempo, new_tempo);
_editor->mid_tempo_change (Editor::MappingChanged); _editor->mid_tempo_change (Editor::MappingChanged);
} }
@ -3554,7 +3557,7 @@ void
MappingLinearDrag::finished (GdkEvent* event, bool movement_occurred) MappingLinearDrag::finished (GdkEvent* event, bool movement_occurred)
{ {
if (!_drag_valid) { if (!_drag_valid) {
_editor->abort_tempo_mapping (); aborted (false);
return; return;
} }
@ -3572,7 +3575,7 @@ MappingLinearDrag::finished (GdkEvent* event, bool movement_occurred)
XMLNode &after = map->get_state(); XMLNode &after = map->get_state();
_editor->session()->add_command (new Temporal::TempoCommand (_("move BBT point"), _before_state, &after)); _editor->session()->add_command (new Temporal::TempoCommand (_("stretch tempo"), _before_state, &after));
_editor->commit_reversible_command (); _editor->commit_reversible_command ();
/* 2nd argument means "update tempo map display after the new map is /* 2nd argument means "update tempo map display after the new map is
@ -3585,8 +3588,9 @@ MappingLinearDrag::finished (GdkEvent* event, bool movement_occurred)
} }
void void
MappingLinearDrag::aborted (bool moved) MappingLinearDrag::aborted (bool /* moved */)
{ {
_editor->abort_reversible_command ();
_editor->abort_tempo_mapping (); _editor->abort_tempo_mapping ();
} }
@ -3791,10 +3795,9 @@ MappingTwistDrag::motion (GdkEvent* event, bool first_move)
const double pixel_distance = last_pointer_x() - _drags->current_pointer_x(); const double pixel_distance = last_pointer_x() - _drags->current_pointer_x();
const double spp = _editor->get_current_zoom(); const double spp = _editor->get_current_zoom();
const double scaling_factor = 0.4 * (spp / 1000.); const double scaling_factor = 0.4 * (spp / 1500.);
delta += scaling_factor * pixel_distance; delta += scaling_factor * pixel_distance;
std::cerr << "pixels: " << pixel_distance << " @ " << spp << " spp SF " << scaling_factor << " => delta " << delta << std::endl;
map->twist_tempi (prev, focus, next, initial_npm + delta); map->twist_tempi (prev, focus, next, initial_npm + delta);
_editor->mid_tempo_change (Editor::MappingChanged); _editor->mid_tempo_change (Editor::MappingChanged);

View file

@ -915,7 +915,7 @@ private:
class MappingLinearDrag : public Drag class MappingLinearDrag : public Drag
{ {
public: public:
MappingLinearDrag (Editor *, ArdourCanvas::Item *, Temporal::TempoMap::WritableSharedPtr&); MappingLinearDrag (Editor *, ArdourCanvas::Item *, Temporal::TempoMap::WritableSharedPtr&, Temporal::TempoPoint&, Temporal::TempoPoint& after, XMLNode& before_state);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0); void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool); void motion (GdkEvent *, bool);
@ -933,7 +933,8 @@ public:
void setup_pointer_offset (); void setup_pointer_offset ();
private: private:
Temporal::TempoPoint* _tempo; Temporal::TempoPoint& _tempo;
Temporal::TempoPoint& _after;
double _grab_bpm; double _grab_bpm;
Temporal::TempoMap::WritableSharedPtr map; Temporal::TempoMap::WritableSharedPtr map;

View file

@ -2928,6 +2928,11 @@ Editor::choose_mapping_drag (ArdourCanvas::Item* item, GdkEvent* event)
return; return;
} }
if (_cursor_stack.empty() || _cursor_stack.back() != cursors()->time_fx) {
/* Not close enough to a beat line to start any mapping drag */
return;
}
Temporal::TempoMap::WritableSharedPtr map = begin_tempo_mapping (); Temporal::TempoMap::WritableSharedPtr map = begin_tempo_mapping ();
/* Decide between a tempo twist drag, which we do if the /* Decide between a tempo twist drag, which we do if the
@ -2941,33 +2946,20 @@ Editor::choose_mapping_drag (ArdourCanvas::Item* item, GdkEvent* event)
TempoPoint* after = const_cast<TempoPoint*> (map->next_tempo (tempo)); TempoPoint* after = const_cast<TempoPoint*> (map->next_tempo (tempo));
if (!after || dynamic_cast<MusicTimePoint*>(after)) { /* Create a new marker, or use the under the mouse */
/* Drag on the bar, not the cursor: just adjust tempo up or
* down.
*/
_drags->set (new MappingLinearDrag (this, item, map), event);
return;
}
/* Use cursor state to determine if we are close enough to a beat line
* to do a twist. We computed that in the motion handler.
*/
if (_cursor_stack.empty() || _cursor_stack.back() != cursors()->time_fx) {
return;
}
BBT_Argument bbt = map->bbt_at (pointer_time);
bbt = BBT_Argument (bbt.reference(), bbt.round_to_beat ());
XMLNode* before_state = &map->get_state();
TempoPoint* before; TempoPoint* before;
TempoPoint* focus; TempoPoint* focus;
/* Reversible command starts here, must be ended/aborted in drag */ bool stretch = false;
begin_reversible_command (_("map tempo/twist")); if (!after || dynamic_cast<MusicTimePoint*>(after)) {
XMLNode* before_state = &map->get_state(); stretch = true;
}
BBT_Argument bbt = map->bbt_at (pointer_time);
bbt = BBT_Argument (bbt.reference(), bbt.round_to_beat ());
if (tempo.bbt() < bbt) { if (tempo.bbt() < bbt) {
@ -2988,12 +2980,24 @@ Editor::choose_mapping_drag (ArdourCanvas::Item* item, GdkEvent* event)
before = const_cast<TempoPoint*> (map->previous_tempo (tempo)); before = const_cast<TempoPoint*> (map->previous_tempo (tempo));
if (!before) { if (!before) {
delete before_state;
return; return;
} }
focus = &tempo; focus = &tempo;
} }
_drags->set (new MappingTwistDrag (this, item, map, *before, *focus, *after, *before_state), event); /* Reversible commands start here, must be ended/aborted in drag */
if (stretch) {
begin_reversible_command (_("map tempo/stretch"));
std::cerr << "STRETCH\n";
_drags->set (new MappingLinearDrag (this, item, map, tempo, *focus, *before_state), event);
return;
}
std::cerr << "TWIST\n";
begin_reversible_command (_("map tempo/twist"));
_drags->set (new MappingTwistDrag (this, item, map, *before, *focus, *after, *before_state), event);
} }