13
0

editing: a partiaally implemented version of TempoTwistDrag, to be completed.

This commit is contained in:
Paul Davis 2022-05-02 16:47:23 -06:00
parent 86e7a1b259
commit b5487415ec
6 changed files with 80 additions and 255 deletions

View File

@ -2492,6 +2492,7 @@ private:
friend class BBTRulerDrag; friend class BBTRulerDrag;
friend class MeterMarkerDrag; friend class MeterMarkerDrag;
friend class TempoMarkerDrag; friend class TempoMarkerDrag;
friend class TempoTwistDrag;
friend class CursorDrag; friend class CursorDrag;
friend class FadeInDrag; friend class FadeInDrag;
friend class FadeOutDrag; friend class FadeOutDrag;

View File

@ -3807,15 +3807,9 @@ BBTRulerDrag::aborted (bool moved)
TempoMap::abort_update (); TempoMap::abort_update ();
} }
#warning NUTEMPO fixme no tempo twist drag for now
#if 0
TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i) TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
: Drag (e, i, Temporal::BeatTime) : Drag (e, i, Temporal::BeatTime)
, _grab_qn (0.0)
, _grab_tempo (0.0)
, _tempo (0) , _tempo (0)
, _next_tempo (0)
, _drag_valid (true) , _drag_valid (true)
, _before_state (0) , _before_state (0)
{ {
@ -3827,120 +3821,97 @@ void
TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{ {
Drag::start_grab (event, cursor); Drag::start_grab (event, cursor);
TempoMap& map (_editor->session()->tempo_map());
/* get current state */
_before_state = &map.get_state();
_tempo = const_cast<TempoSection*> (&map.tempo_section_at_sample (raw_grab_sample()));
if (_tempo->locked_to_meter()) {
_drag_valid = false;
return;
}
_next_tempo = map.next_tempo_section (_tempo);
if (_next_tempo) {
if (!map.next_tempo_section (_next_tempo)) {
_drag_valid = false;
finished (event, false);
return;
}
_editor->tempo_curve_selected (_tempo, true);
_editor->tempo_curve_selected (_next_tempo, true);
ostringstream sstr;
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
show_verbose_cursor_text (sstr.str());
} else {
_drag_valid = false;
}
_grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
} }
void void
TempoTwistDrag::setup_pointer_offset () TempoTwistDrag::setup_pointer_offset ()
{ {
TempoMap& map (_editor->session()->tempo_map()); // _pointer_offset = timepos_t (_grab_qn).distance (raw_grab_time());
const double beat_at_sample = max (0.0, map.beat_at_sample (raw_grab_sample()));
const uint32_t divisions = _editor->get_grid_beat_divisions (0);
double beat = 0.0;
if (divisions > 0) {
beat = floor (beat_at_sample) + (floor (((beat_at_sample - floor (beat_at_sample)) * divisions)) / divisions);
} else {
/* while it makes some sense for the user to determine the division to 'grab',
grabbing a bar often leads to confusing results wrt the actual tempo section being altered
and the result over steep tempo curves. Use sixteenths.
*/
beat = floor (beat_at_sample) + (floor (((beat_at_sample - floor (beat_at_sample)) * 4)) / 4);
}
_grab_qn = map.quarters_at_beat (beat);
_pointer_offset = raw_grab_sample() - map.sample_at_quarter_note (_grab_qn);
} }
void void
TempoTwistDrag::motion (GdkEvent* event, bool first_move) TempoTwistDrag::motion (GdkEvent* event, bool first_move)
{ {
if (!_drag_valid) {
if (!_next_tempo || !_drag_valid) {
return; return;
} }
TempoMap& map (_editor->session()->tempo_map());
if (first_move) { if (first_move) {
_editor->begin_reversible_command (_("twist tempo")); map = _editor->begin_tempo_map_edit ();
/* Get the tempo point that starts this section */
_tempo = const_cast<TempoPoint*> (&map->tempo_at (raw_grab_time()));
assert (_tempo);
if (!(_next_tempo = const_cast<TempoPoint*> (map->next_tempo (*_tempo)))) {
_drag_valid = false;
return;
}
_grab_qn = _tempo->beats();
if (_tempo->locked_to_meter() || _next_tempo->locked_to_meter()) {
_drag_valid = false;
return;
}
} }
samplepos_t pf; /* get current state */
_before_state = &map->get_state();
_editor->tempo_curve_selected (_tempo, true);
if (_next_tempo) {
_editor->tempo_curve_selected (_next_tempo, true);
}
Beats mouse_beats;
if (_editor->grid_musical()) { if (_editor->grid_musical()) {
pf = adjusted_current_sample (event, false); mouse_beats = adjusted_current_time (event, false).beats();
} else { } else {
pf = adjusted_current_sample (event); mouse_beats = adjusted_current_time (event).beats();
} }
/* adjust this and the next tempi to match pointer sample */
double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0)); map->twist_tempi (_tempo, timepos_t (_tempo->beats()), timepos_t (mouse_beats));
_editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.sample_at_quarter_note (_grab_qn), pf);
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";
sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n"; sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute(); if (_next_tempo) {
sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
}
show_verbose_cursor_text (sstr.str()); show_verbose_cursor_text (sstr.str());
_editor->mid_tempo_change ();
} }
void void
TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred) TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
{ {
if (!movement_occurred || !_drag_valid) { if (!movement_occurred || !_drag_valid) {
aborted (false);
return; return;
} }
_editor->tempo_curve_selected (_tempo, false); _editor->tempo_curve_selected (_tempo, false);
_editor->tempo_curve_selected (_next_tempo, false); if (_next_tempo) {
_editor->tempo_curve_selected (_next_tempo, false);
}
TempoMap& map (_editor->session()->tempo_map()); _editor->begin_reversible_command (_("twist tempo"));
XMLNode &after = map.get_state(); XMLNode &after = map->get_state();
_editor->session()->add_command (new Temporal::TempoCommand (_("twist tempo"), _before_state, &after)); _editor->session()->add_command (new Temporal::TempoCommand (_("twist tempo"), _before_state, &after));
_editor->commit_reversible_command (); _editor->commit_reversible_command ();
TempoMap::update (map);
} }
void void
TempoTwistDrag::aborted (bool moved) TempoTwistDrag::aborted (bool moved)
{ {
if (moved) { _editor->abort_tempo_map_edit ();
_editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
}
} }
#endif
TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i) TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
: Drag (e, i, Temporal::BeatTime) : Drag (e, i, Temporal::BeatTime)

View File

@ -923,8 +923,6 @@ private:
bool _drag_valid; bool _drag_valid;
}; };
#warning NUTEMPO may or may not need this in the new world
#if 0
/** tempo curve twist drag */ /** tempo curve twist drag */
class TempoTwistDrag : public Drag class TempoTwistDrag : public Drag
{ {
@ -948,13 +946,13 @@ public:
private: private:
Temporal::Beats _grab_qn; Temporal::Beats _grab_qn;
Temporal::Tempo _grab_tempo;
Temporal::TempoPoint* _tempo; Temporal::TempoPoint* _tempo;
Temporal::TempoPoint* _next_tempo; Temporal::TempoPoint* _grab_tempo;
Temporal::TempoPoint const * _next_tempo;
Temporal::TempoMap::WritableSharedPtr map;
bool _drag_valid; bool _drag_valid;
XMLNode* _before_state; XMLNode* _before_state;
}; };
#endif
/** tempo curve twist drag */ /** tempo curve twist drag */
class TempoEndDrag : public Drag class TempoEndDrag : public Drag

View File

@ -829,7 +829,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
_drags->set (new CursorDrag (this, *_playhead_cursor, false), event); _drags->set (new CursorDrag (this, *_playhead_cursor, false), event);
} else if (ArdourKeyboard::indicates_constraint (event->button.state) } else if (ArdourKeyboard::indicates_constraint (event->button.state)
&& Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { && Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
// _drags->set (new TempoTwistDrag (this, item), event); _drags->set (new TempoTwistDrag (this, item), event);
} else if (ArdourKeyboard::indicates_constraint (event->button.state)) { } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
_drags->set (new BBTRulerDrag (this, item), event); _drags->set (new BBTRulerDrag (this, item), event);
} }

View File

@ -3026,201 +3026,54 @@ TempoMap::set_ramped (TempoPoint & tp, bool yn)
reset_starting_at (tp.sclock() + 1); reset_starting_at (tp.sclock() + 1);
} }
#if 0 void
bool TempoMap::twist_tempi (TempoPoint* ts, timepos_t const & start, timepos_t const & end)
TempoMap::twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
{ {
TempoSection* next_t = 0; assert (start.time_domain() == end.time_domain());
TempoSection* next_to_next_t = 0;
Metrics future_map;
bool can_solve = false;
/* minimum allowed measurement distance in frames */ TempoPoint* nxt = const_cast<TempoPoint*> (next_tempo (*ts));
framepos_t const min_dframe = 2; assert (nxt);
if (!ts) { superclock_t end_scpqn;
return false;
}
TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts); if (ts->clamped()) {
TempoSection* prev_to_prev_t = 0; end_scpqn = ts->end_superclocks_per_quarter_note();
const frameoffset_t fr_off = end_frame - frame;
if (!tempo_copy) {
return false;
}
if (tempo_copy->pulse() > 0.0) {
prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
}
for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
next_t = static_cast<TempoSection*> (*i);
break;
}
}
if (!next_t) {
return false;
}
for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
next_to_next_t = static_cast<TempoSection*> (*i);
break;
}
}
if (!next_to_next_t) {
return false;
}
double prev_contribution = 0.0;
if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
}
const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
framepos_t old_tc_minute = tempo_copy->minute();
double old_next_minute = next_t->minute();
double old_next_to_next_minute = next_to_next_t->minute();
double new_bpm;
double new_next_bpm;
double new_copy_end_bpm;
if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
/ (double) (end_frame - tempo_copy->frame()));
} else { } else {
new_bpm = tempo_copy->note_types_per_minute(); end_scpqn = nxt->superclocks_per_quarter_note ();
} }
/* don't clamp and proceed here. if (!ts->ramped()) {
testing has revealed that this can go negative, ((Rampable*) ts)->set_ramped (true);
which is an entirely different thing to just being too low.
*/
if (new_bpm < 0.5) {
return false;
} }
new_bpm = min (new_bpm, (double) 1000.0); if (start.time_domain() == Temporal::AudioTime) {
ts->compute_omega_from_audio_duration (end.samples() - start.samples(), end_scpqn);
tempo_copy->set_note_types_per_minute (new_bpm);
if (tempo_copy->type() == TempoSection::Constant) {
tempo_copy->set_end_note_types_per_minute (new_bpm);
}
recompute_tempi (future_map);
if (check_solved (future_map)) {
if (!next_t) {
return false;
}
ts->set_note_types_per_minute (new_bpm);
if (ts->type() == TempoSection::Constant) {
ts->set_end_note_types_per_minute (new_bpm);
}
recompute_map (_metrics);
can_solve = true;
}
if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
/ (double) ((old_next_to_next_minute) - old_next_minute));
} else {
new_next_bpm = next_t->note_types_per_minute();
}
next_t->set_note_types_per_minute (new_next_bpm);
recompute_tempi (future_map);
if (check_solved (future_map)) {
for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
next_t = static_cast<TempoSection*> (*i);
break;
}
}
if (!next_t) {
return false;
}
next_t->set_note_types_per_minute (new_next_bpm);
recompute_map (_metrics);
can_solve = true;
}
} else { } else {
double next_frame_ratio = 1.0; ts->compute_omega_from_quarter_duration (end.beats() - start.beats(), end_scpqn);
double copy_frame_ratio = 1.0; }
if (next_to_next_t) { return;
next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute)); nxt->set (samples_to_superclock (end.samples(), TEMPORAL_SAMPLE_RATE), nxt->beats(), nxt->bbt());
}
new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio; if (nxt->ramped()) {
new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio; TempoPoint const * nxtnxt = next_tempo (*nxt);
assert (nxtnxt);
tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm); if (ts->clamped()) {
end_scpqn = nxt->end_superclocks_per_quarter_note();
if (next_t->clamped()) {
next_t->set_note_types_per_minute (new_copy_end_bpm);
} else { } else {
next_t->set_note_types_per_minute (new_next_bpm); end_scpqn = nxtnxt->superclocks_per_quarter_note ();
} }
recompute_tempi (future_map); if (start.time_domain() == Temporal::AudioTime) {
nxt->compute_omega_from_audio_duration (superclock_to_samples (nxtnxt->sclock(), TEMPORAL_SAMPLE_RATE) - end.samples(), end_scpqn);
if (check_solved (future_map)) { } else {
for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { nxt->compute_omega_from_quarter_duration (nxtnxt->beats() - end.beats(), end_scpqn);
if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
next_t = static_cast<TempoSection*> (*i);
break;
}
}
if (!next_t) {
return false;
}
if (next_t->clamped()) {
next_t->set_note_types_per_minute (new_copy_end_bpm);
} else {
next_t->set_note_types_per_minute (new_next_bpm);
}
ts->set_end_note_types_per_minute (new_copy_end_bpm);
recompute_map (_metrics);
can_solve = true;
} }
} }
Metrics::const_iterator d = future_map.begin();
while (d != future_map.end()) {
delete (*d);
++d;
}
MetricPositionChanged (PropertyChange ()); // Emit Signal
return can_solve;
} }
#endif
void void
TempoMap::init () TempoMap::init ()
{ {

View File

@ -733,6 +733,8 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
LIBTEMPORAL_API void set_time_domain (TimeDomain td); LIBTEMPORAL_API void set_time_domain (TimeDomain td);
LIBTEMPORAL_API int set_state (XMLNode const&, int version); LIBTEMPORAL_API int set_state (XMLNode const&, int version);
void twist_tempi (TempoPoint* ts, timepos_t const & start, timepos_t const & end);
/* END OF MODIFYING METHODS */ /* END OF MODIFYING METHODS */
LIBTEMPORAL_API TimeDomain time_domain() const { return _time_domain; } LIBTEMPORAL_API TimeDomain time_domain() const { return _time_domain; }